Linux 指定編譯時動態庫路徑和運行時動態庫路徑--解決報錯symbol lookup error和cannot open shared object file

動態庫的兩種路徑

linux指定動態庫路徑包括:1.指定編譯時動態庫路徑;2.指定運行時動態庫路徑。後文提到的動態庫路徑都是指的是這兩種路徑。衆所周知,Linux動態庫的默認搜索路徑是/lib和/usr/lib(不管是編譯還是運行都會默認搜索這兩個路徑)。動態庫被創建後,一般都複製到這兩個目錄中。當程序執行時需要某動態庫,並且該動態庫還未加載到內存中,則系統會自動到這兩個默認搜索路徑中去查找相應的動態庫文件,然後加載該文件到內存中,這樣程序就可以使用該動態庫中的函數,以及該動態庫的其它資源了。

指定動態庫路徑的三種方式

在Linux 中,動態庫的搜索路徑除了默認的搜索路徑外,還可以通過以下三種方法來指定。

1.在配置文件/etc/ld.so.conf中指定動態庫搜索路徑。

可以通過編輯配置文件/etc/ld.so.conf來指定動態庫的搜索路徑,該文件中每行是一個動態庫搜索路徑。每次編輯完該文件後,都必須運行命令ldconfig使修改後的配置生效

以動態庫的創建與使用爲例:

//so_test.h
#include "stdio.h"

void test_a();
void test_b();
void test_c();


//test_a.c
#include "so_test.h"

void test_a()
{
    printf("this is in test_a...\n");
}


//test_b.c
#include "so_test.h"
void test_b()
{
    printf("this is in test_b...\n");
}


//test_c.c
#include "so_test.h"
void test_c()
{
    printf("this is in test_c...\n");
}

測試程序

//test.c
#include “so_test.h”

int main()
{
    test_a();
    test_b();
    test_c();
    return 0;
}

操作過程:
我們通過以下命令用源程序test_a.c、test_b.c、test_c.c來創建動態庫 libtest.so。

# gcc -c test_a.c test_b.c test_c.c
# gcc -shared -fPIC -o libtest.so *.o

或者直接一條指令:
#gcc -shared -fPIC -o libtest.so test_a.c test_b.c test_c.c

這樣在當前目錄下就生成了動態庫libtest.so。對於頭文件位置的存放請參考博文《linux頭文件》。

注意:
-fPIC參數聲明鏈接庫的代碼段是可以共享的(https://blog.csdn.net/u011285208/article/details/103070486);
-shared參數聲明編譯爲共享庫。

請注意這次我們編譯的共享庫的名字叫做libtest.so,這也是Linux共享庫的一個命名的慣例了:後綴使用so,而名稱使用libxxxx格式。

接着通過以下命令編譯test.c,生成目標程序main。

# gcc -o main -L. –ltest test.c

當應用程序調用動態庫的時候,要用-l選項,指定所調用的庫名。用-L選項指定庫所在的路徑(如果沒有使用後文所述的三種方法處理的情況下,用這種方式指定庫所在路徑)。

(注意:此時,頭文件以及庫文件都在當前目錄下)

在當前目錄下生成可執行文件main

運行程序main

#./main

./main: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

出錯了,系統未找到動態鏈接庫libtest.so。原因是系統會默認的到/lib或/usr/lib中尋找需要的庫,但是我們需要的庫放在了當前的目錄下,也就說雖然編譯的時候指定了所需要的動態庫,但是執行的時候並不會到之前編譯時指定的動態庫的位置去尋找動態庫,這也就是爲什麼在文章開頭講清楚,什麼是指定編譯時動態庫路徑,什麼是指定運行時動態庫路徑。

解決的方法就是找到動態庫。怎樣找到動態庫呢?最簡單的解決的方法是將動態庫拷貝到/lib或/usr/lib目錄下,這樣在編譯時不需要指定動態庫的路徑,運行時也不會找不到動態庫(不管編譯還是執行都會到/lib或/usr/lib目錄下尋找動態庫)。對於編譯階段找到動態庫的方法還有上面的通過-L選項指定動態庫的路徑。對於執行階段是否可以通過選項指定動態庫,下面的方法3會有介紹。

除了上面提到的方法外,我們更需要這樣一種解決思路:我們可以靈活的指定動態庫的存放的位置,然後由操作系統負責動態庫的查找。當然強大的linux提供了這樣的功能,並且這種功能的實現不止一種,詳細內容可以參考博文《/etc/ld.so.conf文件》。

現在簡單介紹/etc/ld.so.conf的使用,我們打算將製作好的動態庫放在/root/lib目錄下,因此執行如下命令:

# mkdir /root/lib
# mv libtest.so /root/lib/libtest.so

最後編輯配置文件/etc/ld.so.conf,在該文件中追加一行/root/lib。

運行程序main

# ./main
./main: error while loading shared libraries: lib_test.so: cannot open shared object file: No such file or directory

仍然出錯,系統未找到動態庫libtest.so。找找原因,原來在編輯完配置文件/etc/ld.so.conf後,沒有運行命令ldconfig,所以剛纔的修改還未生效。我們運行ldconfig後再試試。

# ldconfig
# ./main
this is in test_a...
this is in test_b...
this is in test_c...
#

程序main運行成功,並且打印出正確結果。

2.通過環境變量LD_LIBRARY_PATH指定動態庫搜索路徑。

通過設定環境變量LD_LIBRARY_PATH也可以指定動態庫搜索路徑。當通過該環境變量指定多個動態庫搜索路徑時,路徑之間用冒號”:”分隔。下面通過例2來說明本方法。

舉一個例子:
這次我們把上面得到的文件lib_test.so移動到另一個地方去,如/root下面,然後設置環境變量LD_LIBRARY_PATH找到libtest.so。設置環境變量方法如下:

# export LD_LIBRARY_PATH=/root
#

然後運行:

#./main.out
this is in test_a...
this is in test_b...
this is in test_c...
#

注意:設置環境變量LD_LIBRARY_PATH=/root是不行的,非得export纔行。這種設置LD_LIBRARY_PATH環境變量只是臨時性的,下次開啓LD_LIBRARY_PATH的值會失效,因此可以將環境變量寫入到/etc/bash.bashrc文件中

3.在編譯目標代碼時指定該程序運行時的動態庫搜索路徑

還可以在編譯目標代碼時指定程序的動態庫搜索路徑。

-Wl,表示後面的參數將傳給link程序ld(因爲gcc可能會自動調用ld)。這裏通過gcc 的參數”-Wl,-rpath,”指定
舉一個例子:
這次我們還把上面得到的文件libtest.so移動到另一個地方去,如/root/test/lib下面,
因爲我們需要在編譯目標代碼時指定可執行文件的動態庫搜索路徑,所以需要用gcc命令重新編譯源程序test.c來生成可執行文件main。

# gcc -o main -L. –ltest -Wl,-rpath=/root/test/lib test.c
#

運行結果:

# ./main.out
this is in test_a...
this is in test_b...
this is in test_c...
#

程序./main運行成功。因此程序main搜索到的動態庫是/root/test/lib/lib_test.so。

關於-Wl,rpath的使用方法我再舉一個例子,應該不難從中看出指定多個路徑的方法:

gcc -Wl,-rpath,/home/arc/test,-rpath,/lib/,-rpath,/usr/lib/,-rpath,/usr/local/lib test.c

以上介紹了三種指定動態庫搜索路徑的方法,加上默認的動態庫搜索路徑/lib和/usr/lib,共五種動態庫的搜索路徑,那麼它們搜索的先後順序是什麼呢?讀者可以用下面的方法來試驗一下:
(1) 用前面介紹的方法生成5個libtest.so放在5個不同的文件夾下面,要求每一個libtest.so都唯一對應一個搜索路徑,並注意main程序輸出的不同。
(2) 運行main,即可看出他是那個搜索路徑下的,然後刪除這個路徑下的libtest.so,然後再運行。依此類推操作,即可推出搜索順序。

可以得出動態庫的搜索路徑搜索的先後順序是:

1.編譯目標代碼時指定的動態庫搜索路徑;

2.環境變量LD_LIBRARY_PATH指定的動態庫搜索路徑;

3.配置文件/etc/ld.so.conf中指定的動態庫搜索路徑;

4.默認的動態庫搜索路徑/lib;

5.默認的動態庫搜索路徑/usr/lib。

在上述1、2、3指定動態庫搜索路徑時,都可指定多個動態庫搜索路徑,其搜索的先後順序是按指定路徑的先後順序搜索的。有興趣的讀者自己驗證。

“symbol lookup error”錯誤

如果將自己的動態庫放置在非默認路徑,而且自己未指定搜索路徑時,會在默認路徑下搜索。這時候如果默認路徑下有同名動態庫,那麼就不會報錯“cannot open shared object file”,但由於兩個動態庫只是同名而不是完全一致,會出現“symbol lookup error”錯誤,使用ldd命令可以看到程序連接的是哪個動態庫。

[root@localhost ]# ./test_rcgs 
./test_rcgs: symbol lookup error: /root/DI/phantom/Nf_Phantom_DI_20140506_4200/lib/libdi_file_recogniser.so: undefined symbol: clist_new
[root@localhost ]# ldd ./test_rcgs
        linux-gate.so.1 =>  (0x00611000)
        libdi_file_recogniser.so => /root/DI/phantom/Nf_Phantom_DI_20140506_4200/lib/libdi_file_recogniser.so (0x003b3000)
        libc.so.6 => /lib/libc.so.6 (0x00110000)
        /lib/ld-linux.so.2 (0x0037a000)

[root@localhost ]#  export LD_LIBRARY_PATH=./lib/
[root@localhost ]# echo $LD_LIBRARY_PATH
./lib/
[root@localhost ]# ldd test_rcgs 
        linux-gate.so.1 =>  (0x007b4000)
        libdi_file_recogniser.so => ./lib/libdi_file_recogniser.so (0x00d6c000)
        libc.so.6 => /lib/libc.so.6 (0x0039d000)
        /lib/ld-linux.so.2 (0x0037a000)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章