如何在NASM中调用全局C函数指针且避免编译警告?
在NASM中调用全局C函数指针且消除链接警告的方法
问题重现
现有代码
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(); }
foo.s:
extern fn_ptr global foo foo: mov rax, fn_ptr call [rax] ret
编译运行命令
clear && nasm foo.s -f elf64 -O0 && clang main.c foo.o -z noexecstack && ./a.out
链接警告
/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
尝试使用wrt ..plt时出现错误:
foo.s:7: error: ELF format cannot produce non-PC-relative PLT references
补充:-z noexecstack用于消除栈可执行的警告:
/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
解决方案
修改foo.s,使用PC相对寻址加载全局函数指针的地址,替换原来的绝对地址加载:
extern fn_ptr global foo foo: lea rax, [rel fn_ptr] ; 使用PC相对方式获取fn_ptr的地址 call [rax] ret
重新编译运行,链接警告会完全消除,程序依然正常输出bar。
原理说明
原代码警告原因:
原来的mov rax, fn_ptr会生成R_X86_64_64类型的绝对重定位,链接器需要在只读的.text段中修改指令的地址值,违反了只读段的属性,因此触发relocation against ... in read-only section警告;同时在PIE(位置无关可执行文件)中会创建DT_TEXTREL,这是不推荐的做法。PC相对寻址的优势:
lea rax, [rel fn_ptr]使用PC相对寻址,生成R_X86_64_RELATIVE类型的重定位。这种重定位不需要修改.text段内容,而是在程序加载时,根据当前指令地址计算出fn_ptr的实际地址,完全符合PIE的位置无关要求,因此不会产生任何链接警告。为什么
wrt ..plt不可行:
PLT(过程链接表)是用于延迟绑定函数调用的机制,针对的是函数符号,而fn_ptr是一个数据指针变量,并非函数符号本身;此外NASM不支持非PC相对的PLT引用,因此会报错。
内容的提问来源于stack exchange,提问作者MyNameIsTrez
相关产品推荐
相关产品推荐

