基础知识
环境信息
OS:debian11
测试用例
dlopen.c 源码:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <gnu/lib-names.h> /* Defines LIBM_SO (which will be a
string such as "libm.so.6") */
int
main(void)
{
void *handle[3];
int (*func)(void) = NULL;
handle[0] = dlopen("libfunc1.so", RTLD_LAZY);
if (!handle[0]) {
fprintf(stderr, "%s\n", dlerror());
goto err;
}
handle[1] = dlopen("libfunc2.so", RTLD_LAZY);
if (!handle[1]) {
fprintf(stderr, "%s\n", dlerror());
goto err;
}
if ((func = dlsym(handle[1], "func2")))
func();
if ((func = dlsym(handle[0], "func1")))
func();
handle[3] = dlopen("libfunc3.so", RTLD_LAZY);
if (!handle[3]) {
fprintf(stderr, "%s\n", dlerror());
goto err;
}
if ((func = dlsym(handle[1], "func3")))
func();
err:
if (handle[0])
dlclose(handle[0]);
if (handle[1])
dlclose(handle[1]);
if (handle[2])
dlclose(handle[2]);
return 0;
}
编译命令:
gcc dlopen.c -o dlopen_demo -ldl
so1.c :
#include <stdio.h>
int func1(void)
{
printf("func1\n");
test2();
return 0;
}
int test1(void)
{
printf("test1\n");
return 0;
}
编译命令:
gcc -shared -fPIC so1.c -o libfunc1.so
so2.c :
#include <stdio.h>
int func2(void)
{
printf("func2\n");
return func1();
}
int test2(void)
{
printf("test2\n");
return 0;
}
编译命令:
gcc -shared -fPIC so2.c -o libfunc2.so
so3.c:
#include <stdio.h>
int func3(void)
{
printf("func3\n");
return test1();
}
编译命令:
gcc -shared -fPIC so3.c -o libfunc3.so
测试过程
测试前的配置:
export LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH
RTLD_LOCAL flag 功能测试
libfunc*.so 均使用 RTLD_LOCAL flag 进行加载,此时 libfunc2.so 中的 func2 函数中引用的 func1 函数在 libfunc1.so 中,libfunc2.so 检索不到此符号,故而报 undefine reference func1 的错误信息。同时由于是 lazy_binding,func2 函数调用 func1 符号时才会触发异常。
测试过程记录如下:
[longyu@debian] dlopen $ ./dlopen_demo
func2
./dlopen_demo: symbol lookup error: /home/longyu/dlopen_demo/libfunc2.so: undefined symbol: func1
程序输出信息与描述一致,说明 RTLD_LOCAL 功能理解正确。
执行如下命令重新编译 libfunc2.so:
gcc -shared -fPIC so2.c -o libfunc2.so -L . -lfunc1
重新测试,此时程序运行日志如下:
[longyu@debian] dlopen $ ./dlopen_demo
func2
func1
test2
func1
test2
func3
./dlopen_demo: symbol lookup error: /home/longyu/my_program/dlopen/libfunc3.so: undefined symbol: test1
当 libfunc2.so 的编译参数中添加了 -lfunc1 后,libfunc2.so 增加了一条依赖对象——libfunc1.so。此时尽管 libfunc1.so 使用 RTLD_LOCAL 标志加载,但 libfunc2.so 中的 func2 函数仍旧能够解析 func1 函数地址,此过程 func1 符号引用解析符合如下规则:
此共享对象自己包含的符号定义及其任何被加载的依赖对象中的符号
然而 libfunc1.so 的 func1 函数使用了 libfunc2.so 中定义的 test2 符号,它也能被成功解析到,但 libfunc1.so 链接参数并未添加对 libfunc2.so 的依赖项目。
我猜测可能是 libfunc1.so 的共享库类型被修改为了 RTLD_GLOBAL,但是增加一个新的 libfunc3.so 库,在其中的 func3 函数中调用 libfunc1.so 中的 test1 符号却报了 undefine reference 的错误,表明此猜测不正确。
设置 LD_DEBUG=”all” 重新运行 dlopen_demo 程序,在 dlopen 打开 libfunc2.so 时有如下 log 信息:
324555: object=/home/longyu/my_program/dlopen/libfunc2.so [0]
324555: scope 0: ./dlopen_demo /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
324555: scope 1: /home/longyu/my_program/dlopen/libfunc2.so /home/longyu/my_program/dlopen/libfunc1.so /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
324555:
324555: object=/home/longyu/my_program/dlopen/libfunc1.so [0]
324555: scope 2: /home/longyu/my_program/dlopen/libfunc2.so /home/longyu/my_program/dlopen/libfunc1.so /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
324555:
324555: object=/lib/x86_64-linux-gnu/libc.so.6 [0]
324555: scope 0: ./dlopen_demo /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
324555:
324555: object=/lib64/ld-linux-x86-64.so.2 [0]
324555: no scope
此信息中的 object 表示一个共享对象, scope 表示此共享对象的可见范围。可以观察到 scope 1 中包含 libfunc1.so 的项目,表明 libfunc2.so 中的符号对 libfunc1.so 来说也是可见的,故而在 libfunc1.so 中访问以 RTLD_LOCAL 打开的 libfunc2.so 中的符号也能够正常引用。