交叉編譯其實是相對於本地編譯(native build)來說的,我相信大家最開始學習 C/C++ 這些語言的時候,都是在電腦上寫程序,然後在電腦上編譯生成可執行文件,最後在電腦上運行。程序的編輯——》編譯——》運行,整個過程都是在一臺 X86 電腦上。
當我們開始接觸嵌入式開發後,事情變的不一樣了,你在電腦上寫程序,在電腦上編譯出可執行文件,最後這個可執行文件需要下載到你的開發板上運行。程序最後運行的環境變了,比如你的開發板是基於 Arm 的——程序在 X86 上編輯,編譯,最終運行在另一個和 X86 完全不同的架構的 Arm 晶片上。
之所以整個流程變成了這個樣子,這是由嵌入式系統的特性決定的:一般嵌入式系統裡面使用的晶片性能都比較弱,而且絕大部分都不能像 X86 一樣運行 Windows/Ubuntu 桌面系統,即使能運行,性能也很弱,無法給你提供一個在開發板上寫代碼、編譯代碼的環境。所以我們還是離不開 X86 電腦強大高效的桌面環境進行軟體開發。
但是這樣有一個問題,X86、Arm、MIPS、RISC-V 這些晶片,它們的指令集是由不同的組織或者公司設計的,彼此並不兼容——Arm 和 MIPS 的 CPU 無法運行以 X86 的指令集編碼的程序,反之亦然。所以我們要在 X86 的電腦上編譯出能夠在 Arm 上運行的程序,我們必須明確告訴編譯器,編譯生成的可執行文件需要以 Arm 指令集的標準編碼。為了讓這個流程變得簡單,開發者們為不同的晶片開發了不同的編譯器,比如針對 Arm 平臺的 arm-linux-gcc,針對 mips 平臺的 mips-linux-gnu-gcc,這些編譯器都是基於 GCC 針對具體的架構指令集進行對應配置,所以它們在運行的時候就就會生成和該目標平臺對應的可執行文件。
這篇文章主要講 Arm 的交叉編譯,所以這裡後面都以 Linux 開發環境下的 Arm gcc 為例。
工具鏈的種類GCC 的命名規則為:arch [-vendor] [-os] [-(gnu)eabi]-gcc
比如 arm-linux-gnueabi-gcc , arm-none-eabi-gcc, aarch64-linux-gnu-gcc
帶 [] 的是可選部分。
arch:晶片架構,比如 32 位的 Arm 架構對應的 arch 為 arm,64 位的 Arm 架構對應的 arch 為 aarch64。
vendor :工具鏈提供商,大部分工具鏈名字裡面都沒有包含這部分。
os :編譯出來的可執行文件(目標文件)針對的作業系統,比如 Linux。
arm-none-eabi-gcc 一般適用用於 Arm Cortex-M/Cortex-R 平臺,它使用的是 newlib 庫。
arm-linux-gnueabi-gcc 和 aarch64-linux-gnu-gcc 適用於 Arm Cortex-A 系列晶片,前者針對 32 位晶片,後者針對 64 位晶片,它使用的是 glibc 庫。可以用來編譯 u-boot、linux kernel 以及應用程式。
另外需要補充一點的是,32 位的 Arm 和 64 位的 Arm,它們的指令集是不同的,所以需要使用不同的工具鏈。當然,Arm64 為了保證前向兼容,提供了一個 32 位的兼容模式,所以我們用 arm-linux-gnueabi-gcc 編譯的應用程式也是可以直接在Arm64 的系統上運行的,但是 Linux Kernel 和 U-Boot 就不行,除非你提前把 CPU 切換到 32 位模式。曾經有個項目使用了一顆四核的 Arm64 晶片,但是內存只有64M,為了節省空間,在 CPU 運行到 U-Boot 之前,我們就把它切到了 32 位模式,後面的 U-Boot、Linux Kernel,應用全部都用 32 位編譯,加上 Thumb 指令集,節省了不少空間。
工具鏈的下載安裝下載地址現在 Arm 平臺上用的最廣泛的工具鏈是 Linaro 發布的,大家可以到 Linaro 官網下載,地址如下:
http://releases.linaro.org/components/toolchain/binaries/
同時我發現 Linaro 申明稱後續新版本的工具鏈會通過 Arm 官方發布。
https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain
4.9、6.3、7.4 這些是工具鏈的版本號,理論上越新的版本,性能越好。
在一次優化 u-boot 大小的時候我發現 6.3 版本的工具鏈生成的二進位文件要比 4.9 版本生成的小几百個字節,進一步對比分析後發現是因為 6.3 版本的工具鏈把代碼中沒用到的一些字符串全部過濾掉了,雖然 4.9 的版本也有過濾,但是沒有 6.3 做的乾淨。
目前用的多的版本應該是 6.x,當然我也看到有些開發組織在使用 7.x 的工具鏈,比如 Armbian 目前在用 7.4 編譯內核。
另外目前最新版本的 U-Boot 已經強制必須使用 6.0 以上版本的 GCC 進行編譯。
這兩個網站下載可能會比較慢,可以考慮使用國內的鏡像下載:
https://mirrors.tuna.tsinghua.edu.cn/armbian-releases/_toolchains/
可以直接點擊下載,也可以通過命令行用 wget 命令下載:
wget https://mirrors.tuna.tsinghua.edu.cn/armbian-releases/_toolchains/gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf.tar.xz
wget https://mirrors.tuna.tsinghua.edu.cn/armbian-releases/_toolchains/gcc-linaro-6.4.1-2017.11-x86_64_aarch64-linux-gnu.tar.xz
下載了兩個,arm 針對是是 32 位的, aarch64 針對 Arm64.
安裝解壓:
xz -d gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf.tar.xz
xz -d gcc-linaro-6.4.1-2017.11-x86_64_aarch64-linux-gnu.tar.xz
安裝到 /usr/local/toolchain/ 目錄下,當然也可以放在其他任何你喜歡的地方:
sudo mkdir -p /usr/local/toolchain
tar -xvf gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf.tar -C /usr/local/toolchain/
sudo tar -xvf gcc-linaro-6.4.1-2017.11-x86_64_aarch64-linux-gnu.tar -C /usr/local/toolchain/
成功後執行 ls 命令,可以看到兩個 toolchain 都被安裝到 /usr/local/toolchain/ 目錄下了。
只有把可執行文件對應的路徑加入到 PATH 環境變量裡,系統才可以認到這些命令。
這裡的路徑就是上面截圖中 pwd 命令顯示的路徑,可以添加到 ~/.bashrc 文件的最後:
PATH=$PATH:/usr/local/toolchain/gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf/bin:/usr/local/toolchain/gcc-linaro-6.4.
1-2017.11-x86_64_aarch64-linux-gnu/bin
注意:兩個獨立的路徑之間用 冒號 : 隔開。
執行 source~/.bashrc 命令讓配置生效,然後我們就可以在命令行裡執行這些命令了:
比如 arm-linux-gnueabihf-gcc-v 和 aarch64-linux-gnu-gcc-v 查看編譯器版本:
能看到如圖的信息,說明已經大功告成了。
編譯內核這裡下載 imx 的官方內核:
git clone https://source.codeaurora.org/external/imx/linux-imx
切換到 4.19 版本:
git checkout -b imx_v2019.04_4.19.35_1.0.0 origin/imx_v2019.04_4.19.35_1.0.0
編譯:
make ARCH=arm imx_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
如果你電腦上其他的依賴庫都安裝的完整,就能順利編譯成功。