Почему 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]