最近在看dpdk的代码,看到examples/performance-thread/pthread_shim/pthread_shim.c文件 有一段宏
static void *__libc_dl_handle = RTLD_NEXT; #define get_addr_of_loaded_symbol(name) do { \ char *error_str; \ _sys_pthread_funcs.f_##name = dlsym(__libc_dl_handle, (#name)); \ error_str = dlerror(); \ if (error_str != NULL) { \ fprintf(stderr, "%s\n", error_str); \ } \ } while (0)
宏里面 dlsym函数的用法让我觉得好奇怪,之前没见过。于是用man 查看了手册 RTLD_NEXT 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 lay‐ ers of preloading). 大概意思就是说,传入这个参数,找到的函数指针是后面第一次出现这个函数名的函数指针。理解上有点模糊,我写了几行代码加深理解。 文件 first_one.c
#include <stdio.h> void print_message() { printf("the first lib~~\n"); } void first() { printf("init first\n"); }
编译成动态库
gcc -fpic -c first_one.c gcc --share first_one.o -o libfirst_one.so
文件 second_one.c
#include <stdio.h> void print_message() { printf("the second lib~~\n"); } void second() { printf("init second \n"); }
编译成动态库
gcc -fpic -c second_one.c gcc --share second_one.o -o libsecond_one.so
文件 wrap.c
# define RTLD_NEXT ((void *) -1l) #include <stdio.h> #include <dlfcn.h> #include <errno.h> void(*f)(); void load_func() __attribute__((constructor)); void load_func() { f = (void(*)())dlsym(RTLD_NEXT,"print_message"); char *error_str; error_str = dlerror(); if (error_str != NULL) { printf("%s\n", error_str); } printf("load func first f=%p\n",f); } void print_message() { printf("the wrap lib~~\n"); f(); }
编译成动态库
gcc -fpic -c wrap.c gcc --share wrap.o -o libwrap.so
文件 main.c
void print_message(); void first(); void second(); int main() { first(); second(); print_message(); return 0; }
编译成 目标文件
gcc -c main.c
第一种方式生成链接文件
gcc -o first main.o -lwrap -lfirst_one -lsecond_one -ldl -L.
第二种方式生成连接文件
gcc -o second main.o -lwrap -lsecond_one -lfirst_one -ldl -L.
设置执行时环境变量
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
查看库加载顺序
duanxc@DESKTOP-LVGREDM:~/dlysm$ ldd first linux-vdso.so.1 => (0x00007fffee2e9000) libwrap.so (0x00007f586c1a0000) libfirst_one.so (0x00007f586bf90000) libsecond_one.so (0x00007f586bd80000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f586bb70000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f586b7a0000) /lib64/ld-linux-x86-64.so.2 (0x00007f586c400000) duanxc@DESKTOP-LVGREDM:~/dlysm$ ldd second linux-vdso.so.1 => (0x00007fffc2321000) libwrap.so (0x00007fddd1d40000) libsecond_one.so (0x00007fddd1b30000) libfirst_one.so (0x00007fddd1910000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fddd1700000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fddd1330000) /lib64/ld-linux-x86-64.so.2 (0x00007fddd2000000)
查看执行结果
duanxc@DESKTOP-LVGREDM:~/dlysm$ ./first load func first f=0x7f600f3e06c0 init first init second the wrap lib~~ the first lib~~ duanxc@DESKTOP-LVGREDM:~/dlysm$ ./second load func first f=0x7f92457e06c0 init first init second the wrap lib~~ the second lib~~
分析 库libfirst_one.so 和库libsecond_one.so中都有print_message函数,根据库的加载顺序,出入RTLD_NEXT作为句柄的dlsym函数会返回对应的函数指针,当libfirst_one.so先加载则返回这个库中的函数地址,反之则返回libsecond_on.so中的函数指针。 如上例,我们可以通过这个特性对libc中的库函数进行封装,用于测试目的。如wrap.c中所写,先获取libc中想要封装的函数名称的指针,然后在wrap.c中写对应函数名实现。在实现时,先做一些记录或者统计,然后再通过指针调用libc中的函数。当程序开发完毕,不需要记录或统计值时,在连接时,不连接wrap库即可。