Scott
Scott

Reputation: 178

g++ issues with shared libraries and libpthread.so

I've hit a wall with this issue and I'm hoping I can find some help here with this issue. I've created a small sample executable and shared library that exhibits the problem.

Sorry, I realize this has turned into a wall of text, but I was trying to make sure to include all the relevant information.

My Setup

System: CentOS release 5.11 (Final)
g++: gcc version 4.4.7 20120313 (Red Hat 4.4.7-1) (GCC) 
libc.so.6: Compiled by GNU CC version 4.1.2 20080704 (Red Hat 4.1.2-55).

I have also tried this on a Redhat 6.6 machine with similar results.

My scenario:

I have an application that is trying to load a shared library at runtime via ::dlopen(). If I don't link in pthread then it appears to work but it will eventually crash in the shared library trying to throw an exception. The reason for this is that the system runtime libraries were built expecting thread local storage (TLS) and the exception processing uses a data structure from TLS but in this case it's NULL and it causes a crash. The functions are __cxa_allocate_exception and __cxa_get_globals, and it looks like they are using the stub functions from libc since pthread isn't linked in.

The problem I'm having now is trying to link in pthread to correct the issue mentioned above. If I build with pthreads, the application segfaults trying to load libpthread.so.0 as a dependency of my shared library. Everything I've read about this crash is that the the application was built without pthread while the shared library was built with pthread. However I'm building both binaries with pthreads and I still experience the issue.

Sample Code:

Shared Library Files (foo.*)

foo.h

#pragma once
extern "C"
{
    extern void DoWork();
}

foo.cpp

#include "foo.h"
#include <stdio.h>

void DoWork()
{
    printf( "SharedLibrary::DoWork()\n" );
}

Application File (main.cpp)

main.cpp

#include "foo.h"
#include <stdio.h>
#include <dlfcn.h>

void LoadSharedLibrary()
{
    void* handle = 0;
    void(*function)();

    try
    {
        printf( "Loading the shared library\n" );
        handle = ::dlopen( "libfoo.so", 2 );
        function = (void (*)())::dlsym( handle, "DoWork" );
        printf( "Done loading the shared library\n" );

        function();
    }
    catch(...)
    {
        printf( "ERROR - Exception while trying to load the shared library\n" );
    }
}

int main(int argc, char* argv[])
{
    LoadSharedLibrary();
    return 0;
}

Explicit Loading

Trying to load the shared library at runtime using the following build script results in a segfault trying to load libpthread.so.0.

Build script:

compiler=g++
arch=-m32
echo gcc architecture flag: ${arch}

${compiler} -c -fPIC -g ${arch} -pthread -o ./foo.o foo.cpp
${compiler} ${arch} -shared -g -o ./libfoo.so ./foo.o -lpthread

${compiler} -c -fPIC -g ${arch} -pthread -o ./main.o main.cpp
${compiler} ${arch} -static -g -o main.out ./main.o -lpthread -ldl -lc 

The stack trace for this crash is:

#0  0x00000000 in ?? ()
#1  0x0089a70a in __pthread_initialize_minimal_internal () at init.c:417
#2  0x0089a218 in call_initialize_minimal () from /lib/libpthread.so.0
#3  0x00899da8 in _init () from /lib/libpthread.so.0
#4  0x0808909b in call_init ()
#5  0x080891b0 in _dl_init ()
#6  0x08063a87 in dl_open_worker ()
#7  0x0806245a in _dl_catch_error ()
#8  0x0806349e in _dl_open ()
#9  0x08053106 in dlopen_doit ()
#10 0x0806245a in _dl_catch_error ()
#11 0x08053541 in _dlerror_run ()
#12 0x08053075 in __dlopen ()
#13 0x0804830f in dlopen ()
#14 0x0804824f in LoadSharedLibrary () at main.cpp:13
#15 0x080482d3 in main (argc=1, argv=0xffffd3e4) at main.cpp:27

The loaded shared libraries are:

From        To          Syms Read   Shared Object Library
0xf7ffb3b0  0xf7ffb508  Yes         libfoo.so
0x0089a210  0x008a5bc4  Yes (*)     /lib/libpthread.so.0
0xf7f43670  0xf7fbec24  Yes (*)     /usr/lib/libstdc++.so.6
0x009a8410  0x009c35a4  Yes (*)     /lib/libm.so.6
0xf7efb660  0xf7f02f34  Yes (*)     /lib/libgcc_s.so.1
0x0074dcc0  0x0084caa0  Yes (*)     /lib/libc.so.6
0x007197f0  0x0072f12f  Yes (*)     /lib/ld-linux.so.2
(*): Shared library is missing debugging information.

Implicit Loading

This uses a different build script that tries to setup the dependency at build time and would in theory not require an explicit load call. This isn't a valid use case for our real world scenario, but I tried to do this while looking into this issue.

Build Script:

compiler=g++
arch=-m32
echo gcc architecture flag: ${arch}

${compiler} -c -fPIC -g ${arch} -pthread -o ./foo.o foo.cpp
${compiler} ${arch} -shared -g -o ./libfoo.so ./foo.o -lpthread

${compiler} -c -fPIC -g ${arch} -pthread -o ./main.o main.cpp
${compiler} ${arch} -static -g -L. -o main.out ./main.o -lpthread -ldl -Wl,-Bdynamic -lfoo -Wl,-static -lc

Behavior:

Starting program: /app_local/dev3/stack_overflow/main.out 
/bin/bash: /app_local/dev3/stack_overflow/main.out: /usr/lib/libc.so.1: bad ELF interpreter: No such file or directory
/bin/bash: /app_local/dev3/stack_overflow/main.out: Success

During startup program exited with code 1.

The weird thing is that I've done objdump -p <library> | grep NEEDED and none of the libraries in the dependency chain have libc.so.1 as a dependency. The version of libc they depend on is libc.so.6.

End of build scenarios

I'm really hoping somebody here has an idea about what is going on and could help me out. My Google and StackOverflow skills have failed me as everything I've found points to mismatched pthread usage as the root problem.

Thanks in advance!

Upvotes: 3

Views: 3960

Answers (1)

Employed Russian
Employed Russian

Reputation: 213375

${compiler} ${arch} -static -g -o main.out ./main.o -lpthread -ldl -lc

This is a fully-static link.

On most OSes, no calls to dlopen can be made from a fully-static binary (dlopen is simply not provided in libdl.a, and the link fails).

GLIBC is an exception, but only so far as dlopen is needed to support /etc/nsswitch.conf. Almost certainly dynamic loading of libpthread.so.0 into a fully-static a.out that contains its own copy of libpthread.a is not supported. The short answer is: it hurts, don't do that.

Fully-static linking is in general a very bad idea on any modern UNIX system. Fully-static linking of multithreaded apps doubly so. Fully-static linking that then dynamically loads another copy of libpthread? Really bad idea.

Update:

GLIBC consists of many libraries (200+), and I would strongly advise against mixing static and dynamic linking for any such library. In other words, if you link against libc.a, then make it a completely static link. If you link against libc.so, then don't statically link libpthread.a, libdl.a, or any other part of GLIBC.

Upvotes: 3

Related Questions