了解c/c++編譯器的基本使用,能夠在后續(xù)移植第三方框架進(jìn)行交叉編譯時(shí)(編譯android可用的庫(kù)),清楚的了解應(yīng)該傳遞什么參數(shù)。android的Android.mk就是一段段Makefile單元,很多第三方庫(kù)直接提供makefile,需要能夠大致的讀懂makefile文件。
gcc/g++
gcc
GNU C編譯器。原本只能處理C語(yǔ)言,很快擴(kuò)展,變得可處理C++。
g++
gcc和g++都能夠編譯c/c++,但是編譯時(shí)候行為不同。
對(duì)于gcc與g++:
- 后綴為.c的源文件,gcc把它當(dāng)作是C程序,而g++當(dāng)作是C++程序;后綴為.cpp的,兩者都會(huì)認(rèn)為是c++程序。
- g++會(huì)自動(dòng)鏈接c++標(biāo)準(zhǔn)庫(kù)stl,gcc不會(huì)。
- gcc不會(huì)定義__cplusplus宏,而g++會(huì)。
編譯器過(guò)程
一個(gè)C/C++文件要經(jīng)過(guò)預(yù)處理(preprocessing)、編譯(compilation)、匯編(assembly)、和鏈接(linking)才能變成可執(zhí)行文件。
1、預(yù)處理
?gcc -E main.c -o main.i
?-E的作用是讓gcc在預(yù)處理結(jié)束后停止編譯。
?預(yù)處理階段主要處理include和define等。它把#include包含進(jìn)來(lái)的.h 文件插入到#include所在的位置,把源程序中使用到的用#define定義的宏用實(shí)際的字符串代替
2、編譯階段
gcc -S main.i -o main.s
-S的作用是編譯后結(jié)束,編譯生成了匯編文件。
?在這個(gè)階段中,gcc首先要檢查代碼的規(guī)范性、是否有語(yǔ)法錯(cuò)誤等,以確定代碼的實(shí)際要做的工作,在檢查無(wú)誤后,gcc把代碼翻譯成匯編語(yǔ)言。
3、匯編階段
gcc -c main.s -o main.o
匯編階段把 .s文件翻譯成二進(jìn)制機(jī)器指令文件.o,這個(gè)階段接收.c, .i, .s的文件都沒(méi)有問(wèn)題。
4、鏈接階段
?gcc -o main main.s
鏈接階段,鏈接的是函數(shù)庫(kù)。在main.c中并沒(méi)有定義”printf”的函數(shù)實(shí)現(xiàn),且在預(yù)編譯中包含進(jìn)的”stdio.h”中也只有該函數(shù)的聲明。系統(tǒng)把這些函數(shù)實(shí)現(xiàn)都被做到名為libc.so的動(dòng)態(tài)庫(kù)。
/etc/ld.so.conf
在沒(méi)有特別指定時(shí),gcc會(huì)到系統(tǒng)編譯器只會(huì)使用/lib和/usr/lib這兩個(gè)目錄下的庫(kù)文件。如果存在一個(gè)so不在這兩個(gè)目錄,在編譯時(shí)候就會(huì)出現(xiàn)找不到的情況。
/etc/ld.so.conf文件中可以指定而外的編譯鏈接庫(kù)路徑。
輸入:cat /etc/ld.so.conf:
include /etc/ld.so.conf.d/*.conf #引入其他的conf文件
/usr/local/lib #增加庫(kù)搜索目錄
#編輯完成后 使用 ldconfig 更新
靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)
庫(kù)通常以前綴“ lib”命名。對(duì)于所有C標(biāo)準(zhǔn)庫(kù)都是如此。鏈接時(shí),對(duì)庫(kù)的命令行引用將不包含庫(kù)前綴或后綴。
比如liblog.so這個(gè)庫(kù),引用的使用去掉前綴lib和后綴.so即可引用該庫(kù)。
.a 為靜態(tài)庫(kù),可以是一個(gè)或多個(gè).o合在一起,用于靜態(tài)連接;多個(gè).o文件可以鏈接生成一個(gè).exe的可執(zhí)行文件。靜態(tài)庫(kù)在程序編譯時(shí)會(huì)被鏈接到目標(biāo)代碼中,相當(dāng)于將你使用庫(kù)里的函數(shù)加載到程序里,在編譯的時(shí)候直接編譯進(jìn)去,這樣,在編譯之后執(zhí)行程序時(shí)將不再需要該靜態(tài)庫(kù)。編譯之后
程序文件大,但加載快,隔離性也好。所以它的優(yōu)點(diǎn)就顯而易見(jiàn)了,即編譯后的執(zhí)行程序不需要外部的函數(shù)庫(kù)支持,因?yàn)樗惺褂玫暮瘮?shù)都已經(jīng)被編譯進(jìn)去了。當(dāng)然這也會(huì)成為它的缺點(diǎn),因?yàn)槿绻o態(tài)函數(shù)庫(kù)改變了,那么你的程序必須重新編譯。
.so 為動(dòng)態(tài)庫(kù)(共享庫(kù)),類似windows平臺(tái)的.dll文件。動(dòng)態(tài)庫(kù)在程序
編譯時(shí)并不會(huì)被鏈接到目標(biāo)代碼中,而是在編譯時(shí)僅引用,體積小,在程序運(yùn)行到相關(guān)函數(shù)時(shí)才調(diào)用函數(shù)庫(kù)里的相應(yīng)函數(shù),才被載入,因此在程序運(yùn)行時(shí)還需要?jiǎng)討B(tài)庫(kù)存在。多個(gè)應(yīng)用程序可以使用同一個(gè)動(dòng)態(tài)庫(kù),啟動(dòng)多個(gè)應(yīng)用程序的時(shí)候,只需要將動(dòng)態(tài)庫(kù)加載到內(nèi)存一次即可。
簡(jiǎn)而言之:
靜態(tài)庫(kù)節(jié)省時(shí)間,靜態(tài)庫(kù)在程序編譯時(shí)會(huì)被連接到目標(biāo)代碼中,程序運(yùn)行時(shí)將不再需要該靜態(tài)庫(kù)。
動(dòng)態(tài)庫(kù)節(jié)省空間,動(dòng)態(tài)庫(kù)在程序編譯時(shí)并不會(huì)被連接到目標(biāo)代碼中,而是在程序運(yùn)行是才被載入,因此在程序運(yùn)行時(shí)還需要?jiǎng)討B(tài)庫(kù)存在。
Java中在不經(jīng)過(guò)封裝的情況下只能直接使用動(dòng)態(tài)庫(kù)。
生成靜態(tài)庫(kù)
gcc -fPIC -c Test.c -o Test.o
ar r libTest.a Test.o
-fPIC 產(chǎn)生與位置無(wú)關(guān)代碼
可能會(huì)被不同的進(jìn)程加載到不同的位置上,如果共享對(duì)象中的指令使用了絕對(duì)地址。
那么在共享對(duì)象被加載時(shí)就必須根據(jù)相關(guān)模塊的加載位置對(duì)這個(gè)地址做調(diào)整,也就是修改這些地址,
讓它在對(duì)應(yīng)進(jìn)程中能正確訪問(wèn),那么就不能實(shí)現(xiàn)多進(jìn)程共享一份物理內(nèi)存(無(wú)法動(dòng)態(tài)共享)
這里有一個(gè)例子,詳細(xì)介紹了靜態(tài)庫(kù)的生成和如何鏈接靜態(tài)庫(kù)。
生成動(dòng)態(tài)庫(kù)
gcc -fPIC -shared Test.c -o libTest.so
#或者
gcc -fPIC -c Test.c #生成.o
gcc -shared Test.o -o libTest.so
-fPIC:編譯器指令,用于輸出位置無(wú)關(guān)的代碼,這是共享庫(kù)所需的特征。
-shared:生成一個(gè)共享對(duì)象,然后可以將其與其他對(duì)象鏈接以形成可執(zhí)行文件。
-Wl 選項(xiàng):將選項(xiàng)傳遞給鏈接器。
-o:操作輸出。
這里有一個(gè)例子,詳細(xì)介紹了靜態(tài)庫(kù)的生成和如何鏈接靜態(tài)庫(kù)。
使用庫(kù)
默認(rèn)優(yōu)先使用動(dòng)態(tài)庫(kù)
gcc main.c -L. -lTest -o main
強(qiáng)制使用靜態(tài)庫(kù)
-Wl 表示傳遞給 ld 鏈接器的參數(shù)
最后的 -Bdynamic 表示 默認(rèn)仍然使用動(dòng)態(tài)庫(kù)
gcc main.c -L. -Wl,-Bstatic -lTest -Wl,-Bdynamic -o main
使用動(dòng)態(tài)庫(kù)鏈接的程序,linux運(yùn)行需要將動(dòng)態(tài)庫(kù)路徑加入/etc/ld.so.conf。
查看可執(zhí)行文件符號(hào)
nm main
打包 .a 到so
--whole-archive: 將未使用的靜態(tài)庫(kù)符號(hào)(函數(shù)實(shí)現(xiàn))也鏈接進(jìn)動(dòng)態(tài)庫(kù)
--no-whole-archive : 默認(rèn),未使用不鏈接進(jìn)入動(dòng)態(tài)庫(kù)
gcc -shared -o libMain.so -Wl,--whole-archive libMain.a -Wl,--no-whole-archive
頭文件與庫(kù)文件的指定
--sysroot=XX
使用xx作為這一次編譯的頭文件與庫(kù)文件的查找目錄,查找下面的 usr/include usr/lib目錄。
-isysroot XX
頭文件查找目錄,覆蓋--sysroot ,查找 XX/usr/include
-isystem XX
指定頭文件查找路徑(直接查找根目錄)
-IXX
頭文件查找目錄
優(yōu)先級(jí):-I > -isystem > sysroot
-L:XX
指定庫(kù)文件查找目錄
-lxx.so
指定需要鏈接的庫(kù)名