前言
GCC的--wrap=symbol
或--wrap,symbol
是一个链接器选项,可以重定向未定义的符号。要配合-Wl,<options>
使用,表明是一个链接器选项。具体作用如下:
- 如果符号
symbol
未在当前编译单元中(一般为当前源文件)定义,则链接到符号__wrap_symbol
;
- 如果符号
__real_symbol
未在当前编译单元中(一般为当前源文件)定义,则链接到符号symbol
;
这方便我们在移植代码时,将已有的库函数(不兼容新平台)重定向到新的自定义函数,而又不需要对每处函数调用进行修改;或者在调试代码时,对目标函数包装一下,以监视其行为而又不需要修改函数本身。
重定向自定义函数
自定义的功能函数代码:
#include <stdio.h> #include "foo.h"
void foo(void) { printf("call foo\n"); }
void call_foo(void) { foo(); }
|
void foo(void); void call_foo(void);
|
重定向代码:
#include <stdio.h>
void __real_foo(void); void __wrap_foo(void) { printf("call __wrap_foo\n"); __real_foo(); }
|
调用代码:
#include "foo.h"
int main() { printf("foo(): "); foo();
printf("call_foo(): "); call_foo();
return 0; }
|
CMakeLists.txt:
cmake_minimum_required(VERSION 3.6) project(wrap_foo) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wl,--wrap,foo") add_executable(wrap_foo main.c foo.c wrap.c)
|
准备好上述文件后,编译执行:
mkdir build cd build cmake .. make -j8 ./wrap_foo
|
有如下打印:
foo(): call __wrap_foo
call foo
call_foo(): call foo
注意,call_foo
函数中调用的 foo
函数并没有被重定向,是因为其跟foo
函数的定义在同一源文件,链接器能找到foo
的定义就不会重定向。可以通过objdump
命令查看汇编以确认链接情况:
objdump -d wrap_foo > wrap_foo.asm
|
摘取main
和call_foo
部分如下:
000000000000068a <main>: 68a: 55 push %rbp 68b: 48 89 e5 mov %rsp,%rbp 68e: 48 8d 3d ef 00 00 00 lea 0xef(%rip),%rdi # 784 <_IO_stdin_used+0x4> 695: b8 00 00 00 00 mov $0x0,%eax 69a: e8 c1 fe ff ff callq 560 <printf@plt> 69f: e8 3c 00 00 00 callq 6e0 <__wrap_foo> 6a4: 48 8d 3d e1 00 00 00 lea 0xe1(%rip),%rdi # 78c <_IO_stdin_used+0xc> 6ab: b8 00 00 00 00 mov $0x0,%eax 6b0: e8 ab fe ff ff callq 560 <printf@plt> 6b5: e8 1a 00 00 00 callq 6d4 <call_foo> 6ba: b8 00 00 00 00 mov $0x0,%eax 6bf: 5d pop %rbp 6c0: c3 retq
...
00000000000006d4 <call_foo>: 6d4: 55 push %rbp 6d5: 48 89 e5 mov %rsp,%rbp 6d8: e8 e4 ff ff ff callq 6c1 <foo> 6dd: 90 nop 6de: 5d pop %rbp 6df: c3 retq
|
重定向系统函数(malloc)
wrap的一个常见用途就是包装malloc函数以监控内存申请情况,或者在某些嵌入式平台上重定向到该平台所支持的内存管理模块。一个简单的测试用例如下:
#include <stdio.h>
void* __real_malloc(size_t size); void* __wrap_malloc(size_t size) { printf("__wrap_malloc called, size:%zd\n", size); return __real_malloc(size); }
void __real_free(void* ptr); void __wrap_free(void* ptr) { printf("__wrap_free called\n"); __real_free(ptr); }
|
#include <stdio.h> #include <stdlib.h>
int main() { char* c = (char*)malloc(sizeof(char)); printf("c = %p\n", c); free(c); return 0; }
|
// CMakeLists.txt cmake_minimum_required(VERSION 3.6)
project(wrap_malloc)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wl,--wrap,malloc -Wl,--wrap,free") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wl,--wrap,malloc -Wl,--wrap,free")
add_executable(wrap_malloc main.cpp wrap.c)
|
mkdir build cd build cmake .. make -j8 ./wrap_malloc objdump -d wrap_malloc > wrap_malloc.asm
|
000000000000071a <main>: 71a: 55 push %rbp 71b: 48 89 e5 mov %rsp,%rbp 71e: 48 83 ec 10 sub $0x10,%rsp 722: bf 01 00 00 00 mov $0x1,%edi 727: e8 2f 00 00 00 callq 75b <__wrap_malloc> 72c: 48 89 45 f8 mov %rax,-0x8(%rbp) 730: 48 8b 45 f8 mov -0x8(%rbp),%rax 734: 48 89 c6 mov %rax,%rsi 737: 48 8d 3d 06 01 00 00 lea 0x106(%rip),%rdi # 844 <_IO_stdin_used+0x4> 73e: b8 00 00 00 00 mov $0x0,%eax 743: e8 98 fe ff ff callq 5e0 <printf@plt> 748: 48 8b 45 f8 mov -0x8(%rbp),%rax 74c: 48 89 c7 mov %rax,%rdi 74f: e8 39 00 00 00 callq 78d <__wrap_free> 754: b8 00 00 00 00 mov $0x0,%eax 759: c9 leaveq 75a: c3 retq
|
执行有如下打印:
__wrap_malloc called, size:1
c = 0x563621af1670
__wrap_free called