Guida Programmare nel kernel Linux [parte 3]

U

Utente cancellato 277320

Ultima modifica da un moderatore:
Cross-compilare il kernel per schede embedded

Questa guida vi consentira' di compilare il vostro kernel (partendo dai sorgenti) per qualsiasi architettura/schedina e sostituirlo a quello presente. Una delle caratteristiche fissate da Torvalds e' che il kernel possa essere sostituito senza che il root file system richieda cmabiamenti..

Un aspetto noto del linguaggio C e' che si considera "portabile". Cosa significa, che lo stesso codice C compilato per x86_64 puo essere compilato per un'altra architettura. Questo sempre che siano disponibili le librerire necessarie (se ve ne sono di usate dal codice), e ovviamente il compilatore.

Nel mondo embedded in genere si lavora su un host PC che e' architettura x86_64, quindi si cross-compila, ovvero si genera un binario finale che invece andra' eseguito su una diversa architettura.

Per farlo, serve dunque un set di strumenti chiamato "toolchain" (che comprende compilatore, assembler, libc e altre librerie del compilatore etc) ovvero un set di programmi che possano essere eseguiti nell'architettura in cui lavori, ma che generino un binario finale composto da codici operativi dell'architettura target, per essere appunto trasferito ed eseguito nel dispositivo target.

Per quanto riguarda il kernel Linux, esso non richiede librerie durante la compilazione, e' codice C che va a generare un binario elf, un elf compresso, o anche puro senza alcun header ne compressione (per l'execution in place (XIP)), con eventuali moduli caricabili.

Su quali architetture (cpu/SoC) si puo eseguire il kernel linux ?

Le vedete con un semplice "ls" nella directory "arch" dei sorgenti linux.

1638007686320.png


Dunque, per ogni diverso target serve una toolchain diversa. Su questo pc ho organizzato le cose in questo modo:

Bash:
angelo@dfj ~ $ ls -al /opt/toolchains
total 20
drwxr-xr-x  5 root root 4096 Dec  1  2020 .
drwxr-xr-x 16 root root 4096 Nov  3 17:12 ..
drwxr-xr-x 10 root root 4096 Jul 19 16:45 arm
drwxr-xr-x  7 root root 4096 Sep  9  2020 m68k
drwxr-xr-x  3 root root 4096 Dec  1  2020 powerpc

Bash:
angelo@dfj ~/dev-sysam/linux.git/arch (wip/sunxi*) $ ls -al /opt/toolchains/arm
total 40
drwxr-xr-x 10 root   root   4096 Jul 19 16:45 .
drwxr-xr-x  5 root   root   4096 Dec  1  2020 ..
drwxr-xr-x  8 angelo angelo 4096 Nov 23  2020 gcc-arm-10.2-2020.11-x86_64-aarch64-none-elf
drwxr-xr-x  8 angelo angelo 4096 Mar 25  2019 gcc-arm-8.3-2019.03-x86_64-arm-eabi
drwxr-xr-x  9 root   root   4096 Sep  2  2019 gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf
drwxr-xr-x  8 angelo angelo 4096 Dec 13  2019 gcc-arm-9.2-2019.12-x86_64-aarch64-none-elf
drwxr-xr-x  9 angelo angelo 4096 Dec 13  2019 gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf
drwxr-xr-x  6 angelo angelo 4096 Jul 19 16:53 gcc-arm-none-eabi-10-2020-q4-major
drwxr-xr-x  8 angelo angelo 4096 Feb  1  2017 gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi
drwxr-xr-x  8 angelo angelo 4096 Feb  1  2017 gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf

Come vedete, i nomi delle varie toolchains riportano prima l'architettura "host" e subito dopo
quella del target "...x86_64_arm-...". La nomenclatura della toolchain dovrebbe appunto seguire uno standard:

arch-vendor-(os-)abi

arm-none-linux-gnueabi = ARM architecture, no vendor, linux OS, gnueabi ABI

ma non e' sempre cosi', come nel caso di queste per m68k/coldfire:

Bash:
angelo@dfj ~/dev-sysam/linux.git/arch (wip/sunxi*) $ ls -al /opt/toolchains/m68k
total 28
drwxr-xr-x 7 root   root   4096 Sep  9  2020 .
drwxr-xr-x 5 root   root   4096 Dec  1  2020 ..
drwxr-xr-x 3 angelo angelo 4096 Jul 13  2020 gcc-10.1.0-nolibc
drwxr-xr-x 8 angelo angelo 4096 Apr 17  2016 gcc-4.9.0-nolibc
drwxr-xr-x 8 root   root   4096 Sep  2  2019 gcc-5.2.0-nolibc
drwxr-xr-x 4 root   root   4096 May  3  2021 m68k-openadk-linux-uclibc
dr-xr-xr-x 8 angelo angelo 4096 Sep  7  2020 m68k-sysam-uclinux-uclibc


Per compilare il kernel basta una toolchain senza supporto linux, ovvero, il kernel e' un binario indipendente, "bare metal", dunque senza "-linux-" nel nome,
ma anche le toolchain col supporto linux vanno bene lo stesso per il nostro scopo.

Toochains piu comuni si possono scaricare dai siti di ARM e Linaro, essendo appunto ARM l'architettura piu largamente usata nel settore embedded (lo vedete dalal dimensione della cartella "arm", una delle piu grandi dell'intero kernel. Ora ha preso piede molto bene anche aarch64 (arm 64 bit).

Dunque, ad esempio, per cross compilare il kernel per una banana pi m2 zero, organizziamo uno script come questo:

Bash:
#!/bin/bash

# Qui indichiamo il percorso della toolchain e la prima parte
# del nome dei tool.
# Nota, la parte finale "arm-linux-gnueabihf-" e' comune per
# tutti i tools, e CROSS_COMPILE esportata viene anteposta
# automaticamente dal compilatore al nome del tool, quindi invocando
#     gcc    oppure
#     objdump
# il comando diventera'
#     arm-linux-gnueabihf-gcc, arm-linux-gnueabihf-objdump, etc
#
export CROSS_COMPILE=/opt/toolchains/arm/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
export ARCH=arm

# Il kernel mainline, per tutte quelle schede i cui
# costruttori hanno scelto di contribure al kernel, fornisce
# una configurazione di defaul per quella scheda:
# per architettura arm vedi in "ls arch/arm/configs".
# Settando board=sunxi useremo sunxi_defconfig
board=sunxi

# Devicetree: assieme al kernel, le schede embedded
# utilizzano un piccolo binario aggiuntivo che definisce
# i device presenti sulla scheda. Questo perche' in un PC
# i dispositivi connessi vengono rilevati dinamicamente
# sul bus pci, ma non su schede embedded. Su schede embedded,
# si definiscono solo i device presenti sulla scheda e si
# abilitano solo i relativi driver, cosi da avere un kernel
# piu piccolo possibile e veloce da caricare in ram.
# Anche i devicetree, per le schedce piu comuni, sono gia
# presenti nei sorgenti, in arch/arm/boot/dts.
dtb=sun8i-h2-plus-bananapi-m2-zero.dtb

# Alcune architetture, specie nommu, richiedono un binario compilato
# con offsets dei salti "assoluti", legati a un entry point,m per arm
# e' inifluente, codice e' libero (pic).
entry_point=40008000
load_addr=0x${entry_point}

# Ora cross-compiliamo
echo "starting build process ..."
if [ "${dtb}" != ""  ]; then
        echo "compiling dtb blob ..."
        make ${dtb}
fi
defconfig=${board}_defconfig
make $defconfig
make -j20 KALLSYMS_EXTRA_PASS=1 LOADADDR=${load_addr}

# Se si vogliono compilare anche i moduli:
make modules

# Da qui in poi ci si puo divertire ad arricchire lo
# script con comandi per la copia diretta su sdcard

Commenti, segnalazione imprecisioni, errori, etc benvenuti.
Messaggio unito automaticamente:

Grazie dei like amici !