LD_PRELOAD和ld --wrap

前言

LD_PRELOAD和ld --wrap都能实现不修改原始代码,替换指定函数的实现。通常我们会使用这些方法,替换如malloc)()/free()、read()/write()等函数,并在替换函数中做一些记录,以便能分析程序执行时的内存分配和IO情况。这些函数一般叫包裹函数。

LD_PRELAD

启动程序时,链接器会做符号绑定,一般是按照编译时指定的链接顺序,查找动态库对应的符号地址。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编译成testgcc -fPIC -shared my.c -o libmy.somy.c编译成libmy.so

LD_PRELOAD=./libmy.so test后执行test会发现main()中执行的malloc()实际是libmy.so中的malloc(),其会输出分配的内存地址和大小。因为没有free的输出,所以我们可以得知test存在内存泄漏。这是一个很简陋的例子。

export使得LD_PRELOAD对所有该终端执行的程序都生效,不使用时还需要unset LD_PRELOAD,我们可以通过LD_PRELOAD=./libmy.so ./test使得其只对test生效。

LD_PRELOAD的本质是先加载的先使用。就像我们在编译时会发现如果两个库中有同名函数,那么哪个库在前,就用哪个库中的函数。

可以使用LD_DEBUG=all LD_PRELOAD=./libmy.so ./test观察链接器是如何绑定符号的。

ld --wrap

ld --wrap是一个编译链接参数,其原理是在编译时将--wrap指定的函数名进行替换,比如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了。

通过nm查看可以发现并不存在__real前缀的函数。

转载于:https://www.cnblogs.com/yizui/p/11368082.html

你可能感兴趣的:(LD_PRELOAD和ld --wrap)