【问题标题】:Is libgcc_s.so linked both statically and dynamically in the same process ok?libgcc_s.so 是否可以在同一进程中静态和动态链接?
【发布时间】:2015-05-30 17:51:39
【问题描述】:

我的应用引入了许多共享库。有些是用 C++ 编写的,它引入了 libstdc++.so,它引入了 libgcc_s.so。还有一些是用纯 C 编写的,并与 -static-libgcc 链接。

所以现在我有一些 libgcc 静态链接在多个共享库中,并且 libstdc++ 在运行时动态加载 libgcc 的其他位。

Q1:这个设置会给我带来什么麻烦吗? libgcc 是否具有会使这种混合链接出现问题的内部状态,还是只是内联函数?

Q2:为了让我的应用程序在较旧的 Linux 上运行,我应该发布 libstdc++.so 和 libgcc_s.so 并在主 exe 上使用 rpath 来加载它。这是正确的做法吗?

【问题讨论】:

  • 我注意到如果我在运行时加载一个 c++ 库(加载 libgcc_s),我使用 -static-libgcc 构建的应用程序会崩溃退出,但仅在Mingw-w64 32bit(在 Mingw-64bit 和 Linux 32 和 64 上它存在而不会崩溃)。

标签: c++ c linux libstdc++ libgcc


【解决方案1】:

这个设置会给我带来麻烦吗? libgcc 是否具有会使这种混合链接出现问题的内部状态,还是只是内联函数?

绝对是的。我刚刚花了大约一周的全天调试时间来找出在 Windows/MinGW 上调用 std::call_once 时导致我的程序崩溃的原因。这是一个简化的测试用例:

mybin.cpp:

#include <pthread.h>
#include <mutex>
#include <iostream>

extern "C" int* getVar();

void print()
{
    std::cout << "Hello, var=" << *getVar() << "\n";
}

int main()
{
    pthread_key_t key;
    // Create first key, which will occupy the zero value, so that
    // __emutls_get_address will get a nonzero one when initializing emutls_key
    // (otherwise due to some C+pthread symmetries we'll not get the crash).
    pthread_key_create(&key, nullptr);

    std::once_flag f;
    // Crash
    std::call_once(f, print);
}

mylib.c:

// Make gcc emit some calls to __emutls_get_address to import
// libgcc implementing this function
static __thread int someVar;
int* getVar(void)
{
    if(!someVar)
        someVar=5;
    return &someVar;
}

Makefile:

test: libmylib.dll mybin.o
    g++ mybin.o -o test -pthread -static-libgcc -L. -lmylib

libmylib.dll: mylib.c Makefile
    gcc -fPIC -shared mylib.c -o libmylib.dll

mybin.o: mybin.cpp Makefile
    g++ -c mybin.cpp -o mybin.o

这里的崩溃是由于以下原因而发生的。 std::call_once 将被调用者的地址(此处为print)写入线程本地指针__once_call,其地址可通过调用__emutls_get_address 找到。这个调用直接来自.exe,所以它由static libgcc 解决(参见上面的Makefile)。接下来发生的事情是 libstdc++ 调用它的__once_proxy,然后尝试通过从 dynamic libgcc 导入的__emutls_get_address 找到__once_call 的地址,因为 libstdc++ 是一个动态库。

结果是 emutls_key,它是 libgcc 的一个静态全局变量,被初始化两次,每个 libgcc 副本(前提是 pthread_key_create 被预先调用),__once_call 在一个 TLS 中被写入副本并从另一个 TLS 中的副本中读取。随后尝试调用__once_call 会导致空指针取消引用。

以上只是我调试调查的结果,不是我故意制作的。你的经历可能更容易或更难。所以,总而言之,是的,静态和动态链接 libgcc绝对会造成麻烦。

【讨论】:

    【解决方案2】:

    这个设置会给我带来麻烦吗?

    绝对不是。一个静态链接库在程序内部被合并到程序中,就好像在程序自己的结构中实现一样。
    来自Wikipedia(我的重点)

    静态库或静态链接库是一组例程、外部函数和变量,它们在编译时在调用者中解析复制到目标中由编译器、链接器或绑定器应用,生成目标文件和独立可执行文件。

    this supportive idea
    至于您评论中的崩溃,这很可能是一个错误。

    libgcc 是否有内部状态会导致这种混合链接出现问题,还是只是内联函数?

    没有。只是内联函数。

    为了使我的应用程序在较旧的 Linux 上运行,我应该发布 libstdc++.so 和 libgcc_s.so 并在主 exe 上使用 rpath 来加载它。这是正确的做法吗?

    这个问题有点建议,所以我要说一下我会怎么做。我会在运行时在标准/默认动态链接搜索路径中搜索 libstdc++.so(可能在环境变量或特定文件中,具体取决于系统),如果找到,则加载它。如果未找到,请改为加载随附的库。如果您事先知道系统没有这些动态库,那么我建议您改用静态构建的程序。

    【讨论】:

    • “只是内联函数”:错了:还有一些全局静态变量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-09
    • 1970-01-01
    • 1970-01-01
    • 2011-09-05
    • 1970-01-01
    相关资源
    最近更新 更多