Cross-compilation adalah proses membangun executable untuk arsitektur target yang berbeda dari arsitektur host tempat compiler berjalan. Skenario umum: kita menulis kode di laptop x86-64 Linux, tetapi binary yang dihasilkan akan berjalan di Raspberry Pi (ARM), router OpenWRT (MIPS), atau perangkat Android (ARM64). Cross-compilation memungkinkan kita membangun binary untuk perangkat target tanpa perlu men-compile di perangkat target itu sendiri (yang seringkali lambat atau memiliki keterbatasan memori). Pada chapter ini kita akan memahami konsep target triple, struktur cross-toolchain, dan dua mekanisme utama: Autoconf ./configure --host= dan CMake toolchain file.
Konsep Target Triple #
Setiap toolchain GCC/Clang diidentifikasi oleh target triple — string berformat <arch>-<vendor>-<os>-<abi>. Contoh:
x86_64-linux-gnu: arsitektur x86-64, OS Linux, ABI GNU glibc (toolchain native di Debian/Ubuntu 64-bit).aarch64-linux-gnu: arsitektur ARM64 (AArch64), OS Linux, ABI GNU (toolchain cross untuk Raspberry Pi 4 64-bit, atau server ARM).arm-linux-gnueabihf: arsitektur ARM 32-bit, OS Linux, ABI GNU hard-float (toolchain cross untuk Raspberry Pi 2/3 32-bit).arm-none-eabi: arsitektur ARM bare-metal (tanpa OS), ABI EABI (toolchain untuk microcontroller ARM Cortex-M).x86_64-w64-mingw32: arsitektur x86-64, OS Windows, ABI mingw (toolchain cross dari Linux ke Windows).
Perintah gcc -dumpmachine menampilkan target triple toolchain yang sedang aktif:
gcc -dumpmachineOutput yang Dihasilkan:
x86_64-linux-gnuOutput ini menandakan GCC yang terinstal adalah toolchain native x86-64 untuk Linux glibc. Bila kita memasang cross-toolchain (misalnya apt install gcc-arm-linux-gnueabihf), perintah arm-linux-gnueabihf-gcc -dumpmachine akan menghasilkan arm-linux-gnueabihf.
Struktur Cross-Toolchain #
Cross-toolchain biasanya terdiri dari sekumpulan tool yang diberi prefix target triple. Misalnya untuk target arm-linux-gnueabihf, tool yang terinstal adalah:
arm-linux-gnueabihf-gcc # compiler driver
arm-linux-gnueabihf-g++ # C++ compiler driver
arm-linux-gnueabihf-ar # archiver
arm-linux-gnueabihf-as # assembler
arm-linux-gnueabihf-ld # linker
arm-linux-gnueabihf-nm # symbol table reader
arm-linux-gnueabihf-objcopy # binary utility
arm-linux-gnueabihf-objdump # disassembler
arm-linux-gnueabihf-ranlib # archive index generator
arm-linux-gnueabihf-strip # symbol stripperSelain tool, cross-toolchain juga menyertakan sysroot — direktori berisi library dan header target. Sysroot biasanya berada di /usr/<triple>/ (misal /usr/arm-linux-gnueabihf/) dan berisi struktur seperti filesystem target (lib/, usr/lib/, usr/include/). Compiler mencari header dan library di sysroot ini, bukan di /usr/include dan /usr/lib host.
Cross-Compilation dengan Autoconf #
Proyek yang memakai GNU Autotools (Autoconf/Automake) menerima flag --host= untuk cross-compilation. Saat flag ini diberikan, ./configure mengganti variabel CC, AR, RANLIB dengan versi yang diawali target triple:
./configure --host=arm-linux-gnueabihf
makeBerikut simulasi output ./configure (skrip demo yang menunjukkan pola ini):
./configure-demo.sh
./configure-demo.sh arm-linux-gnueabihfOutput yang Dihasilkan:
# Native:
Configuring for host: native
CC = gcc
CFLAGS = -O2 -g
# Cross:
Configuring for host: arm-linux-gnueabihf
CC = arm-linux-gnueabihf-gcc
CFLAGS = -O2 -g
AR = arm-linux-gnueabihf-ar
RANLIB = arm-linux-gnueabihf-ranlibMari kita telaah output di atas:
- Saat native:
CC = gcc— compiler host dipakai langsung. VariabelARdanRANLIBtidak disebut karena default (ar,ranlib) sudah benar. - Saat cross:
CC = arm-linux-gnueabihf-gcc—./configuremenerapkan prefixarm-linux-gnueabihf-ke nama tool bawaan. VariabelARdanRANLIBjuga di-prefix karena kita perlu archive tool target, bukan host (format archive sebenarnya sama, tetapi ar dari cross-toolchain tahu di mana mencari library). CFLAGStetap sama — flag compiler seperti-O2 -gtidak bergantung target. Bila target membutuhkan flag khusus (misalnya-mcpu=cortex-a53), kita tambahkan keCFLAGSsaat konfigurasi.
Cross-Compilation dengan CMake #
CMake memakai mekanisme toolchain file — file .cmake berisi definisi variabel yang menentukan compiler, target, dan sysroot. File ini diberikan saat konfigurasi lewat -DCMAKE_TOOLCHAIN_FILE=.
Berikut toolchain-arm-linux-gnueabihf.cmake (template konseptual untuk target ARM hard-float):
# Toolchain file untuk target arm-linux-gnueabihf
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
# Sysroot tempat library dan header target berada
set(CMAKE_SYSROOT /usr/arm-linux-gnueabihf)
# Aturan pencarian find_package, find_library, find_path
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)Mari kita telaah baris-baris toolchain file di atas:
CMAKE_SYSTEM_NAME Linux: menandakan target adalah sistem Linux. CMake memakai nilai ini untuk memilih platform module yang benar (mis.Linux.cmake,Windows.cmake,Darwin.cmake). Tanpa variabel ini, CMake menganggap target sama dengan host.CMAKE_SYSTEM_PROCESSOR arm: menandakan arsitektur target. Berguna untuk logika kondisional diCMakeLists.txt(mis.if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm")).CMAKE_C_COMPILER arm-linux-gnueabihf-gcc: nama compiler target. CMake mencari binary ini diPATH. Bila tidak ada, konfigurasi gagal dengan errorcannot find compiler.CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++: nama compiler C++ target (bila kita membangun kode C++).CMAKE_SYSROOT /usr/arm-linux-gnueabihf: path absolut ke sysroot target. Compiler akan mencari header di<sysroot>/usr/includedan library di<sysroot>/usr/lib.CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER: saatfind_programmencari executable (mis.find_program(PYTHON python3)), CMake hanya mencari di path host — bukan sysroot target. Logikanya: program sepertipython3dijalankan saat build (mis. untuk code generation), jadi harus bisa dieksekusi di host.CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY: saatfind_librarymencari library target, CMake hanya mencari di sysroot target — bukan di host. Mencari library host akan menghasilkan binary yang salah (mis.libfoo.soversi x86-64 alih-alih ARM).CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY: saatfind_pathmencari header, hanya di sysroot target. Header host mungkin ada, tetapi tidak kompatibel ABI-nya.CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY: saatfind_packagemencari CMake config file target, hanya di sysroot target.
Demo dengan Toolchain Same-Arch #
Pada sandbox tutorial ini hanya toolchain native x86-64 yang terinstal, tetapi binary x86_64-linux-gnu-gcc (dengan prefix) tersedia sebagai alias ke gcc native. Kita bisa memverifikasi pola toolchain file dengan toolchain same-arch:
# toolchain-x86_64.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_C_COMPILER x86_64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER x86_64-linux-gnu-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)CMakeLists.txt yang menampilkan variabel target:
cmake_minimum_required(VERSION 3.10)
project(cross-demo C)
add_executable(app main.c)
message(STATUS "CMAKE_SYSTEM_NAME = ${CMAKE_SYSTEM_NAME}")
message(STATUS "CMAKE_SYSTEM_PROCESSOR = ${CMAKE_SYSTEM_PROCESSOR}")
message(STATUS "CMAKE_C_COMPILER = ${CMAKE_C_COMPILER}")cmake -B build_cross -DCMAKE_TOOLCHAIN_FILE=toolchain-x86_64.cmake
cmake --build build_cross
./build_cross/appOutput yang Dihasilkan:
-- Check for working C compiler: /usr/bin/x86_64-linux-gnu-gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- CMAKE_SYSTEM_NAME = Linux
-- CMAKE_SYSTEM_PROCESSOR = x86_64
-- CMAKE_C_COMPILER = /usr/bin/x86_64-linux-gnu-gcc
-- Configuring done (0.1s)
-- Generating done (0.0s)
-- Build files have been written to: /tmp/test_cross/build_cross
[ 50%] Building C object CMakeFiles/app.dir/main.c.o
[100%] Linking C executable app
[100%] Built target app
size of pointer = 8 bytes
size of int = 4 bytes
size of long = 8 bytesPerhatikan baris CMAKE_C_COMPILER = /usr/bin/x86_64-linux-gnu-gcc — CMake memakai compiler yang kita sebut di toolchain file, bukan /usr/bin/cc default. Program menghasilkan size of pointer = 8 karena target sama dengan host (x86-64), tetapi bila kita pakai toolchain ARM yang sebenarnya, output akan menampilkan size of pointer = 4 (ARM 32-bit) atau size of pointer = 8 (AArch64).
Code yang Membedakan Target #
Saat menulis kode yang akan di-cross-compile, kita sering perlu membedakan arsitektur target saat preprocessing. Standar C mendefinisikan macro standar (__SIZEOF_POINTER__), sementara compiler mendefinisikan macro spesifik arsitektur (__x86_64__, __arm__, __aarch64__):
#include <stdio.h>
int main(void) {
printf("size of pointer = %zu bytes\n", sizeof(void *));
printf("size of int = %zu bytes\n", sizeof(int));
printf("size of long = %zu bytes\n", sizeof(long));
return 0;
}Karena sizeof(void *) dievaluasi saat kompilasi, output mencerminkan arsitektur target. Saat di-cross-compile ke ARM 32-bit, output menjadi size of pointer = 4 bytes. Saat ke AArch64, tetap 8 bytes seperti x86-64.