1. 程式人生 > >GCC編譯、連結、執行時庫查詢順序(最真實可信)

GCC編譯、連結、執行時庫查詢順序(最真實可信)

參考了不少資料,其中最靠譜是這個:http://www.mingw.org/wiki/librarypathhowto

和http://www.kaizou.org/2015/01/linux-libraries/

經過線上實際驗證,GCC編譯、連結、執行時庫查詢順序如下,這個順序真實可信,網上很多說法有些地方都是有些問題的,

導致遇到問題時總是不確定到底是哪裡出了問題,花了不少時間,絕知此事要躬行。

略微讓人頭疼的是,沒有找到官方文件清晰說明什麼情況下該如何查詢依賴庫目錄,連結時查詢直接依賴庫和間接依賴庫的方式

差別挺大。。。

一、概括GCC連結時搜尋直接依賴庫的先後順序:

1、LDFLAGS選項 -L 引數指定的路徑。
2、系統環境變數 LIBRARY_PATH(某些系統或編譯器下可能無效)。
3、gcc安裝時自身配置的搜尋路徑,gcc --print-search-dir | grep libraries 可檢視,

一般會包含該版本gcc必需的庫而不一定包含當前系統庫路徑,連結時會以-L引數形式傳遞給ld。

4、ld安裝時自身配置的搜尋路徑,ld -verbose | grep SEARCH_DIR 可檢視,

gcc通過呼叫collect2工具呼叫ld。

To summarize, when linking an executable against a static library, you need to specify explicitly all dependencies towards shared libraries introduced by the static library on the link command.


二、概括GCC鏈接時搜間接依賴庫直接依賴庫的依賴庫的先後順序:(即secondary dependencies

特別說明:搜尋直接依賴庫的連結查詢順序同樣適用於編譯生成動態的( shared)、可重定位(relocatable)目標的情形。

特別說明:本條目所列連結順序僅適用於編譯生成非動態的( non-shared)、非可重定位(non-relocatable)目標的情形,比如生成二進位制可執行程式,ld手冊中有明確說明,是否適合其它情形不確定。在此種情形下,查詢一個依賴庫自身的依賴庫時,-L引數指定的搜尋目錄無效(至少某些情況下是如此),這不同於連結一個二進位制時查詢直接依賴目錄的順序。(1) 參考linux man手冊 ld說明:https://linux.die.net/man/1/ld  “-rpath-link=
dir”章節(2)參考同樣問題的分析:http://www.kaizou.org/2015/01/linux-libraries/連結時對於依賴庫中依賴的搜尋順序如下(摘自ld手冊):

-rpath-link=dir

When using ELF or SunOS, one shared library may require another. This happens when an "ld -shared" link includes a shared library as one of the input files.

When the linker encounters such a dependency when doing a non-shared, non-relocatable link, it will automatically try to locate the required shared library and include it in the link, if it is not included explicitly. In such a case, the -rpath-link option specifies the first set of directories to search. The -rpath-link option may specify a sequence of directory names either by specifying a list of names separated by colons, or by appearing multiple times.

This option should be used with caution as it overrides the search path that may have been hard compiled into a shared library. In such a case it is possible to use unintentionally a different search path than the runtime linker would do.

The linker uses the following search paths to locate required shared libraries:

(1)  Any directories specified by -rpath-link options.

(2) Any directories specified by -rpath options. The difference between -rpath and -rpath-link is that directories specified by -rpath options are included in the executable and used at runtime, whereas the -rpath-link option is only effective at link time. Searching -rpath in this way is only supported by native linkers and cross linkers which have been configured with the --with-sysroot option.

(3) On an ELF system, for native linkers, if the -rpath and -rpath-link options were not used, search the contents of the environment variable "LD_RUN_PATH".

(4) On SunOS, if the -rpath option was not used, search any directories specified using -L options.

(5) For a native linker, the search the contents of the environment variable "LD_LIBRARY_PATH".

(6) For a native ELF linker, the directories in "DT_RUNPATH" or "DT_RPATH" of a shared library are searched for shared libraries needed by it. The "DT_RPATH" entries are ignored if "DT_RUNPATH"entries exist.

(7) The default directories, normally /lib and /usr/lib.(這裡應該是指 ld -verbose | grep SEARCH_DIR顯示的搜尋目錄)

(8) For a native linker on an ELF system, if the file /etc/ld.so.conf exists, the list of directories found in that file.

If the required shared library is not found, the linker will issue a warning and continue with the link.

“Ok, this is not crystal-clear, but what it actually means is that when specifying the path for a secondary dependency, you should not use -L but -rpath-link

To summarize, when linking an executable against:

  • static library, you need to specify all dependencies towards other shared libraries this static library depends on explicitly on the link command.

  • shared library, you don’t need to specify dependencies towards other shared libraries this shared library depends on, but you may need to specify the path to these libraries on the link command using the -rpath/-rpath-link options.

Note however that expressing, discovering and adding implicit libraries dependencies is typically a feature of your build system (autotoolscmake), as demonstrated in my samples.”


二、概括GCC執行時庫的搜尋庫先後順序:
1、程式自身的RPATH(Library rpath), readelf -d bin可檢視,在連結時通過-rpath引數寫入程式ELF結構資訊中,而傳入連結器中預設的-rpath引數是來自安裝gcc時配置的檔案specs(其中的配置項linker)。
2、編譯時LDFLAGS選項 -L 引數指定的路徑。
3、系統環境變數LD_LIBRARY_PATH。
4、在 /etc/ld.so.conf.d/ 目錄下的配置檔案指定的動態庫絕對路徑(通過ldconfig生效,一般是非root使用者時使用)。
5、gcc安裝時自身配置的搜尋路徑,gcc --print-search-dir | grep libraries 可檢視,一般會包含該版本gcc必需的庫而不一定包含當前系統庫路徑。三、補充說明:
1、在連結和執行查詢庫的過程中,只要找到同名的庫則不管該庫是否相容均停止查詢,即便相容的庫可能出現在靠後的搜尋路徑中。
2、安裝高版本gcc時一般是直接解壓已經編譯好的gcc,然後直接複製一份至新的路徑下,bcloud就是這麼做的,可能是為了便於恢復編譯環境吧。
3、如果系統自帶的是低版本gcc,而需要使用高版本gcc編譯程式,如果該程式依賴了非系統庫(低版本gcc編譯),如果這個非系統庫的安裝目錄下恰好有和高版本gcc依賴的庫同名的庫,那麼為了能夠使用這個非系統庫,這時高版本gcc編譯時很可能會出現問題,必需保證連結器在查詢庫時,查詢高版本gcc自身庫一定要先於任意系統庫(或任意庫)所在的目錄。原因是高版本gcc應該是改進了相當一部分系統庫,和當前低版本系統庫並不相容,而對於相容的那些庫,高版本gcc庫可以直接使用當前系統庫。 
  • You can pass the -v option to gcc so that it shows you how it invokes the linker. In fact, it normally does not invoke ld directly, but indirectly via a tool called collect2 (which lives in one of its internal directories), which in turn invokes ld. That will show you what -L options are being used.
  • You can add -Wl,--verbose to the gcc options to make it pass --verbose through to the linker, to see the linker script as described above.

四、庫版本號

  如果 major version number不一致,說明庫的ABI介面是不相容的,不同major version 庫之間不能相互引用或依賴,相同major version一般是可以相互引用和依賴的,但工作時具體行為是否正確需要進一步驗證。

  Linux上的shared library有三個名字,分別是:

  • 共享庫本身的檔名(real name)

    其通常包含完整的版本號,比如:libmath.so.1.1.1234 。lib是Linux庫的約定字首,math是共享庫名字,so是共享庫的字尾名,1.1.1234的是共享庫的版本號,由主版本號+小版本號+build號組成。主版本號,代表當前動態庫的版本,如果共享庫的介面發生變化,那麼這個版本號就要加1;後面的兩個版本號(小版本號和 build號)是用來指示庫的更新迭代號,表示在介面沒有改變的情況下,由於需求發生變化等因素,開發的新程式碼。

  • 共享庫的soname(Short for shared object name)

    用來告訴應用程式,在載入共享庫的時候,應該使用的檔名。其格式為lib + math + .so + (major version number) 其只包含主版本號,換句話說,也就是隻要共享庫的介面沒有變,soname就能與real name保持一致,因為主版本號一樣。所以在庫的real name的小版本號和 build號發生改變時,應用程式仍然可以通過soname得知,要使用的是哪個real name。

  • 共享庫的連結名(link name)

    是專門為應用程式在編譯時的連結階段而用的名字。這個名字就是lib + math +.so ,比如libmath.so。其是不帶任何版本資訊的。在共享庫的編譯過程中,編譯器將生成一個共享庫及real name,同時將共享庫的soname寫在共享庫檔案裡的檔案頭裡面。可以用命令readelf -d sharelibrary | grep soname檢視。 在應用程式引用共享庫時,連結選項裡面用的是共享庫的link name。通過link名字找到對應的real name動態庫,並且把其中的soname提取出來,寫在應用程式自己的檔案頭的共享庫欄位裡面。當應用程式執行時,就會通過soname,結合動態連結程式(ld.so),在給定的路徑下載入real name的共享庫。

以上。

相關推薦

GCC編譯連結執行查詢順序真實可信

參考了不少資料,其中最靠譜是這個:http://www.mingw.org/wiki/librarypathhowto和http://www.kaizou.org/2015/01/linux-libraries/經過線上實際驗證,GCC編譯、連結、執行時庫查詢順序如下,這個順

linux下編譯連線及執行環境變數設定boost為例

以boost庫的存放目錄/usr/boost為例, 開啟/etc/profile, 追加以下內容(前兩行為編譯時路徑): export CPLUS_INCLUDE_PATH=/usr/boost/include:$CPLUS_INCLUDE_PATH export LIB

js小數相加相乘失去精度問題解析詳解優方案

var CMX = CMX || {}; /** ** 加 **/ CMX.add = function (arg1, arg2) { var r1, r2, m, c; try { r1 = arg1.toString().split(".")[1].length; } catc

數據查詢操作fetchone,fetchall

一個 而在 mysql 沒有 備註 sele one 訪問 使用 數據庫查詢操作 Python查詢Mysql使用 fetchone() 方法獲取單條數據, 使用fetchall() 方法獲取多條數據。 fetchone(): 該方法獲取下一個查詢結果集

VC++編譯器背後的故事:編譯連結執行作業系統

MulinB按:當你在IDE裡點選build時,背後都發生了什麼?什麼是編譯錯誤(compiling error)、什麼是連結錯誤(linking error)?程式的記憶體結構是怎樣的?執行到main函式之前都發生了什麼?VC++編譯器中的眾多編譯選項該如何設定?什麼是include, lib

【C++】編譯連結執行原理+強弱符號

【C++】編譯、連結、執行原理+強弱符號 虛擬地址空間 作用:程序地址空間需要隔離,防止惡意的程式修改其他程式的記憶體資料,所以計算機需要虛擬地址空間 其中: .data:已經初始化,並且初始化不為0的資料。 .bss:未初始化,或者初始化為0的資料。 例如: #i

Linux下的C/C++開發基礎編寫makefile編譯C/C++連結執行程式

本文重點介紹C/C++原始碼工程的編譯連結,編譯器gcc/g++的安裝配置略過... 1. 安裝配置gcc g++ 2. 建立檔案 test.h /test.c / file.h  / file.cpp  3. 編譯.o庫: gcc -c / g++ -c     連結生成靜

程式的實現過程編譯連結執行

在ANSI C的任何一種實現中,存在兩個不同的環境 第1種是翻譯環境,在這個環境中原始碼被轉換為可執行的機器指令。第2種是執行環境,它用於實際執行程式碼。 標準明確說明:這兩種環境不必位於同一臺機器上。 例如,交叉編譯器就是在同一臺機器上執行,但它所產生的可執行程式碼

C/C++——程式實現過程之編譯連結執行

從寫一個簡單的“hello world!”到完成一個大型程式,當程式從編輯完成到執行成功都會經過5個步驟,分別是預處理(Prepressing)、編譯(Compilation)、彙編(Assembly)、連結(Linking)和執行(Executing)。瞭解這五個過程中所

Qt在pro中設定執行MTMTdMDMDd,只適合VS版本的Qt

轉自:http://blog.csdn.net/caoshangpa/article/details/51416077 一.在pro中設定執行時庫 最近在用Qt5.6.0(VS2013版本)呼叫一份用Visual Studio 2013編譯的Debug版本靜態庫時出現如下錯誤:

VC 執行 /MD/MDd 和 /MT/MTd

有段時間在寫cuda程式是出現過 error LNK2005: _exit 已經在 MSVCRTD.lib(MSVCR71D.dll) 中定義 等類似錯誤 原因應該是在vs2010 工程屬性中 c/c++的程式碼生成(code generation)中的設定與cuda

執行MTMTdMDMDd的研究

在開發window程式是經常會遇到編譯好好的程式拿到另一臺機器上面無法執行的情況,這一般是由於另一臺機器上面沒有安裝響應的執行時庫導致的,那麼這個與編譯選項MT、MTd、MD、MDd有什麼關係呢?這是msdn上面的解釋: MT:mutithread,多執行緒庫,編譯器會

【VS開發】MFC執行與debugrelease版本之間的配置關係

參考內容:  前段時間從網上下來一個有意思的程式碼,用VS2010開啟時需要將工程轉換為2010的工程,轉化後卻出現了編譯不通過的問題,類似這樣的錯誤:c:\program files\microsoft visual studio 10.0\vc\atlmfc\inc

c++中的編譯連結執行

1.編譯與連結的區別: 預處理:處理巨集定義指令#define 、標頭檔案#include等 #include<filename> ,尖括號表示系統提供的標頭檔案,直接去系統目錄查詢; #include“animal.h”,雙引號表示自己編寫的標頭檔案,

自己在linux上編譯連結動態和靜態的學習筆記

在平常的專案中,我們都是使用公司要求的makefile、makedebug一類的檔案,因此,在編譯、連結、生成和連結動態庫與靜態庫的時候,我們只是簡單的使用一些已經設定的變數,只是簡單的修改、新增一些檔名,或許這次我們編譯通過了,但是,在某一個時候,可能出現了一個問題,無論

C語言學習篇-1Hello, World!(編寫編譯連結執行)

說明:初識第一個程式。 開發工具的選擇 寫程式碼的工具:記事本、ULtraEdit、Vim、Xcode等。 選擇Xcode的原因:蘋果公司官方提供的開發利器、簡化開發的工程、有高亮顯示功能。

程式的處理步驟——預處理編譯連結執行

在c++中對記憶體的管理分為三種: (1)從靜態儲存區域分配。記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個執行期間都存在。例如全域性變數,static變數。 (2)在棧上建立。在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單元自動被釋放。棧記憶體分配運算內置於處

linux下使用eclipse編譯連結動態的學習筆記

 一、建立動態連結庫     1、建立工程new->project->c++ project選擇Shared Library->Empty Project.輸入工程名MySharedLib,點選finish,完成工程的建立。   2. 庫程式碼的

關於Class物件類載入機制虛擬機器執行記憶體佈局的全面解析和推測

簡介: 本文是對Java的類載入機制,Class物件,反射原理等相關概念的理解、驗證和Java虛擬機器中記憶體佈局的一些推測。本文重點講述瞭如何理解Class物件以及Class物件的作用。 歡迎探討,如有錯誤敬請指正 如需轉載,請註明出處 http://www.cnblogs.com/nul

gcc編譯出現dlopendlerrordlsymdlcolse的解決方法

  ➜  test_sqlite3 gcc *.c -I . -o xixi -pthread      /tmp/cckGKTrr.o: In function `unixDlOpen':