cassemblyglobal-variablesx86-64nasm

How do I call a global C function pointer in NASM without warnings?


Given main.c:

#include <stdio.h>

void (*fn_ptr)(void);

void foo(void);

void bar(void) {
    printf("bar\n");
}

int main(void) {
    fn_ptr = bar;
    foo();
}

and foo.s:

extern fn_ptr

global foo
foo:
    mov rax, fn_ptr
    call [rax]
    ret

we can run and compile it like so:

clear &&
nasm foo.s -f elf64 -O0 &&
clang main.c foo.o -z noexecstack &&
./a.out

which successfully prints bar, but also prints a warning:

/usr/bin/ld: foo.o: warning: relocation against `fn_ptr' in read-only section `.text'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
bar

I couldn't find any online examples of calling global function pointers in NASM, and changing it to mov rax, fn_ptr wrt ..plt or call [fn_ptr wrt ..plt] prints this error:

foo.s:7: error: ELF format cannot produce non-PC-relative PLT references

(For anyone wondering what warning the -z noexecstack gets rid of):

/usr/bin/ld: warning: foo.o: missing .note.GNU-stack section implies executable stack
/usr/bin/ld: NOTE: This behaviour is deprecated and will be removed in a future version of the linker

Solution

  • From comments; I can now type answer.

    The following assembly instructions don't work:

        mov rax, fn_ptr wrt ..plt
        call fn_ptr wrt ..plt
        call [fn_ptr wrt ..plt]
    

    But these do:

        mov rax, [rel fn_ptr wrt ..plt]
        call [rel fn_ptr wrt ..plt]
    

    The ELF format error is true. I'm surprised it can't handle mov rax, fn_ptr wrt ..plt but call [fn_ptr wrt ..plt] definitely cannot work. There's no such instruction as call [qword] but only call rel dword and call [rel dword].

    The mov instruction that surprises me that it doesn't work wouldn't get you out of the problem anyway; that's asking for an absolute fixup in the text segment to refer to the plt by absolute address. So if it did work that's still a warning.

    What you want to access functions elsewhere is always something with rel so you get a PIC-relative reference to the fixup area. Basic forms:

       lea rax, [rel fn_ptr wrt ..plt]  ; gets the absolute address of the address of fn_ptr
       mov rax, [rel fn_ptr wrt ..plt]  ; gets the absolute address of fn_ptr
       call [rel fn_ptr wrt ..plt]      ; calls fn_ptr