-1

I want to encode a 64 bit relative jump to the address stored in %rax in x64 assembly. AFAIK, there is no opcode for this, so I calculate the corresponding absolute address for the relative address manually and then I do an absolute jump to the absolute address:

# destination address, relative to end of jmp instruction, is stored in %rax
00007ffff7ff6020: 0x0000488d1505000000   lea 0x5(%rip),%rdx # load %rip+5 (rip + size of add and jmpq) into %rdx
00007ffff7ff6027: 0x0000000000004801d0   add %rdx,%rax # calculate absolute address based on %rdx (behind jmpq) and %rax (the relative address)
00007ffff7ff602a: 0x00000000000000ffe0   jmpq *%rax # do an absolute jump to absolute address

But this looks unnecessarily complex to me. Is there a better way with less instructions? Or is there another reason why 64 bit relative jumps should be avoided?

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
Daniel S.
  • 6,458
  • 4
  • 35
  • 78

2 Answers2

2

64-bit relative jumps should be avoided because the +-2GiB code-size range for jmp rel32 direct jumps is normally plenty of range.

If your target is farther away, you normally just know the absolute address, and shouldn't have calculated a 64-bit relative displacement in the first place.

Relative displacements only make sense when the displacement is a link-time constant, in which case you (the linker) are normally placing the source and target within 2GiB of each other so you can use jmp rel32.

If you can't do that, then normally you would use (for a "huge" code model where other addresses are assumed to be > 32 bits away) movabs $imm64, %rax / jmp *%rax. This still sucks a lot, though. (And it's not position-independent, so if you wanted to randomize its load address, you'd need a load-time fixup for the 64-bit absolute target address. Executable file formats like ELF on Linux do support that, though, so you can use 64-bit absolute addresses in jump tables even in PIE executables or PIC shared library code.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
1

All relative jump instructions have the jump distance encoded in the instruction as an immediate (=integer constant) operand. Relative jumps (and calls) exist to divert execution from one known location in the code to another in the normal course of execution, e.g. to perform a conditional jump (think of the if operator in many high level programming languages), an unconditional jump (think of goto in BASIC or C/C++ or unconditional jumps hiding inside of the if or for statements) or call a subroutine. For that purpose (and it's a practically important one) it's enough.

If you need to access data relative to RIP, you can do so because in 64-bit mode there is a way to encode a memory operand as RIP-relative (there's no such encoding in 32-bit mode). This helps with generation of position-independent code and helps reduce the overhead of full 64-bit addresses in programs fitting all of their code and static data into 2GB of memory.

Other xIP-relative uses are less common. And so are computed jumps (other than return from a subroutine). Computed jumps are a common way to implement things like the switch statement, found in e.g. C/C++, Java. There are several ways to implement switch, but the most obvious one is to have an array/table of addresses of where to jump to and then just use an integer index into it to retrieve the corresponding to it address. And then you can jump using an indirect jump, e.g. jmp rax (or whatever it is in AT&T syntax). You can fetch the address from the array/table and jump in one go: jmp [table + rax*8] (adjust to your AT&T syntax).

Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180