在linux中,交叉編譯是指在一個(gè)平臺(tái)上生成另一個(gè)平臺(tái)上的可執(zhí)行代碼,即編譯源代碼的平臺(tái)和執(zhí)行源代碼編譯后程序的平臺(tái)是兩個(gè)不同的平臺(tái)。使用交叉編譯的原因:1、目標(biāo)系統(tǒng)沒(méi)有能力在其上進(jìn)行本地編譯;2、有能力進(jìn)行源代碼編譯的平臺(tái)與目標(biāo)平臺(tái)不同。
本教程操作環(huán)境:linux5.9.8系統(tǒng)、Dell G3電腦。
交叉編譯
所謂"交叉編譯(Cross_Compile)",是指編譯源代碼的平臺(tái)和執(zhí)行源代碼編譯后程序的平臺(tái)是兩個(gè)不同的平臺(tái)。比如,在Intel x86架構(gòu)/Linux(Ubuntu)平臺(tái)下、使用交叉編譯工具鏈生成的可執(zhí)行文件,在ARM架構(gòu)/Linux下運(yùn)行。
簡(jiǎn)單地說(shuō),就是在一個(gè)平臺(tái)上生成另一個(gè)平臺(tái)上的 可執(zhí)行代碼。同一個(gè) 體系結(jié)構(gòu)可以運(yùn)行不同的操作系統(tǒng);同樣,同一個(gè)操作系統(tǒng)也可以在不同的體系結(jié)構(gòu)上運(yùn)行。
交叉編譯是相對(duì)復(fù)雜的,必須考慮如下幾個(gè)問(wèn)題:
-
CPU架構(gòu):比如ARM,x86,MIPS等等;
-
字節(jié)序:大端(big-endian)和小端(little-endian);
-
浮點(diǎn)數(shù)的支持;
-
應(yīng)用程序二進(jìn)制接口(Application Binary Interface,ABI);
為什么要使用交叉編譯呢?主要有兩個(gè)原因:
-
交叉編譯的目標(biāo)系統(tǒng)一般都是內(nèi)存較小、顯示設(shè)備簡(jiǎn)陋甚至沒(méi)有,沒(méi)有能力在其上進(jìn)行本地編譯;
-
有能力進(jìn)行源代碼編譯的平臺(tái)CPU架構(gòu)或操作系統(tǒng)與目標(biāo)平臺(tái)不同;
交叉編譯工具鏈?zhǔn)沁M(jìn)行交叉編譯的必不可少的工具,是嵌入式開(kāi)發(fā)人員必須熟練掌握的技能。
為什么交叉編譯很難?
便攜式本機(jī)編譯很困難。
大多數(shù)程序是在 x86 硬件上開(kāi)發(fā)的,在本地編譯的。交叉編譯會(huì)遇到兩種類型的問(wèn)題:程序本身的問(wèn)題和構(gòu)建系統(tǒng)的問(wèn)題。
第一類問(wèn)題會(huì)影響所有非 x86 目標(biāo),包括本機(jī)和交叉構(gòu)建。大多數(shù)程序?qū)\(yùn)行的機(jī)器類型做出假設(shè),必須與相關(guān)平臺(tái)匹配,否則程序?qū)o(wú)法運(yùn)行。常見(jiàn)的假設(shè)包括:
-
Word size – 將指針復(fù)制到 int 可能會(huì)在 64 位平臺(tái)上丟失數(shù)據(jù),通過(guò)乘以 4 而不是 sizeof(long) ,確定 malloc 的大小不好。整數(shù)溢出導(dǎo)致細(xì)微安全漏洞,ala“if (x+y < size) memset(src+x,0,y);”,當(dāng) x=1000 時(shí),在 32 位硬件上產(chǎn)生 4 GB 的 memset y=0xFFFFFFF0…
-
Endianness – 不同的系統(tǒng)用不同的方式在內(nèi)部存儲(chǔ)二進(jìn)制數(shù)據(jù),從磁盤或網(wǎng)絡(luò)中,讀取 int 或 float 數(shù)據(jù)可能需要轉(zhuǎn)換。
-
Alignment – 某些平臺(tái)(例如 arm)只能從 4 字節(jié)的偶數(shù)倍的地址,讀取或?qū)懭胝麛?shù),否則出現(xiàn)段錯(cuò)誤。處理任意alignment的處理,未alignment的數(shù)據(jù)都較慢,編譯器通常會(huì)填充結(jié)構(gòu)alignment變量。將結(jié)構(gòu)視為可以發(fā)送到磁盤或通過(guò)網(wǎng)絡(luò)發(fā)送的數(shù)據(jù)塊,需要額外的工作確保一致的表示。
-
默認(rèn)簽名- “char”數(shù)據(jù)類型,默認(rèn)為有符號(hào)或無(wú)符號(hào),因平臺(tái)而異(從編譯器到編譯器),導(dǎo)致一些非常令人驚訝的錯(cuò)誤。簡(jiǎn)單解決方法是提供一個(gè)編譯器參數(shù),如“-funsigned-char”,強(qiáng)制默認(rèn)值為已知值。
-
NOMMU – 如果目標(biāo)平臺(tái)沒(méi)有內(nèi)存管理單元,需要更改幾項(xiàng)內(nèi)容。需要 vfork(),不是 fork(),只有某些類型的 mmap() 工作(共享或只讀,但不能在寫入時(shí)復(fù)制),堆棧不會(huì)動(dòng)態(tài)增長(zhǎng)。
大多數(shù)包的目標(biāo)是在本地編譯時(shí)可移植,至少會(huì)接受補(bǔ)丁,修復(fù)提交到適當(dāng)?shù)拈_(kāi)發(fā)郵件列表的任何上述問(wèn)題(NOMMU 問(wèn)題除外)。
然后是交叉編譯。
除了本機(jī)編譯的問(wèn)題外,交叉編譯還有其自身的一系列問(wèn)題:
-
配置問(wèn)題- 具有單獨(dú)配置步驟的包(標(biāo)準(zhǔn) configure/make/make install 的“./configure”部分),通常會(huì)測(cè)試字節(jié)順序或頁(yè)面大小等內(nèi)容,在本機(jī)編譯時(shí)可移植。交叉編譯時(shí),這些值在主機(jī)系統(tǒng)和目標(biāo)系統(tǒng)之間不同,在主機(jī)系統(tǒng)上運(yùn)行測(cè)試,給出錯(cuò)誤的答案。當(dāng)目標(biāo)沒(méi)有該軟件包或版本不兼容時(shí),配置檢測(cè)主機(jī)上,是否存在軟件包支持。
-
HOSTCC vs TARGETCC -構(gòu)建過(guò)程需要編譯在主機(jī)系統(tǒng)上運(yùn)行,如上述配置測(cè)試,或生成代碼的程序(如創(chuàng)建 .h 文件的 C 程序,在main構(gòu)建期間 #included )。用目標(biāo)編譯器替換主機(jī)編譯器,破壞在構(gòu)建過(guò)程中運(yùn)行庫(kù)。這樣的庫(kù)需要訪問(wèn)主機(jī)和目標(biāo)編譯器,需要說(shuō)明何時(shí)使用。
-
工具鏈泄漏- 配置不當(dāng)?shù)慕徊婢幾g工具鏈,將主機(jī)系統(tǒng)的一些內(nèi)容泄漏到已編譯的程序中,導(dǎo)致通常易于檢測(cè),但難以診斷和糾正的故障。工具鏈可能 #include 錯(cuò)誤的頭文件,或在鏈接時(shí)搜索錯(cuò)誤的庫(kù)路徑。共享庫(kù)通常依賴于其它共享庫(kù),可能會(huì)潛入對(duì)主機(jī)系統(tǒng)的意外鏈接時(shí)引用。
-
庫(kù)- 動(dòng)態(tài)鏈接的程序必須在編譯時(shí),訪問(wèn)適當(dāng)?shù)墓蚕韼?kù)。目標(biāo)系統(tǒng)的共享庫(kù),需要添加到交叉編譯工具鏈中,以便程序可以鏈接到。
-
測(cè)試- 在本機(jī)構(gòu)建上,開(kāi)發(fā)系統(tǒng)提供了方便的測(cè)試環(huán)境。交叉編譯時(shí),確認(rèn)“hello world”構(gòu)建成功,可能需要(至少)配置引導(dǎo)加載程序,內(nèi)核,根文件系統(tǒng)和共享庫(kù)。
-
腳注 1:計(jì)算機(jī)類型之間最顯著的區(qū)別是執(zhí)行程序的處理器,其它差異包括庫(kù) ABI(例如 glibc 與 uClibc),具有可配置字節(jié)序的機(jī)器(arm 與 armeb),或不同模式的機(jī)器,可以運(yùn)行 32 位和 64 位代碼(例如 x86 上的 x86-64)。
-
腳注 2:在構(gòu)建編譯器時(shí),第三種類型稱為“加拿大交叉”,一種不在主機(jī)系統(tǒng)上運(yùn)行的交叉編譯器。加拿大交叉構(gòu)建了一個(gè)編譯器,該編譯器在一個(gè)目標(biāo)平臺(tái)上運(yùn)行,另一臺(tái)目標(biāo)機(jī)器生成代碼。首先創(chuàng)建從主機(jī)到第一個(gè)目標(biāo)的臨時(shí)交叉編譯器,作為第二個(gè)目標(biāo)構(gòu)建另一個(gè)交叉編譯器構(gòu)建這樣的外部編譯器。第一個(gè)交叉編譯器的目標(biāo)成為運(yùn)行新編譯器的主機(jī),第二個(gè)目標(biāo)是新編譯器生成輸出的平臺(tái)。這種技術(shù)通常用于為目標(biāo)平臺(tái)交叉編譯新的本機(jī)編譯器。
-
腳注 3:現(xiàn)代桌面系統(tǒng)足夠快,模擬目標(biāo)在模擬器下進(jìn)行本地編譯,實(shí)際上是一種可行的策略。比交叉編譯慢得多,需要為目標(biāo)查找或生成本機(jī)構(gòu)建環(huán)境(無(wú)論如何都必須設(shè)置交叉編譯器),可能會(huì)因模擬器和要部署的真實(shí)硬件之間的差異崩潰。
-
腳注 4:交叉編譯工具鏈傾向于為其實(shí)用程序的名稱加上前綴,ala “armv5l-linux-gcc”。如果簡(jiǎn)單地稱為“gcc”,主機(jī)和本機(jī)編譯器就不能同時(shí)在 $PATH 中。