32位支援:使用 GCC 交叉編譯
使用 GCC 在單一的構建機器上來為不同的 CPU 架構交叉編譯二進位制檔案。
如果你是一個開發者,要建立二進位制軟體包,像一個 RPM、DEB、Flatpak 或 Snap 軟體包,你不得不為各種不同的目標平臺編譯程式碼。典型的編譯目標包括 32 位和 64 位的 x86 和 ARM。你可以在不同的物理或虛擬機器器上完成你的構建,但這需要你為何幾個系統。作為代替,你可以使用 GNU 編譯器集合 (GCC) 來交叉編譯,在單一的構建機器上為幾個不同的 CPU 架構產生二進位制檔案。
假設你有一個想要交叉編譯的簡單的擲骰子游戲。在大多數系統上,以 C 語言來編寫這個相對簡單,出於給新增現實的複雜性的目的,我以 C++ 語言寫這個示例,所以程式依賴於一些不在 C 語言中東西 (具體來說就是 iostream
)。
#include <iostream>
#include <cstdlib>
using namespace std;
void lose (int c);
void win (int c);
void draw ();
int main() {
int i;
do {
cout << "Pick a number between 1 and 20: \n";
cin >> i;
int c = rand ( ) % 21;
if (i > 20) lose (c);
else if (i < c ) lose (c);
else if (i > c ) win (c);
else draw ();
}
while (1==1);
}
void lose (int c )
{
cout << "You lose! Computer rolled " << c << "\n";
}
void win (int c )
{
cout << "You win!! Computer rolled " << c << "\n";
}
void draw ( )
{
cout << "What are the chances. You tied. Try again, I dare you! \n";
}
在你的系統上使用 g++
命令編譯它:
$ g++ dice.cpp -o dice
然後,執行它來確認其工作:
$ ./dice
Pick a number between 1 and 20:
[...]
你可以使用 file
命令來檢視你剛剛生產的二進位制檔案的型別:
$ file ./dice
dice: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically
linked (uses shared libs), for GNU/Linux 5.1.15, not stripped
同樣重要,使用 ldd
命令來檢視它連結哪些庫:
$ ldd dice
linux-vdso.so.1 => (0x00007ffe0d1dc000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6
(0x00007fce8410e000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6
(0x00007fce83d4f000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6
(0x00007fce83a52000)
/lib64/ld-linux-x86-64.so.2 (0x00007fce84449000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1
(0x00007fce8383c000)
從這些測試中,你已經確認了兩件事:你剛剛執行的二進位制檔案是 64 位的,並且它連結的是 64 位庫。
這意味著,為實現 32 位交叉編譯,你必需告訴 g++
來:
- 產生一個 32 位二進位制檔案
- 連結 32 位庫,而不是 64 位庫
設定你的開發環境
為編譯成 32 位二進位制,你需要在你的系統上安裝 32 位的庫和標頭檔案。如果你執行一個純 64 位系統,那麼,你沒有 32 位的庫或標頭檔案,並且需要安裝一個基礎集合。最起碼,你需要 C 和 C++ 庫(glibc
和 libstdc++
)以及 GCC 庫(libgcc
)的 32 位版本。這些軟體包的名稱可能在每個發行版中不同。在 Slackware 系統上,一個純 64 位的帶有 32 位相容的發行版,可以從 Alien BOB 提供的 multilib
軟體包中獲得。在 Fedora、CentOS 和 RHEL 系統上:
$ yum install libstdc++-*.i686
$ yum install glibc-*.i686
$ yum install libgcc.i686
不管你正在使用什麼系統,你同樣必須安裝一些你工程使用的 32 位庫。例如,如果你在你的工程中包含 yaml-cpp
,那麼,在編譯工程前,你必需安裝 yaml-cpp
的 32 位版本,或者,在很多系統上,安裝 yaml-cpp
的開發軟體包(例如,在 Fedora 系統上的 yaml-cpp-devel
)。
一旦這些處理好了,編譯是相當簡單的:
$ g++ -m32 dice.cpp -o dice32 -L /usr/lib -march=i686
-m32
標誌告訴 GCC 以 32 位模式編譯。-march=i686
選項進一步定義來使用哪種最優化型別(參考 info gcc
瞭解選項列表)。-L
標誌設定你希望 GCC 來連結的庫的路徑。對於 32 位來說通常是 /usr/lib
,不過,這依賴於你的系統是如何設定的,它可以是 /usr/lib32
,甚至 /opt/usr/lib
,或者任何你知道存放你的 32 位庫的地方。
在程式碼編譯後,檢視你的構建的證據:
$ file ./dice32
dice: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
dynamically linked (uses shared libs) [...]
接著,當然, ldd ./dice32
也會指向你的 32 位庫。
不同的架構
在 64 位相同的處理器家族上允許 GCC 做出很多關於如何編譯程式碼的假設來編譯 32 位軟體。如果你需要為完全不同的處理器編譯,你必需安裝適當的交叉構建實用程式。安裝哪種實用程式取決於你正在編譯的東西。這個過程比為相同的 CPU 家族編譯更復雜一點。
當你為相同處理器家族交叉編譯時,你可以期待找到與 32 位庫集的相同的 64 位庫集,因為你的 Linux 發行版是同時維護這二者的。當為一個完全不同的架構編譯時,你可能不得不窮追你的程式碼所需要的庫。你需要的版本可能不在你的發行版的儲存庫中,因為你的發行版可能不為你的目標系統提供軟體包,或者它不在容易到達的位置提供所有的軟體包。如果你正在編譯的程式碼是你寫的,那麼你可能非常清楚它的依賴關係是什麼,並清楚在哪裡找到它們。如果程式碼是你下載的,並需要編譯,那麼你可能不熟悉它的要求。在這種情況下,研究正確編譯程式碼需要什麼(它們通常被列在 README
或 INSTALL
檔案中,當然也出現在原始檔程式碼自身之中),然後收集需要的元件。
例如,如果你需要為 ARM 編譯 C 程式碼,你必須首先在 Fedora 或 RHEL 上安裝 gcc-arm-linux-gnu
(32 位)或 gcc-aarch64-linux-gnu
(64 位);或者,在 Ubuntu 上安裝 arm-linux-gnueabi-gcc
和 binutils-arm-linux-gnueabi
。這提供你需要用來構建(至少)一個簡單的 C 程式的命令和庫。此外,你需要你的程式碼使用的任何庫。你可以在慣常的位置(大多數系統上在 /usr/include
)放置標頭檔案,或者,你可以放置它們在一個你選擇的目錄,並使用 -I
選項將 GCC 指向它。
當編譯時,不使用標準的 gcc
或 g++
命令。作為代替,使用你安裝的 GCC 實用程式。例如:
$ arm-linux-gnu-g++ dice.cpp \
-I/home/seth/src/crossbuild/arm/cpp \
-o armdice.bin
驗證你構建的內容:
$ file armdice.bin
armdice.bin: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV) [...]
庫和可交付結果
這是一個如何使用交叉編譯的簡單的示例。在真實的生活中,你的原始檔程式碼可能產生的不止於一個二進位制檔案。雖然你可以手動管理,在這裡手動管理可能不是好的正當理由。在我接下來的文章中,我將說明 GNU 自動工具,GNU 自動工具做了使你的程式碼可移植的大部分工作。
via: https://opensource.com/article/19/7/cross-compiling-gcc
作者:Seth Kenlon 選題:lujun9972 譯者:robsean 校對:wxy
訂閱“Linux 中國”官方小程式來檢視
相關文章
- GCC編譯器的使用GC編譯
- 交叉編譯編譯
- gcc編譯GC編譯
- 交叉編譯-Mac環境使用NDK編譯FFmpeg編譯Mac
- lazarus交叉編譯編譯
- GDB交叉編譯編譯
- golang交叉編譯Golang編譯
- 交叉編譯和交叉除錯環境搭建及使用編譯除錯
- protobuf 的交叉編譯使用(C++)編譯C++
- GCC 使用指南及gcc最佳編譯引數(轉)GC編譯
- 交叉編譯入門編譯
- go 交叉編譯,部署Go編譯
- 淺談交叉編譯編譯
- ubuntu下編譯交叉編譯工具鏈Ubuntu編譯
- GCC編譯選項GC編譯
- GCC 編譯選項GC編譯
- gcc 編譯器與 clang 編譯器GC編譯
- 交叉編譯和 RPC編譯RPC
- wifidog交叉編譯WiFi編譯
- android NDK 交叉編譯Android編譯
- golang初學:交叉編譯Golang編譯
- gcc編譯cpp檔案GC編譯
- Rust 交叉編譯與條件編譯總結Rust編譯
- 編譯器的自展和自舉、交叉編譯編譯
- gcc g++支援C++11 標準編譯及其區別GCC++編譯
- 使用 goxc 方便的進行交叉編譯分發Go編譯
- cmake使用教程(九)-關於安卓的交叉編譯安卓編譯
- gcc 和 g++ 的聯絡和區別,使用 gcc 編譯 c++GC編譯C++
- Go跨平臺交叉編譯Go編譯
- linux與windows交叉編譯LinuxWindows編譯
- 交叉編譯mupdf1.15.0編譯
- Go - armv7 交叉編譯Go編譯
- Ubuntu交叉編譯環境配置Ubuntu編譯
- Linux中gcc編譯工具LinuxGC編譯
- linux下的gcc編譯LinuxGC編譯
- gcc 編譯多個原始檔GC編譯
- gcc最佳編譯引數(轉)GC編譯
- 安裝 GCC 編譯器(轉)GC編譯