我們知道在linux下,.so檔案相當與windows上的dll檔案,即動態連結程式庫。
動態連結程式庫是為了減少發布程式的大小,可以將具有相同功能的code放在動態連結程式庫中,隨應用程式一起發布。而對於應用程式來說,只需要知道其介面就可以,在運行時動態載入代碼到記憶體中,與其相反的是靜態連結庫。
在接手一個項目的代碼後,因為此代碼需要很多第三方庫,所以,在本機安裝這些第三方庫之後,我也成功的通過了編譯與連結階段,產生了可以執行的代碼。
可是在啟動並執行時候,出現了以下的錯誤提示:
baiyang@baiyang-Lenovo-G450:~/Desktop$ ./test_cal_features./test_cal_features: error while loading shared libraries: libCGAL.so.5: cannot open shared object file: No such file or directory
這裡涉及幾個問題:
1.為何能通過編譯與連結,卻不能運行?
2.我明明將libCGAL.so.5,安裝到了/usr/local/lib下啊,既然能通過編譯與連結,應該能找到啊?
3.執行./test_cal_features時,到底發生了什麼事?
4.linux下,應用程式如何對.so進行搜尋?
但本質問題,就是linux下gcc搜尋路徑設定問題.
好,今天就一一來回答以上到問題。
開始一個新的例子
plus.c 檔案
view plaincopy to clipboardprint?
- int plus(int a, int b)
- {
- return a + b;
- }
編譯成動態連結程式庫
gcc plus.c -o libfoo.so -shared -fPIC
產生動態連結程式庫libfoo.so
另外寫一個main.c檔案
view plaincopy to clipboardprint?
- #include <stdio.h>
- #include <stdlib.h>
-
- int main(int argc, char *argv[])
- {
- int sum = plus(3, 5);
- printf("%d\n", sum);
-
- return 0;
- }
現在開始思考如何存放libfoo.so位置?
開始,我們先將libfoo.so存放在目前的目錄中,進行編譯和連結
gcc main.c -o output -lfoo //-lfoo告訴gcc,我們需要動態連結foo庫。
出現以下提示:
baiyang@baiyang-Lenovo-G450:~/workspace/test_so$ lslibfoo.so main.c plus.cbaiyang@baiyang-Lenovo-G450:~/workspace/test_so$ gcc main.c -o main -lfoo/usr/bin/ld: cannot find -lfoocollect2: ld returned 1 exit status
也就是說,gcc不能找到foo庫,那麼我們如果顯示的指示gcc如何搜尋foo呢,我們可以修改LIBRARY_PATH值
baiyang@baiyang-Lenovo-G450:~/workspace/test_so$ export LIBRARY_PATH="."baiyang@baiyang-Lenovo-G450:~/workspace/test_so$ gcc main.c -o main -lfoobaiyang@baiyang-Lenovo-G450:~/workspace/test_so$ lslibfoo.so main main.c plus.c
恭喜,成功找到了.
那好,如果此時我們運行代碼
baiyang@baiyang-Lenovo-G450:~/workspace/test_so$ ./main./main: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory
嗚嗚,遇到開始提到的問題了.
那我們怎麼看輸出到可執行檔main怎麼讀取libfoo.so呢,在運行過程中到底讀的什麼檔案呢?
我們可以用strace命令查看到底有什麼訊號發生
strace ./main
你會發現,它搜尋到路徑是
/lib/i386-linux-gnu,/usr/lib等路徑目錄下
也就說,是因為沒有搜尋目前的目錄,我們可以通過修改以下命令進行修改
baiyang@baiyang-Lenovo-G450:~/workspace/test_so$ export LD_LIBRARY_PATH=".":$LD_LIBRARY_PATHbaiyang@baiyang-Lenovo-G450:~/workspace/test_so$ lslibfoo.so main main.c plus.cbaiyang@baiyang-Lenovo-G450:~/workspace/test_so$ ./main8
成功運行,用strace查看存在這一句open(“./libfoo.so”, O_RDONLY|O_CLOEXEC) = 3
故,現在搜尋了目前的目錄。
總結一下,前面提到過的幾個環境變數:
- LIBRARY_PATH 該環境變數可設定為一個或多個目錄名字列表,串連 程式會搜尋該目錄,以尋找特殊串連程式檔案,和由 -l (字母 l )命令列選項指定名字的庫。由 -L 命令列選項指定的目錄在環境變 量的前面,首先被尋找。也見 COMPILER_PATH 。
- LD_LIBRARY_PATH 該環境變數不會影響編譯器,但程式啟動並執行時 候會有影響。變數指定一個目錄列表,程式會尋找該列表定位共用庫。 只有當未在編譯器的目錄中找到共用庫的時候,執行程式必須設定該變數。
那麼在預設到情況下,如何進行搜尋呢?
動態庫的搜尋路徑搜尋的先後順序是:
- 1 編譯目標代碼時指定的動態庫搜尋路徑,LIBRARY_PATH【編譯階段】;
- 2 在運行時,環境變數LD_LIBRARY_PATH指定的動態庫搜尋路徑【運行階段】;
- 3 設定檔/etc/ld.so.conf中指定的動態庫搜尋路徑【編譯階段】;
- 4 預設的動態庫搜尋路徑/lib【編譯階段】;
- 5 預設的動態庫搜尋路徑/usr/lib【編譯階段】。
對二進位檔案進行處理
以上工具,都可以讓你查看二進位檔案到底幹了什麼,比如,我們用objdump可以查看可執行檔需要什麼連結庫
baiyang@baiyang-Lenovo-G450:~/workspace/test_so$ objdump -x main | grep NEEDNEEDED libfoo.soNEEDED libc.so.6VERNEED 0x08048354VERNEEDNUM 0x00000001
熟練掌握它們,會對系統跟進一步的認識,加油吧.
額外資料
- 詳細講解gcc如何對標頭檔和連結庫進行搜尋;
- strace
-----------------打造高品質的文章 更多關注 把酒泯恩仇---------------
為了打造高品質的文章,請 推薦 一下吧。。。。謝謝了,請關注我後續的文章,會更精彩哦
請關注sina微博:http://weibo.com/baiyang26
把酒泯恩仇官方部落格:http://www.ibaiyang.org 【推薦用google reader訂閱】
把酒泯恩仇官方豆瓣:http://www.douban.com/people/baiyang26/
如果您想轉載本部落格,請註明出處
如果您對本文有意見或者建議,歡迎留言