linux下多個源文件的編譯
(1)通過多個源文件編譯,直接合成一個.o文件。
(2)通過創建靜態鏈接庫.a,在調用函數的時候調用靜態庫。
(3)通過創建動態鏈接庫.so,在調用函數的時候調用動態庫。
1.編譯多個源文件
例子,在系統提示符下鍵入命令得到hello.o文件
#gcc -c hello.c -o hello.o
我們通常使用的gcc -c是將.c源文件編譯成為一個可執行的二進制代碼(-o選項其實是指定輸出文件文件名,如果不加-c選項,gcc默認會編譯鏈接生成可執行文件,文件的名稱由-o選項指定),這包括調用作為gcc內的一部分真正的c編譯器(ccl),以及調用gnu c編譯器的輸出中實際可執行代碼的外部gnu匯編器(as)和連接器工具(ld)。
而gcc -c是使用GNU匯編器將源文件轉化為目標代碼之后就結束,在這種情況下,只調用了c編譯器(ccl)和匯編器(as),而連接器(ld)并沒有被執行,所以輸出的目標文件不會包含作為Linux程序在被裝載和執行時所必須的包含信息,但它可以在以后被連接到一個程序。
代碼執行步驟
# gcc -c hello.c -o hello.o
# gcc -c main.c -o main.o
// 單個編譯所有文件生成.o文件,然后鏈接為一個可執行文件
# gcc -o hello hello.o main.o
2.靜態鏈接庫
靜態庫文件名的命名規范是以lib為前綴,緊接著跟靜態庫名,擴展名為.a。例如:我們將創建的靜態庫名為,則靜態庫文件名就是.a。
創建靜態庫命令
# ar rcs libmyhello hello.o
靜態庫制作完成了,如何使用它內部的函數呢?只需要在使用到這些公有函數的源程序中包含這些公有函數的原型聲明c標準庫有靜態和動態庫,然后在用gcc命令生成目標文件時指明靜態庫名,gcc將會從靜態庫中將公有函數連接到目標文件中。注意,gcc會在靜態庫名前加上前綴lib,然后追加擴展名.a得到的靜態庫文件名來查找靜態庫文件,因此,我們在寫需要鏈接庫時,只寫名字就可以,如.a,致謝-l 。
代碼示例
# gcc -o hello main.c -static -L. -l myhello
通過測試可以發現,刪除依賴的靜態庫程序依然可以運行,說明靜態庫的實現已被鏈接到可執行文件里了。
靜態鏈接庫的一個缺點是,如果我們同時運行了許多程序,并且他們使用了同一個庫函數,這樣,在內存中會大量拷貝同一函數。浪費存儲空間c標準庫有靜態和動態庫,使用了共享鏈接庫的linux就可以避免這個問題。
共享函數庫和靜態函數庫很相似,只是后綴有所不同。比如,在一個典型的linux系統,標準的共享數序函數庫是/usr/lib/libm.so。
當一個程序使用共享函數庫時,在連接階段并不把函數代碼連接進來,而只是鏈接函數的一個引用。當最終的函數導入內存開始真正執行時,函數引用被解析,共享函數庫的代碼才真正導入到內存中。這樣,共享鏈接庫的函數就可以被許多程序同時共享,并且只需存儲一次就可以了。共享函數庫的另一個優點是,它可以獨立更新,與調用它的函數毫不影響。
3.動態鏈接庫(共享函數庫)
編譯生成動態鏈接庫需要用到.o文件(注意編譯生成.o文件的時候需要加入-fPIC選項,不然后面的指令會出錯)
動態庫文件名命名規范和靜態庫名命名規范類似,也是在動態庫名增加前綴lib,但其文件擴展名為.so。例如:我們將創建的動態庫名為,則動態庫文件名就是.so。用gcc來創建動態庫。
命令如下:
# gcc -shared -fPIC -o libmyhello.so hello.o
"PIC"命令行標記告訴gcc產塵的代碼不要包含對函數和變量具體內存位置的引用,只是因為現在還無法知道使用該消息代碼的應用程序會將它連接到哪一段內存地址空間。這樣編譯出來的hello.o可以被用于創建共享鏈接庫。建立共享鏈接庫只需要用gcc的"-"標記即可。
在程序中使用動態庫和使用靜態庫完全一樣,也是在使用到這些公有函數的源程序中包含這些公有函數的原型聲明,然后在用gcc命令生成目標文件時指明動態庫名進行編譯。
執行命令如下:
gcc -o hello main.c -L. -l myhello
(使用"-l "標記來告訴gcc驅動程序在連接階段引用共享庫函數.so。"-L."標記告訴gcc函數庫可能位于當前目錄。否則,gnu連接器會查找標準系統函數目錄:它先后搜索
1 .elf文件的段
2 環境變量
3 ./etc/ld.so.cache文件列表
4 ./lib,/usr/lib目錄找到庫文件后將其載入內存,但是我們生成的共享庫在當前文件夾下并沒有加到上述的4個路徑的任何一個中,因此,執行后會出現錯誤)
# ./hello
./hello:error while loading shared
libraries:libmyhello.so:cannot open shared object file:No such
file or directory
#
錯誤提示,找不到動態庫文件.so。程序在運行時,會在/usr/lib和lib等目錄中找需要的動態庫文件。若找到,則載入動態庫,否則將提示類似上述錯誤而終止程序運行。
解決方法:
(1)將文件.so復制到目錄/lib或者/usr/lib中。
(2)既然連接器會搜索所指定的目錄,那么我們可以將這個環境變量設置成當前目錄: =$(pwd)。
(3) /usr/zheng/lib 注:當用戶在某個目錄下面創建或拷貝了一個動態鏈接庫,若想使其被系統共享,可以執行一下" 目錄名“這個命令。此命令的功能在于讓將指定目錄下的動態鏈接庫被系統共享起來,意即:在緩存文件 /etc/ld.so.cache中追加進指定目錄下的共享庫。本例讓系統共享了/usr/zheng/lib目錄下的動態鏈接庫。該命令會重建/etc/ld.so.cache文件
編譯動態庫時遇到 a local 錯誤
編譯代碼
$ gcc -c hello.c
$ gcc -c main.c
$ gcc -shared -fPIC -o hello main.o hello.o
.. `a local ' can not be used when a ; with -fPIC
... could not read : Bad value
解決辦法編譯器已經提示了: with -fPIC
某些版本的gcc默認沒加-fPIC參數
解決辦法:保證你編譯.o文件的時候,都加上-fPIC
$ gcc -fPIC -c hello.c
$ gcc -fPIC -c main.c
$ gcc -shared -fPIC -o hello mian.o hello.o
知識小結:
(1)ldd hello 可以看到執行時調用動態庫的過程。
(2)編譯參數解析
-:該選項指定生成動態鏈接庫(讓連接器生成T類型的導出符號表,有時候也可以生成 弱連接W類型的導出符號),不用該標志外部程序無法連接。相當于一個可執行文件
-fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關的,所以動態載入時通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的。
-L.:表示要連接的庫在當前目錄中
-l test:編譯器查找動態鏈接庫時由隱含的命名規則,即在給出的名字前面加上lib,后面加上.so來確定庫的名稱。
:這個環境變量指示動態連接器可以裝載動態庫的路徑。
調用動態庫的時候有幾個問題會經常碰到,有時,明明已經將庫的頭文件所在目錄,通過"I"進來了,庫所在文件通過"-L"參數引導,并指定了”l”的庫名,但通過ldd命令查看時,就是找不到指定鏈接的so文件,這時你要做的就是通過修改或者/etc/ld.so.文件來指定動態庫的目錄。
靜態庫鏈接是搜索路徑順序:
1.ld會去找gcc命令中的參數-L
2.再找gcc的環境變量
3.再找內定目錄/lib /usr/lib/ /usr/local/lib這是當初 gcc 時寫在程序內的
動態鏈接時,執行時搜索路徑順序:
1.編譯目標代碼是指定的動態庫搜索路徑
2.環境變量指定的動態庫搜索路徑
3.配置文件/etc/ld.so.中指定的動態庫搜索路徑
4.默認的動態庫搜索路徑/lib
5.默認的動態庫搜索路徑/usr/lib
有關環境變量:
環境變量:指定程序靜態鏈接庫文件搜索路徑
環境變量:指定程序動態鏈接庫文件搜索路徑
拓展知識
GNU
GNU不是一個公司名,而是一個軟件項目名。它開發了許多應用程序。
GCC
GCC全稱是 GNU C , 最早的時候就是一個c編譯器。但是后來因為這個項目里邊集成了更多其他不同語言的編譯器,GCC就代表 the GNU ,所以表示一堆編譯器的合集。
G++
G++則是GCC的c++編譯器