#LINUX#通過編譯鏈接,卻運行時找不到.so文件
我們知道在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$ ls
libfoo.so main.c plus.c
baiyang@baiyang-Lenovo-G450:~/workspace/test_so$ gcc main.c -o main -lfoo
/usr/bin/ld: cannot find -lfoo
collect2: 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 -lfoo
baiyang@baiyang-Lenovo-G450:~/workspace/test_so$ ls
libfoo.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
嗚嗚,遇到開始提到的問題了.
//http://www.software8.co/wzjs/czxt/1733.html
//http://www.software8.co/wzjs/czxt/1733.html
那我們怎么看輸出到可執行文件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_PATH
baiyang@baiyang-Lenovo-G450:~/workspace/test_so$ ls
libfoo.so main main.c plus.c
baiyang@baiyang-Lenovo-G450:~/workspace/test_so$ ./main
8
成功運行,用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【編譯階段】。
對二進制文件進行處理
strace
gdb
objdump
nm
以上工具,都可以讓你查看二進制文件到底干了什么,比如,我們用objdump可以查看可執行文件需要什么鏈接庫
baiyang@baiyang-Lenovo-G450:~/workspace/test_so$ objdump -x main | grep NEED
NEEDED libfoo.so
NEEDED libc.so.6
VERNEED 0x08048354
VERNEEDNUM 0x00000001
熟練掌握它們,會對系統跟進一步的認識,加油吧.