Почему jmpq x86-64 нужен только 32-разрядный адрес?
Поскольку я использую objdump -D
для дизассемблирования двоичного файла, типичный код jmpq
похож на e9 7f fe ff ff
, который используется для представления отрицательного смещения. Однако адрес x86-64 равен 64 (48) -битам (насколько мне известно), поэтому как этот 32-разрядный адрес 7f fe ff ff
представляет отрицательное смещение 64-битного абсолютного адреса?
Кроме того, существуют ли какие-либо другие команды, такие как jmp
и jmpq
, но имеющие 64-разрядное смещение адреса? Как найти инструкции в руководстве Intel или AMD (я искал jmpq
, но ничего не нашел)?
Когда я искал, он, как представляется, называется RIP-относительной адресацией. И кажется, что не все инструкции делают это. Есть ли 64-битная относительная адресация? Если это косвенный скачок, 64-разрядный абсолютный адрес будет в регистре или памяти, верно?
Ответы
Ответ 1
Как отмечали другие, команда "jmp relative" для x86-64 ограничена 32-разрядным смещением, используемым как относительное смещение относительно счетчика программ.
OP спросил, почему нет относительного перехода с 64-битным смещением. Я не могу говорить о дизайнерах Intel, но, похоже, довольно ясно, что эта инструкция просто не будет очень полезна, особенно при наличии 32-битного относительного jmp. Единственный раз, когда это потребуется, - это когда ваша программа была размером 2 гигабайта, так что 32-разрядный относительный jmp не мог достичь всего ее из любой точки внутри него. Недавно видели какие-либо объектные файлы 2Gb? Таким образом, кажущаяся полезность для таких инструкций кажется очень маленькой.
В основном, когда программы становятся действительно большими, они начинают разбиваться на более управляемые элементы, которые могут развиваться с разной скоростью. (DLL - пример этого). Взаимодействие между такими элементами выполняется более тайными средствами (векторами перехода и т.д.), Чтобы гарантировать постоянство интерфейсов перед лицом эволюции. Крайне длинный JMP-родственник может использоваться для перехода от приложения к точке входа в другом модуле, но фактическая стоимость загрузки абсолютного адреса в регистр и выполнения косвенно-косвенного вызова на практике достаточно мала, что это нет Оптимизация стоит. И современный дизайн ЦП - это оптимизация, когда вы ставите свои транзисторы для максимальной производительности.
Чтобы быть полным, x86 (многие варианты) имеют очень короткие относительные команды jmp (8-разрядное смещение). На практике даже 32-разрядные относительные команды jmp редко нужны, особенно если у вас есть хороший генератор кода, который может переупорядочить блоки кода. Возможно, Intel могла оставить их по той же причине; Я подозреваю, что их полезность немного выше, чтобы оправдать транзисторы.
Вопрос о "больших литералов-операндах" проявляется смешными способами во многих архитектурах. Если вы изучите распределение буквенных значений в коде, вы обнаружите, что небольшие значения (0,1, коды символов ascii) покрывают довольно хороший процент; почти все остальное - адреса памяти. Поэтому вам не нужны "большие литературные значения" в программах, но вам как-то приходится обращаться с адресами памяти. Чип Sparc лихо имеет значение "load literal value low in register" (что означает "небольшие константы" ) и реже используется "значение литра нагрузки" (для заполнения верхних бит в регистре), используемое в качестве второй инструкции для создания больших констант, и используется реже. Это уменьшает код, за исключением случаев, когда требуется большая константа; малый код означает более высокую эффективную скорость выборки команд и способствует повышению производительности.
Ответ 2
Код операции E9 в режиме 64 бит принимает знак смещения знака 32 бит, расширенный до 64 бит:
E9 cd → JMP rel32 → Перейти рядом, относительный, RIP = RIP + 32-бит знак смещения, расширенный до 64 бит
Код операции FF можно использовать для перехода на 64-разрядный адрес:
FF/4 → JMP r/m64 → Перейти рядом, абсолютное косвенное, RIP = 64-битное смещение от регистра или памяти
Цитаты, взятые из руководства Инструкция по установке инструкций Intel для JMP.
Ответ 3
В 64-битном режиме применяется следующее.
JMP может выполняться прямо или косвенно.
Прямые прыжки относятся к указателю инструкции RIP
. Существует два типа прямых прыжков: короткие и близкие.
- Короткие прыжки используют Opcode
EB
, за которым следует 8-битное смещение, и поэтому RIP –128 to +127
bytes.
- Рядом с прыжками используется Opcode
E9
, за которым следует 32-разрядное смещение, и поэтому RIP -2147483648 to +2147483647
.
Ваш ассемблер будет использовать короткие прыжки, когда это возможно, поскольку они нуждаются только в двух байтах. Но в NASM вы можете заставить близкий прыжок использовать ключевое слово near
, например.
test:
jmp test ; eb fb
jmp near test ; e9 f6 ff ff ff
Режимы 64-разрядной адресации: RIP-относительные, 32-битные абсолютные, 64-битные абсолютные и относительно базового указателя. Инструкция JMP
может использовать все, кроме 64-битного абсолютного. Косвенные прыжки используют Opcode FF
. Некоторые примеры с использованием синтаксиса NASM:
jmp [a] ;ff 24 25 00 00 00 00 - 32-bit absolute
jmp [rel a] ;ff 25 e7 ff ff ff - RIP + 32-bit displacement
jmp [rdi] ;ff 27 - base pointer
jmp [rdi +4*rsi + a] ;ff a4 b7 00 00 00 00 - base pointer +4*index + displacement
В OSX, однако, 32-битная абсолютная адресация невозможна, потому что база изображения больше 2 ^ 32.
Единственной инструкцией, которая может использовать 64-разрядную абсолютную адресацию, является mov
, а затем источник или получатель должен быть AL, AX, EAX or RAX
. Например, в NASM
mov rax, [qword a]