linux C 包装函数使用

以前在meego性能优化团队实习的时候,我曾经被要求计算一个软件调用malloc的次数和耗时,并探讨如果使用内存池对软件可能产生的影响。通用的讲,我需要将某些函数的调用重定向到我们自己定义的替代函数中来,这样的替代函数就叫做包装函数(wrapper function)。对malloc这个具体的场景来说我们不仅需要hack函数的调用,还要获取原本glibc定义的“real malloc”的句柄(函数指针)以使程序正常运行下去,当然如果这一切可以在不需要重新编译软件的前提下完成就更好了。

我总结了Linux环境下包装函数的几种实现方法:

1.LD_PRELOAD+dlsym

这对组合中,LD_PRELOAD完成了hack,而dlsym可以帮组我们获取到real malloc。LD_PRELOAD是一个环境变量,它可以影响程序的运行时的链接,让我们指定优先加载的动态链接库。虽然这个方法无法hack静态链接的符号,不过还好gcc编译的标准C函数大多包含在动态链接库libc.so.6中,所以这个方法还是可行的。

my_malloc.c

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
void* malloc(size_t sz) { 
    void *(*libc_malloc)(size_t) = dlsym(RTLD_NEXT, "malloc");
    printf("malloc\n");
    return libc_malloc(sz);
}

main.c

#include<stdio.h>
#include <malloc.h>
int main(int argc, char** argv) {
    int* ptr = (int *)malloc(sizeof(int));
    free(ptr);
    return 0;
}

gcc -o main.o -Wall -O2 main.c
gcc -o my_malloc.so -shared my_malloc.c -Wall -ldl -fPIC
export LD_PRELOAD=/path/.../my_malloc.so
./main.o

注意:
1.my_malloc.c中如果不加"#define _GNU_SOURCE",编译时报错“RTLD_NEXT undeclared”,因为glibc不会自动使用某些GNU extensions,所以需要定义这个宏启用这些extensions。
2.LD_PRELOAD后面的so文件一定要使用绝对路径或者把so文件放入LD_LIBRARY_PATH指定的目录下,否则会找不到so文件。
3.LD_PRELOAD的作用效果是全局的,使用完后要及时恢复。

2.ld --wrap

适用:Linux

ld有一个wrap选项,linux man的解释是:Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to "__wrap_symbol". Any undefined reference to "__real_symbol" will be resolved to symbol.

void * __wrap_malloc (size_t size) {
    printf ("malloc\\n");
    return __real_malloc (size);
}

所有对malloc的调用被解析为调用__wrap_malloc,而__real_malloc则会被解析为原始malloc函数。这种方法在gDEBugger上得到了实际应用,gDEBugger是一个OpenGL Profiler,它可以给出每个OpenGL函数的调用次数使用时间等数据,它的实现方法就是为所有OpenGL函数定义wrapper函数,在wrapper函数中添加统计代码。

3.GCC Malloc Hook

gcc only malloc only

参考文献:http://www.gnu.org/s/hello/manual/libc/Hooks-for-Malloc.html 这种方法其实有很强的局限性,只针对使用gcc编译的malloc,realloc,free等内存管理函数。gcc定义了一批函数指针变量,例如__malloc_hook指向“malloc”实际调用的函数,初始化时先保存真正的malloc函数指针然后将自己定义的malloc函数指针赋值给__malloc_hook。

/* Prototypes for __malloc_hook, __free_hook */     
#include &lt;malloc.h&gt;          
 
/* Prototypes for our hooks.  */     
static void my_init_hook (void);     
static void *my_malloc_hook (size_t, const void *);     
static void my_free_hook (void*, const void *);          
 
/* Override initializing hook from the C library. */     
void (*__malloc_initialize_hook) (void) = my_init_hook;          
static void     my_init_hook (void)     { 
    old_malloc_hook = __malloc_hook;       
    old_free_hook = __free_hook;       
    __malloc_hook = my_malloc_hook;       
    __free_hook = my_free_hook;     
}         
static void *     my_malloc_hook (size_t size, const void *caller)     {       
    void *result;       
    /* Restore all old hooks */       
    __malloc_hook = old_malloc_hook;       
    __free_hook = old_free_hook;       
    
    /* Call recursively */       
    result = malloc (size);       
    /* Save underlying hooks */       
    old_malloc_hook = __malloc_hook;       
    old_free_hook = __free_hook;       
    /* printf might call malloc, so protect it too. */       
    printf ("malloc (%u) returns %p\\n", (unsigned int) size, result);       
    /* Restore our own hooks */       
    __malloc_hook = my_malloc_hook;       
    __free_hook = my_free_hook;       
    return result;     
}          

static void     my_free_hook (void *ptr, const void *caller)     {       
    /* Restore all old hooks */       
    __malloc_hook = old_malloc_hook;       
    __free_hook = old_free_hook;       
    
    /* Call recursively */       
    free (ptr);       
    
    /* Save underlying hooks */       
    old_malloc_hook = __malloc_hook;       
    old_free_hook = __free_hook;       
    
    /* printf might call free, so protect it too. */       
    printf ("freed pointer %p\\n", ptr);       
    /* Restore our own hooks */       
    __malloc_hook = my_malloc_hook;       
    __free_hook = my_free_hook;     
}          

int main ()     {       ...     }

 

posted @ 2013-07-06 23:04  Knowledge Base  阅读(1645)  评论(0编辑  收藏  举报