如果在多个共享库中调用并且启用了 libstdc++ 静态链接,C++ 流就会变得糟糕
C++ stream becomes bad if called in multiple shared libs and libstdc++ static linking is enabled
我在启用 --static-libstdc++ 的情况下编译了 2 个共享库。
2个共享库有相同的功能f
,只是输出一个字符串和一个整数到stdout。
主程序将使用 dlopen
加载 2 个共享库,并使用 dlsym
.
调用其中的 f
但是,第二个加载的共享库未能输出整数,C++ 流 cout
变为 bad & fail
。
ADD: After discussion, I know this is normal... However, I want to change my question to: what implementation of libstdc++ caused this issue? Is there any shared global state? I think if there is no shared global state, it shouldn't be a problem. I wrote the similar program in Windows by static linking to VCRuntime and using LoadLibrary, and it works normally. So why libstdc++ is designed like this?
以下是2个共享库的代码。 (他们共享相同的代码)
他们只会 cout
一个字符串和一个整数。
// dll.cpp
#include <iostream>
using namespace std;
extern "C" void f()
{
cout << "hi" << 1 << endl;
bool is_eof = cout.eof();
bool is_fail = cout.fail();
bool is_bad = cout.bad();
cout.clear();
cout << endl;
cout << "eof: " << to_string(is_eof) << endl;
cout << "fail: " << to_string(is_fail) << endl;
cout << "bad: " << to_string(is_bad) << endl;
}
这是主程序,它加载共享库并调用它们的 f
函数。
// main.cpp
#include <iostream>
#include <dlfcn.h>
#include <cassert>
using namespace std;
using fn_t = void(void);
void call_f_in_dll(const char *dll_path)
{
auto dll = dlopen(dll_path, RTLD_LAZY);
assert(dll);
fn_t *fn = (fn_t *)dlsym(dll, "f");
assert(fn);
fn();
dlclose(dll);
}
int main()
{
call_f_in_dll("./libmydll.so");
cout << endl;
call_f_in_dll("./libmydll2.so");
return 0;
}
这是 CMakeLists。
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project (TestGCC)
add_link_options(-static-libgcc -static-libstdc++)
add_library(mydll SHARED dll.cpp)
add_library(mydll2 SHARED dll.cpp)
add_executable (main main.cpp)
target_link_libraries(main dl)
输出为:
hox@HOX-PC:~/repos/test-gcc/out$ ./main
hi1
eof: 0
fail: 0
bad: 0
hi
eof: 0
fail: 1
bad: 1
Notice the second part, there is no 1
after hi
and fail & bad
become 1
.
您可以在此处查看代码:https://github.com/xuhongxu96/dlopen-iostream-issue
最后我在 Linux(GNU 扩展)中找到了解决问题的方法。
使用 dlmopen
可以更好地隔离 btw 对象。
auto dll = dlmopen(LM_ID_NEWLM, dll_path, RTLD_LAZY);
非常感谢所有评论者!
Still welcome to explain the details about what the conflict states are.
我在启用 --static-libstdc++ 的情况下编译了 2 个共享库。
2个共享库有相同的功能f
,只是输出一个字符串和一个整数到stdout。
主程序将使用 dlopen
加载 2 个共享库,并使用 dlsym
.
f
但是,第二个加载的共享库未能输出整数,C++ 流 cout
变为 bad & fail
。
ADD: After discussion, I know this is normal... However, I want to change my question to: what implementation of libstdc++ caused this issue? Is there any shared global state? I think if there is no shared global state, it shouldn't be a problem. I wrote the similar program in Windows by static linking to VCRuntime and using LoadLibrary, and it works normally. So why libstdc++ is designed like this?
以下是2个共享库的代码。 (他们共享相同的代码)
他们只会 cout
一个字符串和一个整数。
// dll.cpp
#include <iostream>
using namespace std;
extern "C" void f()
{
cout << "hi" << 1 << endl;
bool is_eof = cout.eof();
bool is_fail = cout.fail();
bool is_bad = cout.bad();
cout.clear();
cout << endl;
cout << "eof: " << to_string(is_eof) << endl;
cout << "fail: " << to_string(is_fail) << endl;
cout << "bad: " << to_string(is_bad) << endl;
}
这是主程序,它加载共享库并调用它们的 f
函数。
// main.cpp
#include <iostream>
#include <dlfcn.h>
#include <cassert>
using namespace std;
using fn_t = void(void);
void call_f_in_dll(const char *dll_path)
{
auto dll = dlopen(dll_path, RTLD_LAZY);
assert(dll);
fn_t *fn = (fn_t *)dlsym(dll, "f");
assert(fn);
fn();
dlclose(dll);
}
int main()
{
call_f_in_dll("./libmydll.so");
cout << endl;
call_f_in_dll("./libmydll2.so");
return 0;
}
这是 CMakeLists。
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project (TestGCC)
add_link_options(-static-libgcc -static-libstdc++)
add_library(mydll SHARED dll.cpp)
add_library(mydll2 SHARED dll.cpp)
add_executable (main main.cpp)
target_link_libraries(main dl)
输出为:
hox@HOX-PC:~/repos/test-gcc/out$ ./main
hi1
eof: 0
fail: 0
bad: 0
hi
eof: 0
fail: 1
bad: 1
Notice the second part, there is no
1
afterhi
andfail & bad
become1
.
您可以在此处查看代码:https://github.com/xuhongxu96/dlopen-iostream-issue
最后我在 Linux(GNU 扩展)中找到了解决问题的方法。
使用 dlmopen
可以更好地隔离 btw 对象。
auto dll = dlmopen(LM_ID_NEWLM, dll_path, RTLD_LAZY);
非常感谢所有评论者!
Still welcome to explain the details about what the conflict states are.