__wrap_malloc 研究
即__malloc_hook 不推荐使用后, 用什么办法来在malloc 之前来注入代码呢?
gcc/g++ 的链接选项 -Wl,--wrap-malloc 可以解决这个问题. 下面给出一个具体
的实例来包装malloc, 包装free, 甚至包装任意一个函数.
它的作用是,在调用一个真实的库函数之前, 先调用包装函数.
先看一下主体代码, test.cpp中调用了 foo(), malloc(),free()函数.
$ cat test.cpp
#include <stdio.h>
#include <stdlib.h>
#include "foo.h"
#include "wrap.h"
//必须要声明foo()的函数原型,否则编译不通过
//test.cpp|7 col 2| error: ‘foo’ was not declared in this scope
int main()
{
foo();
//因为添加了链接选项 -wl,--wrap=malloc, 所以会链接 __wrap_malloc
// 否则, test.cpp:(.text+0xe):对‘__wrap_malloc’未定义的引用
void* p1 = malloc(10);
//因为添加了链接选项 -wl,--wrap=free, 所以会链接 __wrap_free
// 否则, test.cpp:(.text+0x1e):对‘__wrap_free’未定义的引用
free(p1);
fprintf(stdout, "test finish\n");
return 0;
}
$ cat foo.h
#ifndef _FOO_H
#define _FOO_H
extern "C"
{
extern void foo();
}
#endif
$ cat foo.cpp
#include <stdio.h>
#include "foo.h"
void foo()
{
fprintf(stdout, "call foo function\n");
}
如果我们写Makefile
$ cat 1.mak
test: test.o foo.o
g++ -o test $^
生成执行文件test: make -f 1.mak
执行:
$ ./test
call foo function
test finish
现在,我们想注入,或者说包装或者说探测foo(),malloc(),free(), 怎么样操作呢? gcc 给了我们答案.
先写包装函数, 包装foo(),malloc(),free() 再改写一下Makefile.
先看一下Makefile 的改变
cat Makefile
test: test.o foo.o wrap.o
g++ -o test $^ -Wl,--wrap=malloc -Wl,--wrap=free -Wl,--wrap=foo
我们看到多了-Wl,--wrap=malloc -Wl,--wrap=free -Wl,--wrap=foo
同时也多了一个wrap.o是需要我们生成的. 下面看看wrap.cpp 长什么样
$ cat wrap.cpp
#include <stdio.h>
#include "wrap.h"
//定义__wrap_malloc 及__wrap_free
//由于调用了__real_malloc, __real_free, 所以需要声明
//void * __real_malloc(size_t size);
//void __real_free(void *ptr)
//并且是 c_decl , 否则会有编译错误
// ‘__real_malloc’ was not declared in this scope
//同时把__wrap_malloc,__wrap_free 声明为 c_decl, 否则还是链接不上
//‘__wrap_malloc’未定义的引用
void* __wrap_malloc(size_t size)
{
fprintf(stdout, "_^_ call wrap malloc function _^_\n");
return __real_malloc(size);
}
void __wrap_free(void* ptr)
{
fprintf(stdout, "_^_ call wrap free function _^_\n");
__real_free(ptr);
}
void __wrap_foo()
{
fprintf(stdout, "_^_ call wrap foo function _^_\n");
__real_foo();
}
命名规范必须是__wrap_xxx 形式, 通过__real_xxx方式调用真正的,原始的函数定义. gcc 连接时负责把malloc 转化成__wrap_malloc 调用, 报___real_malloc 转化成malloc 来调用.
注意 __wrap_malloc, __real_malloc 都是cdecl 形式的调用, 所以,要在头文件中声明:
$ cat wrap.h
#ifndef _WRAP_H
#define _WRAP_H
//需要声明为 c_decl
extern "C"
{
void* __real_malloc(size_t size);
void __real_free(void* ptr);
void* __wrap_malloc(size_t size);
void __wrap_free(void* ptr);
//添加一个自定义函数
void __wrap_foo();
void __real_foo();
}
#endif
编译生成可执行文件,运行看看结果吧.
$ ./test
_^_ call wrap foo function _^_
call foo function
_^_ call wrap malloc function _^_
_^_ call wrap free function _^_
test finish
我们看到确实先调用了wrap 函数, 这也叫调用的乾坤大挪移之法吧.