Если я сделаю что-то вроде (фиктивный пример):
jmp 1f
1: ret
на gcc/clang генерируется короткий относительный переход, потому что метка находится рядом.
Мне любопытно, можно ли принудительно поставить JMP rel32
независимо от расстояния до метки?
@MichaelPetch Люблю грубую силу этого. Спасибо!
Для справки, NASM позволяет jmp strict near ...
JMPN lf
или явному модификатору расстояния Short|Near|Far JMP lf, dist=near
.
Согласно руководству GAS, раздел 9.16.8 «Инструкции по прыжкам всегда оптимизированы для использования наименьших возможных перемещений». Кажется, это означает, что нет ручного способа переопределить его. В 9.16.6 есть префикс инструкции addr32, но он разрешен только в .code16
. Кажется, я не могу найти опцию, которая контролировала бы размер смещения jmp
в любом «официальном» источнике.
Однако, согласно этому источнику, пометка метки, на которую вы переходите, как глобальной, заставит jmp
инструкцию использовать rel32
смещение. Мне удалось воспроизвести поведение только с помощью clang, хотя GCC, похоже, не работает. Кроме того, я не могу найти более достоверный источник такого поведения, чем упомянутый 15-летний архивный ответ из списка рассылки, поэтому я бы не назвал его «надежным». Я предполагаю, что это может остаться незамеченным с каким-то будущим обновлением clang/llvm-as.
Например, следующий файл test_asm.s
:
.global main
main:
jmp lab
.global lab
lab: ret
скомпилировано с помощью clang test_asm.s
на моей машине, что приводит к:
000000000000111c <main>:
111c: e9 00 00 00 00 jmp 1121 <lab>
0000000000001121 <lab>:
1121: c3 ret
Между тем, после удаления строки .global lab
результат будет таким:
000000000000111c <main>:
111c: eb 00 jmp 111e <lab>
000000000000111e <lab>:
111e: c3 ret
Для надежного, хотя и утомительного решения, вы всегда можете вручную закодировать инструкцию jmp
в байты, а затем ввести их, используя директиву .byte
вместо мнемоники jmp <operand>
, как указано в комментариях.
Спасибо. В итоге решение Майкла Петча: #define MY_asm_jmpq(Target) .byte 0xe9; .long ((Target) - 4) - .
(Я использую макросы C. Я помещаю asm с макросами в свои исходники C и использую макросы C внутри asm, где это уместно). Я нахожу надежный менее утомительным, чем навигация по причудам ассемблера.
Пришлось это делать с ВАСМом для 68000 сборки. Мой код прерывания давал сбой, потому что ассемблер оптимизировал JSR
в BSR
(абсолютный вызов по сравнению с относительным вызовом), и это портило мои обработчики прерываний (которые копировались в ОЗУ, чтобы избежать накладных расходов на случаи переключения)
addr32
это не то, на что вы хотите повлиять, как кодируется смещение. Он переопределит размер адреса с помощью байта префикса 0x67
, чтобы он не усекал EIP в IP. Или в 64-битном режиме, усекая RIP в EIP.
Вы можете смоделировать это хакерским способом, создав jmp rel32 вручную (вы можете улучшить его, поместив в макрос):
.byte 0xe9
.long (1f - 4) - .
1: ret