2015-10-05 74 views
2

我遇到了这个问题,我希望能在这里找到一些帮助。我创建了一个展示问题的小示例可执行文件和共享库。共享库和libpthread.so的g ++问题

对不起,我意识到这已经变成了一面墙,但我试图确保包括所有相关信息。

我的设置

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中链接以更正上述问题。如果我使用pthread构建,应用程序段错误尝试加载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"); 
} 

应用程序文件(主。 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 <library> | grep NEEDED并没有库在依赖链中有libc.so.1作为依赖关系。他们依赖的libc版本是libc.so.6

的建设场景

我真的希望有人在这里结束了关于正在发生的事情的想法,并可以帮助我。我的Google和StackOverflow技能使我失败了,因为我发现的一切都指出pthread使用不匹配是根本问题。

在此先感谢!

回答

2

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

这是一个全静态链接。

在大多数操作系统上,不能通过全静态二进制对dlopen进行调用(dlopen根本不在libdl.a中提供,链接失败)。

GLIBC是一个例外,但只有dlopen需要支持​​。几乎可以肯定,不支持动态加载libpthread.so.0到完全静态a.out,其中包含自己的libpthread.a副本。简短的回答是:伤害,不要这样做。

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

更新:

GLIBC包括许多图书馆(200+),我会强烈建议不要混合静态和动态链接任何这样的库。换句话说,如果你链接到libc.a,那么使它成为一个完全静态的链接。如果您链接到libc.so,那么不会静态链接libpthread.a,libdl.a或GLIBC的任何其他部分。

+0

@EmployedRusian介意我选择你的大脑?这当然是一个很大的遗留应用程序(20多岁),我们试图一次更新碎片。在这种情况下,情况并非总是如此吗? 静态链接的应用程序构建过程很难修改,所以我一直在试图用最小的mod来做什么(尝试去除下一个静态链接)。我确实尝试将应用程序更改为链接libpthread.so,动态地将所有其他内容保留为静态。你认为这应该工作,因为它可以避免冲突的pthread库? – Scott

+0

当我得出几乎相同的结论时,也将此标记为答案,但没有@EmployedRussian提供的基本原因。 谢谢! – Scott

+0

@Scott我已经更新了答案。 –