在同一个进程中静态链接和动态链接libgcc_s.so库是否可行?

7

我的应用程序引用了许多共享库。其中一些是用C++编写的,它们引用了libstdc++.so,而后者又引用了libgcc_s.so。但是其他库则是用纯C编写的,并通过-static-libgcc进行链接。

现在,我有libgcc的一些静态链接代码位于多个共享库中,而libstdc++在运行时动态加载其他部分的libgcc。

Q1:这种设置会给我带来任何问题吗?libgcc是否具有内部状态,使得此混合链接成为问题,还是只是内联函数?

Q2:为了使我的应用程序在旧版本的Linux上工作,我应该发布libstdc++.so和libgcc_s.so并在主exe上使用rpath进行加载。这是正确的方式吗?


我注意到我的应用程序使用-static-libgcc构建,在运行时加载C++库(它会加载libgcc_s),但只在Mingw-w64 32位上退出时崩溃(在Mingw-64位和Linux 32位和64位上没有崩溃)。 - user519179
2个回答

3

这个设置会引起任何问题吗?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 进行,因此由 静态 libgcc 解决(请参见上面的 Makefile )。 接下来发生的是,libstdc ++ 调用其 __once_proxy,然后尝试通过 __emutls_get_address 找到 __once_call 的地址,该地址是从 动态 libgcc 导入的,因为 libstdc ++ 是一个动态库。
结果是 emutls_key,它是 libgcc 的静态全局变量,在每个 libgcc 副本中都被初始化两次(前提是先调用 pthread_key_create),并且 __once_call 在一个 TLS 中写入副本,而在另一个 TLS 中读取。随后尝试调用 __once_call 导致空指针解引用。
以上只是我的调试调查结果,不是我故意制作的。你的经验可能更容易或更难。因此,总之,链接libgcc既静态又动态地确实可能会引起问题。

0
这个设置会给我带来麻烦吗?
绝对不会。静态链接库被内部合并到程序中,就好像实现在程序自身的结构中一样。
来自Wikipedia(我的强调) 静态库或静态链接库是一组例程、外部函数和变量,在编译时由编译器、链接器或绑定器解析为调用者,并通过复制到目标应用程序中,生成一个对象文件和一个独立的可执行文件。
请参见this supportive idea
至于您评论中的崩溃,那很可能是一个错误。 libgcc是否有内部状态会使这种混合链接方式出现问题,还是只有内联函数?
没有。只有内联函数。
为了让我的应用程序在旧版Linux上运行,我应该打包libstdc++.so和libgcc_s.so,并在主可执行文件上使用rpath来加载它。这是正确的做法吗?
这个问题有点需要建议,所以我会说一下我会怎么做。我会在标准/默认动态链接搜索路径中(可能在环境变量或某个文件中,具体取决于系统)运行时搜索libstdc++.so,如果找到,则加载它。如果没有找到,则加载已打包的库。如果您事先知道该系统没有这些动态库,那么我建议使用程序的静态构建。

1
“只有内联函数”这种说法是错误的:还有一些全局静态变量。 - Ruslan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接