Cross-Compilation & Toolchain
TEKNİK REHBER CROSS-COMPILATION GCC FLAGS 2026

GCC Cross-Compile —
bayraklar & optimizasyon.

--sysroot'tan LTO'ya, hard-float ABI'dan DWARF debug sembollerine — embedded hedef için GCC'yi tam kontrol et.

00 --sysroot, -I, -L ve -rpath-link farkı

Cross-compile'da derleyicinin başlık ve kütüphane dosyalarını doğru yerden okuması kritiktir. Bu üç bayrak farklı aşamalarda devreye girer.

--sysroot=DIR
Tüm sistem aramalarının kök dizinini belirler. -I/usr/include yerine DIR/usr/include aranır. Aynı zamanda -L/lib yerine DIR/lib kullanılır. En temiz ve önerilen yöntem.
-I DIR
Yalnızca preprocessor için başlık arama dizini ekler. --sysroot'u geçersiz kılmadan ek dizin eklemek için kullanılır. Cross'ta: -I${SYSROOT}/usr/include/libfoo.
-L DIR
Linker'ın .so/.a dosyası aradığı dizini ekler. --sysroot ile birlikte kullanılırsa path eklenir. Cross'ta: -L${SYSROOT}/usr/lib.
-Wl,-rpath-link,DIR
Dinamik linker'ın dolaylı bağımlılıkları çözmek için kullandığı dizin. Runtime'da kullanılmaz, yalnızca link zamanında. Cross'ta çok katmanlı .so bağımlılıklarında gerekir.

Kullanım örneği

bash
SYSROOT=~/x-tools/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/sysroot
CROSS=arm-unknown-linux-gnueabihf

# en temiz yöntem: --sysroot her şeyi halleder
${CROSS}-gcc \
    --sysroot=${SYSROOT} \
    -o app main.c \
    -lssl -lcrypto

# ek başlık dizini gerekirse
${CROSS}-gcc \
    --sysroot=${SYSROOT} \
    -I${SYSROOT}/usr/include/openssl \
    -o app main.c -lssl

# rpath-link ile dolaylı bağımlılık çözümü
${CROSS}-gcc \
    --sysroot=${SYSROOT} \
    -Wl,-rpath-link,${SYSROOT}/lib/arm-linux-gnueabihf \
    -o app main.c -lfoo
UYARI

Cross-compile'da asla host sisteminin /usr/include veya /usr/lib dizinlerinden başlık/kütüphane okuma. Bu, undefined symbol veya mimari uyumsuzluğu hatasına yol açar. --sysroot kullanmak bu riski ortadan kaldırır.

01 -march / -mcpu / -mtune / -mfpu / -mfloat-abi

ARM için CPU-spesifik bayraklar hem doğruluk hem performans açısından kritiktir. Yanlış ABI seçimi runtime'da illegal instruction hatası verir.

-march=ARCHv
ISA seviyesini belirler. Örn: armv7-a, armv8-a, armv8-a+crc+crypto. Bu seviyeyi destekleyen tüm CPU'larda çalışır — taşınabilir.
-mcpu=CPU
Belirli CPU modeli için optimize eder. Hem ISA hem scheduling. Örn: cortex-a53, cortex-a72, cortex-a55. -march'ı da belirler.
-mtune=CPU
Yalnızca instruction scheduling optimize eder, ISA değişmez. Farklı CPU'da da çalışan ama belirli CPU için optimize binary üretmek için.
-mfpu=FPU
Kullanılacak FPU donanımını belirtir. Örn: neon-fp-armv8, vfpv4, neon-vfpv4. Yalnızca 32-bit ARM'de gerekli.
-mfloat-abi=ABI
soft: FPU yok, yazılımda. softfp: FPU var ama ABI soft. hard: FPU register'larını kullanır — en hızlı.

Yaygın ARM kombinasyonları

bash
# Raspberry Pi 4 (Cortex-A72) — 64-bit
aarch64-linux-gnu-gcc \
    -mcpu=cortex-a72 \
    -O2 -o app main.c

# Raspberry Pi 3 (Cortex-A53) — 32-bit hard-float
arm-linux-gnueabihf-gcc \
    -march=armv8-a -mcpu=cortex-a53 \
    -mfpu=neon-fp-armv8 -mfloat-abi=hard \
    -O2 -o app main.c

# BeagleBone Black (Cortex-A8) — 32-bit hard-float
arm-linux-gnueabihf-gcc \
    -march=armv7-a -mcpu=cortex-a8 \
    -mfpu=neon -mfloat-abi=hard \
    -O2 -o app main.c

# Eski ARM926 (soft-float)
arm-linux-gnueabi-gcc \
    -march=armv5te -mcpu=arm926ej-s \
    -mfloat-abi=soft \
    -O2 -o app main.c

# RISC-V 64-bit, rv64gc
riscv64-linux-gnu-gcc \
    -march=rv64gc -mabi=lp64d \
    -O2 -o app main.c

Mevcut seçenekleri sorgula

bash
# desteklenen CPU listesi
arm-linux-gnueabihf-gcc --target-help | grep -A1 "Known ARM CPUs"

# desteklenen FPU listesi
arm-linux-gnueabihf-gcc -mfpu=help 2>&1 | head -30

# aktif tüm özellikleri göster
arm-linux-gnueabihf-gcc -Q -mcpu=cortex-a53 --help=target 2>/dev/null | head -40

02 Multilib — --print-multi-lib

Multilib, tek bir toolchain'in farklı ABI kombinasyonları için önceden derlenmiş kütüphaneler içermesidir. Sistem toolchain'lerinde yaygındır.

Multilib'i sorgula

bash
# toolchain'in desteklediği multilib kombinasyonları
arm-linux-gnueabihf-gcc --print-multi-lib
# .;@march=armv7-a@mfpu=vfpv3-d16@mfloat-abi=hard@mmode=thumb
# thumb;@mthumb
# armv7-a;@march=armv7-a

# belirli bayraklarla hangi multilib seçileceğini göster
arm-linux-gnueabihf-gcc \
    -march=armv7-a -mfloat-abi=hard \
    --print-multi-directory
# armv7-a/thumb

# multilib dizini nerede?
arm-linux-gnueabihf-gcc --print-multi-os-directory
# ../lib
CROSSTOOL-NG VE MULTİLİB

crosstool-NG ile oluşturulan toolchain'ler varsayılan olarak multilib içermez — tek bir ABI için optimize edilmiştir. Bu kasıtlıdır: her hedef için ayrı toolchain tutmak daha temizdir. Multilib gerekiyorsa CT_MULTILIB=y seçeneğini etkinleştir.

03 LTO — Link-Time Optimization cross ortamda

LTO, derleme birimlerinin birbirini "görerek" optimize edilmesini sağlar. Cross ortamda özel dikkat gerektirir.

LTO etkinleştirme

bash
# derleme ve link aşamasında -flto gerekir
CROSS=arm-unknown-linux-gnueabihf

${CROSS}-gcc -flto -O2 -c main.c -o main.o
${CROSS}-gcc -flto -O2 -c util.c -o util.o

# link aşamasında da -flto
${CROSS}-gcc -flto -O2 -o app main.o util.o

# paralel LTO (GCC 10+)
${CROSS}-gcc -flto=4 -O2 -o app main.o util.o

LTO ve cross araçları

bash
# LTO, cross toolchain'in ar/nm/ranlib araçlarını kullanır
# Makefile'da cross araçlarını ayarla
AR      = arm-unknown-linux-gnueabihf-ar
NM      = arm-unknown-linux-gnueabihf-nm
RANLIB  = arm-unknown-linux-gnueabihf-ranlib
CC      = arm-unknown-linux-gnueabihf-gcc
CFLAGS  = -flto -O2 -march=armv8-a -mcpu=cortex-a53
LDFLAGS = -flto

# LTO ile statik kütüphane oluştururken
# gcc-ar kullan (plugin destekli)
arm-unknown-linux-gnueabihf-gcc-ar rcs libfoo.a foo.o bar.o
FAT LTO OBJECT

-ffat-lto-objects bayrağı hem LTO IR hem normal object code içeren "fat" nesne dosyası üretir. LTO'yu desteklemeyen araçlarla (eski ar, nm) uyumlu kütüphaneler için kullanılır. Boyut artar ama esneklik kazanılır.

04 PIE ve ASLR embedded'da

PIE (Position Independent Executable), ASLR ile birlikte adres tahmin saldırılarını zorlaştırır. Ancak embedded'da bazen performans veya bellek kısıtları nedeniyle devre dışı bırakılır.

-fPIE / -pie
Position Independent Executable üretir. Her load'da farklı adrese yüklenir (ASLR). -fPIE derleme, -pie link bayrağı.
-fPIC
Paylaşımlı kütüphane (.so) için Position Independent Code. Global Offset Table (GOT) kullanır — küçük overhead.
-no-pie
PIE'yi devre dışı bırakır. Belirli bir adrese yüklenmesi gereken bootloader veya bare-metal binary için.

Embedded senaryolar

bash
# Normal uygulama — PIE önerilen (güvenlik)
arm-linux-gnueabihf-gcc \
    -fPIE -pie \
    -O2 -o app main.c

# PIE kontrolü
arm-linux-gnueabihf-readelf -h app | grep Type
# Type: DYN (Position-Independent Executable file)

# Bellek kısıtlı sistem — PIE devre dışı
arm-linux-gnueabihf-gcc \
    -no-pie -fno-PIE \
    -O2 -o app main.c

arm-linux-gnueabihf-readelf -h app | grep Type
# Type: EXEC (Executable file)

# Paylaşımlı kütüphane
arm-linux-gnueabihf-gcc \
    -fPIC -shared \
    -o libfoo.so foo.c
EMBEDDED KARAR

Eğer hedef sistem ASLR destekliyorsa (modern Linux kernel + glibc) ve bellekte yeri varsa PIE kullan. Sabit adres gerektiren boot-time kodunda, MCU stub'larında veya çok kısıtlı sistemlerde -no-pie tercih et.

05 Debug sembolleri — -g / -g3 / -gdwarf-4

Debug sembolleri binary'nin boyutunu artırır ama GDB, Valgrind ve perf gibi araçlar için gereklidir. Cross ortamda DWARF sürümü ve boyut dengesini yönetmek önemlidir.

-g
Varsayılan debug bilgisi. Derleyicinin tercih ettiği DWARF sürümü (GCC 11+ → DWARF 5).
-g0
Debug bilgisi yok. Üretim binary'si için.
-g1
Minimal debug — stack trace için yeterli, tam kaynak satırı yok.
-g2
-g ile aynı. Standart debug seviyesi.
-g3
Macro genişletme bilgisi dahil. Preprocessor macro'larını GDB'de görmek için.
-gdwarf-4
DWARF sürüm 4 zorla. GDB 7.x ve eski araçlarla uyumluluk için. DWARF 5 daha verimli ama eski toolchain'lerle sorunlu olabilir.
-gsplit-dwarf
Debug bilgisini ayrı .dwo dosyasına yazar. Binary boyutunu küçük tutar, debug bilgisi ayrı dağıtılır.

Debug + optimizasyon kombinasyonu

bash
# geliştirme: debug semboller, optimizasyon yok
arm-linux-gnueabihf-gcc -g3 -gdwarf-4 -O0 -o app_debug main.c

# üretim debug: optimize + debug (GDB'de değişkenler inline edilmiş olabilir)
arm-linux-gnueabihf-gcc -g -O2 -o app_reldbg main.c

# yayın: debug yok, maksimum optimize
arm-linux-gnueabihf-gcc -O2 -DNDEBUG -o app_release main.c

# debug bilgisi miktarı karşılaştır
arm-linux-gnueabihf-size app_debug app_reldbg app_release
#    text    data     bss     dec filename
#    1234     104      16    1354 app_debug
#     892     104      16    1012 app_reldbg
#     756     104      16     876 app_release

# DWARF bilgisini incele
arm-linux-gnueabihf-objdump --dwarf=info app_debug | head -50

06 objcopy ile stripped ve debug binary ayırma

Standart yaklaşım: debug sembolli binary'yi ayrı dosyaya kaydet, hedefe debug sembolsüz ("stripped") binary gönder. GDB build host'tan debug sembollerini okur.

Adım adım ayırma

bash
CROSS=arm-unknown-linux-gnueabihf

# 1. debug sembolli binary derle
${CROSS}-gcc -g -O2 -o app main.c util.c

# 2. debug sembollerini ayrı dosyaya çıkar (.debug uzantısı standart)
${CROSS}-objcopy --only-keep-debug app app.debug

# 3. ana binary'den debug sembollerini sil
${CROSS}-strip --strip-debug app

# veya daha agresif (sembol tablosunu da sil)
${CROSS}-strip --strip-unneeded app

# 4. stripped binary'yi debug dosyasına bağla (GNU debug link)
${CROSS}-objcopy --add-gnu-debuglink=app.debug app

# boyut karşılaştır
ls -lh app app.debug
# -rwxr-xr-x app       12K   ← hedefe bu gider
# -rw-r--r-- app.debug 48K   ← geliştirici ortamında kalır

GDB ile uzak debug (remote GDB)

bash
# Hedef sistemde (ARM kart)
gdbserver :2345 ./app

# Host'ta (x86_64)
arm-unknown-linux-gnueabihf-gdb app.debug

# GDB içinde
(gdb) set sysroot ~/x-tools/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/sysroot
(gdb) target remote 192.168.1.100:2345
(gdb) continue
BUILDID

GNU debug link yerine build-id tabanlı ayrım da kullanılabilir: --build-id=sha1 bayrağıyla derleme. Debug semboller /usr/lib/debug/.build-id/ hiyerarşisine yerleştirilebilir. eu-strip (elfutils) bu akışı otomatikleştirir.

07 --gc-sections ile boyut optimizasyonu

Embedded sistemlerde flash/ROM boyutu kritik olabilir. GCC ve linker birlikte kullanılmayan kod ve veri bölümlerini binary'den çıkarabilir.

Temel bayraklar

-ffunction-sections
Her fonksiyonu kendi ELF section'ına koyar (.text.myfunc). Linker'ın kullanılmayan fonksiyonu çıkarmasını mümkün kılar.
-fdata-sections
Her global/statik veriyi kendi section'ına koyar (.data.myvar). Kullanılmayan global değişkenleri çıkarmak için.
-Wl,--gc-sections
Linker'a kullanılmayan section'ları çöp toplama yap. Entry point veya KEEP() direktifi ile işaretlenmeyen section'lar silinir.
bash
CROSS=arm-unknown-linux-gnueabihf

# gc-sections olmadan
${CROSS}-gcc -O2 -o app_fat main.c libutil.c

# gc-sections ile
${CROSS}-gcc \
    -O2 \
    -ffunction-sections \
    -fdata-sections \
    -Wl,--gc-sections \
    -o app_slim main.c libutil.c

# boyut karşılaştır
${CROSS}-size app_fat app_slim
#   text    data     bss     dec filename
#  24680     312     128   25120 app_fat
#  18240     280      96   18616 app_slim   ← ~26% küçük

# hangi section'ların çıkarıldığını görmek için
${CROSS}-gcc \
    -ffunction-sections -fdata-sections \
    -Wl,--gc-sections \
    -Wl,--print-gc-sections \
    -o app_slim main.c libutil.c 2>&1 | head -20

Ek boyut optimizasyonu bayrakları

bash
# -Os: boyut için optimize (O2 yerine)
${CROSS}-gcc -Os -ffunction-sections -fdata-sections \
    -Wl,--gc-sections -o app main.c

# -Oz: GCC 12+ — daha agresif boyut optimizasyonu
${CROSS}-gcc -Oz -ffunction-sections -fdata-sections \
    -Wl,--gc-sections -o app main.c

# strip ile son boyut küçültme
${CROSS}-strip --strip-all app

# UPX ile sıkıştırma (dinamik linked exe için dikkatli kullan)
# upx --best app

08 CFLAGS / LDFLAGS / CC / CXX ortam değişkenleri

Autotools ve birçok build sistemi, cross-compile konfigürasyonunu ortam değişkenleri üzerinden alır. Bu değişkenlerin doğru ayarlanması projeyi yeniden yazmadan cross-compile etmenin en pratik yoludur.

CC
C derleyicisi. arm-linux-gnueabihf-gcc
CXX
C++ derleyicisi. arm-linux-gnueabihf-g++
AR
Arşivleyici. arm-linux-gnueabihf-ar
STRIP
Strip aracı. arm-linux-gnueabihf-strip
CFLAGS
C derleme bayrakları. -march=armv8-a -mcpu=cortex-a53 -mfpu=neon-fp-armv8 -mfloat-abi=hard --sysroot=...
CXXFLAGS
C++ derleme bayrakları. Genellikle CFLAGS ile aynı.
LDFLAGS
Linker bayrakları. --sysroot=... -L${SYSROOT}/usr/lib
PKG_CONFIG_PATH
pkg-config arama dizini. Hedef kütüphane .pc dosyaları için.

cross-compile ortam şablonu

cross-env.sh
#!/bin/bash
# ARM Cortex-A53 cross-compile ortam değişkenleri
# Kullanım: source cross-env.sh

CROSS_TRIPLE="arm-unknown-linux-gnueabihf"
TOOLCHAIN_DIR="${HOME}/x-tools/${CROSS_TRIPLE}"
SYSROOT="${TOOLCHAIN_DIR}/${CROSS_TRIPLE}/sysroot"

export PATH="${TOOLCHAIN_DIR}/bin:${PATH}"

export CC="${CROSS_TRIPLE}-gcc"
export CXX="${CROSS_TRIPLE}-g++"
export AR="${CROSS_TRIPLE}-ar"
export AS="${CROSS_TRIPLE}-as"
export LD="${CROSS_TRIPLE}-ld"
export RANLIB="${CROSS_TRIPLE}-ranlib"
export STRIP="${CROSS_TRIPLE}-strip"
export OBJCOPY="${CROSS_TRIPLE}-objcopy"
export OBJDUMP="${CROSS_TRIPLE}-objdump"

CPU_FLAGS="-march=armv8-a -mcpu=cortex-a53 -mfpu=neon-fp-armv8 -mfloat-abi=hard"

export CFLAGS="${CPU_FLAGS} --sysroot=${SYSROOT} -O2 -ffunction-sections -fdata-sections"
export CXXFLAGS="${CFLAGS}"
export LDFLAGS="--sysroot=${SYSROOT} -Wl,--gc-sections -L${SYSROOT}/usr/lib"

export PKG_CONFIG_SYSROOT_DIR="${SYSROOT}"
export PKG_CONFIG_LIBDIR="${SYSROOT}/usr/lib/pkgconfig:${SYSROOT}/usr/share/pkgconfig"
export PKG_CONFIG_PATH=""

echo "Cross-compile ortamı aktif: ${CROSS_TRIPLE}"

09 Pratik: configure script ile cross-compile

Autotools tabanlı projeler (libssl, libcurl, zlib, vb.) configure script kullanır. Cross-compile için --host parametresi ve ortam değişkenleri yeterlidir.

zlib cross-compile örneği

bash
source cross-env.sh    # ortamı yükle

SYSROOT="${HOME}/x-tools/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/sysroot"
INSTALL_PREFIX="${SYSROOT}/usr"

# zlib kaynak kodunu indir
wget https://zlib.net/zlib-1.3.1.tar.gz
tar xf zlib-1.3.1.tar.gz
cd zlib-1.3.1

# configure — zlib --host desteklemez, CC ile geçilir
./configure \
    --prefix="${INSTALL_PREFIX}" \
    --static

make -j$(nproc)
make install

ls ${INSTALL_PREFIX}/lib/libz.a
# /home/user/x-tools/.../sysroot/usr/lib/libz.a

libcurl cross-compile örneği

bash
source cross-env.sh

SYSROOT="${HOME}/x-tools/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/sysroot"
CROSS_TRIPLE="arm-unknown-linux-gnueabihf"

wget https://curl.se/download/curl-8.5.0.tar.gz
tar xf curl-8.5.0.tar.gz
cd curl-8.5.0

./configure \
    --host="${CROSS_TRIPLE}" \
    --prefix="${SYSROOT}/usr" \
    --with-ssl="${SYSROOT}/usr" \
    --disable-shared \
    --enable-static \
    --disable-ldap \
    --disable-sspi \
    --without-libidn \
    --without-librtmp

make -j$(nproc)
make install

# doğrula — ARM binary olmalı
file ${SYSROOT}/usr/bin/curl-config
# ELF 32-bit LSB shared object, ARM, EABI5
--build vs --host

configure script'e --build derleme makinesini, --host hedef makinesini belirtir. --target yalnızca derleyici gibi araç zincirleri için geçerlidir. Normal kütüphane/uygulama cross-compile'ında --host=arm-unknown-linux-gnueabihf yeterlidir.