En esta entrega vamos a construir un compilador cruzado para generar ejecutables para la arquitectura ARM. El compilador que vamos a utilizar es GCC.

¿Qué es un compilador cruzado?

Un compilador cruzado es un compilador que se ejecuta en una plataforma A(el host), pero genera ejecutables para la plataforma B(el target). Estas dos plataformas pueden variar en la arquitectura de la CPU, sistema operativo y/o el formato del ejecutable. En nuestro caso el host es nuestra máquina Linux y el target la raspberry pi.

Instrucciones de compilación

En nuestro caso vamos a construir un compilador cruzado basado en GCC. Tal y como explicamos anteriormente, el compilador se ejecuta en la arquitectura x86 y genera código para la arquitectura ARM.

Fase previa

Creamos la carpeta donde vamos a realizar la compilación y descargamos los paquetes necesarios

INSTALL_DIR=/home/andres/pi
mkdir $INSTALL_DIR
mkdir -p $INSTALL_DIR/src $INSTALL_DIR/build
cd $INSTALL_DIR/src
#descargar paquetes
wget -nc https://ftp.gnu.org/gnu/gcc/gcc-10.1.0/gcc-10.1.0.tar.xz
wget -nc https://ftp.gnu.org/gnu/glibc/glibc-2.31.tar.xz
wget -nc https://ftp.gnu.org/gnu/binutils/binutils-2.34.tar.xz
wget -nc https://ftp.gnu.org/gnu/gmp/gmp-6.2.0.tar.xz
wget -nc https://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz
wget -nc https://ftp.gnu.org/gnu/mpfr/mpfr-4.0.2.tar.xz
##extraer paquetes
xz -d gcc-10.1.0.tar.xz
tar xf gcc-10.1.0.tar
xz -d glibc-2.31.tar.xz
tar xf glibc-2.31.tar
xz -d binutils-2.34.tar.xz
tar xf binutils-2.34.tar
xz -d gmp-6.2.0.tar.xz
tar xf gmp-6.2.0.tar
xz -d mpfr-4.0.2.tar.xz
tar xf mpfr-4.0.2.tar
tar xf mpc-1.1.0.tar.gz

Binutils

Contiene el ensamblador, el linker, etc.

#compilar binutils
mkdir $INSTALL_DIR/build/binutils-2.34
cd $INSTALL_DIR/build/binutils-2.34
../../src/binutils-2.34/configure \
--with-sysroot=$INSTALL_DIR/sysroot \
--prefix=$INSTALL_DIR \
--disable-nls \
--target=arm-linux-gnueabihf \
--disable-multilib
make
make install
export PATH=$PATH:$INSTALL_DIR/bin

GCC fase 1

Compilamos un compilador básico sin soporte para la librería estándar de C.

cd $INSTALL_DIR/src/gcc-10.1.0/
ln -s ../gmp-6.2.0 gmp
ln -s ../mpc-1.1.0 mpc
ln -s ../mpfr-4.0.2 mpfr
mkdir $INSTALL_DIR/build/gcc-10.1-fase1/
cd $INSTALL_DIR/build/gcc-10.1-fase1/
../../src/gcc-10.1.0/configure \
--prefix=$INSTALL_DIR \
--target=arm-linux-gnueabihf \
--enable-languages=c \
--without-headers \
--disable-nls \
--disable-multilib \
--disable-threads \
--disable-shared \
--with-float=hard \
--with-fpu=vfp \
--with-arch=armv6
make all-gcc all-target-libgcc
make install-gcc install-target-libgcc

Cabeceras del kernel de Linux

Instalamos las cabeceras del kernel necesarias para pasos posteriores.

#kernel headers
cd $INSTALL_DIR/src
mkdir -p $INSTALL_DIR/sysroot/usr
git clone https://github.com/raspberrypi/linux --depth=1
cd linux
make headers_install ARCH=arm INSTALL_HDR_PATH=$INSTALL_DIR/sysroot/usr

Glibc

Compilamos con el compilador gcc fase 1 la librería estándar de C. Genera las librerías de C para ser ejecutadas en ARM

#compilar glibc
mkdir $INSTALL_DIR/build/glibc-2.31
cd $INSTALL_DIR/build/glibc-2.31
../../src/glibc-2.31/configure \
--prefix=/usr \
--host=arm-linux-gnueabihf \
--with-headers=$INSTALL_DIR/sysroot/usr/include \
--disable-multilib \
--disable-werror
make
make install DESTDIR=$INSTALL_DIR/sysroot

GCC fase 2

Volvemos a compilar GCC pero esta vez con soporte completo de librería de C. Listo para ser usado en nuestros proyectos

#compilar gcc fase 2
mkdir $INSTALL_DIR/build/gcc-10.1-fase2
cd $INSTALL_DIR/build/gcc-10.1-fase2
../../src/gcc-10.1.0/configure \
--prefix=$INSTALL_DIR \
--target=arm-linux-gnueabihf \
--enable-languages=c,c++\
--disable-nls \
--disable-multilib \
--with-sysroot=$INSTALL_DIR/sysroot \
--with-float=hard \
--with-fpu=vfp \
--with-arch=armv6
make
make install

Resultado

Por último, en la ruta $INSTALL_DIR/bin/ tenemos nuestro compilador de C y C++, vamos a probarlo.

Creamos un hola mundo en C++

#include <iostream>
using namespace std;
int main(void) {
    cout << "Hola mundo" << endl;
    return 0;
}

Y lo compilamos. En mi caso lo instalé en la carpeta home

/home/andres/pi/bin/arm-linux-gnueabihf-g++ -g -Wall -std=c++11 main.cc -o main

Si lanzamos el comando file al ejecutable tendremos:

¡Ya tenemos nuestro nuestro compilador cruzado!

Dejo un enlace a un gist para ver el script en su conjunto

Enlaces

https://wiki.osdev.org/GCC_Cross-Compiler

https://gcc.gnu.org/install/build.html

https://wiki.osdev.org/Building_GCC