链接和运行时打桩

2019-05-16  本文已影响0人  Teech

这里说个很有意思的东西,就库打桩技术。允许我们在编译,链接,以及加载阶段替换部分系统函数的调用。比如malloc,free。
举个例子 编译时打桩,其实就是用#define 替换掉一些系统函数名,这个没什么好说的。

//inc.c
#include<stdio.h>
int main(){
    void* p = malloc(32);
    free(p);
    return 0;
}
//mymalloc.c
#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);
    printf("malloc size(%d) ptr=%x\n",size,(unsigned long)ptr);
    return ptr;
}
void __wrap_free(void* ptr){
    printf("free ptr=%x\n",(unsigned long)ptr);
    __real_free(ptr);
}
#Makefile
intc : inc.o mymalloc.o
    gcc -Wl,--wrap=malloc,--wrap=free -o intc inc.o mymalloc.o
mymalloc.o : mymalloc.c
    gcc -c mymalloc.c
inc.o : inc.c
    gcc  -c inc.c
clean :
    rm intc inc.o mymalloc.o

其中-Wl,--wrap=malloc,--wrap=free的意思,链接时替换掉“引用符号表”里的malloc为__wrap_malloc,free替换为__wrap_free。
编译后inc.o文件里有未解的引用"malloc","free",ld链接的时候把这两个符号替换掉成__wrap_malloc和__wrap_free。这样mymalloc.o文件的就能找到定义的地方,这样就替换glib里的malloc。

//inc.c
#include<stdio.h>
int main(){
    void* p = malloc(32);
    free(p);
    return 0;
}
//mymalloc.c
#define _GNU_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<dlfcn.h>

void* malloc(size_t size){
        void* (*mallocp)(size_t size); 
        char* error;
        mallocp = dlsym(RTLD_NEXT,"malloc");
        if ((error = dlerror()) != NULL){
            fputs(error,stderr);
            exit(1);
        }
        char* ptr = mallocp(size);
        fputs("newmalloc\n",stderr);
        //printf("malloc size(%d) ptr=%x\n",size,(unsigned long)ptr);
        return ptr;
}

void free(void* ptr){
    void (*freep)(void*) = NULL;
    char* error;
    freep = dlsym(RTLD_NEXT,"free");
    if ((error = dlerror()) != NULL){
        fputs(error,stderr);
        exit(1);
    }   
    freep(ptr);
    fputs("newfree\n",stderr);
    //printf("free ptr=%x\n",(unsigned long)ptr);
}
#生成mymalloc.so 
mymalloc.so : mymalloc.o
    gcc -shared -o mymalloc.so mymalloc.o -ldl
mymalloc.o : mymalloc.c
    gcc -fPIC -c mymalloc.c

然后在执行这个命令,生成inc可执行程序,这里默认的是使用glibc里的malloc.

gcc -o inc inc.c 

我们运行时想使用我们的打桩函数

LD_PRELOAD = "./mymalloc.so" ./inc

意味着解引用的时候,先从我们LD_PRELOAD这个定义的文件里找,也就是调用了mymalloc.so里的malloc和free函数。
在看看dlsym函数里的第一个参数RTLD_NEXT,我们查看man文档。
Find the next occurrence of the desired symbol in the search order after the current object. This allows one to provide a wrapper around a function in another shared object, so that, for example, the definition of a function in a preloaded shared
object (see LD_PRELOAD in ld.so(8)) can find and invoke the "real" function provided in another shared object (or for that matter, the "next" definition of the function in cases where there are multiple layers of preloading).
解引用的时候,首先找到的是mymalloc.so,因为设置了RTLD_NEXT,所以接着往下找,肯定找到时glibc里的malloc。
这里注释掉了malloc里的printf函数,因为printf内部一定有调用malloc函数的,这样会形成死循环了,堆栈溢出了。如果打开printf后的core文件的dump如下。printf里的malloc调用到mymalloc.so里的malloc函数,然后mymalloc.so里的malloc函数又会调用到printf。这样就会出现死循环。

其实打桩技术在用来做内存泄露检测工具的时候非常有用,记录下文件名以及行号,比如对比两次内存快照这样来辅助查找内存泄露的地方。

上一篇 下一篇

猜你喜欢

热点阅读