2015-10-05 122 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我已經更新了答案。 –