library Interpositioning 库(内插)干预技术:
- linux 链接器支持库内插干预技术,它允许使用者截获对共享库函数的调用取而代之执行自己的代码,使用库干预机制,可以追踪感兴趣库函数的调用次数,验证和追踪器输入输出值, 或者将其完全替换为一个不同的实现,这在嵌入式平台上非常常见
- 其基本思想是:对要干预的库函数,创建对应的包装函数wrapper function,其原型和目标函数一模一样,使用库干预技术,就可以欺骗系统调用包装函数而不是目标函数;
- Interpositioning 可以发生在compile,link,或者 load and run 的阶段,
下面结合具体实例进行分析:
1. 编译时干预
- linux> gcc -DCOMPILETIME -c mymalloc.c
- linux> gcc -I. -o test test.c mymalloc.o
由于有-I.参数,多以会启动编译时干预,C预处理器会优先在当前目录中查找malloc.h
注意,mymalloc.c 中的包装函数里面调用的系统函数,是使用库函数编译的;
malloc.h:
#ifndef _MYMALLOC_H
#define _MYMALLOC_H
#define malloc(size) mymalloc(size)
#define free(size) myfree(size)
void *mymalloc(size_t size);
void *myfree(void* ptr);
#endif // _MYMALLOC_H
malloc.c:
#ifdef COMPILETIME
#include<stdio.h>
#include<malloc.h>
/**< malloc wrapper function */
void *mymalloc(size_t size)
{
void *ptr = malloc(size); ///< call libc.so
printf("malloc(%d) = %p\n",
(int)size, ptr);
return ptr;
}
/**< free wrapper function */
void *myfree(void* ptr)
{
free(ptr); ///< call libc.so
printf("free(%p)\n",
ptr);
return ptr;
}
#endif
test.c
#include<stdio.h>
#include<malloc.h>
int main()
{
printf("in main......\n");
int *p = malloc(32);
free(p);
return 0;
}
2. 链接时干预
linux静态链接器支持用–wrap func 标志进行链接时干预,这个标志告诉链接器把对符号func的引用解析成__wrap_func 的形式;还要把对符号__real_func的引用解析为func:
将源文件编译为可重定义目标文件:
- linux> gcc -DLINKTIME -c malloc
- linux> gcc -c test.c
把目标文件链接为可执行文件:
- linux> gcc -Wl,–wrap,malloc -Wl,–wrap,free -o test test.o malloc.o
Wl,operation 标志把option传递给链接器,option中的每个逗号要替换为一个空格,所以-Wl,–wrap,malloc 就把–wrap malloc 传递给链接器,类似的方式传递–wrap free
test.c 和 malloc.h 同上
malloc.c:
#ifdef LINKTIME
#include<stdio.h>
void *__real_malloc(size_t size);
void __real_free(void* ptr);
/**< malloc wrapper function */
void *__wrap_malloc(size_t size)
{
void *ptr = __real_malloc(size); ///< call libc.so malloc
printf("malloc(%d) = %p\n", (int)size, ptr);
return ptr;
}
/**< free wrapper function */
void __wrap_free(void *ptr)
{
__real_free(ptr); ///< call lic.so free
printf("free(%p)\n", ptr);
}
#endif
3. 加载执行时干预
编译时干预需要能访问程序的源码,链接时干预需要访问程序的可执行对象文件而运行时干预只需要程序的可执行目标文件,该机制基于动态链接器的LD_PRELOAD环境变量。如果LD_PRELOAD环境变量被设置为一个共享库路径的列表(以空格或分号隔开)当加载执行程序需要解析未定义引用时,动态链接器(ld-linux.so)优先搜索LD_PRELOAD库,基此可对共享库中的任何函数干预;
- linux> gcc -DRUNTIME -shared -fpic -o run_malloc.so malloc.c -ldl
- linux> gcc -o test test.c
- linux> LD_PRELOAD=./run_malloc.so ./test
test.c 和 malloc.h 同上
malloc.c:
#ifdef RUNTIME
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
void *malloc(size_t size)
{
void* (*real_malloc)(size_t) = NULL;
real_malloc = dlsym(RTLD_NEXT, "malloc");
if (NULL == real_malloc) {
fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
exit(1);
}
void *p = NULL;
fprintf(stderr, "malloc(%ld) = ", size);
p = real_malloc(size);
fprintf(stderr, "%p\n", p);
return p;
}
void free(void* ptr)
{
void (*real_free)(void* ptr)=NULL;
real_free = dlsym(RTLD_NEXT, "free");
if (NULL == real_free) {
fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
exit(1);
}
fprintf(stderr, "free(%p)\n", ptr);
real_free(ptr);
}
#endif
参考文献《深入理解计算机系统.5th》