Toolchains

Ofta är targetsystemet/målsystemet av annan arkitektur än utvecklingsdatorn. Target kan även köra annat operativsystem än utvecklingsdatorn eller inte köra något operativsystem alls, så kallad bare metal. För att kunna kompilera program för att köra dem på target behövs en korskompilator. Ett toolchain består vanligtvis av kompilator, länkare och standardbibliotek. Debugger och andra utvecklingsverktyg kan också ingå i ett toolchain.

Färdiga toolchains

Färdiga toolchains för user space program

Det finns färdiga toolchains för att bygga program för user space. Många Liuxdistributioner har en del färdiga toolchains för korskompilering bland sina standardpaket. Toolchains kan man också hitta hos bootlin.com, https://toolchains.bootlin.com/

Om man använder Buildroot eller Yocto Project för att bygga en Linuxdistribution för det inbyggda systemet så bygger de även toolchain. Med Buildroot och Yocto Project kan man även bygga externa toolchains. En SDK (Software Development Kit) brukar kunna bestå av toolchain och annan programvara för utveckling mot targetsystemet. En SDK är vanligtvis anpassad för hårdavruplatform och operativsystems-image.

Jag tar här inte upp grunderna i hur man jobbar med Yocto Project. För detta finns det många nätverksresurser och många bra videos på YouTube. Officiell dokumentation för Yocto Project finns på https://www.yoctoproject.org/.

För att bygga en SDK med Yocto Project kör man bitbake -c populate_sdk IMAGE där IMAGE är namnet på den image du ska bygga din SDK för. Om du har en image som heter core-image-minal och du vill använda den för att bygga en SDK anpassad för ditt målsystem då gör du på följande sätt för att skapa denna SDK:

bitbake -c populate_sdk core-image-minimal

Som resultat får du ett shellscript under build/tmp/deploy/sdk. Scriptet får ett namn som speglar utvecklingssystem och target-system, Exempel: poky-glibc-x86_64-meta-toolchain-cortexa8hf-neon-toolchain-3.3.1.sh Detta shellscript innehåller ett komplett SDK. Kopiera scriptet till en annan dator och kör det för att installera denna SDK. För att använda den installerade SDK måste du först köra source på environment-scriptet som installerades tillsammans med SDK. Scriptet kan t.ex. heta environment-setup-cortexa8hf-neon-poky-linux-gnueabi. Detta script sätter upp environment för korskompilering.

För Yocto Project finns även möjligheten att bygga en Extensible SDK. Yocto Project Extensible SDK (eSDK) har verktyg som låter dig enkelt lägga till nya applikationer och bibliotek till en image, ändra källan till en befintlig komponent och testa ändringar på target. Den största fördelen jämfört med standard-SDK:n är ett förbättrat arbetsflöde i teamet på grund av stramare integration med OpenEmbedded-byggsystemet. För att bygga en Extensible SDK gör man på samma sätt som för en SDK men anger istället populate_sdk_ext till bitbake.

bitbake -c populate_sdk_ext core-image-minimal

Det finns även proprietära toolchains. Dessa är dock utanför vad jag tar upp här.

Toolchain för att kompilera Linukärnan

Toolchains baserade på gcc för att kompilera Linuxkärnan finns på kernel.org under https://mirrors.edge.kernel.org/pub/tools/crosstool/
Toolchains baserade på LLVM finns på https://mirrors.edge.kernel.org/pub/tools/llvm/

Bygga toolchain

För att kunna bygga ett toolchain behöver du installera en kompilator, t.ex. gcc, binutils och andra verktyg. I binutils ingår bland annat assemblator, länkare och verktyg för att studera och hantera ELF-binärer. I Debianbaserade system kan du installera dessa verktyg med:

  sudo apt-get install build-essential
  sudo apt-get install flex bison ncurses-dev texinfo gcc gperf patch libtool automake g++ libncurses5-dev gawk subversion expat libexpat1-dev python-all-dev binutils-dev bc libcap-dev autoconf libgmp-dev pkg-config libmpc-dev libmpfr-dev autopoint gettext txt2man liblzma-dev libssl-dev libz-dev mercurial wget tar zstd
I RedHat, CentOS, AlmaLinux, RockyLinux, Fedora och liknande kan du istället göra:
  dnf groupinstall 'Development Tools'
  dnf install install mpfr-devel gmp-devel libmpc-devel zlib-devel glibc-devel.i686 glibc-devel binutils-devel g++ texinfo bison flex cmake which clang ninja-build lld bzip2
or
  yum groupinstall 'Development Tools'
Eventuellt kan det finnas fler paket som behöver installeras.

För att bygga ett toolchain baserat på gcc, binutils och glibc behöver du göra:

För mera information om att bygga ett gnu toolchain se https://wiki.osdev.org/GCC_Cross-Compiler

Att göra detta manuellt enligt ovan är ganska tidsödande och det är väldigt lätt att det blir fel. Det finns dock hjälpmedel. Ett sådant är crosstool-ng, se nedan.

Bygga med hjälp av crosstool-ng

Crosstool-ng är ett verktyg för att bygga ett eller flera toolchains på ett enklare sätt men fortfarande med stora möjligheter till konfiguration. Du kan gå till https://crosstool-ng.github.io/ och ladda ner crosstool-ng eller checka ut den från https://github.com/crosstool-ng/crosstool-ng

Med crosstool-ng bygger man ett verktyg som heter ct-ng. Med ct-ng kan man lista färdiga konfigurationer och man kan bygga ett eller flera toolchains.
Skiss med en crosstool-ng cirkel med pil till en ct-ng cirkel och pilar till flera olika toolchains cirklar

Ladda ner crosstool-ng och packa upp den eller klona den från github. Gå till katalogen du fick och kör ./configure --prefix=/path/till/dit/du/vill/ha/den; make; make install. Exempel:

    ./configure --prefix=~/ct-ng
    make -j 8
    make install
Här installeras ct-ng under ~/ct-ng/bin. Lägg till den till din PATH. Nu kan du lista fördefinierade konfigurationer med ct-ng list-samples. Exempel:
kjell-e@maxwell:~/crosstool/build$ ct-ng list-samples
Status  Sample name
[G...]   aarch64-ol7u9-linux-gnu
[G...]   aarch64-rpi3-linux-gnu
[G...]   aarch64-rpi4-linux-gnu
[G..X]   aarch64-unknown-linux-android
[G...]   aarch64-unknown-linux-gnu
[G...]   aarch64-unknown-linux-uclibc
[G...]   alphaev56-unknown-linux-gnu
[G...]   alphaev67-unknown-linux-gnu
[G...]   arc-arc700-linux-uclibc
[G...]   arc-archs-linux-gnu
[G...]   arc-multilib-elf32
[G...]   arc-multilib-linux-gnu
[G...]   arc-multilib-linux-uclibc
[G...]   arm-bare_newlib_cortex_m3_nommu-eabi
[G...]   arm-cortex_a15-linux-gnueabihf
[G..X]   arm-cortexa5-linux-uclibcgnueabihf
[G...]   arm-cortex_a8-linux-gnueabi
[G..X]   arm-cortexa9_neon-linux-gnueabihf
[G..X]   x86_64-w64-mingw32,arm-cortexa9_neon-linux-gnueabihf
[G...]   armeb-unknown-eabi
[G...]   armeb-unknown-linux-gnueabi
[G...]   armeb-unknown-linux-uclibcgnueabi
[G...]   arm-multilib-linux-uclibcgnueabi
[G...]   arm-nano-eabi
[G...]   arm-ol7u9-linux-gnueabi
[G...]   arm-ol7u9-linux-gnueabihf
[G...]   arm-picolibc-eabi
[G...]   arm-unknown-eabi
[G...]   arm-unknown-linux-gnueabi
[G..X]   arm-unknown-linux-musleabi
[G...]   arm-unknown-linux-uclibcgnueabi
[G..X]   arm-unknown-linux-uclibcgnueabihf
[G...]   armv6-nommu-linux-uclibcgnueabi
[G...]   armv6-unknown-linux-gnueabi
[G...]   armv6-unknown-linux-gnueabihf
[G...]   armv7-rpi2-linux-gnueabihf
[G...]   armv8-rpi3-linux-gnueabihf
[G...]   armv8-rpi4-linux-gnueabihf
[G...]   avr
[G...]   i586-geode-linux-uclibc
[G...]   i686-centos6-linux-gnu
[G...]   i686-centos7-linux-gnu
[G...]   i686-nptl-linux-gnu
[G...]   i686-ubuntu12.04-linux-gnu
[G...]   i686-ubuntu14.04-linux-gnu
[G...]   i686-ubuntu16.04-linux-gnu
[G..X]   i686-w64-mingw32
[G...]   m68k-unknown-elf
[G...]   m68k-unknown-uclinux-uclibc
[G...]   powerpc-unknown-linux-uclibc,m68k-unknown-uclinux-uclibc
[G...]   mips64el-multilib-linux-uclibc
[G...]   mips64-unknown-linux-gnu
[G...]   mips-ar2315-linux-gnu
[G...]   mipsel-multilib-linux-gnu
[G...]   mipsel-sde-elf
[G...]   mipsel-unknown-linux-gnu
[G...]   mips-malta-linux-gnu
[G...]   mips-unknown-elf
[G...]   mips-unknown-linux-uclibc
[G..X]   moxiebox
[G..X]   moxie-unknown-elf
[G..X]   x86_64-multilib-linux-uclibc,moxie-unknown-moxiebox
[G..X]   msp430-unknown-elf
[G...]   nios2-altera-linux-gnu
[G..X]   i686-w64-mingw32,nios2-spico-elf
[G...]   nios2-unknown-elf
[G...]   powerpc-405-linux-gnu
[G...]   powerpc64le-unknown-linux-gnu
[G...]   powerpc64-multilib-linux-gnu
[G...]   powerpc64-unknown-linux-gnu
[G...]   powerpc-8540-linux-gnu
[G...]   powerpc-860-linux-gnu
[G...]   powerpc-e300c3-linux-gnu
[G...]   powerpc-e500v2-linux-gnuspe
[G...]   x86_64-multilib-linux-uclibc,powerpc-unknown-elf
[G...]   powerpc-unknown-linux-gnu
[G...]   powerpc-unknown-linux-uclibc
[G...]   powerpc-unknown_nofpu-linux-gnu
[G...]   pru
[G..X]   riscv32-hifive1-elf
[G..X]   riscv32-unknown-elf
[G..X]   riscv64-unknown-elf
[G..X]   riscv64-unknown-linux-gnu
[G..X]   s390-ibm-linux-gnu
[G..X]   s390-unknown-linux-gnu
[G...]   s390x-ibm-linux-gnu
[G...]   s390x-unknown-linux-gnu
[G...]   sh-multilib-linux-gnu
[G...]   sh-multilib-linux-uclibc
[G...]   sh-unknown-elf
[G...]   sparc64-multilib-linux-gnu
[G...]   sparc-leon-linux-uclibc
[G...]   sparc-unknown-linux-gnu
[G..X]   tic6x-uclinux-uclibc
[G...]   x86_64-centos6-linux-gnu
[G...]   x86_64-centos7-linux-gnu
[G...]   x86_64-multilib-linux-gnu
[G..X]   x86_64-multilib-linux-musl
[G...]   x86_64-multilib-linux-uclibc
[G..X]   x86_64-w64-mingw32,x86_64-pc-linux-gnu
[G...]   x86_64-ubuntu12.04-linux-gnu
[G...]   x86_64-ubuntu14.04-linux-gnu
[G...]   x86_64-ubuntu16.04-linux-gnu
[G...]   x86_64-unknown-linux-gnu
[G...]   x86_64-unknown-linux-uclibc
[G..X]   x86_64-w64-mingw32
[G..X]   xtensa-fsf-elf
[G...]   xtensa-fsf-linux-uclibc
 L (Local)       : sample was found in current directory
 G (Global)      : sample was installed with crosstool-NG
 X (EXPERIMENTAL): sample may use EXPERIMENTAL features
 B (BROKEN)      : sample is currently broken
 O (OBSOLETE)    : sample needs to be upgraded


Jag rekommenderar att du utgår från en fördefinierad konfiguration och gör några få ändringar och provbygger och om det går bra gör fler nödvändiga ändringar tills du är nöjd. För att välja en konfiguration av de listade gör man ct-ng KONFIGURATIONEN. Exempel:
    ct-ng arm-cortex_a8-linux-gnueabi

Därefter går det att ändra i konfigurationen med ct-ng menuconfig.
Om du är van vid att konfigurera Linuxkärnan, u-boot och/eller busybox kommer du att känna igen dig då det är samma typ av system för konfigurationen.

bild på menuconfig

Under "Paths and misc options" kan du bland annat ställa in var ditt toolchain ska installeras, var nedladdad källkod ska sparas, om ditt installerade toolchain ska vara read only. Lämpligt kan vara att kryssa ur "Render the toolchain read-only" så att det i efterhand går att installera bibliotek tillsammans med detta toolchain.

Under "Operating System" kan du bland annat göra inställningar för om det ska vara för big endian eller little endian och om target har hårdvarustöd för flyttal m.m.

Under "Operating System" ställer du in om det ska vara ett bare metal toolchain eller om det ska vara för Linux och i sådana fall för vilken version av linuxkärnan. Observera att version av linuxkärna inte får vara nyare än den version som kör på ditt/dina targetsystem.

Under "C-library" kan du ställa in vilket standardbibliotek som ska användas, t.ex. glibc, uClibc, newlib.

Under "C compiler" ställer du bland annat in för vilka språk detta toolchain ska ha stöd för förutom c.

Under "Debug facilities" väljer du vilka debugverktyg som ska ingå.

När du är klar avsluta och välj att konfigurationen ska sparas. Bygg sedan med ct-ng build.

    ct-ng build
  
ct-ng laddar ner all källkod från Internet och bygger binutils, gcc, bibliotek, kompilator och eventuella övriga verktyg och bibliotek som du har valt. Som standard installeras allt under ~/x-tools/TOOLCHAINNAMN. Kompilatorn hittar du i ~/x-tools/TOOLCHAINNAMN/bin. Exempel:
    ~/x-tools/arm-cortex_a8-linux-gnueabihf/bin/
  
Nu är det bara att lägga till denna bin-katalog till PATH och använda detta toolchain där programmen har namn på formen TOOLCHAINNAMN-gcc, TOOLCHAINNAMN-as etc. Exempel: arm-cortex_a8-linux-gnueabihf-gcc, arm-cortex_a8-linux-gnueabihf-as

Bare metal

Om man ska bygga program för att köra på ett system som saknar operativsystem vill man ha en bare metal korskompilator. Det finns färdiga toolchains, se ovan. Nedan visas ett script för att bygga ett toolchain för arm-none-eabi. Här byggs gcc 12.2.0, binutils 2.40, newlib 3.3.0 och gdb 13.1. Scriptet är baserat på https://xw.is/wiki/Bare_metal_ARM_GCC_9.3.0_cross_compiler_instructions. När det gäller host- och target-specifikation så kan man läsa mer om det för gcc på https://gcc.gnu.org/install/specific.html

#! /bin/bash

set -x
set -e

GCC_VERSION=12.2.0
BINUTILS_VERSION=2.40
NEWLIB_VERSION=3.3.0
GDB_VERSION=13.1
NUMPROC=$(nproc)

TRIPLET="arm-none-eabi"

INSTALL_PATH="$HOME/xgcc/${TRIPLET}"


set +x
echo "######################################################################################"
echo "#                                                                                    #"
echo "#                        Build bare metal toolchain                                  #"
echo "#                            for  ${TRIPLET}                                      #"
echo "#                                                                                    #"
echo "#               Install it in ${INSTALL_PATH}                                    #"
echo "#                                                                                    #"
echo "######################################################################################"
set -x


mkdir -p $HOME/build/build-crosstool-gcc-toolchain/from-scratch/from-internet-arm
cd $HOME/build/build-crosstool-gcc-toolchain/from-scratch/from-internet-arm

mkdir -p src obj/{binutils-build,gcc-build,gdb-build,newlib-build}

curl https://ftp.gnu.org/gnu/binutils/binutils-${BINUTILS_VERSION}.tar.xz > binutils-${BINUTILS_VERSION}.tar.xz
curl https://ftp.gnu.org/gnu/gdb/gdb-${GDB_VERSION}.tar.xz > gdb-${GDB_VERSION}.tar.xz
curl https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VERSION}/gcc-${GCC_VERSION}.tar.xz > gcc-${GCC_VERSION}.tar.xz
curl ftp://sourceware.org/pub/newlib/newlib-${NEWLIB_VERSION}.tar.gz > newlib-${NEWLIB_VERSION}
cd src
tar xf ../binutils-${BINUTILS_VERSION}.tar.xz
tar xf ../gdb-${GDB_VERSION}.tar.xz
tar xf ../gcc-${GCC_VERSION}.tar.xz
tar xf ../newlib-${NEWLIB_VERSION}.tar.gz


export PATH=${INSTALL_PATH}/bin:$PATH

cd gcc-${GCC_VERSION}
./contrib/download_prerequisites
set +x
echo "######################################################################################"
echo "#                                                                                    #"
echo "#                                 Build binutils                                     #"
echo "#                                                                                    #"
echo "######################################################################################"
set -x
cd ../../obj/binutils-build
rm -rf *
../../src/binutils-${BINUTILS_VERSION}/configure --prefix ${INSTALL_PATH} --bindir ${INSTALL_PATH}/bin --target ${TRIPLET} --enable-multilib
make -j${NUMPROC} && make install

set +x
echo "######################################################################################"
echo "#                                                                                    #"
echo "#                                 Build bootstrap gcc                                #"
echo "#                                                                                    #"
echo "######################################################################################"
set -x
cd ../gcc-build
rm -rf *
../../src/gcc-${GCC_VERSION}/configure --prefix ${INSTALL_PATH} --bindir ${INSTALL_PATH}/bin --target ${TRIPLET} --enable-multilib --enable-languages=c,c++ --without-headers
make -j${NUMPROC} all-gcc && make install-gcc

set +x
echo "######################################################################################"
echo "#                                                                                    #"
echo "#                                 Build newlib                                       #"
echo "#                                                                                    #"
echo "######################################################################################"
set -x
cd ../newlib-build
rm -rf *
../../src/newlib-${NEWLIB_VERSION}/configure --prefix ${INSTALL_PATH} --bindir ${INSTALL_PATH}/bin --target ${TRIPLET} --enable-multilib
make -j${NUMPROC} all && make install

set +x
echo "######################################################################################"
echo "#                                                                                    #"
echo "#                                 Build final gcc                                    #"
echo "#                                                                                    #"
echo "######################################################################################"
set -x
cd ../gcc-build
rm -rf *
../../src/gcc-${GCC_VERSION}/configure --prefix ${INSTALL_PATH} --bindir ${INSTALL_PATH}/bin --target ${TRIPLET} --enable-multilib --enable-languages=c,c++ --with-newlib
make -j${NUMPROC} all && make install

set +x
echo "######################################################################################"
echo "#                                                                                    #"
echo "#                                    Build gdb                                       #"
echo "#                                                                                    #"
echo "######################################################################################"
set -x
cd ../gdb-build
rm -rf *
../../src/gdb-${GDB_VERSION}/configure --prefix ${INSTALL_PATH} --bindir ${INSTALL_PATH}/bin --target ${TRIPLET} --enable-multilib
make -j${NUMPROC} && make install

set +x
echo "######################################################################################"
echo "#                                                                                    #"
echo "#                                       DONE                                         #"
echo "#                                                                                    #"
echo "######################################################################################"

#################################### end of script ##########################################

Scriptet bygger först binutils, därefter en boostrap gcc. Denna bootstrap gcc används därfter för att bygga biblioteket newlib, den slutgiltiga gcc och därfter debuggern gdb. Variabeln INSTALL_PATH anger var detta toolchain inklusive bibliotek och gdb ska installeras. Variabel NUMPROC sätts till antal CPU cores vilken används för att parallelisera bygget.



Copyright © 2010-2024 Kjell Enblom.
This document is covered by the GNU Free Documentation License, Version 1.3

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".