32位支持:使用 GCC 交叉編譯
如果你是一個開發者,要創建二進位軟體包,像一個 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 中國: https://github.com/Linux-CN/archive