16

分析this question我在Linux上發現了有關動態加載(dlopen)上的弱符號分辨率行爲的一些問題。現在我正在尋找這方面的規範。我們需要an example。假設有一個程序a,其按照該順序動態加載庫b.soc.so。如果c.so依賴於另外兩個庫foo.so(實際上在該示例中爲libgcc.so)和bar.so(實際上爲libpthread.so),則通常可以使用由bar.so導出的符號來滿足foo.so中的弱符號鏈接。但如果b.so也取決於foo.so而不是bar.so,那麼這些弱符號顯然不會與bar.so鏈接。看起來好像foo.so墨水只能查找ab.so及其所有依賴關係中的符號。動態加載和弱符號分辨率

這在一定程度上是有道理的,因爲否則加載c.so可能會在b.so已經使用該庫的某個點上更改foo.so的行爲。另一方面,在讓我開始的這個問題上引起了相當多的麻煩,所以我想知道是否有解決這個問題的辦法。爲了找到解決辦法,我首先需要很好地瞭解如何在這些情況下指定符號解析的具體細節。

什麼是規範或其他技術文件來定義在這些情況下的正確行爲?

+0

您有沒有看過[此PDF](http://refspecs.linuxbase.org/elf/elf.pdf)?很多有趣的數據,但不知道它是否包含你所尋找的。 – rodrigo

+0

@rodrigo:不知道它是否是這個或類似的東西,但到目前爲止,我發現的所有ELF文檔只是描述了執行二進制文件之前的動態鏈接,而不是涉及動態加載對象的鏈接。這是一個很長的文件,我可能在錯誤的地方看過,但到目前爲止,它似乎不是我正在尋找的東西。 – MvG

+0

那麼[Drepper的帖子](http://www.sourceware.org/ml/libc-hacker/2000-06/msg00029.html)及其或多或少的[相關文檔](http:// www。 akkadia.org/drepper/dsohowto.pdf)(見第1.5.2節)?按照我的理解,弱符號僅用於靜態鏈接。所以'dlopen()'不會對弱符號和強符號產生影響。 – rodrigo

回答

11

不幸的是,權威性文檔是源代碼。 Linux的大多數發行版都使用glibc或其fork,eglibc。在兩者的源代碼,即應記錄dlopen()的文件的內容如下:

手動/ libdl.texi

@c FIXME these are undocumented: 
@c dladdr 
@c dladdr1 
@c dlclose 
@c dlerror 
@c dlinfo 
@c dlmopen 
@c dlopen 
@c dlsym 
@c dlvsym 

有可以從ELF specification和POSIX標準繪製什麼技術規範。 ELF規範是使弱符號有意義的原因。 POSIX本身就是實際的specification for dlopen()

這是我發現的ELF規範中最相關的部分。

當鏈接編輯器搜索歸檔庫時,它將提取包含未定義全局符號定義的歸檔文件 。成員的定義可以是全局符號或弱符號。

ELF規範沒有提到動態加載,所以本段的其餘部分是我自己的解釋。我發現上述相關的原因是解析符號出現在一個單獨的「when」時。在您給出的示例中,當程序a動態加載b.so時,動態加載器將嘗試解析未定義的符號。它可能會以全局或弱符號結束。當程序動態加載c.so時,動態加載程序再次嘗試解析未定義的符號。在你描述的場景中,b.so中的符號用弱符號解決。一旦解決,這些符號不再未定義。使用全局或弱符號來定義它們並不重要。在加載c.so時,它們已不再未定義。

ELF規範沒有提供關於鏈接編輯器或鏈接編輯器必須組合目標文件的準確定義。推測這是一個非問題,因爲該文件考慮了動態鏈接。

POSIX描述了一些dlopen()功能,但留下了許多實現,包括問題的實質。一般來說,POSIX不提及ELF格式或弱符號。對於實現dlopen()的系統,甚至不需要任何弱符號的概念。

http://pubs.opengroup.org/onlinepubs/9699919799/functions/dlopen.html

POSIX標準是另一個標準,Linux標準庫的一部分。 Linux發行版可能會或可能不會選擇遵循這些標準,可能會或可能不會去認證的麻煩。例如,據我所知,Open Group的正式Unix認證非常昂貴 - 因此大量的「類Unix」系統。

關於dlopen()的標準符合性的一個有趣點在Wikipedia article for dynamic loading上進行。按照POSIX的規定,dlopen()返回一個void *,但是,如ISO所強制的那樣,C說void *是一個指向對象的指針,這樣的指針不一定與函數指針兼容。

的事實仍然是功能和對象 指針之間任意轉換必須被視爲(固有的非便攜式) 實現擴展,而對於直接 轉換沒有「正確」的方式存在,因爲在這方面POSIX和ISO標準 相互矛盾。

無論如何,確實存在的標準是矛盾的,哪些標準文件可能並不特別有意義。這裏是Ulrich Drepper寫的關於他對Open Group及其「規格」的蔑視。

http://udrepper.livejournal.com/8511.html

類似的情緒在由羅德里戈鏈接後表示。

我做了這個變化的原因是不是真的更符合的 (這是不錯的,但因爲沒有人沒有理由抱怨老 行爲)。

看過之後,我相信你問的問題的正確答案是dlopen()在這方面沒有正確或錯誤的行爲。可以說,一旦搜索已經解決了一個符號,它就不再是未定義的,並且在隨後的搜索中動態加載器不會嘗試解析已定義的符號。

最後,正如您在評論中所述,您在原始帖子中描述的內容不正確。動態加載的共享庫可用於解析先前動態加載的共享庫中未定義的符號。實際上,這並不侷限於動態加載代碼中未定義的符號。下面是一個例子,其中可執行文件本身有一個通過動態加載解析的未定義符號。

的main.c

#include <dlfcn.h> 

void say_hi(void); 

int main(void) { 
    void* symbols_b = dlopen("./dyload.so", RTLD_NOW | RTLD_GLOBAL); 
    /* uh-oh, forgot to define this function */ 
    /* better remember to define it in dyload.so */ 
    say_hi(); 
    return 0; 
} 

dyload。c

#include <stdio.h> 
void say_hi(void) { 
    puts("dyload.so: hi"); 
} 

編譯並運行。

gcc-4.8 main -fpic -ldl -Wl,--unresolved-symbols=ignore-all -o main 
gcc-4.8 dyload.c -shared -fpic -o dyload.so 
$ ./main 
dyload.so: hi 

請注意,主可執行文件本身被編譯爲PIC。

+0

這就是我稱之爲卓越的答案! – paulotorrens