5

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?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • 3
    You could simulate it a hackish way by building the jmp rel32 manually (you could improve it by putting it in a macro): `.byte 0xe9` `.long (1f - 4) - .` `1: ret` – Michael Petch Dec 12 '22 at 14:03
  • 1
    @MichaelPetch Love the brute force of that. Thank you! – Petr Skocik Dec 12 '22 at 14:07
  • 2
    For reference, NASM allows `jmp strict near ...` – ecm Dec 12 '22 at 14:08
  • 2
    [€ASM](https://euroassembler.eu/eadoc/#InstructionModifiers) allows suffix S|N|F to mnemonic name `JMPN lf` or explicit distance modifier Short|Near|Far `JMP lf, dist=near`. – vitsoft Dec 12 '22 at 15:17

1 Answers1

5

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.

msaw328
  • 1,459
  • 10
  • 18
  • 3
    Thanks. Ended up with Michael Petch's solution: `#define MY_asm_jmpq(Target) .byte 0xe9; .long ((Target) - 4) - .` (I use C macros. I put macro-stringified asm in my C sources and use C macros inside of the the asm where appropriate). I find reliable less tedious than navigating around assembler quirks. – Petr Skocik Dec 12 '22 at 14:32
  • 2
    I had to do this with VASM for 68000 Assembly. My interrupt code was crashing because the assembler was optimizing `JSR` into `BSR` (an absolute call vs. a relative call) and it was messing up my interrupt handlers (which were being copied to RAM to avoid the overhead of switch cases) – puppydrum64 Dec 12 '22 at 15:30
  • 1
    `addr32` is *not* what you want to affect how the displacement is encoded. It would override the address-size with a `0x67` prefix byte, making it not truncate EIP to IP. Or in 64-bit mode, making it truncate RIP to EIP. – Peter Cordes Dec 12 '22 at 19:34