• 如何理解 dlopen 的 RTLD_LOCAL flag?


    基础知识

    理解 dlopen manual

    环境信息

    OS:debian11

    测试用例

    dlopen.c 源码:

    #include 
    #include 
    #include 
    #include   /* 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    编译命令:

    gcc dlopen.c -o dlopen_demo -ldl
    
    • 1

    so1.c :

    #include 
    
    int func1(void)
    {
    	printf("func1\n");
    	
    	test2();
    	return 0;
    }
    
    int test1(void)
    {
    	printf("test1\n");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    编译命令:

    gcc -shared -fPIC so1.c -o libfunc1.so
    
    • 1

    so2.c :

    #include 
    
    int func2(void)
    {
    	printf("func2\n");
    	return func1();
    }
    
    int test2(void)
    {
    	printf("test2\n");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    编译命令:

    gcc -shared -fPIC so2.c -o libfunc2.so
    
    • 1

    so3.c:

    #include 
    
    int func3(void)
    {
    	printf("func3\n");
    	return test1();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    编译命令:

    gcc -shared -fPIC so3.c -o libfunc3.so
    
    • 1

    测试过程

    测试前的配置:

    export LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH
    
    • 1

    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
    
    • 1
    • 2
    • 3

    程序输出信息与描述一致,说明 RTLD_LOCAL 功能理解正确。

    执行如下命令重新编译 libfunc2.so:

    gcc -shared -fPIC so2.c -o libfunc2.so -L . -lfunc1
    
    • 1

    重新测试,此时程序运行日志如下:

    [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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    此信息中的 object 表示一个共享对象, scope 表示此共享对象的可见范围。可以观察到 scope 1 中包含 libfunc1.so 的项目,表明 libfunc2.so 中的符号对 libfunc1.so 来说也是可见的,故而在 libfunc1.so 中访问以 RTLD_LOCAL 打开的 libfunc2.so 中的符号也能够正常引用。

  • 相关阅读:
    如何将base64图片转化为URL格式
    北京印刷学院计算机考研资料汇总
    opencv中的图像操作
    python每日学5:python工程(大型项目)的组织架构:包、模块、类、方法
    Docker搭建harbor仓库
    MR混合现实情景实训教学系统模拟高空作业情景实训教学
    GitHub上9款美观大气的后台管理系统,基于但不限于vue、react、Angular、Bootstrap框架
    通过pipeline配置sonar自动化实现过程解析
    设计模式之单列模式
    H41H-40C法兰止回阀型号解析
  • 原文地址:https://blog.csdn.net/Longyu_wlz/article/details/127831747