2016-01-20 95 views
0

我已經編程了一段時間,但我仍然不完全理解鏈接器的行爲。makefile混淆鏈接器庫路徑

例如,今天我下載並安裝了一個我想在我的Linux應用程序中使用的庫。 (這是Xerces - 用於解析XML文件)。

我創建了一個makefile,並在我的命令中爲它指定了.so和.a文件的路徑:-L/usr/local/lib並且還告訴它要包含的庫的名稱:-lxerces-c- 3.1。

我的應用程序編譯得很好,但在運行時失敗,出現「無法打開共享對象文件libxerces-c-3.1.so」。爲什麼會這樣,當我正確地給它的路徑和名稱在生成文件?

然後,我將庫路徑添加到我的.bashrc文件中的LD_LIBRARY_PATH變量,然後它工作。這很好,但如果我現在刪除我的makefile中的庫的路徑,甚至不包括庫的名稱,它仍然有效。

我很困惑這是怎麼回事。如何通過將路徑分配給LD_LIBRARY_PATH變量來找到正確的庫,並且僅在我這樣做時才能正常工作?我已經讀過其他地方甚至不使用LD_LIBRARY_PATH。

我很感激任何答案。這個問題有點長,希望不是脫離主題,但我希望別人也可以從中學習。謝謝

+0

我建議從你的makefile文件中添加最小的一組規則來演示這個問題,最多10行。現在,我們仍然在猜測makefile中的內容,以及是否可以提供最佳的改進。祝你好運。 – shellter

+0

看看這個帖子:http://stackoverflow.com/questions/1904990/what-is-the-difference-between-ld-library-path-and-l-at-link-time,其中類似的問題已經回答 –

+0

'-L'和'LD_LIBRARY_PATH'影響兩個非常*不同的(但相關的)操作。編譯時鏈接和運行時鏈接。 –

回答

1

這裏沒有神祕感。鏈接器(您調用的用於創建可執行文件的目錄,例如ld)和運行時鏈接程序(在執行程序時負責加載共享庫的鏈接程序(例如,ld.so))的默認庫路徑是不同的。運行時鏈接程序使用LD_LIBRARY_PATH,而鏈接程序使用在構建ld時配置的任何內容。

在你的情況下,看起來像/usr/local/lib是一個部分,但不是另一個。

+0

謝謝你。運行時鏈接程序如何知道鏈接tho的庫? LD_LIBRARY_PATH只有庫目錄的路徑,並且沒有命名實際庫 – Engineer999

+0

@ Engineer999,因爲可執行文件中所需的實際.so所需的實際名稱.so已被記錄在可執行文件中。你可以運行'ldd <你的可執行文件>'來查看哪一個。這是您的文件需求,以及運行時鏈接程序可以在給定環境中找到它們的位置 – SergeyA

+0

當我甚至在編譯時沒有提供信息時,我的可執行文件如何知道.so,只有頭文件包含在內? – Engineer999

2

-Wl,-rpath=/usr/local/lib編譯程序。這樣,您將添加在/ usr/local/lib目錄到運行程序的庫搜索補丁,你會不會需要LD_LIBRARY_PATH
警告:由於現代動態鏈接程序會考慮rpath爲廢棄還可以runpath設置(它取代它)通過指定-Wl,-rpath=/usr/local/lib,--enable-new-dtags

+0

rpath已過時,不被使用。一直爲looooong時間。 – SergeyA

+0

@SergeyA:你能否詳細說明一下? – nsilent22

+0

而不是rpath,應該使用運行路徑 - 因爲後者可以仍然與LD_LIBRARY_PATH重疊,而前者不能。 – SergeyA

0

如果你使用靜態鏈接,你所要做的就是告訴鏈接器你的庫在編譯/鏈接時的位置。該庫(或儘可能多的)被複制到您的可執行文件中,並且您的可執行文件是獨立的。

但由於各種原因,現在我們通常使用動態鏈接,而不是靜態鏈接。通過動態鏈接,你必須告訴鏈接器在編譯/鏈接時間哪裏可以找到庫,動態鏈接器(ld.so)必須能夠在運行時找到庫。

如果圖書館在標準地點之一(/lib,/usr/lib等)沒有問題。但是,如果您在非標準位置鏈接到庫,則通常必須將該非標準位置添加到LD_LIBRARY_PATH中,以便運行時鏈接程序始終能夠找到它。

4

編譯和運行是不同的事情。:)

1)make文件包含有關如何構建應用程序的規則。所以當你編寫一條規則時:

-L/usr/local/lib -lxerces-c-3.1 

你正在傳遞選項給鏈接器。 -L選項告訴鏈接程序將其他庫(在本例中爲'/ usr/local/lib')添加到鏈接程序的搜索路徑。 -l選項命名應該鏈接的庫。

2)當您運行可執行文件時,加載器需要找到所有需要的庫。例如,在Linux系統上,您可以使用ldd命令查看使用哪些共享庫。例如我的系統上:

ldd FEParser 
    linux-vdso.so.1 => (0x00007ffcdc7c9000) 
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f835b143000) 
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f835ae3d000) 
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f835ac27000) 
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f835a862000) 
/lib64/ld-linux-x86-64.so.2 (0x00007f835b447000) 

由此看來,你可以看到一個鏈接到「=>」標記的左側庫的名稱和路徑到圖書館的權利。如果找不到圖書館,它會顯示爲丟失。

現在你的情況,你已經能夠成功地編譯和鏈接你的程序,因爲給路徑和庫名稱使用。由於裝載程序在運行時無法找到該庫,因此無法運行該程序。

您有幾種選擇在這裏:

1)您可以在一個目錄是在裝載機的搜索路徑移動圖書館。

2)您可以修改LD_LIBRARY_PATH,這會將其他目錄添加到加載程序搜索路徑中。此外,LD_LIBRARY_PATH中指定的目錄將傳遞給鏈接器(它將在所有-L標誌之後附加)。你需要小心這一點,就好像你在全局設置它(例如在.bashrc中),那麼它會影響你所做的所有編輯。你可能會也可能不想要這種行爲。

3)正如其他人已經指定,您可以使用-Wl,rpath=....,但它已被棄用,我很少使用它。

4)如果您需要安裝在一個不尋常的位置庫,你可以添加一個創建/etc/ld.so.conf.d包含,例如下一個文件(文件yaml.conf):

# yaml default configuration 
/opt/yaml/lib 

在系統啓動時,加載器讀取此目錄中的所有文件並將路徑附加到加載器路徑。如果您對此目錄進行了修改,則可以使用ldconfig命令使加載程序重新處理/etc/ld.so.con.d。注:我知道這適用於GNU/Linux的centOS和Ubuntu版本 - 不能在其他版本上進行權威性的說明。

+0

謝謝。非常有幫助 – Engineer999

+0

這可能是其他地方的問題,但可執行文件如何決定是使用提供的靜態庫還是共享庫? – Engineer999

+1

@ Engineer999應用程序不做任何決定。運行時鏈接程序的確如此。使用靜態鏈接庫(在編譯時),根本不會爲該庫完成共享對象的運行時鏈接。 –