2009-12-04 89 views
7

我想創建一個使用第三方靜態庫中的函數的共享庫。例如,來自libfoobar.afoobar。我知道我的主要應用程序也使用foo,並將導出該符號。所以我只想鏈接bar以節省代碼大小並保留'foo'未解決(因爲它將由主應用程序提供)。如果我包含libfoobar.a,鏈接器ld將在我的共享庫中包含這兩個函數。如果我不包含libfoobar.a,我的程序庫將無法訪問函數bar,因爲應用程序本身沒有在bar中鏈接。問題:共享庫中庫函數的選擇性靜態鏈接

  • 有沒有辦法告訴LD只能解決某些符號構建共享庫時?
  • libfoobar.a轉成共享庫?
  • 包含函數bar的提取文件從libfoobar.a並指定在鏈接器行?
  • 不用擔心,運行時加載程序將從您的應用程序中使用bar,因此共享庫中的bar副本將不會被加載?

回答

4

以下幾點嘗試回答我提出的問題:

  • LD似乎並沒有讓你忽略從靜態庫中的某些符號鏈接。 --just-symbols--undefined(或EXTERN鏈接描述文件命令)的用法不會阻止鏈接符號的ld
  • 到靜態庫,libfoobar.a,轉換成共享一個,libfoobar.so.1.0和導出所有可見的符號。您還可以使用--version-script和其他方法僅導出符號的一個子集。

    ld -shared -soname libfoobar.so.1 -o libfoobar.so.1.0 --whole-archive libfoobar.a --no-whole-archive

  • 這是更好地從靜態庫的副本刪除歸檔成員比它提取出來,因爲有可能是你需要管理內部的依賴關係。例如,假設您正在導出所有符號,則可以從主可執行文件生成一個映射文件。然後,您可以從靜態庫的副本中爲所有可執行文件引入的歸檔成員進行grep,並從副本中刪除它們。所以當你的DSO在靜態庫中鏈接時,它會留下相同的符號未解決。

  • 如果您使用--pie選項編譯可執行文件,則可以將DSO的主要可執行文件指定爲DSO的共享庫。如果DSO在鏈接命令中的靜態庫之前,您的DSO將首先鏈接到您的可執行文件。需要注意的是主要的可執行文件必須通過LD_LIBRARY_PATH-rpath。此外,使用strace表明,由於可執行文件是庫的依賴項,因此在DSO加載時會再次加載它。

    ld -shared -rpath '$ORIGIN' -L. -lc -ldl -o DSO.so DSO.o app libfoobar.a

  • 動態鏈接程序將使用第一,除非你調用dlopen()的的可執行文件的版本與RTLD_DEEPBIND標誌。使用strace表明整個DSO被映射到mmap2()到內存中。然而,維基百科聲稱,對於mmap「在訪問特定位置後,實際從磁盤讀取的數據以」懶惰「方式執行。」如果這是真的,那麼重複的foo將不會被加載。請注意,只有在您的DSO 導出函數foo時纔會發生覆蓋。否則,只要您的DSO呼叫foo,靜態鏈接到您的DSO的功能foo將被使用。

總之,如果mmap()的使用一個懶惰的讀取,那麼最好的辦法是你的DSO以正常方式連接,讓動態連接器和Linux採取其他的事情。

1

我不是共享庫的最大專家,所以我可能在這裏錯了!

如果我正在猜測你想做什麼,只需將你的共享庫與libc.so鏈接起來。您不需要在庫中嵌入sscanf的額外副本。

我在回答你的問題之前,我已經弄清楚了你的情況,以防你對答案感興趣。

有沒有辦法告訴ld在構建共享庫時只解析某些符號?

只有外部,而不是靜態的函數和變量進入共享庫的符號表。

當您構建共享庫時,鏈接器命令行中對象中找不到的任何符號將保持未解析狀態。如果連接器對此抱怨,那麼您可能需要將共享庫鏈接到共享的 libc。您可以共享依賴於其他共享庫的庫,並且ld.so可以處理依賴鏈。

如果我有更多的代表,我會問這是一個評論: 你有一個自定義版本的sprintf/sscanf,或者它可以讓共享庫在-lc中使用實現嗎?如果-lc很好,那麼我的答案可能會解決您的問題。如果沒有,那麼你需要從只有你需要的功能的對象中建立共享庫。即不要將其鏈接到/usr/lib/libc.a。

也許我就要你的

的libc.a(實際上沒有 「真正」 的libc) 線混淆。 /usr/lib/libc.a實際上是glibc(在linux上)。它是libc.so中相同代碼的靜態鏈接副本。除非你在談論你自己的libc.a(這是我最初想的)...

將libc.a變成共享庫? 您可能可以,但不可以,因爲它可能不會被編譯爲與位置無關的代碼,所以在運行時需要ld.so進行大量重定位。

從libc.a中提取sscanf並指定在鏈接器行上?

可能。 ar t /usr/lib/libc.a列出內容。 (ar的參數與tar類似,tar是磁帶的ar ...老式的Unix在這裏)。可能不那麼容易,因爲sscanf可能依賴於.a中其他.o文件中的符號。

+0

對不起libc的困惑。我只是指任何第三方靜態庫,並以libc爲例。我要修改我的問題來澄清這一點。 – KlaxSmashing 2009-12-07 19:30:09

1

回答您的修訂更清晰的問題。

請記住,通常共享庫的點是多個程序可以鏈接它。因此,如果主程序始終提供該符號(通過靜態庫或其他方式),那麼只需使用主程序的符號進行優化即可。這通常不是人們想要做的。

如果它只是一些小功能,可能你應該放過它。你最終可能會得到兩個函數代碼副本,一個在你的shlib中,一個在主程序中。如果它們很小(或者至少不是很大),或者不經常被調用,而且性能不重要,那麼由於具有兩個副本而產生的代碼大小/ I-cache不會被擔憂。 (譯文:我不知道如何避免它離開我的頭頂,所以我可能不會花時間去查看它並製作更復雜的Makefile以避免它。)

查看我的其他答案對於一些關於用ar來從靜態庫中提取東西的評論。總結:可能並不重要,因爲您不知道.a中各個.o文件之間的依賴關係。

通過讓共享庫導出它從靜態庫中引入的符號,可以做到您希望做的。然後,當您鏈接主應用程序時,請將您的共享庫放在鏈接程序命令行上的靜態庫之前。 ld會在shlib中找到「foo」,並使用該副本(如果這種重新導出技巧是可能的話),但是對於「bar」,它將不得不包含來自靜態庫的副本。

ld - 導出動態可能是您需要導出動態符號表中的所有符號。試試看。並在docs/man頁面中搜索「導出」。 「導出」是在庫中使符號可見的行話。 --export-all-symbols在i386 PE(windows DLL)部分,否則它可能會做伎倆。

+0

在ld手冊頁中注意到了一些事情:--just-symbols = filename:「從文件名讀取符號名稱及其地址,但不要重定位或將其包含在輸出中,這樣可以使輸出文件以符號方式指向絕對位置的其他程序中定義的內存「。 – 2009-12-10 04:40:58

+0

由於共享庫是一個「插件」(即並不總是被加載),因此它不能爲其他代碼,特別是主應用程序提供符號。 最簡單的可能是將第三方靜態庫變成動態庫。 – KlaxSmashing 2009-12-11 14:30:18