Search code examples
c++fstreamiostreamstatic-linkingdlopen

C++ stream becomes bad if called in multiple shared libs and libstdc++ static linking is enabled


I compiled 2 shared libs with --static-libstdc++ enabled.

The 2 shared libs have the same function f, which just outputs a string and an integer to stdout.

Main program will load the 2 shared libs using dlopen, and call the f in it using dlsym.

However, the second loaded shared lib failed to output the integer and the C++ stream cout becomes 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?


The following is the code for 2 shared libraries. (They share the same code)
They will just cout a string and a integer.

// 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;
}

This is the main program, which loads the shared libs and calls their f functions.

// 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;
}

Here's the 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)

And the output is:

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.

result

You can checkout the code here: https://github.com/xuhongxu96/dlopen-iostream-issue


Solution

  • Finally I found a way to resolve the issue in Linux (GNU Extensions).

    Use dlmopen which provides better isolation btw objects.

    auto dll = dlmopen(LM_ID_NEWLM, dll_path, RTLD_LAZY);
    

    Great thanks for all commenters!

    Still welcome to explain the details about what the conflict states are.