如何理解 dlopen 的 RTLD_LOCAL flag?

14 篇文章 3 订阅

基础知识

理解 dlopen manual

环境信息

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 中的符号也能够正常引用。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值