0

I need to perform a x86 far jmp without using any 64 bit register in 64 bit mode.

I am using NASM and linux inline ASM. So far, when trying to do a jmp 64bit value, the assembler complains. I can't clobber any 64 bit registers before the jump.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
suncowiam
  • 1
  • 1
  • You could push two 32-bit immediates then `ret`. – ElderBug Dec 22 '22 at 03:31
  • @ElderBug: That's guaranteed to mispredict, and will cause future mispredicts on other `ret`s, so `push` / `mov dword [rsp+4], imm32` / `ret` is not ideal. Using space in the red-zone below RSP for a `jmp qword [rsp-8]` would work, assuming x86-64 System V; space below RSP isn't safe to use on Windows x64, but there is shadow space that you can use equivalently. Or there's the trick of using RIP-relative addressing and putting the target address in the next 8 bytes after the `jmp`, although it's usually better to put the pointer in with other data. – Peter Cordes Dec 22 '22 at 03:33
  • 2
    When you say a jmp far, do you actually mean `far` as in setting a new CS as well as RIP? Or do you just mean to a new CS value that's farther than +-2 GiB away? There are no encodings of `jmp far` that take a new CS:RIP from a register anyway, only memory. https://www.felixcloutier.com/x86/jmp . Do you want to do something equivalent to `mov r11, 0x123456789a` / `jmp r11`, or do you want something like `retf` or `jmp far [rsp-10]` with a 10-byte m16:64 memory operand? – Peter Cordes Dec 22 '22 at 03:36
  • @PeterCordes True the push/ret is probably the "worse" solution, but it would be the easiest and most portable. Be it on Windows or linux, locating the shadow space could be tricky with inline assembly. You also have no guarantee the compiler didn't use it. Same for the red zone. Otherwise I totally agree this is probably optimal in perf. – ElderBug Dec 22 '22 at 03:43
  • @ElderBug: NASM is a stand-alone assembler for `.asm` source files, you can't use it in *inline* `asm` statements inside C functions. Oh, I just read the last paragraph of the question and noticed it mentions "inline" asm as well as NASM. That's weird, I guess they want this code for two different assemblers? Or they're confused about what syntax GNU C inline asm uses? Its `-masm=intel` syntax is not NASM, it's GAS `.intel_syntax noprefix` which is somewhat MASM-like. – Peter Cordes Dec 22 '22 at 03:46
  • But yes, GNU C inline asm statements have to be described to the compiler, and there's no way to say it doesn't use the red zone. OTOH, if you jump away, you're presumably not coming back, so could just ask for an `"m"` operand with the target pointer if you need to set all 15 other registers to different values. An operand like `[target] "r,m"(address)` operand could work well for `jmp *%[target]`, letting the compiler use a register if there's one free, otherwise memory. Until/unless the question is edited with details of what they're actually doing, it should stay closed as a duplicate. – Peter Cordes Dec 22 '22 at 03:50
  • @PeterCordes No idea, I agree the question isn't very clear. I wonder if you can output data in inline assembly for a RIP-relative jmp, I never tried. – ElderBug Dec 22 '22 at 03:50
  • 1
    @ElderBug: Yes, `.quad` works inside an asm statement. But more normally you'd let the compiler put a constant in `.rodata` and invent an addressing mode for you, like `static const uint64_t addr = 0xdeadbeef11;` (or `void*` if you want) ; `asm("jmp *%0" :: "r,m"(addr));` ; `__builtin_unreachable(); // let the compiler know execution doesn't come out the other side of the asm()`. https://godbolt.org/z/xqcqPPvnj shows that, including without the `"r"` option so it has to pick memory. Directives are filtered, there's a `.section .rodata` before the constant. – Peter Cordes Dec 22 '22 at 03:57
  • Yes, I am using multiple assemblers. @PeterCordes, I need something equivalent to mov rax, (64bit addr), jmp [rax]. Also, can you give me an example of: "Or there's the trick of using RIP-relative addressing and putting the target address in the next 8 bytes after the jmp, although it's usually better to put the pointer in with other data."? Thanks – suncowiam Dec 23 '22 at 16:08
  • I did, that's Jester's answer on the first duplicate, [How to jump to memory location in intel syntax for both x64 and x32](https://stackoverflow.com/q/72179508). See the other duplicates for other ways. If you have a question about inline asm where my last comment would be an answer, ask it separately because that's a different context; you have a compiler that can put stuff in .rodata for you without using `.pushsection .rodata` / `1: .quad ...` / `.popsection` – Peter Cordes Dec 23 '22 at 20:55

0 Answers0