• 小心两个共享库共用同一个静态库


    小心两个共享库共用同一个静态库.pdf

    下载测试代码:x.zip和帖子的略不同,x.zip包中的全局变量是个类对象,带有构造和析构函数
    推荐阅读:http://blog.chinaunix.net/uid-20682147-id-351108.htmlLinux上制作可执行的共享库示例


    问1:如果测试中的全局变量global_var是个带构造和析构的类对象,会如何?(答案在最后
    问2:如果使用-fPIE替代-fPIC编译链接,会是什么结果了?


    位置无关代码(PIC)对常量和函数入口地址的操作都是采用基于基寄存器(base register)BASE+ 偏移量的相对地址的寻址方式,即使程序被装载到内存中的不同地址(即 BASE值不同),而偏移量是不变的,所以程序仍然可以找到正确的入口地址或者常量。

    为何要小心?原因是在使用dlopen动态加载共享库时,如果静态库中包含有全局变量,可能会出现名同地址不同的全局变量。
    解决办法:总是使用RTLD_GLOBAL加载共享库,而不是RTLD_LOCAL。以下是测试程序:

    Makefile

    1. # test shared libraries use static a same static library
    2. # the global variables defined at static library have the same address
    3. all: x libshared_lib1.so libshared_lib2.so
    4. x: x.cpp #libstatic_lib.a #libshared_lib1.so #libshared_lib2.so
    5. g++ -g -o $@ $^ -ldl
    6. libstatic_lib.a: static_lib.h static_lib.cpp
    7. g++ -g -fPIC -c static_lib.cpp -I.
    8. ar cr $@ static_lib.o
    9. libshared_lib1.so: shared_lib1.cpp libstatic_lib.a
    10. g++ -g -fPIC -shared -o $@ $^ -I.
    11. libshared_lib2.so: shared_lib2.cpp libstatic_lib.a
    12. g++ -g -fPIC -shared -o $@ $^ -I.
    13. clean:
    14. rm -f static_lib.o libstatic_lib.a
    15. rm -f shared_lib1.o libshared_lib1.so
    16. rm -f shared_lib2.o libshared_lib2.so
    17. rm -f x

    测试程序x.cpp
    1. #include <dlfcn.h>
    2. #include <stdio.h>
    3. #include <stdlib.h>

    4. extern void call_foo(const char* name, int load_flag);
    5. int main()
    6. {
    7.         int flag = RTLD_GLOBAL|RTLD_NOW;  // 如果是RTLD_GLOBAL则静态库中定义的全局变量在共享库中名同地址也同
    8.         //int flag = RTLD_LOCAL|RTLD_NOW;  // 如果是RTLD_LOCAL则静态库中定义的全局变量在共享库中名同地址不同
    9.                 
    10.         call_foo("./libshared_lib1.so", flag);
    11.         call_foo("./libshared_lib2.so", flag);

    12.         return 0;
    13. }

    14. // RTLD_NOW
    15. // RTLD_LAZY
    16. // RTLD_GLOBAL
    17. // RTLD_LOCAL
    18. void call_foo(const char* name, int load_flag)
    19. {
    20.         char *error;
    21.         void (*foo)();

    22.         void* handle = dlopen(name, load_flag);
    23.         if (NULL == handle)
    24.         {
    25.                 fprintf (stderr, "%s ", dlerror());
    26.                 exit(1);
    27.         }

    28.         dlerror(); /* Clear any existing error */
    29.         *(void **) (&foo) = dlsym(handle, "foo");
    30.         if ((error = dlerror()) != NULL)
    31.         {
    32.                 fprintf (stderr, "%s ", error);
    33.                 exit(1);
    34.         }

    35.         (*foo)();
    36. }

    静态库头文件static_lib.h
    1. extern int global_var;

    静态库实现文件static_lib.cpp
    1. #include <stdio.h>
    2. int global_var = 2013;

    第1个共享库实现文件shared_lib1.cpp 
    1. #include "static_lib.h"
    2. #include <stdio.h>

    3. extern "C" void foo()
    4. {
    5.         global_var = 1111;
    6.         printf("%p 1-> %d ", &global_var, global_var);
    7. }

    第2个共享库实现文件shared_lib2.cpp
    1. #include "static_lib.h"
    2. #include <stdio.h>

    3. extern "C" void foo()
    4. {
    5.         printf("%p 2-> %d ", &global_var, global_var);
    6. }

    测试环境:
    x86_64 x86_64 GNU/Linux 2.6.16

    附:
    如果你想覆盖系统调用,可以使用LD_PRELOAD或/etc/ld.so.preload,也可进一步了解RTLD_NEXT

    答:结果是即使以RTLD_GLOBAL方式加载,都会出现两次构造和析构调用,如果是RTLD_GLOBAL方式,地址仍然相同,也就是同一个对象执行了两次构造和析构,后果当然是非常危险。运行测试代码x.zip即可得到验证。

    • 如果被依赖的不是静态库,而是共享库,则无论何种方式都不存在问题
    • 为何即使RTLD_GLOBAL加载,也会执行两次构造和析构?原因是两个共享库存在相同的代码段,如果被依赖的是共享库,则不存在这个问题

  • 相关阅读:
    poj 2349 Arctic Network
    hdu 1596 find the safest road
    Codeforces 768B. Code For 1
    Codeforces 448C. Painting Fence
    Problem D. Ice Cream Tower(2016 China-Final)
    poj 2785 4 Values whose Sum is 0
    Codeforces 797C. Minimal string
    Codeforces 264A. Escape from Stones
    乌龟棋(noip2010)
    noip2018模拟题(类背包+贪心)
  • 原文地址:https://www.cnblogs.com/aquester/p/9891726.html
Copyright © 2020-2023  润新知