編譯代碼時動態地鏈接庫
編譯軟體在你如何運行你的系統方面給你很大的靈活性。
LD_LIBRARY_PATH
變數,以及 GCC 的-L
和-l
選項,是這種靈活性的組成部分。
編譯軟體是開發者經常做的事情,在開源世界中,一些用戶甚至選擇自己動手。Linux 播客 Dann Washko 稱源碼為「通用包格式」,因為它包含了使一個應用在任何平台上運行所需的所有組件。當然,並不是所有的源碼都是為所有的系統編寫的,所以它只是在目標系統的子集內是「通用」的,但問題是,源碼是非常靈活的。有了開源,你可以決定代碼的編譯和運行方式。
當你在編譯代碼時,你通常要處理多個源文件。開發人員傾向於將不同的類或模塊放在不同的文件中,這樣它們可以被單獨維護,甚至可能被不同的項目使用。但當你編譯這些文件時,許多文件會被編譯成一個可執行文件。
這通常是通過創建共享庫來完成的,然後從可執行文件中動態鏈接回它們。這樣可以通過保持模塊化功能的外部性來保持可執行文件的小型化,並確保庫可以獨立於使用它們的應用而被更新。
在編譯過程中定位一個共享對象
當你 用 GCC 編譯 時,你通常需要在你的工作站上安裝一個庫,以便 GCC 能夠定位到它。默認情況下,GCC 假定庫在系統庫路徑中,例如 /lib64
和 /usr/lib64
。然而,如果你要鏈接到一個你自己的尚未安裝的庫,或者你需要鏈接到一個沒有安裝在標準位置的庫,那麼你必須幫助 GCC 找到這些文件。
有兩個選項對於在 GCC 中尋找庫很重要:
-L
(大寫字母 L)在 GCC 的搜索位置上增加一個額外的庫路徑。-l
(小寫字母 L)設置你要鏈接的庫的名字。
例如,假設你寫了一個叫做 libexample.so
的庫,並且你想在編譯你的應用 demo.c
時使用它。首先,從 demo.c
創建一個對象文件:
$ gcc -I ./include -c src/demo.c
-I
選項在 GCC 搜索頭文件的路徑中增加了一個目錄。在這個例子中,我假設自定義頭文件在一個名為 include
的本地目錄中。-c
選項防止 GCC 運行鏈接器,因為這個任務只是為了創建一個對象文件。結果如下:
$ ls
demo.o include/ lib/ src/
現在你可以使用 -L
選項為你的庫設置一個路徑,然後進行編譯:
$ gcc -L`pwd`/lib -o myDemo demo.o -lexample
注意,-L
選項在 -l
選項之前。這很重要,因為如果在你告訴 GCC 查找非默認庫之前沒有將 -L
添加到 GCC 的搜索路徑中,GCC 就不知道要在你的自定義位置上搜索。編譯成功了,但當你試圖運行它時,卻出現了問題:
$ ./myDemo
./myDemo: error while loading shared libraries:
libexample.so: cannot open shared object file:
No such file or directory
用 ldd 排除故障
ldd
工具可以列印出共享對象的依賴關係,它在排除類似問題時很有用:
$ ldd ./myDemo
linux-vdso.so.1 (0x00007ffe151df000)
libexample.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007f514b60a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f514b839000)
你已經知道定位不到 libexample
,但 ldd
輸出至少確認了它對工作庫的期望位置。例如,libc.so.6
已經被定位,ldd
顯示其完整路徑。
LD_LIBRARY_PATH
LD_LIBRARY_PATH
環境變數 定義了庫的路徑。如果你正在運行一個依賴於沒有安裝到標準目錄的庫的應用程,你可以使用 LD_LIBRARY_PATH
添加到系統的庫搜索路徑。
有幾種設置環境變數的方法,但最靈活的是在運行命令前放置環境變數。看看設置 LD_LIBRARY_PATH
對 ldd
命令在分析一個「損壞」的可執行文件時的作用:
$ LD_LIBRARY_PATH=`pwd`/lib ldd ./
linux-vdso.so.1 (0x00007ffe515bb000)
libexample.so => /tmp/Demo/lib/libexample.so (0x0000...
libc.so.6 => /lib64/libc.so.6 (0x00007eff037ee000)
/lib64/ld-linux-x86-64.so.2 (0x00007eff03a22000)
這也同樣適用於你的自定義命令:
$ LD_LIBRARY_PATH=`pwd`/lib myDemo
hello world!
然而,如果你移動庫文件或可執行文件,它又會失效:
$ mv lib/libexample.so ~/.local/lib64
$ LD_LIBRARY_PATH=`pwd`/lib myDemo
./myDemo: error while loading shared libraries...
要修復它,你必須調整 LD_LIBRARY_PATH
以匹配庫的新位置:
$ LD_LIBRARY_PATH=~/.local/lib64 myDemo
hello world!
何時使用 LD_LIBRARY_PATH
在大多數情況下,LD_LIBRARY_PATH
不是你需要設置的變數。按照設計,庫安裝到 /usr/lib64
中,因此應用自然會在其中搜索所需的庫。在兩種情況下,你可能需要使用 LD_LIBRARY_PATH
:
- 你正在編譯的軟體需要鏈接到本身剛剛編譯但尚未安裝的庫。良好設計的構建系統,例如 Autotools 和 CMake,可以幫助處理這個問題。
- 你正在使用設計為在單個目錄之外運行的軟體,它沒有安裝腳本,或安裝腳本將庫放置在非標準目錄中。一些應用具有 Linux 用戶可以下載、複製到
/opt
並在「不安裝」的情況下運行的版本。LD_PATH_LIBRARY
變數是通過封裝腳本設置的,因此用戶通常甚至不知道它已被設置。
編譯軟體為你在運行系統方面提供了很大的靈活性。LD_LIBRARY_PATH
變數以及 -L
和 -l
GCC 選項是這種靈活性的組成部分。
via: https://opensource.com/article/22/5/compile-code-ldlibrarypath
作者:Seth Kenlon 選題:lkxed 譯者:geekpi 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive