我們知道linux鏈結so有兩種途徑:顯示和隱式。所謂顯示就是程式主動呼叫dlopen開啟相關so;這裡需要補充的是,如果使用顯示鏈結,上篇文章討論的那些問題都不存在。首先,dlopen的so使用ldd是檢視不到的。其次,使用dlopen開啟的so並不是在程序啟動時候載入對映的,而是當程序執行到呼叫dlopen**地方才載入該so,也就是說,如果每個程序顯示鏈結a.so;但是如果發布該程式時候忘記附帶發布該a.so,程式仍然能夠正常啟動,甚至如果執行邏輯沒有觸發執行到呼叫dlopen函式**地方。該程式還能正常執行,即使沒有a.so.
既然顯示載入這麼多優點,那麼為什麼實際生產中很少碼農使用它呢, 主要原因還是起使用不是很方便,需要開發人員多寫不少**。所以不被大多數碼農使用,還有乙個重要原因應該是能提前發現錯誤,在部署的時候就能發現缺少哪些so,而不是等到實際上限執行的時候才發現缺東少西。
下面舉個工作中最常碰到的問題,來引申出本篇內容吧。
寫乙個最簡單的so, tmp.cpp
1. int test()
2.
[stevenrao]
$g++ -fpic -c tmp.cpp
[stevenrao]$
g++ -shared -o libtmp.so tmp.o
[stevenrao]$
mv libtmp.so /tmp/
[stevenrao]$
g++ -o demo -l/tmp -ltmp main.cpp
[stevenrao]$
./demo
./demo: error while loading shared libraries: libtmp.so: cannot open shared object file: no such file or directory
[stevenrao]$
ldd demo
linux-vdso.so.1 => (0x00007fff7fdc1000)
libtmp.so => not found
這個錯誤是最常見的錯誤了。執行程式的時候找不到依賴的so。一般人使用方法是修改ld_library_path這個環境變數
export ld_library_path=/tmp
[stevenrao]$ ./demo
test
這樣就ok了, 不過這樣export 只對當前shell有效,當另開乙個shell時候,又要重新設定。可以把export ld_library_path=/tmp 語句寫到 ~/.bashrc中,這樣就對當前使用者有效了,寫到/etc/bashrc中就對所有使用者有效了。
前面鏈結時候使用 -l/tmp/ -ltmp 是一種設定相對路徑方法,還有一種絕對路徑鏈結方法。
[stevenrao]$ g++ -o demo /tmp/libtmp.somain.cpp
[stevenrao]$ ./demo
test
[stevenrao]$ ldd demo
linux-vdso.so.1=> (0x00007fff083ff000)
/tmp/libtmp.so(0x00007f53ed30f000)
絕對路徑雖然申請設定環境變數步驟,但是缺陷也是致命的,這個so必須放在絕對路徑下,不能放到其他地方,這樣給部署帶來很**煩。所以應該禁止使用絕對路徑鏈結so。
搜尋路徑分兩種,一種是鏈結時候的搜尋路徑,一種是執行時期的搜尋路徑。像前面提到的 -l/tmp/ 是屬於鏈結時期的搜尋路徑,即給ld程式提供的編譯鏈結時候尋找動態庫路徑;而ld_library_path則既屬於鏈結期搜尋路徑,又屬於執行時期的搜尋路徑。
這裡需要介紹鏈-rpath鏈結選項,它是指定執行時候都使用的搜尋路徑。聰明的同學馬上就想到,執行時搜尋路徑,那它記錄在哪兒呢。也像. ld_library_path那樣,每部署一台機器就需要配一下嗎。呵呵,不需要..,因為它已經被硬編碼到可執行檔案內部了。看看下面演示
1. [stevenrao] $g++ -o demo -l /tmp/ -ltmp main.cpp
2. [stevenrao] $./demo
3. ./demo: error while loading sharedlibraries: libtmp.so: cannot open shared object file: no such file or directory
4. [stevenrao] $g++ -o demo -wl,-rpath /tmp/ -l/tmp/ -ltmp main.cpp
5. [stevenrao] $ ./demo
6. test
7. [stevenrao] $readelf -d demo
8.
9. dynamic section at offset 0xc58 contains 26entries:
10. tag type name/value
11. 0x0000000000000001(needed) shared library: [libtmp.so]
12. 0x0000000000000001(needed) shared library:[libstdc++.so.6]
13. 0x0000000000000001(needed) shared library: [libm.so.6]
14. 0x0000000000000001(needed) shared library:[libgcc_s.so.1]
15. 0x0000000000000001(needed) shared library: [libc.so.6]
16. 0x000000000000000f(rpath) library rpath: [/tmp/]
17. 0x000000000000001d(runpath) library runpath: [/tmp/]
看看是吧,編譯到elf檔案內部了,路徑和程式深深的耦合到一起
so搜尋路徑,還有乙個類似於-path,叫
ld_run_path環境變數, 它也是把路徑編譯進可執行檔案內,不同的是它只設定rpath。
[stevenrao]$
g++ -o demo -l /tmp/ -ltmp main.cpp
[stevenrao]$
readelf -d demo
dynamic section at offset 0xb98 contains 25 entries:
tag type name/value
0x0000000000000001 (needed) shared library: [libtmp.so]
....
0x000000000000000f(rpath)library rpath: [/tmp/]
另外還可以通過配置
/etc/ld.so.conf,在其中加入一行
/tmp/
這個配置項也是只對執行期有效,並且是全域性使用者都生效,需要root許可權修改,修改完後需要使用命令
ldconfig將
/etc/ld.so.conf 載入到
ld.so.cache中,避免重啟系統就可以立即生效。
除了前面介紹的那些搜尋路徑外,還有預設搜尋路徑/usr/lib/ /lib/ 目錄,可以通過-z nodefaultlib編譯選項禁止搜尋預設路徑。
[stevenrao] $g++ -o demo -z nodefaultlib -l/tmp -ltmp main.cpp
[stevenrao] $./demo
./demo: error while loading shared libraries:
libstdc++.so.6: cannot open shared object file
這麼多搜尋路徑,他們有個先後順序如下
1、rumpath 優先順序最高
2、rpath 其次
3、ld_library_path
4、 /etc/ld.so.cache 5、
/usr/lib/ /lib/
檢視乙個程式搜尋其各個動態庫另乙個簡單的辦法是使用
ld_debug這個環境變數;
[stevenrao] $
export ld_debug=libs
[stevenrao] $./demo
linux下so動態庫一些不為人知的秘密
linux 下有動態庫和靜態庫,動態庫以.so為副檔名,靜態庫以.a為副檔名。二者都使用廣泛。本文主要講動態庫方面知識。基本上每乙個linux 程式都至少會有乙個動態庫,檢視某個程式使用了那些動態庫,使用 ldd命令檢視 ldd bin ls linux vdso.so.1 0x00007fff59...
linux下so動態庫一些不為人知的秘密(中)
介紹了linux下so一些依賴問題,本篇將介紹linux的so路徑搜尋問題。我們知道linux鏈結so有兩種途徑 顯示和隱式。所謂顯示就是程式主動呼叫dlopen開啟相關so 這裡需要補充的是,如果使用顯示鏈結,上篇文章討論的那些問題都不存在。首先,dlopen的so使用ldd是檢視不到的。其次,使...
linux下so動態庫一些不為人知的秘密(中)
本篇將介紹linux的so路徑搜尋問題。我們知道linux鏈結so有兩種途徑 顯示和隱式。所謂顯示就是程式主動呼叫dlopen開啟相關so 這裡需要補充的是,如果使用顯示鏈結,上篇文章討論的那些問題都不存在。首先,dlopen的so使用ldd是檢視不到的。其次,使用dlopen開啟的so並不是在程序...