C语言函数指针指向jmp指令而非函数实际起始地址的技术问询
为什么取函数地址得到的是jmp指令位置而非真实入口?
这个现象其实是动态链接机制里的**PLT(Procedure Linkage Table,过程链接表)**在起作用,很常见,我来给你拆解一下:
首先你的观察完全正确:当你用
printf("%p", &func2);打印函数地址时,输出的确实是PLT段里的jmp指令地址,而不是函数实际栈帧开始的push ebp(或者x64下的push rbp)那一行的地址。背后的原因是动态链接的延迟绑定优化:
现代操作系统默认会把程序编译为动态链接的形式,哪怕是你自己写的本地函数,只要编译器开启了位置无关代码(PIC)选项(比如-fPIC,很多系统默认启用),链接器就会为函数生成一个PLT桩。这个桩的核心就是一条jmp指令,指向GOT(Global Offset Table,全局偏移表)中的一个条目——而GOT里的内容会在程序第一次调用该函数时,由动态链接器(比如ld-linux.so)填充为函数的真实内存地址。举个反汇编的实际例子更清楚:
假设你的空函数func2的真实入口反汇编是这样的:0x0000000000400550 <func2>: push rbp mov rbp,rsp nop # 空函数的占位指令 pop rbp ret但你通过
&func2拿到的地址对应的是PLT里的条目:0x0000000000400430 <func2@plt>: jmp QWORD PTR [rip+0x200c0a] # 跳转到GOT里的func2条目 push 0x0 jmp 0x400420 # 动态链接器的解析入口这时候
printf输出的就是0x400430,也就是那条jmp指令的位置。如果想获取函数的真实入口地址,你可以:
- 用
objdump -d your_program查看反汇编,找不带@plt后缀的符号地址; - 用
nm -D your_program查看符号表,找到函数的真实地址; - 在程序里通过
dladdr函数(需要链接-ldl库)来解析,它能返回函数的实际起始地址。
- 用
内容的提问来源于stack exchange,提问作者Bar Fins
相关产品推荐
相关产品推荐

