交叉編譯入門及必要配置方法總結

交叉編譯總結

本文是交叉編譯入門及必要配置方法總結,目的爲新手介紹如何進入交叉編譯的世界,並附帶兩個重要列子:
第一個是使用cmake進行交叉編譯
第二個是交叉編譯Protobuf

交叉編譯的目的是在一臺架構A主機平臺上編譯另一種架構B目標平臺的二進制文件或者庫,交叉編譯在目標系統平臺(開發出來的應用程序序所運行的平臺)難以或不容易編譯時非常有用。主要體現在以下四個方面:

  • 性能: 目標平臺比主機平臺性能差,許多專用的嵌入式硬件被設計爲低成本和低功耗,導致編譯慢
  • 資源: 整個編譯過程是非常消耗資源的,嵌入式系統往往沒有足夠的內存或磁盤空間
  • 初始狀態: 即便當目標平臺性能資源都充足時,第一個在目標平臺上運行的本地編譯器總需要通過交叉編譯獲得
  • 靈活兼容: 完整的Linux編譯環境需要很多支持包,交叉編譯使我們不需要花時間將各種支持包移植到目標板上

確定主機平臺及目標平臺

執行交叉編譯最基本的就是明確自己主機平臺和目標平臺的架構。

  1. 主機平臺
    主機平臺一般都是日常使用的PC平臺,爲通用的Intel或者AMD的CPUx86架構,分32bit和64bit兩種。當然也不排除使用其他平臺架構作爲主機平臺。
  2. 目標平臺
    目標平臺就比較多,較爲主流的有arm、mips、PowerPC等。每個平臺下面都有各自的細分,主要還是分爲32bit和64bit。
    以arm爲例,arm平臺的指令集也在不斷更新,目前存在比較多的是armv7和armv8兩個平臺。armv7爲32位。armv8兼容32位和64位,有aarch32和aarch64。
    mips平臺在嵌入式領域也比較常見,龍芯就是採用mips架構進行開發的,爲mips64。

獲取交叉編譯工具鏈

要執行交叉編譯就需要有對應的交叉編譯工具鏈,稱之爲工具鏈就是編譯都是一整套工具,並不是單一的工具。比較熟知的就是gcc/g++編譯器和ld連接器。

交叉編譯工具鏈命名規則

通常交叉編譯工具鏈命名規則爲:arch-core-kernel-system。其中:

arch:目標平臺架構,如上文提到的arm,mips等;
core:有兩種種情況,第一是CPU Core,如Cortex A8;第二是指定工具鏈的供應商。如果沒有特殊指定,則留空不填。這一組命名比較靈活,有以廠家名稱命名的,有以開發者命名的,也有以開發板命名的,或者直接是none或cross的;
kernel: 目標平臺的OS,見過的有linux,uclinux,bare-metal(無OS);
system:嵌入式應用二進制接口(Embedded Application Binary Interface),交叉編譯工具鏈所選擇的庫函數和目標映像的規範,如gnu,gnueabi等。其中gnu等價於glibc+oabi;gnueabi等價於glibc+eabi。若不指定,則也可以留空不填;

上述命名規則並不是統一的規範,使用的時候作爲參考就行。我使用的交叉編譯工具鏈名稱爲:gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu。

獲取交叉編譯工具鏈兩個途徑:

  1. 直接下載知名廠家已經編譯好的工具鏈。這些編譯好的交叉編譯工具鏈一般來說更加穩定,用的人多。
  • https://www.linaro.org/downloads/
  • http://ftp.arm.linux.org.uk/pub/armlinux/toolchain/
  • http://www.denx.de/en/Software/WebHome
  • https://launchpad.net/gcc-arm-embedded
  • 還有就是購買特定的嵌入式設備,對應廠家會有對應的交叉編譯工具鏈,如樹莓派
  1. 自己編譯交叉編譯工具鏈
    這個一般不推薦,但是在無法下載到對應版本的工具鏈或者工具鏈某些c++特性不能支持等情況就需要自己根據自己的需要進行編譯。
    編譯交叉編譯工具鏈的工具:
  • crosstool-NG
  • Buildroot
  • Embedded Linux Development Kit (ELDK)

配置交叉編譯環境

獲取到對應的交叉編譯工具鏈之後,需要在主機平臺配置交叉編譯環境。
首先解壓交叉編譯工具鏈。一般工具鏈的目錄結構如下:

drwxr-xr-x 7   4096 Dec  4 19:53 aarch64-linux-gnu/
drwxr-xr-x 2   4096 Dec  4 19:55 bin/
-rw-r--r-- 1  11300 Dec  4 19:53 gcc-linaro-7.5.0-2019.12-linux-manifest.txt
drwxr-xr-x 3   4096 Dec  4 19:51 include/
drwxr-xr-x 3   4096 Dec  4 19:55 lib/
drwxr-xr-x 3   4096 Dec  4 19:37 libexec/
drwxr-xr-x 8   4096 Dec  4 19:51 share/

我們重點需要關注的是aarch64-linux-gnu和bin這兩個目錄

【aarch64-linux-gnu】

【aarch64-linux-gnu】可能不同的交叉編譯工具鏈名稱不同,主要是平臺架構不同,其他名稱可能爲【arm-linux-gnueabihf】。根據交叉編譯工具鏈的名稱不同而不同。
這個目錄下的文件結構如下:

drwxr-xr-x 2  4096 Dec  4 19:54 bin/
drwxr-xr-x 3  4096 Dec  4 19:48 include/
drwxr-xr-x 3  4096 Dec  4 19:33 lib/
drwxr-xr-x 3  4096 Dec  4 19:55 lib64/
drwxr-xr-x 7  4096 Dec  4 19:53 libc/

在【aarch64-linux-gnu/bin】目錄下有部分交叉編譯工具,但是不全。其他幾個目錄都是必要的依賴庫和頭文件。我們重點關注的是【aarch64-linux-gnu/libc】,以後編譯的其他依賴都需要安裝在這個目錄下的usr目錄。因爲這個目錄類似於Linux系統的根目錄,文件結構如下:

drwxr-xr-x  2  4096 Dec  4 19:41 etc/
drwxr-xr-x  3  4096 Dec  4 19:55 lib/
drwxr-xr-x  2  4096 Dec  4 19:41 sbin/
drwxr-xr-x 10  4096 Dec 25 17:37 usr/
drwxr-xr-x  3  4096 Dec  4 19:41 var/

在【aarch64-linux-gnu/libc/usr】目錄下有lib和include目錄。在交叉編譯其他依賴的時候,指定的安裝目錄【–prefix】一般指定到這個目錄中。

【bin】目錄

在交叉編譯工具鏈文件夾根目錄下的【bin】文件夾是全部的交叉編譯工具。比如gcc、g++編譯器。可以在這個文件夾下執行以下編譯器二進制文件,查看下版本,看看交叉編譯器是否可用。
我們需要做的是將這個文件夾路徑添加到環境變量PATH中:

export PATH=$PATH:/path/to/your/crosscompile-toolchain/bin

開始交叉編譯

首先我們以一個傳統的例子爲例:

  1. 第一步編寫一個hello world程序。
#include <stdio.h>

void main()
{
    printf("Hello world!!\n");
}
  1. 我們就像使用主機平臺gcc編譯程序一樣的過程編譯hello world,這裏以我的環境爲例:
aarch64-linux-gnu-gcc helloworld.c -o helloworld

然後會生成一個可執行程序helloworld。你直接運行的話會報以下錯誤:

-bash: ./helloworld: cannot execute binary file: Exec format error

提示可執行程序格式錯誤。這是正常的。接着我們拷貝到目標平臺上執行以下,檢測是否正確輸出“Hello world!!”。

這是基本的編譯過程,稍微複雜點的也是一樣,比如連接庫文件的時候。只不過都需要使用交叉編譯工具鏈來完成。
接下來我們講兩個複雜例子,第一個是使用cmake時的交叉編譯方式,第二個是Protobuf的交叉編譯。

使用cmake的交叉編譯方式

在原項目使用的是cmake編譯或者希望使用cmake進行交叉編譯的時候。需要有幾個變量進行定義。

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_SYSROOT /home/devel/rasp-pi-rootfs)

set(tools /home/devel/gcc-4.7-linaro-rpi-gnueabihf)
set(CMAKE_C_COMPILER ${tools}/bin/arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER ${tools}/bin/arm-linux-gnueabihf-g++)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

下面逐個介紹如下:
【CMAKE_SYSTEM_NAME】指定目標平臺系統名稱
【CMAKE_SYSTEM_PROCESSOR】指定目標平臺CPU架構
【CMAKE_SYSROOT】這個很重要了,這個需要填寫的是剛纔交叉編譯工具鏈根目錄下的【aarch64-linux-gnu/libc】目錄,這個目錄當時介紹的時候說它類似主機平臺Linux系統根目錄,在這裏指定的話,cmake交叉編譯中會在這個目錄下尋找安裝的其他目標平臺依賴程序。
【tools】【CMAKE_C_COMPILER】【CMAKE_CXX_COMPILER】這三個變量主要是爲了指定交叉編譯工具的gcc、g++位置。
【CMAKE_FIND_ROOT_PATH_MODE_PROGRAM】以及下面的【X_LIBRARY】【X_INCLUDE】【X_PACKAGE】三個變量都是指示CMake在執行find_X命令時的行爲。有三個可選項,分別是NEVER,ONLY,BOTH。

  • NEVER表示不在你CMAKE_SYSROOT設置的目錄下進行查找;
  • ONLY表示只在這個路徑下查找;
  • BOTH表示先查找這個路徑,再查找全局路徑。

上面的變量有兩種方式跟原先的CMakeLists結合:

  1. 直接將上述幾行變量定義代碼放到原先CMakeLists的最開始處。有一點需要注意的是一定是在【project()】命令之前
  2. 將上述幾行變量定義代碼放到單獨的文件中,命名爲XXX.cmake,然後執行cmake命令的時候使用【-DCMAKE_TOOLCHAIN_FILE=XXX.cmake】

:推薦第二種方式,但是第二種方式在進行交叉編譯的時候,執行cmake容易出現無限循環檢測,直到發送錯誤。

另外cmake在尋找依賴的時候一般去剛纔指定的CMAKE_SYSROOT這個目錄中找,通常是交叉編譯工具鏈的【aarch64-linux-gnu/libc】目錄。因此交叉編譯的依賴最好都安裝在這個目錄中。

Protobuf的交叉編譯方式

這裏額外介紹Protobuf的原因是,在交叉編譯二進制的時候有時候需要對應主機平臺的二進制協助,而且最好是版本一致的。Protobuf就是這樣的,還有libmysqlclient。
所以首先先編譯一個主機平臺的Protobuf,然後再開始目標平臺的編譯。如果主機平臺的Protobuf之前就有就可以直接用(版本最好一致)。
目標平臺的編譯過程:

  1. Protobuf的壓縮包裏有一個【config.guess】工具,可以放到目標平臺執行以下,會返回目標平臺的架構、位數和系統。如:aarch64-linux-gnu
  2. 然後使用如下命令開始交叉編譯,其中需要修改的是參數是【–host】【CC】【CXX】【–with-protoc】【–prefix】:
./configure --host=aarch64-linux-gnu CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ --with-protoc=/usr/bin/protoc --prefix=/path/to/your/toolchain/aarch64-linux-gnu/libc/usr

參數解釋:
【–host】:就是用【config.guess】獲取的;
【CC】【CXX】:交叉編譯工具鏈gcc和g++名稱;
【–with-protoc】:已經編譯好的主機平臺protoc二進制文件路徑;
【–prefix】:交叉編譯後安裝目錄。

總結

交叉編譯的入門介紹就結束了。其實主要就幾個步驟:

  1. 確定主機平臺和目標平臺;
  2. 獲取對應交叉編譯工具鏈;
  3. 開始交叉編譯。

其實第三步還是比較複雜的,不同的軟件各有不同,但是也是有一些共通的地方,就是:

  1. 傳入交叉編譯工具的名稱,如gcc和g++,其他還可能需要ld等。
  2. 傳入目標平臺的架構、系統等詳細信息,因爲交叉編譯的時候,軟件本身無法去檢測平臺的特性,需要明確傳入。
  3. 安裝目錄(可選)
    主要是這些參數的傳入方式不同,需要具體用到的時候具體去查一下。

其他語言

上文整體上是介紹C/C++ 爲主要方向的交叉編譯過程。其實還有很多其他語言也是需要交叉編譯的,比如go,rust。基本的理念是一致的,就是在編譯的時候告知目標平臺架構。
go語言的交叉編譯比較簡單,主要涉及GOOS和GOARCH兩個參數,GOOS設置目標平臺操作系統,GOARCH設置目標平臺架構:

CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build

這裏不展開講了,大家遇到的話可以針對性的查。
以上就是全部內容了,覺得有用就點贊啊!!!

發佈了22 篇原創文章 · 獲贊 36 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章