我正在尝试Override a function call in 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() - &gt;中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() - &gt; 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()函数?
修改:
void get_resolution() __attribute__((weak));
解决了使用mingw-gcc但在我的目标平台QNX / ARM / gcc(4.4.2)答案 0 :(得分:5)
只需使用预处理器:
void __wrap_get_resolution()
{
/* calling the real one here */
get_resolution();
}
#define get_resolution __wrap_get_resolution
int main()
{
/* the __wrap... gets called */
get_resolution();
...
}
我们的想法是在您将所有需要查看原始函数的代码放入之后,将函数调用“重命名”到您的包装函数。
更脏的版本可能是在本地遮蔽函数地址,如下所示:
int main()
{
void(*get_resolution)() = __wrap_get_resolution;
get_resolution();
...
}
这个可行,但可以给你一些讨厌的警告。
修改强>
尽管在评论中指出不希望更改display.c
,但此处weak
属性解决方案来自http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
ELF目标支持弱符号,使用GNU汇编器和链接器时也支持a.out目标。
/* in display.c */
void __real_get_resolution()
{
...
}
void get_resolution() __attribute__((weak, alias("__real_get_resolution")));
/* in test.c */
void get_resolution()
{
/* this version will take precedence over get_resolution() in display.c */
...
/* lastly call the real thing */
__real_get_resolution();
}
从现在开始,无论你在哪里调用get_resolution()
(和都编译了“强”版本),都会调用包装版本。
答案 1 :(得分:0)
答案 2 :(得分:0)
如果你这样做会怎么样:
gcc -Wl, - wrap,get_resolution test.c display.c
答案 3 :(得分:0)
如果在汇编文件中定义符号,汇编器将始终直接调用符号;使用GNU as
无法改变这一点(据我所知)。这假设编译器还没有决定内联函数调用!
解决方案是在get_resolution
内导致display.c
未定义。为此,我建议将其拆分为2个文件,一个用get_resolution
,另一个用于源文件的其余部分。您可以在不实际拆分文件的情况下执行此操作,方法是放入#ifdef
块并使用不同的定义对其进行两次编译。
答案 4 :(得分:0)
我认为除了使用shell代码之外很难实现。你可以看到this的原因。
作者给了我们一个C ++代码的例子,我用类似的方法尝试了你的问题,但是我没能实现你想要的。所以我认为解决它的唯一方法可能是使用shell代码,但是ASLR linux让它很难。这就是我所知道的,希望它可以帮到你
答案 5 :(得分:0)
gcc执行的'wrap'功能是在链接器阶段完成的,而不是由编译器完成的。编译器知道get_resolution在哪里,因为它在同一个编译单元中,因此不需要链接器来解析它(这就是魔术发生的地方)。
我看不出你可以用某种方式改变display.c(或其中一个包含的头文件)来解决这个问题。最简单的方法是将get_resolution放入它自己的C源文件中,与display.c分开。