LD_PRELOAD 的一个应用场景
问题背景
产品 A 中的 libtest.so 提供了一个接口 void test_feature(int num),该接口被下游产品 B 通过 dlopen 调用。现在有人编写了该接口的新实现 test_feature_v2,并打包成 libtest_v2.so。由于产品 A 和 B 均难以进行工程或代码层面的修改,因此需要一种不修改原有代码的方式,让产品 B 在调用 test_feature 时,实际执行 libtest_v2.so 中的 test_feature_v2 函数。
解决方案
利用 LD_PRELOAD 机制,我们可以预加载一个自定义的共享库,在其中重新定义 dlsym 函数,从而拦截动态链接过程。具体做法是:当检测到 dlsym(handle, "test_feature") 被调用时,自动加载 libtest_v2.so,并返回其中 test_feature_v2 的地址。
这样,产品 B 无需任何改动即可使用新的实现进行测试。
代码实现
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
static void *(*real_dlsym)(void *, const char *) = NULL;
static void *v2_handle = NULL; // 记录 libtest_v2.so 的句柄
void *dlsym(void *handle, const char *symbol) {
if (!real_dlsym) {
real_dlsym = dlsym(RTLD_NEXT, "dlsym");
}
if (symbol && strcmp(symbol, "test_feature") == 0) {
// 懒加载 libtest_v2.so
if (!v2_handle) {
v2_handle = dlopen("libtest_v2.so", RTLD_LAZY);
if (!v2_handle) {
// 加载失败时可记录错误信息
return NULL;
}
}
// 从 v2 库中获取 test_feature_v2 符号
return real_dlsym(v2_handle, "test_feature_v2");
}
// 其他符号按原始方式处理
return real_dlsym(handle, symbol);
}
使用方法
- 编译拦截库:
gcc -shared -fPIC -o libdl_patched.so dl_patched.c -ldl
- 运行产品 B,并预加载拦截库:
LD_PRELOAD=./libdl_patched.so ./product_B