Forcing a JMP rel32 (JMPQ)

245 Views Asked by At

If I do something like (dummy example):

jmp 1f
1: ret

on gcc/clang it generates a short relative jump because the label is near.

I'm curious, is it possible to force a JMP rel32 regardless of the label's distance?

Michael Petch's suggestion in the comments to hardcode jmpq via its bytecode 0xe9 unfortunately only works with local labels, but I would like something that works with global linker-resolved labels too.

Since my real goal is to paste a constant-length jump instruction via a macro, I can also make do with stuff like jmp 1: /*definitely short*/; 1: jmp /*short or long*/ actual_target_with_unclear_distance;, relying on the first jump being definitely short, but using just one definitely-long jump would be shorter and cleaner if there's any way to achieve it.

1

There are 1 best solutions below

3
On

According to the GAS manual, section 9.16.8 "Jump instructions are always optimized to use the smallest possible displacements". This seems to imply there is no manual way to override it. There is an addr32 instruction prefix in 9.16.6 but it is only allowed in .code16. I cannot seem to find an option that would control the size of jmp offset in any "official" source.

However, according to this source marking the label you jump to as global will make the jmp instruction use a rel32 offset. I only managed to reproduce the behavior using clang though, GCC does not seem to work. Additionally, i cannot seem to find any more credible source for this behavior than the mentioned 15-year-old archived mailing list reply so i would not exactly call it "reliable". I assume it might go away unnoticed with some future update to clang/llvm-as.

For an example, following file test_asm.s:

.global main
main:
jmp lab
.global lab
lab: ret

compiled with clang test_asm.s on my machine results in:

000000000000111c <main>:
    111c:   e9 00 00 00 00          jmp    1121 <lab>

0000000000001121 <lab>:
    1121:   c3                      ret

Meanwhile, after removing the .global lab line the result is:

000000000000111c <main>:
    111c:   eb 00                   jmp    111e <lab>

000000000000111e <lab>:
    111e:   c3                      ret

For a reliable, although a tedious solution, you can always manually encode the jmp instruction to bytes and then input them using the .byte directive in place of the jmp <operand> mnemonic, as pointed out in the comments.