LD_PRELOAD和ld --wrap
Posted yizui
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LD_PRELOAD和ld --wrap相关的知识,希望对你有一定的参考价值。
前言
LD_PRELOAD和ld --wrap都能实现不修改原始代码,替换指定函数的实现。通常我们会使用这些方法,替换如malloc)()/free()、read()/write()等函数,并在替换函数中做一些记录,以便能分析程序执行时的内存分配和IO情况。这些函数一般叫包裹函数。
LD_PRELAD
链接器会检查LD_PRELOAD这个环境变量,如果不为空,则优先使用LD_PRELOAD指定的动态库中的符号,以malloc()/free()为例。
代码清单 main.c
int main()
malloc(10);
return 0;
代码清单 my.c
void* malloc(size_t size)
void (*__real_malloc)(int) = NULL;
// 获取真实的malloc()地址
__real_malloc = dlsym(RTLD_NEXT, "malloc");
void* ptr = __real_malloc(size);
printf("[malloc] addr=%p, size=%u\n", ret, size);
return ptr;
void free(void* ptr)
printf("[free] addr=%p", ptr);
void (*__real_free)(void*) = NULL;
__real_free = dlsym(RTLD_NEXT, "free");
return __real_free(ptr);
gcc main.c -o test
将main.c编译成test
,gcc -fPIC -shared my.c -o libmy.so
my.c编译成libmy.so
。
export LD_PRELOAD=./libmy.so
后执行test
会发现,main()
中执行的malloc()
实际是libmy.so
中的malloc()
,其会输出分配的内存地址和大小。因为没有free的输出,所以我们可以得知test存在内存泄漏。这是一个很简陋的例子。
export使得LD_PRELOAD对所有该终端执行的程序都生效,不使用时还需要unset LD_PRELOAD
,我们可以通过LD_PRELOAD=./libmy.so ./test
使得其只对test生效。
LD_PRELOAD的本质是先加载的先使用。就像我们在编译时会发现如果两个库中有同名函数,那么哪个库在前,就用哪个库中的函数。
ld --wrap
ld --wrap是一个编译链接时的参数,其原理是在编译时将--wrap指定的函数名进行替换,其替换规则就是替换规则就是包裹函数前加__wrap_
,真实函数前加__real_
,比如gcc main.c -o test -Wl,--wrap=malloc
,编译器会将调用malloc()
的地方替换为__wrap_malloc()
,并将__real_malloc()
符号名映射到真实malloc()的地址。
此时my.c的实现就不一样了,我们需要提供__wrap_malloc()
,并在真正需要调用malloc()
的地方使用__real_malloc()
。
void* __wrap_malloc(size_t size)
void* ptr = __real_malloc(size);
printf("[malloc] addr=%p, size=%u\n", ret, size);
return ptr;
void __wrap_free(void* ptr)
printf("[free] addr=%p", ptr);
return __real_free(ptr);
如果有比较多的--wrap,那么可以将其放在一个文件中,假设文件名为ld-opt,其内容如下
--wrap=malloc
--wrap=free
使用gcc main.c -o test -Wl,@ld-opt
就能替换malloc和free了。
ld --wrap的本质是按指定规则替换符号名(malloc->__wrap_malloc),并将特定符号与执行代码关联(__real_malloc->maloc代码)。
以上是关于LD_PRELOAD和ld --wrap的主要内容,如果未能解决你的问题,请参考以下文章
Golang SSH 加载 LD_PRELOAD 和 LD_LIBRARY_PATH 环境变量