记录CSAPP Lecture-13 中的 Library Interpositioning 相关内容
Generating address traces
// int.c
#include <stdio.h>
#include <malloc.h>
int main()
{
int *p = malloc(32);
free(p);
return(0);
}
Compile-time Interpositioning
- 涉及代码
// mymalloc.c
#ifdef COMPILETIME
#include <stdio.h>
#include <malloc.h>
/* malloc wrapper function */
void *mymalloc(size_t size)
{
void *ptr = malloc(size);
printf("malloc(%d)=%p\n",
(int)size, ptr);
return ptr;
}
/* free wrapper function */
void myfree(void *ptr)
{
free(ptr);
printf("free(%p)\n", ptr);
}
#endif
//malloc.h
#define malloc(size) mymalloc(size)
#define free(ptr) myfree(ptr)
void *mymalloc(size_t size);
void myfree(void *ptr);
- Makefile
intc: int.c mymalloc.c
$(CC) $(CFLAGS) -DCOMPILETIME -c mymalloc.c
$(CC) $(CFLAGS) -I. -o intc int.c mymalloc.o
runc:
./intc
clean:
rm -f *~ intr intl intc *.so *.o
-
下面解析此处涉及到的gcc参数
-DCOMPILETIME- 原型
-Dmacro=.. - 表示进行预定义
#define COMPILETIME
- 原型
-c- 编译时不进行连接,即生成.o文件,是二进制形式但是无法运行,内部的跳转函数的跳转地址是0的形式
-I.- 原型
-I dir - 用于预处理时添加搜索头文件的目录
- 可以覆盖掉同名的系统头文件,此处用于覆盖int.c中的
#include <malloc.h>
- 原型
-
该方法通过编译时把
malloc和free替换成我们提供的版本
Link-time Interpositioning
#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 libc malloc */
printf("malloc(%d) = %p\n", (int)size, ptr);
return ptr;
}
void __wrap_free(void *ptr)
{
__real_free(ptr); /* Call libc free */
printf("free(%p)\n", ptr);
}
#endif
- Makefile
intl: int.c mymalloc.c
$(CC) $(CFLAGS) -D LINKTIME -c mymalloc.c
$(CC) $(CFLAGS) -c int.c
$(CC) $(CFLAGS) -Wl,--wrap,malloc -Wl,--wrap,free -o intl int.o mymalloc.o
runl:
./intl
- 下面解析此处涉及到的gcc参数
-Wl- 原型
-Wl,option,用于给`linker传递选项,具体文档- 此处的逗号会被linker理解为空格
-Wl,--wrap,symbol实际为ld --wrap symbol- 会把对
symbol的引用解析为__wrap_symbol - 而对
__real_malloc的引用则会解析为真实的symbol
- 会把对
- 从而使得
int.c中的malloc指向我们重定义的__wrap_malloc, 而mymalloc.c中实际调用的是真实的malloc(通过__real_malloc)
- 原型
Load/Run-time Interpositioning
#ifdef RUNTIME
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
/* malloc wrapper function */
void *malloc(size_t size)
{
void *(*mallocp)(size_t size);
char *error;
mallocp = dlsym(RTLD_NEXT, "malloc"); /* Get address of libc malloc */
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
void *ptr = mallocp(size); /* Call libc malloc */
fprintf(stderr, "malloc(%d) = %p\n", (int)size, ptr);
return ptr;
}
/* free wrapper function */
void free(void *ptr)
{
void (*freep)(void *) = NULL;
char *error;
if (!ptr)
return;
freep = dlsym(RTLD_NEXT, "free"); /* Get address of libc free */
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
// printf("free(%p)\n", ptr)
fprintf(stderr, "free(%p)\n", ptr);
freep(ptr); /* Call libc free */
}
#endif
-
要使用
RTLD_NEXT,需要宏定义define _GNU_SOURCE, 因为其不是posix定义的 -
此处通过调用
dlsym来获得原始库中的malloc和free函数,RTLD_NEXT表示从下一个库中解析函数符号,而非当前这个调用dlsym的库 -
此处不能使用
printf或fprintf(stdout, ...), 因为当对标准输出流进行写操作时,会通过malloc创建一个缓冲区,导致出现递归调用malloc的情况,此处要访问无缓冲的标准错误流才能正常定义我们的malloc -
Makefile
intr: int.c mymalloc.c
$(CC) $(CFLAGS) -DRUNTIME -shared -fpic -o mymalloc.so mymalloc.c -ldl
$(CC) $(CFLAGS) -o intr int.c
runr:
(LD_PRELOAD="./mymalloc.so" ./intr)
- 下面解析此处涉及到的gcc参数
-shared- 用于生成共享库(
.so) - 一般需要和
-fpic一起作用
- 用于生成共享库(
-fpic- 生成能在共享库中被使用的位置独立的代码,即当被装载时可以放在内存的任意位置
-ldl- 原型
-llibrary - 在连接的时候寻找以参数
library为名的库(liblibrary.so或liblibrary.a)(同名则倾向于使用动态共享库),此处表用连接时需要导入libdl.so - 此处要放在
mymalloc.so之后, 即编译时使用的函数例如dlsym等要在libdl.so中查找到相关依赖
- 原型
-rdynamic- 代表给连接器传
--export-dynamic参数 - 这是调用
dlopen可能要使用的参数,代表把所有的符号(包括模块内部的符号)都加入动态符号表(dynamic symbol table, 是在运行时对所有动态对象可见的对象), - 为了让主程序内定义的全局函数对库函数可见, 尤其需要传入主程序中的函数作为回调函数时需要该选项(参考了该文)
- 代表给连接器传
- 运行时需要给出
LD_PRELOAD,为bash语法, 动态链接器会先在LD_PRELOAD中寻找未定义的引用, 此处可以替换int.c中的malloc变为使用我们自定义的,又因为我们的mymalloc.malloc实际实现时调用的是下一个库(即libc.so)中的malloc,所以不会出现递归调用

浙公网安备 33010602011771号