从同一编译单元重写C中的函数调用
我正在尝试,但是当函数在同一个编译单元中使用时,我面临一个问题。在下面的代码中,我试图替换函数get_resolution(),但只有在test.c中完成,而不是在display.c中完成,我才能实现它从同一编译单元重写C中的函数调用,c,function,gcc,linker,overriding,C,Function,Gcc,Linker,Overriding,我正在尝试,但是当函数在同一个编译单元中使用时,我面临一个问题。在下面的代码中,我试图替换函数get_resolution(),但只有在test.c中完成,而不是在display.c中完成,我才能实现它 // display.c ------------------------------------------------------------- #include <stdio.h> void get_resolution() { printf("Original g
// 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
//display.c-------------------------------------------------------------
#包括
void get_resolution()
{
printf(“原始获取分辨率”);
}
无效显示()
{
获得分辨率();
}
//测试c----------------------------------------------------------------
#包括
void uuu wrap u get u resolution()
{
printf(“模拟获取分辨率”);
//_uuureal_get_resolution();//应该可以调用original
}
int main()
{
display();//**问题**调用原始获取分辨率()
调用get_resolution();//u wrap_get_resolution()
返回0;
}
//gcc-Wl,--wrap,获取分辨率显示.c test.c
我的要求是,当我从main()调用display()时,我希望执行_wrap_get_resolution(),但我总是看到原始的get_resolution()被调用。对dis assembly的一点分析表明,函数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
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
00000000:
0:55推力%ebp
1:89 e5 mov%esp,%ebp
3:83 ec 18子$0x18,%esp
6:c7 04 24 00动产$0x0,(%esp)
d:e8 00呼叫12
12:c9离开
13:c3 ret
00000014 :
14:55%推压ebp
15:89 e5 mov%esp,%ebp
17:83 ec 08子$0x8,%esp
1a:e8 e1 ff呼叫0
1f:c9离开
20:c3 ret
21:90不
22:90不
23:90不
在main()->尚未解析get_解析的地址
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
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
00000000:
0:55推力%ebp
1:89 e5 mov%esp,%ebp
3:83 ec 18子$0x18,%esp
6:c7 04 24 00动产$0x0,(%esp)
d:e8 00呼叫12
12:c9离开
13:c3 ret
00000014 :
14:55%推压ebp
15:89 e5 mov%esp,%ebp
17:83 e4 f0和$0xfffffff0,%esp
1a:e8 00呼叫1f
1f:e8 00呼叫24
24:e8 00呼叫29
29:b8 00 mov$0x0,%eax
2e:c9离开
2f:c3 ret
现在的问题是,如何防止编译器解析函数display()中使用的get_resolution()的地址,而是使用重定位表,以便在链接阶段重写get_resolution()函数
编辑:void get_resolution()_属性__((弱))代码>解决了使用mingw gcc时的问题,但不是在我的目标平台QNX/ARM/gcc(4.4.2)中
这里可能会提供一个答案: 如果要重写库中的函数,可以使用(unser linux)
LD\u PRELOAD
希望这有帮助
问候。如果您这样做会怎么样:
gcc-Wl,--wrap,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
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
,但这里的弱属性解决方案
使用GNU汇编程序和链接器时,ELF目标和a.out目标都支持弱符号
从现在起,无论您在哪里调用get_resolution()
(并编译“强”版本),都会调用包装版本。如果符号在汇编文件中定义,则汇编程序将始终直接调用符号;用GNU作为
(据我所知)无法改变这一点。这是假设编译器尚未决定内联函数调用
解决方案是在display.c
中未定义get\u分辨率。为此,我建议将其拆分为两个文件,一个文件具有get_resolution
,另一个文件具有源文件的其余部分。您可以在不实际拆分文件的情况下执行此操作,方法是放入#ifdef
块,并使用不同的定义编译两次。我认为除了使用shell代码之外,很难实现它。您可以看到原因
<>作者给出了一个C++代码的例子,我用类似的方法对你的问题进行了尝试,但我没有达到你的要求。所以我认为解决问题的唯一方法可能是使用shell代码,但是Linux的ASLR让它很难。这就是我所知道的,希望它能帮助您gcc执行的“wrap”函数是在链接器阶段完成的,而不是由编译器完成的。编译器知道get_解析的位置,因为它在同一个编译单元中,所以不需要链接器解析它(这就是神奇的地方)
如果不以某种方式更改display.c(或其中一个包含的头文件),我看不出您可以解决这个问题。最简单的是t