济南企业网站建设公司,泉州网络推广公司,网站所有页面只显示域名,wordpress怎么重置文章目录库打桩机制1. 编译时打桩2. 链接时打桩3. 运行时打桩库打桩机制 Linux 链接器支持一个很强大的技术#xff0c;称为库打桩 (library interpositioning)#xff0c;它允许你截获对共享库函数的调用#xff0c;取而代之执行自己的代码。使用打桩机制#xff0c;你可以…
文章目录库打桩机制1. 编译时打桩2. 链接时打桩3. 运行时打桩库打桩机制 Linux 链接器支持一个很强大的技术称为库打桩 (library interpositioning)它允许你截获对共享库函数的调用取而代之执行自己的代码。使用打桩机制你可以追踪对某个特殊库函数的调用次数验证和追踪它的输入和输出值或者甚至把它替换成一个完全不同的实现。 下面是它的基本思想给定一个需要打桩的目标函数创建一个包装函数它的原型与目标函数完全一样。使用某种特殊的打桩机制你就可以欺骗系统调用包装函数而不是目标函数了。包装函数通常会执行它自己的逻辑然后调用目标函数再将目标函数的返回值传递给调用者。 打桩可以发生在编译时、链接时或当程序被加载和执行的运行时。 需求: 我们需要在主程序main.c中跟踪对库函数malloc和free的使用情况。(下面3种打桩以这个为例子)
1. 编译时打桩 编译时打桩的本质就是借助#define预处理指令让预处理器在预处理阶段帮助我们替换malloc为我们自己实现的mymalloc这就是编译时打桩。 • mymalloc.c 建立mymalloc.c文件, 定义需要的包装函数mymalloc和myfree。
#ifdef COMPILETIME
#include stdio.h
#include malloc.h//定义malloc 包装函数
void *mymalloc(size_t size)
{void *ptr malloc(size);printf(my_malloc:%d%p\n, (int)size, ptr);return ptr;
}//定义free 包装函数
void *myfree(void *ptr)
{free(ptr);printf(my_free:%p\n, ptr);
}#endif• malloc.h 该文件向预处理器指明用mymalloc.c中的包装函数替换库里的目标函数。
#define malloc(size) mymalloc(size)
#define free(ptr) myfree(ptr)void *mymalloc(size_t size);
void *myfree(void *ptr);• main.c
#include stdio.h
#include malloc.hint main()
{int *p malloc(32);free(p);return 0;
}编译指令:
gcc -DCOMPILETIME -c mymalloc.c
gcc -I. main.c mymalloc.o -o main-D选项: 指定宏参数设置COMPILETIME宏。 -I.选项: 指示C预处理器在搜索通常的系统目录前,先在当前目录中查找malloc.h 运行程序可得到下面的结果:
[wqjVM-0-15-centos compile]$ ./main
my_malloc:320xfb8010
my_free:0xfb80102. 链接时打桩 Linux的静态连接器支持使用--wrap f标志进行链接时打桩。这个标志告诉链接器请把符号f的引用解析为__wrap_f并且将对符号__real_f的引用解析为f。
举个栗子: --wrap malloc 将符号malloc的引用解析为__wrap_malloc将__real_f的引用解析为malloc。 这就使得用户在使用malloc接口时malloc的引用被解析为了__wrap_malloc因此程序会去调用__wrap_malloc。在__wrap_malloc中我们可以再去调用__real_malloc方法此时就会真正的去调用malloc方法。而我们也可以在__wrap_malloc方法中添加或修改一些额外的信息。(当然也可以完全实现一个自己的方法不去调用__real_malloc)
注意: 在Linux指令当中我们使用的是下面的形式的:
linux gcc -Wl,--wrap,malloc -Wl,--wrap,free -o main main.o mymalloc.o这里的--wrap,malloc中的,会被翻译为空格。每一个函数的替换单位就是-Wl,--wrap,f。 • mymalloc.c
#ifdef LINKTIME
#include stdio.h void* __real_malloc(size_t size);
void __real_free(void* ptr); void* __wrap_malloc(size_t size)
{ void* ptr __real_malloc(size); //call libcs malloc printf(mymalloc:%d%p\n, (int)size, ptr); return ptr;
} void __wrap_free(void* ptr)
{ __real_free(ptr); //call libcs free printf(myfree:%p\n, ptr);
}
#endifmain.c文件同1.编译时打桩的main.c 编译指令:
gcc -DLINKTIME -c mymalloc.c
gcc -c main.c
gcc -Wl,--wrap,malloc -Wl,--wrap,free -o main main.o mymalloc.o-Wl,option 标志把 option 传递给链接器。option 中的每个逗号都要替换为一个空格。所以 -Wl,--wrap,malloc 就把 --wrap malloc 传递给链接器以类似的方式传递 -Wl,--wrap,free。 运行结果:
[wqjVM-0-15-centos link]$ ./main
mymalloc:320x1fee010
myfree:0x1fee0103. 运行时打桩 运行时打桩主要依靠动态链接器的LD_PRELOAD环境变量。如果LD_PRELOAD环境变量被设置为一个动态库的路径名的列表(以空格或分隔间隔的列表, 一个元素也可以)那么当你加载和执行一个程序需要解析未定义的引用时动态链接器会优先搜索LD_PRELOAD库然后才会去搜索其它的库。 有了上面这个机制当你加载和执行任意的可执行文件时可以对任何动态库的任何函数打桩包括libc.so。
• mymalloc.c
#ifdef RUNTIME
#define _GNU_SOURCE //定义GUN宏允许你使用一些被限制的特性(feature)
#include stdio.h
#include stdlib.h
#include dlfcn.h //malloc wrapper function
void* malloc(size_t size)
{ void* (*malloc_ptr)(size_t size); char* error; malloc_ptr dlsym(RTLD_NEXT, malloc); //Get Address of libc malloc if((error dlerror()) ! NULL){ fprintf(stderr, %s\n, error); exit(1); } void* ptr malloc_ptr(size); printf(my_malloc:%d%p\n,(int)size, ptr); return ptr;
} //free wrapper function
void free(void* ptr)
{ void (*free_ptr)(void*) NULL; char* error; if(!ptr){ return; } free_ptr dlsym(RTLD_NEXT, free); //Get Address of libc free if((error dlerror()) ! NULL){ fprintf(stderr, %s\n, error); exit(2); } free_ptr(ptr); printf(my_free:%p\n, ptr);
}
#endifmain.c文件同1.编译时打桩的main.c 编译指令:
gcc -DRUNTIME -shared -fPIC mymalloc.c -o libmymalloc.so -ldl
gcc main.c -o main运行指令:
[wqjVM-0-15-centos running]$ LD_PRELOAD./libmymalloc.so ./main
my_malloc:320x1d85010
my_free:0x1d85010或者
[wqjVM-0-15-centos running]$ (setenv LD_PRELOAD ./libmymalloc.so; ./main; unsetenv LD_PRELOAD)
my_malloc:320x1d85010
my_free:0x1d85010上面的那种是设置本地变量LD_PRELOAD下面的则是设置环境变量LD_PRELOAD。本地变量与环境变量的区别见Linux详解 — 进程管理2 (进程状态、环境变量与命令行参数)