對于沒有做過嵌入式編程的人,可能不太理解交叉編譯的概念,那么什么是交叉編譯?它有什么作用?
在解釋什么是交叉編譯之前,先要明白什么是本地編譯。
本地編譯
本地編譯可以理解為,在當前編譯平臺下,編譯出來的程序只能放到當前平臺下運行。平時我們常見的軟件開發,都是屬于本地編譯:
比如,我們在x86 平臺上,編寫程序并編譯成可執行程序。這種方式下,我們使用 x86 平臺上的工具,開發針對 x86 平臺本身的可執行程序,這個編譯過程稱為本地編譯。
交叉編譯
交叉編譯可以理解為,在當前編譯平臺下,編譯出來的程序能運行在體系結構不同的另一種目標平臺上,但是編譯平臺本身卻不能運行該程序:
比如,我們在x86 平臺上,編寫程序并編譯成能運行在 ARM 平臺的程序,編譯得到的程序在 x86 平臺上是不能運行的,必須放到 ARM 平臺上才能運行。
1.2 為什么會有交叉編譯
之所以要有交叉編譯,主要原因是:
Speed: 目標平臺的運行速度往往比主機慢得多,許多專用的嵌入式硬件被設計為低成本和低功耗,沒有太高的性能
Capability: 整個編譯過程是非常消耗資源的,嵌入式系統往往沒有足夠的內存或磁盤空間
Availability: 即使目標平臺資源很充足,可以本地編譯,但是第一個在目標平臺上運行的本地編譯器總需要通過交叉編譯獲得
Flexibility: 一個完整的Linux編譯環境需要很多支持包,交叉編譯使我們不需要花時間將各種支持包移植到目標板上。
1.3 為什么交叉編譯比較困難
交叉編譯的困難點在于兩個方面:
不同的體系架構擁有不同的機器特性
Word size: 是64位還是32位系統
Endianness: 是大端還是小端系統
Alignment: 是否必修按照4字節對齊方式進行訪問
Default signedness: 默認數據類型是有符號還是無符號
NOMMU: 是否支持MMU
交叉編譯時的主機環境與目標環境不同
[if !supportLists]·?[endif]Configuration issues:
[if !supportLists]·?[endif]HOSTCC vs TARGETCC:
[if !supportLists]·?[endif]Toolchain Leaks:
[if !supportLists]·?[endif]Libraries:
[if !supportLists]·?[endif]Testing:
明白了什么是交叉編譯,那我們來看看什么是交叉編譯鏈。
首先編譯過程是按照不同的子功能,依照先后順序組成的一個復雜的流程,如下圖:
那么編譯過程包括了預處理、編譯、匯編、鏈接等功能。既然有不同的子功能,那每個子功能都是一個單獨的工具來實現,它們合在一起形成了一個完整的工具集。
同時編譯過程又是一個有先后順序的流程,它必然牽涉到工具的使用順序,每個工具按照先后關系串聯在一起,這就形成了一個鏈式結構。
此,交叉編譯鏈就是為了編譯跨平臺體系結構的程序代碼而形成的由多個子工具構成的一套完整的工具集。同時,它隱藏了預處理、編譯、匯編、鏈接等細節,當我們指定了源文件(.c)時,它會自動按照編譯流程調用不同的子工具,自動生成最終的二進制程序映像(.bin)。
注意:嚴格意義上來說,交叉編譯器,只是指交叉編譯的gcc,但是實際上為了方便,我們常說的交叉編譯器就是交叉工具鏈。本文對這兩個概念不加以區分,都是指編譯鏈
2.2 交叉編譯鏈的命名規則
我們使用交叉編譯鏈時,常常會看到這樣的名字:
arm-none-linux-gnueabi-gcc
arm-cortex_a8-linux-gnueabi-gcc
mips-malta-linux-gnu-gcc
123
其中,對應的前綴為:
arm-none-linux-gnueabi-
arm-cortex_a8-linux-gnueabi-
mips-malta-linux-gnu-
123
這些交叉編譯鏈的命名規則似乎是通用的,有一定的規則:
arch-core-kernel-system
1
arch: 用于哪個目標平臺。
core: 使用的是哪個CPU Core,如Cortex A8,但是這一組命名好像比較靈活,在其它廠家提供的交叉編譯鏈中,有以廠家名稱命名的,也有以開發板命名的,或者直接是none或cross的。
kernel: 所運行的OS,見過的有Linux,uclinux,bare(無OS)。
systen:交叉編譯鏈所選擇的庫函數和目標映像的規范,如gnu,gnueabi等。其中gnu等價于glibc+oabi;gnueabi等價于glibc+eabi。
注意:這個規則是一個猜測,并沒有在哪份官方資料上看到過。而且有些編譯鏈的命名確實沒有按照這個規則,也不清楚這是不是歷史原因造成的。如果有誰在資料上見到過此規則的詳細描述,歡迎指出錯誤。
Binutils是GNU工具之一,它包括鏈接器、匯編器和其他用于目標文件和檔案的工具,它是二進制代碼的處理維護工具。
Binutils工具包含的子程序如下:
ld ?GNU連接器the GNU linker. ??
as GNU匯編器the GNU assembler. ??
addr2line 把地址轉換成文件名和所在的行數 ??
ar A utility for creating, modifying and extracting from archives. ??
c++filt Filter to demangle encoded C++ symbols. ??
dlltool Creates files for building and using DLLs. ??
gold A new, faster, ELF only linker, still in beta test. ??
gprof Displays profiling information. ??
nlmconv Converts object code into an NLM. ??
nm Lists symbols from object files. ??
objcopy Copys and translates object files. ??
objdump Displays information from object files. ??
ranlib Generates an index to the contents of an archive. ??
readelf Displays information from any ELF format object file. ??
size Lists the section sizes of an object or archive file. ??
strings Lists printable strings from files. ??
strip Discards symbols
GNU編譯器套件,支持C, C++, Java, Ada, Fortran, Objective-C等眾多語言。
Linux上通常使用的C函數庫為glibc。glibc是linux系統中最底層的api,幾乎其它任何運行庫都會依賴于glibc。glibc除了封裝linux操作系統所提供的系統服務外,它本身也提供了許多其它一些必要功能服務的實現。
因為嵌入式環境的資源及其緊張,所以現在除了glibc外,還有uClibc和eglibc可以選擇,三者的關系可以參見這兩篇文章:
GDB用于調試程序
第4 章 如何得到交叉編譯鏈
既然明白了交叉編譯鏈的功能,那么在針對嵌入式系統開發時,我們需要的交叉編譯鏈從哪兒得到?
主要有三個方式可以獲取
4.1 下載已經做好的交叉編譯鏈
用其他人針對某些CPU平臺已經編譯好的交叉編譯鏈。我們只需要找到合適的,下載下來使用即可。
常見的交叉編譯鏈下載地址:
在http://ftp.arm.linux.org.uk/pub/armlinux/toolchain/ 下載已經編譯好的交叉編譯鏈
在http://www.denx.de/en/Software/WebHome 下載已經編譯好的交叉編譯鏈
在https://launchpad.net/gcc-arm-embedded下載已經編譯好的交叉編譯鏈
一些制作交叉編譯鏈的工具中,包含了已經制作好的交叉編譯鏈,可以直接拿來使用。如crosstool-NG
如果購買了某個芯片或開發板,一般廠商會提供對應的整套開發軟件,其中就包含了交叉編譯鏈。
廠家提供的工具一般是經過了嚴格的測試,并打入了一些必要的補丁,所以這種方式往往是最可靠的工具來源。
4.2 使用工具定制交叉編譯鏈
使用現存的制作工具,以簡化制作交叉編譯鏈這個事情的復雜度。我們只需要了解有哪些工具可以實現,并選個合適的工具,搞懂它的操作步驟即可。
crosstool-NG
Buildroot
Embedded Linux Development Kit (ELDK)
工具還有很多,各有各的優勢和劣勢,大家可以慢慢研究,在這就不細說了。
4.3 從零開始構建交叉編譯鏈
這個是最困難也最耗時間的,畢竟制作交叉編譯鏈這樣的事情,需要對嵌入式的編譯原理了解的比較透徹,至少要知道出了問題要往哪個方面去翻閱資料。而且,也是最考耐心和細心的地方,配錯一個選項或是一個步驟,都可能出現以前從來沒見過的問題,而且這些問題往往還無法和這個選項或步驟直接聯系起來。
當然如果搭建出來,肯定也是收獲最大的,至少對于編譯的流程和依賴都比較清楚了,細節上的東西可能還需要去翻看相應的協議或標準,但至少骨架會比較清楚。
詳細的搭建過程可以參看后續的文章,這里面有詳細的參數和步驟:
交叉編譯詳解二從零制作交叉編譯鏈
為了方便大家搭建交叉編譯鏈,我寫了一個一鍵生成的腳本(包括源碼下載和自動編譯)。如果大家自己一直搭建不成功,不妨試試這個腳本,然后對比下自己的流程是否一致,參數是否有差異,也許能幫大家邁過這個障礙:
交叉編譯詳解三使用腳本自動生成交叉編譯鏈