【问题标题】:g++ issues with shared libraries and libpthread.so共享库和 libpthread.so 的 g++ 问题
【发布时间】:2016-01-02 17:28:46
【问题描述】:

我遇到了这个问题,我希望我能在这个问题上找到一些帮助。我创建了一个小型示例可执行文件和共享库来显示该问题。

抱歉,我意识到这已经变成了一堵文字墙,但我试图确保包含所有相关信息。

我的设置

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).

我也在 Redhat 6.6 机器上尝试过,结果相似。

我的场景:

我有一个应用程序试图在运行时通过 ::dlopen() 加载共享库。如果我不在 pthread 中链接,那么它似乎可以工作,但它最终会在共享库中崩溃,试图引发异常。这样做的原因是系统运行时库的构建需要线程本地存储 (TLS),并且异常处理使用来自 TLS 的数据结构,但在这种情况下它为 NULL 并导致崩溃。这些函数是 __cxa_allocate_exception 和 __cxa_get_globals,看起来它们正在使用 libc 中的存根函数,因为 pthread 未链接。

我现在遇到的问题是尝试在 pthread 中链接以纠正上述问题。如果我使用 pthreads 构建,则应用程序会尝试将 libpthread.so.0 作为共享库的依赖项加载。我读到的有关此崩溃的所有信息都是应用程序是在没有 pthread 的情况下构建的,而共享库是使用 pthread 构建的。但是,我正在使用 pthread 构建两个二进制文件,但我仍然遇到这个问题。

示例代码:

共享库文件 (foo.*)

foo.h

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

foo.cpp

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

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

应用程序文件(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;
}

显式加载

尝试使用以下构建脚本在运行时加载共享库会导致尝试加载 libpthread.so.0 时出现段错误。

构建脚本:

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 

这次崩溃的堆栈跟踪是:

#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

加载的共享库是:

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.

隐式加载

这使用了一个不同的构建脚本,它试图在构建时设置依赖关系,理论上不需要显式的加载调用。对于我们的真实场景来说,这不是一个有效的用例,但我在研究这个问题时尝试这样做。

构建脚本:

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

行为:

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

在启动过程中程序以代码 1 退出。

奇怪的是我已经完成了objdump -p &lt;library&gt; | grep NEEDED 并且依赖链中的库都没有libc.so.1 作为依赖。他们所依赖的libc版本是libc.so.6

构建场景结束

我真的希望这里有人对正在发生的事情有所了解并可以帮助我。我的 Google 和 StackOverflow 技能让我失望了,因为我发现的所有问题都表明 pthread 使用不匹配是根本问题。

提前致谢!

【问题讨论】:

    标签: c++ linux g++ pthreads shared-libraries


    【解决方案1】:

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

    这是一个完全静态的链接。

    在大多数操作系统上,不能从完全静态的二进制文件中调用dlopenlibdl.a 中根本没有提供dlopen,并且链接失败)。

    GLIBC 是一个例外,但仅在需要 dlopen 来支持 /etc/nsswitch.conf 的情况下。几乎可以肯定,不支持将libpthread.so.0 动态加载到包含其自己的libpthread.a 副本的完全静态a.out 中。简短的回答是:很痛,不要那样做。

    在任何现代 UNIX 系统上,全静态链接通常都是一个非常糟糕的主意。多线程应用程序的完全静态链接更是如此。然后动态加载libpthread 的另一个副本的全静态链接? 真的坏主意。

    更新:

    GLIBC 由许多库 (200+) 组成,我强烈建议不要为任何此类库混合静态和动态链接。换句话说,如果你链接到libc.a,那么让它成为一个完全静态的链接。如果您链接到 libc.so,则不要静态链接 libpthread.alibdl.a 或 GLIBC 的任何其他部分。

    【讨论】:

    • @EmployedRusian 介意我选你的大脑吗?这当然是我们试图一次更新的大型遗留应用程序(20 多年前)的情况。有这种情况时,不总是这样吗?静态链接的应用程序构建过程很难修改,所以我一直在尝试看看用最少的 mods 可以做什么(接下来是尝试删除静态链接)。我确实尝试将应用程序更改为动态链接 libpthread.so,而将其他所有内容保持为静态。你认为这应该可以避免冲突的 pthread 库吗?
    • 也将此标记为答案,因为我得出了几乎相同的结论,但没有@EmployedRussian 提供的根本原因。谢谢!
    • 非常感谢您的信息/帮助!
    猜你喜欢
    • 1970-01-01
    • 2012-01-14
    • 2015-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多