我正在尝试覆盖 C 中的函数调用,但是当在同一编译单元中使用该函数时我遇到了问题。在下面的代码中,我试图替换函数 get_resolution(),但只有在 test.c 中完成而不是从 display.c 中才能实现它
// display.c -------------------------------------------------------------
#include <stdio.h>
void get_resolution()
{
printf("Original get_resolution\n");
}
void display()
{
get_resolution();
}
// test.c ----------------------------------------------------------------
#include <stdio.h>
void __wrap_get_resolution()
{
printf("Mock get_resolution\n");
// __real_get_resolution(); // Should be possible to call original
}
int main()
{
display(); // **ISSUE** Original get_resolution() is called
get_resolution(); // __wrap_get_resolution() is called
return 0;
}
// gcc -Wl,--wrap,get_resolution display.c test.c
我的要求是,当我从 main() 调用 display() 时,我希望 __wrap_get_resolution() 被执行,但我总是看到原始的 get_resolution() 正在被调用。对反汇编的一点分析表明,函数 get_resolution 的调用方式不同:
在 display() -> get_resolution() 的地址已经解析
00000000 <_get_resolution>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 18 sub $0x18,%esp
6: c7 04 24 00 00 00 00 movl $0x0,(%esp)
d: e8 00 00 00 00 call 12 <_get_resolution+0x12>
12: c9 leave
13: c3 ret
00000014 <_display>:
14: 55 push %ebp
15: 89 e5 mov %esp,%ebp
17: 83 ec 08 sub $0x8,%esp
1a: e8 e1 ff ff ff call 0 <_get_resolution>
1f: c9 leave
20: c3 ret
21: 90 nop
22: 90 nop
23: 90 nop
在 main() -> get_resolution 的地址还没有解析
00000000 <___wrap_get_resolution>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 18 sub $0x18,%esp
6: c7 04 24 00 00 00 00 movl $0x0,(%esp)
d: e8 00 00 00 00 call 12 <___wrap_get_resolution+0x12>
12: c9 leave
13: c3 ret
00000014 <_main>:
14: 55 push %ebp
15: 89 e5 mov %esp,%ebp
17: 83 e4 f0 and $0xfffffff0,%esp
1a: e8 00 00 00 00 call 1f <_main+0xb>
1f: e8 00 00 00 00 call 24 <_main+0x10>
24: e8 00 00 00 00 call 29 <_main+0x15>
29: b8 00 00 00 00 mov $0x0,%eax
2e: c9 leave
2f: c3 ret
现在的问题是,如何防止编译器解析函数 display() 中使用的 get_resolution() 的地址,而是使用重定位表,以便在链接阶段可以覆盖 get_resolution() 函数?
编辑:
- 根据 hroptatyr 的回复,添加
void get_resolution() __attribute__((weak));
解决了使用 mingw-gcc 时的问题,但在我的目标平台 QNX/ARM/gcc(4.4.2) 中没有 - 如果有人可以指向一个支持 ARM 目标的好库,那么即使是像函数挂钩这样的运行时方法也是可以接受的。