TLPI ch41 相當值得一看, 從開發者使用 library 的角度說明 library 的生成、靜態連結、動態連結 (載入) 的行為, 內容不多不少, 正好就是我想知道的, 省了看 linker、loader 的時間。
shared library 的名詞介紹
以 libjpeg 為例, 對應如下:
libjpeg.so -> libjpeg.so.62.0.0 # linker name
libjpeg.so.62 -> libjpeg.so.62.0.0 # soname
libjpeg.so.62.0.0 # real name
這是我在 Ubuntu 裝好 package 後的樣子, 照理說 libjpeg.so 指向 libjpeg.so.62 應該會更彈性。
讀出 soname:
$ readelf -d libjpeg.so | grep SONAME
0x000000000000000e (SONAME) Library soname: [libjpeg.so.62]
static 和 dynamic linker
ld (ld.bfd) 是 static linker。Google 開發的 gold 是取代 ld.bfs 的 static linker。用 gcc 連結 shared library 或 executable 時就是呼叫 ld, 並將需要的參數傳給它。不論連結的是 static library 或 shared library, 都是 static linking。
ld 在連結 shared library 或 executable 時, 會將需要的 shared library 的 soname 寫入結果檔裡。注意, 只有 soname 而已, 沒有完整路徑。
ld-VERSION.so 是 dynamic (runtime) linker, 執行程式時, 由 runtime linker 載入 executable 開始。若 OS 用的 glibc 版本為 2.13, 就叫 ld-2.13.so。用 ldd 看所有執行檔, 都會找到它 (某個 symbolic link 連到 ld-2.13.so)。
以連結 libm.so 為例, 執行 gcc -lm prog.c -o prog 中間的部份行為如下:
gcc 透過 -lm 的指示告知 ld 要連結 libm.so
ld 會找到某處的 libm.so 指向 /lib/x86_64-linux-gnu/libm.so.6, 確認要用到的 symbol 都有, 沒有 link error
ld 從 libm.so.6 的 header 讀出 soname "libm.so.6", 寫入 "libm.so.6" 到 prog 的 header。 執行 prog 時, ld-2.13.so 會從 prog 讀出 "libm.so.6", 再到預設的路徑上找檔名 "libm.so.6"。注意, static linking 時需要 libm.so, 但之後執行 prog 時用不到它, 因為記錄的 soname 為 "libm.so.6"。
關於 static linking 找檔名的順序, 可用 strace 觀察:
$ strace -e open,execve -f -o gcc.trace gcc -lm prog.c -o prog
在 gcc.trace 裡可看出一二。
ps.
ldconfig
執行 ldconfig 後, 它做的事如下:
讀出 /lib, /usr/lib, /etc/ld.so.conf 內的路徑之下的 shared library (ldconfig 會略過 symbolic link), 將結果寫入 /etc/ld.so.cache。之後 ld-2.13.so 會用 ld.so.cache 的記錄來找 shared library。
ldconfig 會自動產生 symbolic link "libX.so.MAJOR" 指向最新版本的 shared library。例如 /lib/libfoo.so.2.0.1 的 soname 是 libbar.so.2, 執行 ldconfig 後, 它會產生 /lib/libbar.so.2 指向 /lib/libfoo.so.2.0.1。
之前困擾我許久的事就是第二步, 而 man ldconfig 裡沒提到這點。
結論是別隨便手動更新 soname 的檔案, 執行 ldconfig 後可能會出問題。
裝套件後, 系統工具會自動跑 ldconfig 更新目錄, 可能會蓋掉自己手動更新的同檔名檔案。另外 ldconfig 沒有管 linker name, 若是自己編的 shared library, 要自己產生。
其它
若想連到舊的 major 版本 shared library, 得在 gcc 參數指定舊版檔名。還有可用 rpath 的參數寫入搜尋 shared library 的路徑到 shared library 或 executable 裡。關於這些細節, 還有 static linker 以及 dynamic linker 尋找 shared library 的完整順序, TLPI ch41 講得相當清楚。ch42 描述 dlopen, 之後再來翻翻。