Linux下如何用共享库重定向C库函数且保留原函数访问?
问题背景
我开发了一款堆内存跟踪包装库,封装了calloc()、malloc()等内存分配函数,在macOS环境下运行正常。移植到Ubuntu 22.04(GCC11.4)、Debian12(GCC12.2)(目标C11标准)后,采用--wrap=symbol及__wrap_*/__real_*机制编译运行无报错,但包装函数从未被触发——库已确认加载,内存操作计数始终为0。此问题与静态链接类同类问题无关,需实现包装逻辑同时调用原函数。
排查与解决方法
修正编译链接顺序
GCC的--wrap选项生效严格依赖链接顺序,必须将你的包装库放在被跟踪的目标程序/依赖库之后链接。示例编译命令:gcc -std=c11 your_program.c -o your_program -Wl,--wrap=malloc,--wrap=calloc -lyour_wrapper_lib若包装库前置,链接器会优先绑定系统库的原函数,直接跳过wrap机制。
强制动态库加载优先级
若包装库为动态库,Linux动态链接器可能优先加载系统库的原函数,可通过LD_PRELOAD强制优先加载你的包装库:LD_PRELOAD=./libyour_wrapper_lib.so ./your_program绕过链接器限制获取原函数
部分程序或第三方库会通过dlsym直接获取内存分配函数地址,绕过--wrap机制。此时可在包装库中直接通过dlsym(RTLD_NEXT)获取原函数地址,替代__real_*:#include <dlfcn.h> void* __wrap_malloc(size_t size) { static void* (*real_malloc)(size_t) = NULL; if (!real_malloc) { real_malloc = dlsym(RTLD_NEXT, "malloc"); } // 你的内存跟踪逻辑 void* ptr = real_malloc(size); // 更新内存计数等操作 return ptr; }编译时需链接动态加载库:
-ldl验证wrap符号导出状态
使用nm命令检查包装库是否正确导出__wrap_*系列符号:nm -D libyour_wrapper_lib.so | grep __wrap_若未找到对应符号,检查编译选项:动态库需加
-fPIC,避免使用-fvisibility=hidden隐藏符号。C++程序适配处理
若目标程序为C++编写,malloc可能被operator new间接调用,需同时包装operator new;且包装函数需用extern "C"避免名字修饰:extern "C" { void* __wrap_malloc(size_t size) { // 跟踪逻辑 } }
内容的提问来源于stack exchange,提问作者LiamF

