我的应用程序引入了许多共享库。有些是用C++编写的,它将拉进libstdc++。so,将其拉进libgcc_s.so。还有一些用普通C语言编写,并与-static-libgcc链接。

因此,现在我在多个共享库中静态链接了libgcc的某些位,并且libstdc++在运行时动态加载了libgcc的其他位。

Q1:这种设置会给我带来麻烦吗? libgcc是否具有使这种混合链接成为问题的内部状态,或者仅仅是内联函数?

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

最佳答案



绝对没错。我刚刚花了整整一周的时间进行调试,以找出导致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,然后尝试通过从动态libgcc导入的__once_call查找__emutls_get_address的地址,因为libstdc++是动态lib。

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

以上只是我的调试研究的结果,而不是我有意制作的。您的经验可能会更容易或更难。因此,总而言之,,绝对静态和动态链接libgcc绝对会给造成麻烦。

08-16 09:13