Is libgcc_s.so linked both statically and dynamically in the same process ok?
Asked Answered
F

2

7

My app pulls in many shared libraries. Some are written in C++ which pulls in libstdc++.so which pulls in libgcc_s.so. Yet others are written in plain C and linked with -static-libgcc.

So now I have bits of libgcc statically linked inside multiple shared libraries and libstdc++ dynamically loading other bits of libgcc at runtime.

Q1: Would this setup give me any trouble? Does libgcc have internal state that would make this mixed linkage problematic, or is it just inlined functions?

Q2: To make my app work on older Linuxes, I should ship libstdc++.so and libgcc_s.so and use rpath on the main exe to load it. Is this the right way to do it?

Fluency answered 26/3, 2015 at 15:54 Comment(1)
I've noticed that my app which is built with -static-libgcc crashes on exit if I load a c++ library at runtime (which loads libgcc_s), but only on Mingw-w64 32bit (on Mingw-64bit and on Linux 32 & 64 it exists without crash).Fluency
P
3

Would this setup give me any trouble? Does libgcc have internal state that would make this mixed linkage problematic, or is it just inlined functions?

Absolutely yes. I've just spent about a week of full-day debugging to find out what caused my program to crash when calling std::call_once on Windows/MinGW. Here's a reduced test case:

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

The crash here happens due to the following. std::call_once writes the address of the callee (here print) into a thread-local pointer __once_call, the address of which is found via a call to __emutls_get_address. This call happens directly from the .exe, so it's resolved by the static libgcc (see the Makefile above). What happens next is that libstdc++ calls its __once_proxy, which then tries to find the address of __once_call via __emutls_get_address, which is imported from the dynamic libgcc, because libstdc++ is a dynamic lib.

The result is that emutls_key, which is a static global variable of libgcc, gets initialized twice, per copy of libgcc (provided pthread_key_create is called beforehand), and __once_call gets written to the copy in one TLS and read from the copy in another TLS. The subsequent attempt to call __once_call results in null pointer dereference.

The above is just the result of my debugging investigation, not something I crafted on purpose. Your experience might be easier or harder. So, in summary, yes, linking libgcc both statically and dynamically absolutely can cause trouble.

Pianette answered 28/1, 2020 at 6:41 Comment(0)
P
0

Would this setup give me any trouble?

Absoulutely not. A statically-linked library is incorporated internally in the program as if implemented in the program's own structure.
From Wikipedia: (my emphasis)

A static library or statically-linked library is a set of routines, external functions and variables which are resolved in a caller at compile-time and copied into a target application by a compiler, linker, or binder, producing an object file and a stand-alone executable.

See this supportive idea.
As for the crash in your comment, that is very likely a bug.

Does libgcc have internal state that would make this mixed linkage problematic, or is it just inlined functions?

No. Just inline functions.

To make my app work on older Linuxes, I should ship libstdc++.so and libgcc_s.so and use rpath on the main exe to load it. Is this the right way to do it?

This question is a bit of an advice, so I'm going to say how I would do it. I would search for libstdc++.so in runtime in the standard/default dynamic-linking search paths (possibly in an environment variable or a certain file depending on the system) and if found, load that. If not found, load the shipped libraries instead. If you know beforehand that the system doesn't have those dynamic libraries then I advise using a static build of the program instead.

Prowess answered 29/6, 2015 at 15:32 Comment(1)
"Just inline functions": that's wrong: there are some global static variables too.Pianette

© 2022 - 2024 — McMap. All rights reserved.