phip1611
phip1611

Reputation: 6140

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?

Upvotes: 1

Views: 460

Answers (1)

phip1611
phip1611

Reputation: 6140

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.

Upvotes: 3

Related Questions