1. 程式人生 > >交叉編譯詳解

交叉編譯詳解

arch soft inf 詳解 處理 cortex a8 del ldr 研究

第 1 章 交叉編譯簡介

1.1 什麽是交叉編譯

對於沒有做過嵌入式編程的人,可能不太理解交叉編譯的概念,那麽什麽是交叉編譯?它有什麽作用?

在解釋什麽是交叉編譯之前,先要明白什麽是本地編譯。

本地編譯

本地編譯可以理解為,在當前編譯平臺下,編譯出來的程序只能放到當前平臺下運行。平時我們常見的軟件開發,都是屬於本地編譯:

比如,我們在 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

交叉編譯時的主機環境與目標環境不同

  • Configuration issues:
  • HOSTCC vs TARGETCC:
  • Toolchain Leaks:
  • Libraries:
  • Testing:

詳細的對比可以參看這篇文章,已經寫的很詳細了,在這就不細說了:Introduction to cross-compiling for Linux

第 2 章 交叉編譯鏈

2.1 什麽是交叉編譯鏈

明白了什麽是交叉編譯,那我們來看看什麽是交叉編譯鏈。

首先編譯過程是按照不同的子功能,依照先後順序組成的一個復雜的流程,如下圖:

技術分享圖片

那麽編譯過程包括了預處理、編譯、匯編、鏈接等功能。既然有不同的子功能,那每個子功能都是一個單獨的工具來實現,它們合在一起形成了一個完整的工具集。

同時編譯過程又是一個有先後順序的流程,它必然牽涉到工具的使用順序,每個工具按照先後關系串聯在一起,這就形成了一個鏈式結構。

因此,交叉編譯鏈就是為了編譯跨平臺體系結構的程序代碼而形成的由多個子工具構成的一套完整的工具集。同時,它隱藏了預處理、編譯、匯編、鏈接等細節,當我們指定了源文件(.c)時,它會自動按照編譯流程調用不同的子工具,自動生成最終的二進制程序映像(.bin)。

註意:嚴格意義上來說,交叉編譯器,只是指交叉編譯的gcc,但是實際上為了方便,我們常說的交叉編譯器就是交叉工具鏈。本文對這兩個概念不加以區分,都是指編譯鏈

2.2 交叉編譯鏈的命名規則

我們使用交叉編譯鏈時,常常會看到這樣的名字:

arm-none-linux-gnueabi-gcc
arm-cortex_a8-linux-gnueabi-gcc
mips-malta-linux-gnu-gcc
  • 1
  • 2
  • 3
  • 4

其中,對應的前綴為:

arm-none-linux-gnueabi-
arm-cortex_a8-linux-gnueabi-
mips-malta-linux-gnu-
  • 1
  • 2
  • 3
  • 4

這些交叉編譯鏈的命名規則似乎是通用的,有一定的規則:

arch-core-kernel-system
  • 1
  • 2
  • arch: 用於哪個目標平臺。
  • core: 使用的是哪個CPU Core,如Cortex A8,但是這一組命名好像比較靈活,在其它廠家提供的交叉編譯鏈中,有以廠家名稱命名的,也有以開發板命名的,或者直接是none或cross的。
  • kernel: 所運行的OS,見過的有Linux,uclinux,bare(無OS)。
  • systen:交叉編譯鏈所選擇的庫函數和目標映像的規範,如gnu,gnueabi等。其中gnu等價於glibc+oabi;gnueabi等價於glibc+eabi。

註意:這個規則是一個猜測,並沒有在哪份官方資料上看到過。而且有些編譯鏈的命名確實沒有按照這個規則,也不清楚這是不是歷史原因造成的。如果有誰在資料上見到過此規則的詳細描述,歡迎指出錯誤。

第 3 章 包含的工具

3.1 Binutils

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

binutils介紹

3.2 GCC

GNU編譯器套件,支持C, C++, Java, Ada, Fortran, Objective-C等眾多語言。

3.3 GLibc

Linux上通常使用的C函數庫為glibc。glibc是linux系統中最底層的api,幾乎其它任何運行庫都會依賴於glibc。glibc除了封裝linux操作系統所提供的系統服務外,它本身也提供了許多其它一些必要功能服務的實現。

glibc 各個庫作用介紹

因為嵌入式環境的資源及其緊張,所以現在除了glibc外,還有uClibc和eglibc可以選擇,三者的關系可以參見這兩篇文章:

uclibc eglibc glibc之間的區別和聯系

Glibc vs uClibc Differences

3.4 GDB

GDB用於調試程序

第 4 章 如何得到交叉編譯鏈

既然明白了交叉編譯鏈的功能,那麽在針對嵌入式系統開發時,我們需要的交叉編譯鏈從哪兒得到?

主要有三個方式可以獲取

4.1 下載已經做好的交叉編譯鏈

使用其他人針對某些CPU平臺已經編譯好的交叉編譯鏈。我們只需要找到合適的,下載下來使用即可。

常見的交叉編譯鏈下載地址:

  1. 在 http://ftp.arm.linux.org.uk/pub/armlinux/toolchain/ 下載已經編譯好的交叉編譯鏈
  2. 在 http://www.denx.de/en/Software/WebHome 下載已經編譯好的交叉編譯鏈
  3. 在https://launchpad.net/gcc-arm-embedded下載已經編譯好的交叉編譯鏈
  4. 一些制作交叉編譯鏈的工具中,包含了已經制作好的交叉編譯鏈,可以直接拿來使用。如crosstool-NG
  5. 如果購買了某個芯片或開發板,一般廠商會提供對應的整套開發軟件,其中就包含了交叉編譯鏈。

廠家提供的工具一般是經過了嚴格的測試,並打入了一些必要的補丁,所以這種方式往往是最可靠的工具來源。

4.2 使用工具定制交叉編譯鏈

使用現存的制作工具,以簡化制作交叉編譯鏈這個事情的復雜度。我們只需要了解有哪些工具可以實現,並選個合適的工具,搞懂它的操作步驟即可。

  1. crosstool-NG
  2. Buildroot
  3. Embedded Linux Development Kit (ELDK)

工具還有很多,各有各的優勢和劣勢,大家可以慢慢研究,在這就不細說了。

4.3 從零開始構建交叉編譯鏈

這個是最困難也最耗時間的,畢竟制作交叉編譯鏈這樣的事情,需要對嵌入式的編譯原理了解的比較透徹,至少要知道出了問題要往哪個方面去翻閱資料。而且,也是最考耐心和細心的地方,配錯一個選項或是一個步驟,都可能出現以前從來沒見過的問題,而且這些問題往往還無法和這個選項或步驟直接聯系起來。

當然如果搭建出來,肯定也是收獲最大的,至少對於編譯的流程和依賴都比較清楚了,細節上的東西可能還需要去翻看相應的協議或標準,但至少骨架會比較清楚。

詳細的搭建過程可以參看後續的文章,這裏面有詳細的參數和步驟:
交叉編譯詳解 二 從零制作交叉編譯鏈

為了方便大家搭建交叉編譯鏈,我寫了一個一鍵生成的腳本(包括源碼下載和自動編譯)。如果大家自己一直搭建不成功,不妨試試這個腳本,然後對比下自己的流程是否一致,參數是否有差異,也許能幫大家邁過這個障礙:
交叉編譯詳解 三 使用腳本自動生成交叉編譯鏈

4.4 對比三種構建方式

項目使用已有交叉編譯鏈自己制作交叉編譯鏈
安裝 一般提供壓縮包 需要自己打包
源碼版本 一般使用較老的穩定版本,對於一些新的GCC特性不支持 可以使用自己需要的GCC特性的版本
補丁 一般都會打上修復補丁 普通開發者很難辨別需要打上哪些補丁,資深開發者可以針對自己的需求合入補丁
源碼溯源 可能不清楚源碼版本和補丁情況 一切都可以定制
升級 一般不會升級 可以隨時升級
優化 一般已經針對特定CPU特性和性能進行優化 一般無法做到比廠家優化的更好,除非自己設計的CPU
技術支持 可以通過FAE進行支持,可能需要收費 只能通過社區支持,免費
可靠性驗證 已經通過了完善的驗證 自己驗證,肯定沒有專業人士驗證的齊全

參考資料

[1] Introduction to cross-compiling for Linux

[2] binutils介紹

[3] glibc 各個庫作用介紹

[4] uclibc eglibc glibc之間的區別和聯系

[5] Glibc vs uClibc Differences

[6] 交叉編譯鏈下載地址

    • http://ftp.arm.linux.org.uk/pub/armlinux/toolchain/
    • http://www.denx.de/en/Software/WebHome
    • https://launchpad.net/gcc-arm-embedded

交叉編譯詳解