程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C語言頭文件、庫文件的查找路徑

C語言頭文件、庫文件的查找路徑

編輯:關於C語言

一、討論環境
*操作系統:Redhat5/Fedora14

*編譯器:gcc 4.5.1

以下言論僅確保在以上環境中適用。別的環境,大家可以通過類比方法,得到啟示。

 


二、C語言頭文件的查找路徑
C語言,使用include指令,包含頭文件,但又細分兩種形式:

1、形式一:#include “file1”
  gcc先在當前目錄(指包含本條#include指令的源文件所在的目錄)尋找file1,如果找不到,繼續在由-iquote選項(如果有的話)指定的目錄中尋找file1。

  例如,在文件/usr/include/sys/stat.h中,包含指令#include “types.h”,那麼gcc先在/usr/include/sys目錄下尋找types.h文件。嗯,在該目錄下,確實存在一個types.h的文件。現假設我們把這個文件移動到另一個目錄:mv /usr/include/sys/types.h /bar/foo/,我們在編譯時,可以通過-iquote選項,在不改變stat.h的情況下,正常編譯(當然,通常不建議這樣做):

gcc -iquote /bar/foo -I/usr/include/sys *.o

2、形式二:#include <file2>
  gcc按照以下順序查找file2:

-Idir1 -Idir2 ...
/usr/local/include
libdir/gcc/<target>/<version>/include
/usr/<target>/include
/usr/include
第一行中,-Idir1 -Idir2 ... 是用戶通過gcc的-I選項指定的目錄。值得一提的是,放在/usr/local/include/下的頭文件也會被gcc自動的檢索,這與/usr/local/lib/目錄下的庫處理方式是不一樣的(gcc的鏈接器在運行時階段不會自動查找該目錄下的庫文件,下一節會提到)。

 


三、C語言庫文件的查找路徑
C語言庫文件的查找路徑,又分為兩個階段:鏈接階段、運行時階段。

1、鏈接階段(link time)
  此階段,需要告訴編譯器,在哪裡找到庫文件?以靜態還是動態的方式鏈接庫文件?默認情況下使用動態方式鏈接,這要求存在對應的.so動態庫文件,如果不存在,則尋找相應的.a靜態庫文件。若在編譯時向gcc傳入-static選項,則使用靜態方式鏈接,這要求所有庫文件都必須有對應的*.a靜態庫。

  那麼,是否可以令某些庫使用動態鏈接,另一些庫使用靜態鏈接?不太確定,請參考ld的使用手冊,我沒有這樣用過。

  切入正題,在鏈接階段,gcc編譯器如何尋找庫文件呢(linker本身並沒有默認的查找路徑,這些查找路徑是由gcc傳遞給linker的)?大家可以在編譯時,向gcc加入-v選項來觀察它向linker傳遞的庫文件查找路徑(觀察LIBRARY_PATH變量的值),通常查找路徑如下:

任何由-rpath-link或-rpath選項指定的目錄
LD_RUN_PATH(如果沒有找到-rpath或-rpath-link選項)
-Ldir1 -Ldir2 ...
/usr/lib/gcc/<target>/<version>/
/usr/lib/
第一行-rpath-link與-rpath選項的區別在於,-rpath選項指定的目錄被硬編碼到可執行文件中,-rpath-link選項指定的目錄只在鏈接階段生效。由於這兩個選項都是鏈接器ld的選項,如何從gcc中向ld傳遞這兩個選項?方法如下(更從細節參考gcc的-Wl選項):

gcc -Wl, -rpath, /usr/local/lib

這相當於向ld向傳遞了如下參數:

ld -rpath /usr/local/lib

第二行,如果沒有設置-rpath或-rpath-link選項,則查找LD_RUN_PATH環境變量指定的目錄,並把它當作-rpath選項來處理。第三行-Ldir1 -Ldir2 ...,是我們通過gcc的-L選項向其指定的庫文件查找路徑,查找順序按照我們傳遞的-L參數從左到右進行搜索;第四行屬於gcc自己的庫目錄;第五行/usr/lib/是Linux系統默認的系統庫文件的目錄。第四、第五行,都是gcc自動向linker傳遞的查找目錄。例如我現在的機器上,使用gcc -v可以看到LIBRARY_PATH變量值為:

LIBRARY_PATH=/usr/lib/gcc/i686-redhat-linux/4.5.1/:/usr/lib/

但是這並不是全部真理!根據我自己的測試,我發現gcc會把/usr/local/lib/目錄也作為鏈接階段的查找路徑,這正是問題的根源——我們在鏈接過程中,使用到了/usr/local/lib/裡面的一些庫文件,但在運行時階段,卻說找不到該庫文件。

2、運行時階段(runtime)
  僅當可執行程序采用動態的方式鏈接庫文件時,才會存在運行時庫文件的查找問題。對於這種可執行程序,它本身只是記錄動態庫的名稱。所以在運行該程序時,操作系統的加載程序(ld.so)需要根據庫的名稱,在必要時加載庫文件到內存中。

  在linux中,在運行時階段,動態庫(又叫共享庫)的查找路徑如下:

-rpath選項指定的目錄(已被硬編碼到可執行文件中)
LD_LIBRARY_PATH
/lib或/usr/lib
系統默認的查找路徑
我們可以通過readelf查看被硬編碼到可執行文件中的rpath:

$ readelf -d <可執行文件名>                #Display the dynamic section (if present)

LD_LIBRARY_PATH則沒有這個問題,但是通常我們不建議使用這個環境變量,因為修改這個變量意味著影響所有依賴於這個環境變量的程序(如果非要使用,請把這個環境變量寫在啟動腳本中,並且讓它只影響腳本中的程序)。

  那麼系統默認的查找路徑又是怎樣的?在Redhat5/Fedora14中,ld.so通過讀取/etc/ld.so.cache文件來查找庫文件的位置,如果沒有找到則繼續從/etc/ld.so.conf文件中指定的目錄查找。這個ld.so.cache文件相當於一個key-value的數據庫,key就是動態庫的名稱,value就是這些庫的存放路徑。

  那麼/etc/ld.so.cache文件是怎麼生成的呢?這就要談到ldconfig這個工具程序了。ldconfig是動態鏈接庫的配置工具,使用它可以更新/etc/ld.so.cache文件,也可以查看這個文件中的key-value信息(使用ldconfig -p),ldconfig的使用細節,請參考它的使用手冊。總結一下系統默認的查找路徑:

/etc/ld.so.cache
/etc/ld.so.conf文件中指定的目錄
四、參考資料
man ld
man ldconfig
html">http://gcc.gnu.org/onlinedocs/cpp/Search-Path.html
http://www.eyrie.org/~eagle/notes/rpath.html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved