Uprobe无法获取fopen参数值的问题排查求助
1. GCC编译优化导致参数传递方式变化
x86-64系统的调用约定中,函数前6个参数默认通过寄存器(rdi、rsi、rdx等)传递,而非栈。当你用char*指向字符串常量,或者直接传字符串字面量调用fopen时,GCC在默认优化级别(-O0以上)会将字符串常量放入只读数据段,并且可能将fopen调用内联,或者直接把常量字符串的地址通过寄存器传递。如果你的bpftrace脚本依赖栈上读取参数(比如误用了栈偏移而非寄存器),就会读取到错误的值;而char[]是栈上分配的数组,参数传递时寄存器指向栈地址,此时脚本能正确读取到栈上的字符串内容。
另外,第一次调用fopen时,编译器可能会做常量折叠或提前解析,导致参数的存储位置和后续调用不一致,这也是只出现第一个调用失败的原因。
2. PLT延迟绑定的影响
Linux动态链接的PLT(过程链接表)机制中,第一次调用动态库函数(比如fopen)时,会触发延迟绑定逻辑,此时PLT的跳转指令和后续已绑定后的指令不同。如果你的uprobe挂载在进程的PLT入口,第一次调用时的参数上下文可能被绑定逻辑干扰,导致无法正确读取参数;而后续调用时PLT已经完成绑定,参数读取恢复正常。
关闭编译验证优化影响
用gcc -O0 test.c编译测试程序,关闭所有优化选项,再运行追踪脚本。如果此时所有fopen调用都能正确获取参数,说明问题确实由编译优化导致。查看汇编代码对比参数传递
生成测试程序的汇编代码:gcc -S test.c,打开生成的.s文件,对比三种声明方式下fopen调用的参数传递逻辑:- 观察
char[]、char*、直接传字符串时,rdi寄存器(对应fopen第一个参数)的值来源,以及是否有栈操作差异。
- 观察
调整bpftrace脚本的参数读取方式
在x86-64架构下,明确通过寄存器读取fopen的第一个参数,替代默认的arg0(部分场景下arg0可能因优化出现偏移错误):uprobe:/lib/x86_64-linux-gnu/libc.so.6:fopen { printf("file: %s\n", str(reg("rdi"))); }直接挂载到libc的
fopen函数而非进程PLT,避免延迟绑定的干扰。验证BCC脚本的参数读取逻辑
如果用BCC编写脚本,确保在BPF.UPROBE中正确指定参数位置,或者通过寄存器读取参数,而非依赖栈偏移。
内容的提问来源于stack exchange,提问作者sony

