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
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