Search code examples
assemblyx86clangx86-64objdump

Assemble far calls or far jumps (j* instructions)


I'm trying to create a dispatch table which changes the location of some instruction in another address which is allocated by AllocateMemoryOnRemoteProcess.

One of the problems that I encountered was almost all of Calls and all kind of Jumps are near and relative and as long as I load the assemblies in new location, then these instructions won't work.

As I know I should convert these instructions to far jump or far call one of the solutions that I saw during my googling was using push and ret like :

push 0xdeadbeef
ret

or someone suggests using registers for absolute addressing like :

mov %eax,0xdeadbeef
jmp %eax

These solutions won't work in my case because as long as I'm in a function routine, changing the stack state or in the second case changing a register like %eax causes failure.

Someone in this question wrote :

  • call far (with opcode 9A) jumps to an absolute segment and offset. ie, it's like setting CS and ?IP at once.

So it seems I should use opcode with 9A for far calls, but this just works for the calls and I have no idea about converting all kinds of Jumps with this method!

I regularly use objdump to disassemble a binary, then use clang as the assembler by using the following command :

clang -c MyAsm.asm -m32

But when I assemble with the above command then the result is relative.

For example when MyAsm.asm is :

call   0x402af2

The result of objdump is :

    MyAsm.o:    file format Mach-O 32-bit i386

Disassembly of section __TEXT,__text:
__text:
       0:   e8 ed 2a 40 00  calll   4205293 <__text+0x402AF2>

These results are relative.

So my questions are :

  1. How can I assemble far calls or far jumps (j* instructions) with clang or any other tools (which of course, work for both 80x86 and Amd64 structures)?
  2. Is there any other instruction like calls or jumps that use relative addressing, so I should reassemble in order to avoid the problem?

Solution

  • If you can spare a register, I advise you to use

        movabs $addr,%rax
        jmp *%rax
    

    or, if you can ensure that the address is within the first 2 GB of address space,

        mov $addr,%eax
        jmp *%eax
    

    I strongly advise you against using

        push $addr
        ret
    

    as this trashes the return prediction, making the next few function returns slower than necessary. Far jumps and calls (ljmp and lcall) are a red herring. While they could technically be used, they won't help you achieve your goal and are actually meant for a different purpose (changing cs) and are implemented as slow, micro-coded instructions on modern processors.

    If you cannot spare a register, you can use this sort of trick instead:

        jmp *0f(%rip)
        ...
    0:  .quad addr
    

    The second line can be anywhere in the program and should be in the data segment for ideal performance. However, if needed, it can also be right after the jump instruction.

    This should just work and in addition doesn't require you to use an extra register. It is slower than using a register though.

    Note that conditional jumps strictly require the jump target to be immediate. If you want to do a conditional jump to an absolute address, use an idiom like this:

        # for jz addr
        jnz 1f
        jmp *0f(%rip)
    0:  .quad addr
    1:  ...
    

    Special considerations for 16 and 32 bit mode

    Note that in 16 and 32 bit mode, there is no rip-relative addressing mode. So you'll have to use an absolute address and write

        jmp *0f
    0:  .long addr
    

    instead. However, that kind of defeats the purpose as if you could use an absolute addressing mode to reach 0f, you could also just use a relative addressing mode to reach addr. So it seems like you'll have to resort to a push + ret sequence, even if it is slow.

    In 16 bit modes, most likely using a far jump is fine. If not, the push + ret sequence is idiomatic (processors of that vintage did not have return prediction).