assemblyx86-64inline-assemblyattgnu-assembler

Why do I get a strangely long opcode when attempting a RIP-relative direct jump with `*...(%rip)` in AT&T assembly?


The thing is that I cannot force gnu as interpret jmp as short or near, it constantly interprets it as far.

For instance, the following code causes segfault:

int main() {
  asm volatile (
//      ".intel_syntax noprefix\n\t"
//      "jmp lbl%=\n\t"
//      "lbl%=:\n\t"
      "jmp *lbl%=(%%rip)\n\t"
      "lbl%=:\n\t"
//      ".att_syntax\n\t"
      : : : );
  return 0;
}

switching to the commented .intel_syntax variant, it works perfectly well.

The only difference is that

jmp lbl%= in intel becomes eb 00

while

jmp *lbl%=(%%rip) in att becomes ff 25 00 00 00 00

how to force it in case of .att_syntax to interpret it as short jump?


Solution

  • jmp *lbl does an indirect jump -- loading the address from memory at lbl: and jumping to that address. In this case it takes the 8 (code) bytes following this (the function epilog) and treats it as an address to jump to, thus causing a segfault.

    For a simple PC relative jump, you want just jmp lbl -- att syntax is the same as intel syntax here. There's no need for %rip anything as all direct jumps are PC relative.