連結器、載入器與目的檔格式

函式庫搜尋順序(the library search order)

你的程式需要 libfoo.so,但它在檔案系統的哪裡?可能有好幾份——一份在 /usr/lib、一份附在你的二進位檔旁邊、一份在某開發者的實驗目錄裡。動態連結器必須恰好挑一個,而它的做法是以既定順序查閱一連串固定的地方,在第一個相符處停止。函式庫搜尋順序就是那個序列:決定哪個 libfoo.so 真正被載入的規則。

在典型的 Linux/glibc 系統上,順序大致是:首先是烤進二進位檔的 DT_RPATH 裡的任何目錄(舊的、已棄用的形式,只有在沒有 RUNPATH 時才用);然後是 LD_LIBRARY_PATH 環境變數所列的目錄;然後是二進位檔的 DT_RUNPATH 裡的目錄(較新的烤入形式);然後是由 /etc/ld.so.conf 建立、執行 ldconfig 刷新的系統快取(這個快取 /etc/ld.so.cache 是讓查找快速的原因);最後是預設系統目錄 /lib 與 /usr/lib。這趟走訪中第一個含有正確名稱檔案的目錄勝出。被比對的名字通常是函式庫的 SONAME(例如 libfoo.so.1),不是光禿禿的 libfoo.so,後者通常只是開發用的符號連結。

它重要,是因為「在我機器上能跑」的臭蟲與安全問題都藏在這裡。若 LD_LIBRARY_PATH 指向舊版,你可能默默載入錯誤的函式庫;若某搜尋目錄可被攻擊者寫入又排在系統目錄之前,他們就能替換成惡意函式庫。基於這個原因,set-user-id 程式完全忽略 LD_LIBRARY_PATH。你可以用 ldd(列出解析後的路徑)或 LD_DEBUG=libs ./app(追蹤每次搜尋)精確看到連結器的決定。一個常見誤解是連結器預設會搜尋你的目前目錄——它不會,除非某路徑明確把它放進去。

# 搜尋順序(glibc,簡化): # 1. DT_RPATH(只有在沒有 RUNPATH 時,已棄用) # 2. LD_LIBRARY_PATH # 3. DT_RUNPATH # 4. /etc/ld.so.cache(由 ldconfig 建立) # 5. /lib、/usr/lib $ LD_DEBUG=libs ./app # 追蹤試過的每個目錄

順序中第一個含有符合所需 SONAME 之檔案的目錄勝出;ldconfig 建立讓常見情況變快的快取。

set-user-id 程式為了安全而忽略 LD_LIBRARY_PATH 與其他不可信變數。連結器預設「不」搜尋目前目錄——依賴 '.' 在路徑中既出人意料又有安全風險。

又稱
shared library lookupld.so search path共享函式庫查找順序