这个设置会给我带来麻烦吗?libgcc 是否具有会使这种混合链接出现问题的内部状态,或者它只是内联函数?
绝对没错。我刚刚花了大约一周的全天调试时间来找出导致我的程序在调用std::call_once
Windows/MinGW 时崩溃的原因。这是一个简化的测试用例:
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
,然后它试图找到__once_call
via的地址,该地址__emutls_get_address
是从动态libgcc 导入的,因为 libstdc++ 是一个动态库。
结果是emutls_key
,它是 libgcc 的静态全局变量,每个 libgcc 副本被初始化两次(前提pthread_key_create
是预先调用),并__once_call
在一个 TLS 中写入副本并在另一个 TLS 中从副本中读取。随后的调用尝试__once_call
导致空指针取消引用。
以上只是我调试调查的结果,不是我故意制作的。你的经历可能更容易或更难。所以,总而言之,是的,静态和动态链接 libgcc绝对会导致麻烦。