You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 14:05:10