关于动态链接库(dynamic library)里static变量和函数的问题

前段时间遇到一个问题,如果有多个动态链接库同时link另外一个静态链接库,这个静态库里的全局变量(或者static变量)会怎么样呢?
拍脑袋想想,总共也就这么几种可能:

  1. link(dlopen)时报错,变量重定义
  2. link(dlopen)时没错,执行时用用同一个变量
  3. link(dlopen)时没错,执行时分别有不同的变量
  4. link(dlopen)时报错,未定义的变量

仔细想想,分析一下,各种可能性都有,得看这些库和可执行文件是怎么编译链接的才行,具体看下面的各种case。
同时,测试的code里在dynamic里加上了同名的函数,看看函数会有什么表现。

假设有如下文件(code见Gist):

    common.h
    common.cpp  // 生成libcommon.a
    dynamic1.h
    dynami1.cpp  // 生成libdynamic1.so
    dynamic2.h
    dynamic2.cpp // 生成libdynamic2.so
    main.cpp  // 生成main

生成libcommon.a总是这么编译:

    g++  -g -Wall -Wextra  -c -o common.o common.cpp
    ar rcs libcommon.a common.o
  • Case 0: 动态链接库不link .a,main直接link .so,生成main的时候也不link .a
        g++ -g -Wall -Wextra -fPIC -shared -o libdynamic1.so dynamic1.cpp
        g++ -g -Wall -Wextra -fPIC -shared -o libdynamic2.so dynamic2.cpp
        g++ -DDIRECT_CALL_SO -o main main.cpp -L. -ldynamic1 -ldynamic2
    

    这种情况结果显然是4,link时出错,找不到.a里的函数。

  • Case 1: 动态链接库不link .a,main直接link .so,生成main的时候link .a
        g++ -g -Wall -Wextra -fPIC -shared -o libdynamic1.so dynamic1.cpp
        g++ -g -Wall -Wextra -fPIC -shared -o libdynamic2.so dynamic2.cpp
        g++ -DDIRECT_CALL_SO -o main main.cpp -L. -ldynamic1 -ldynamic2 -lcommon
        LD_LIBRARY_PATH=. ./main
    

    这种情况下执行的结果是2,link时没错,执行时看到的也是同一个变量。
    GetInt()返回值是1,它依赖于-l的顺序,如果-ldynamic2在前,返回值就是2了。

  • Case 2: 动态链接库link .a,main直接link .so,生成main的时候无所谓要不要link .a
        g++ -g -Wall -Wextra -fPIC -shared -o libdynamic1.so dynamic1.cpp -L. -lcommon
        g++ -g -Wall -Wextra -fPIC -shared -o libdynamic2.so dynamic2.cpp -L. -lcommon
        g++ -DDIRECT_CALL_SO -o main main.cpp -L. -ldynamic1 -ldynamic2
        LD_LIBRARY_PATH=. ./main
    

    执行结果同上

  • Case 3: 动态链接库不link .a,main不链接.so,通过dlopen()调用so,无所谓链接.a
        g++ -g -Wall -Wextra -fPIC -shared -o libdynamic1.so dynamic1.cpp
        g++ -g -Wall -Wextra -fPIC -shared -o libdynamic2.so dynamic2.cpp
        g++ -o main main.cpp -ldl [-L. -lcommon]
        LD_LIBRARY_PATH=. ./main
    

    执行结果是4,dlopen的时候找不到 GetGlobalStatic()

  • Case 4: 动态链接库link .a,main不链接.so,通过dlopen()调用so(不用RTLD_GLOBAL),无所谓链接.a
        g++ -g -Wall -Wextra -fPIC -shared -o libdynamic1.so dynamic1.cpp -L. -lcommon
        g++ -g -Wall -Wextra -fPIC -shared -o libdynamic2.so dynamic2.cpp -L. -lcommon
        g++ -o main main.cpp -ldl
        LD_LIBRARY_PATH=. ./main
    

    执行结果是3,各有各自的变量。
    GetInt()返回的也是各自的变量。

  • Case 5: 动态链接库link .a,main不链接.so,通过dlopen()调用so(使用RTLD_GLOBAL),无所谓链接.a
        g++ -g -Wall -Wextra -fPIC -shared -o libdynamic1.so dynamic1.cpp -L. -lcommon
        g++ -g -Wall -Wextra -fPIC -shared -o libdynamic2.so dynamic2.cpp -L. -lcommon
        g++ -DUSE_RTLD_GLOBAL -o main main.cpp -ldl -L. -lcommon
        LD_LIBRARY_PATH=. ./main
    

    执行结果变成2了,执行时看到了同一个变量。
    GetInt()返回的仍然是各自的变量。

看了这么多case,结果1怎么没有出现呢?别急,如果编译的时候全部编译在一起:

    g++ -DDIRECT_CALL_SO -o main main.cpp dynamic1.cpp dynamic2.cpp common.cpp

就出错了,GetInt()重定义。

结论

  • 对于不同so link的.a里的变量:
    1. 如果是直接link的,总是用同一个变量。仔细想想肯定是这样,否则一定会出现multiple definition
    2. 如果是dlopen,它依赖dlopen()的flag:
      • 如果是RTLD_LOCAL(默认),各个so会使用各自的.a里的变量。
      • 如果用RTLD_GLOBAL,就跟直接link一样,用同一个变量了。
  • 对于不同so里的同名函数:
    1. 通过dynamic link或者dlopen时不会出现变量重定义;
    2. 直接link时的顺序决定了main使用哪个so的实现
    3. dlopen的话,它们有各自的函数
Share