#gold 和 GNU ld 行為差異造成的問題 原因
ToolChain/DSOLinking - Debian Wiki 介紹得很清楚, 主要的差異是處理 DT_NEEDED section 的行為不同。
對 program A 和 shared lib B 和 C 來說, 假設相依性如下:
A 用到 B 和 C
B 用到 C 照理說 link 的設定要如實反應出相依性。若 link 的設定如下:
A: link B
B: link C
linker 是 GNU ld (BFD-based linker) 的時候會成功, 但用 gold 的時候會失敗。原因是 linker 會在目標的 DT_NEEDED section 記錄用到的 shared lib, 然後 GNU ld 預設會將 DT_NEEDED section 的值一併「往上傳」。所以 A link B 的時候不只會在 DT_NEEDED section 加上 B 的記錄, 還會取得 B 的 DT_NEEDED section 內的值, 所以沒寫 link C 也會有 C 的記錄。
這造成一個問題: 日後 B 沒有用到 C 的時候, 會因為升級 B 而造成 A 的 link error, 錯誤原因是沒有 link C。
解法是用 GNU ld link 時也要下 --no-add-needed 避免自動取用 shared lib 內的 DT_NEEDED section 的內容。這樣開發的時候才會注意到漏加 shared lib。
這種情況容易發生嗎? 就我個人的經驗來說, 比我預期的容易。比方說寫 GTK+ 也會用到 glib, 但 GTK+ 自己也有用 glib, 所以 GTK+ 的 DT_NEEDED section 有含 glib, 然後自己就忘了加。若用 GNU ld link, 沒加 glib 也會成功。或是很多 lib 用到 pthread, 自己也用到或加入的 static lib 間接用到, 然後忘了 link pthread。
gold link 的行為不但比較正確,速度比較快也比較省記憶體。可以的話, 改用 gold 作為系統預設 linker 比較省事。Mac OS X 已經用 gold 了, Ubuntu 12.04 要另裝 binutils-gold:
$ sudo apt-get install binutils-gold
然後檢查看看:
$ ls -l /usr/bin/ld
lrwxrwxrwx 1 root root 7 Apr 2 00:05 /usr/bin/ld -> ld.gold*
安裝前會是連到 GNU ld, 像這樣:
$ ls -l /usr/bin/ld
lrwxrwxrwx 1 root root 7 Apr 2 00:02 /usr/bin/ld -> ld.bfd*
若沒有成功, 可能要用 update-alternatives 切換預設 linker (Ubuntu 14.04 好像需要), 詳見這裡。
1. GNU ld 預設會加入所有指定要 link 的 shared lib 到 DT_NEEDED section, 即使沒有用到那些 lib 也一樣。若希望只加入用到的 lib, 要加上參數 --as-needed。
2. 可以用 ldd 列出 executable 或 shared lib 相依的 lib, 像這樣:
$ ldd /bin/ls
linux-vdso.so.1 => (0x00007fffe2351000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007ff642698000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007ff642490000)
libacl.so.1 => /lib/x86_64-linux-gnu/libacl.so.1 (0x00007ff642287000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff641ec8000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ff641cc4000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff6428ce000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ff641aa6000)