Search code examples
assemblyx86-64nasmposition-independent-code

nasm: get offset of relocated binary during runtime from rip (instruction pointer)


I have a multiboot2-compliant ELF file for x86_64, where the start-symbol is defined in start.asm, a NASM assembly file. The multiboot2 header contains the relocatable tag.

Because GRUB doesn't support multiboot2 + a relocatable ELF (at least in July 2021 [3]), I want to resolve some relocations by myself to work around this and just load a static ELF.

For this I need to get the offset during runtime in my very first entry-symbol (specified in ELF header) in order to resolve relocations manually. With offset I mean the difference where GRUB located the binary in memory compared to the static address of the symbol in the ELF file.

In my entry symbol I'm in 64-bit long mode. It is not possible to directly access rip in NASM syntax, therefore I need some kind of workaround.

Solutions like [1] [2] do not work, because the rip keyword/register is not usable in NASM. Therefore I can't use

lea    rax,[rip+0x1020304]
; rax contains offset
sub    rax,0x1020304

How can I solve this?


Solution

  • The only way to access rip in nasm is through the rel-keyword [1]. Without an odd workaround, it can't take an immediate but only a symbol. To solve it with a symbol, the following code works:

    ; the function we want to jump to. We need to calculate the runtime
    ; address manually, because the ELF file is not relocatable, therefore static.
    EXTERN entry_64_bit
    
    ; start symbol must be globally available (linker must find it, don't discard it)
    ; Referenced in ELF-Header.
    GLOBAL start
    
    SECTION .text
    
    ; always produce x-bit x86 code (even if this would be compiled to an ELF-32 file)
    [BITS 64]
    
        ; very first entry point; this is the address where GRUB loads the binary
        start:
            ; save values provided by multiboot2 bootloader (removed here)
            ; ...
    
            ; Set stack top (removed here)
            ; ...
    
            ; rbx: static link address
            mov     rbx, .eff_addr_magic_end
    
            ; rax: runtime address (relative to instruction pointer)
            lea     rax, [rel + .eff_addr_magic_end]
        
        .eff_addr_magic_end:
            ; subtract address difference => offset
            sub     rax, rbx
            ; rax: address of Rust entry point (static link address + runtime offset)
            add     rax, entry_64_bit
            jmp     rax
    

    Note that this is really tricky and needs deep expertise about several low level topics. Use with caution.