Etiquetas

jueves, 26 de diciembre de 2013

Cross-compile para Raspberry Pi con scratchbox2 - Un borrador

En realidad, estas notas son apenas un borrador.
Llevo un tiempo haciendo cross-compile del kernel linux para raspberry pero siempre compilar algún programa en C++ propio me había resultado muy complicado.
Hoy decidí probar con scratchbox2 y este es el borrador del "work-in-progress". La verdad parece muy util y directo que otro acercamiento.

Como dije, esto es un borrador, no un tutorial. Por eso si alguien encuentra errores, o conoce scratchbox más en profundidad cualquier aporte será bienvenido. Es un borrador, porque además fui haciéndolo en días sucesivos, por lo tanto podría haber algun/os error/es.

Decidí utilizar los paquetes más estándares posibles, disponibles en los repositorios de Debian Wheezy.
El sistema "host" es un i386, mientras que el "target", lógicamente será un armv6, el tipo de arquitectura de CPU de la Raspberry Pi.
Por lo que leí, resumido brevemente, la gran ventaja de scratchbox es que es una jaula fakeroot en la que se instalará un fs root de raspbian y allí podremos ir instalando las dependencias (librerías) que el soft que vayamos a compilar requiera. Scratchbox y qemu serán los encargados de mantener la magia, es decir, compilar para la arquitectura "target" e inclusive ejecutar código compilado para arm en la CPU "host". Con algunos ejemplos creo que se puede ver mejor:

1) Instalación de los paquetes necesarios:

# aptitude install scratchbox2 fakeroot realpath qemu-user

2) Instalación del toolchain

El "toolchain" para compilación cruzada podría compilarse, pero lo más directo es usar las rpi-tools, disponibles en este repositorio git.

$ mkdir -p $HOME/raspi_build/rpitools/ && cd $HOME/raspi_build/rpitools/
$ git clone https://github.com/raspberrypi/tools.git --depth 0

Tomemos nota del directorio donde lo instalamos y a seguir

3) Extracción del rootfs de raspbian

Para crear el ambiente que scratchbox precisará para su magia, vamos a copiar de una imagen de raspbian el sistema de archivos raíz (rootfs), que normalmente está en la segunda partición de la imagen de la SD:

Para esto usé una imagen más o menos actual de raspbian: 2013-07-26-wheezy-raspbian.img


$ mkdir -p $HOME/sb2_lab/mnt
$ cd $HOME/sb2_lab
$ mkdir raspbian-rootfs
Crear el dir de trabajo donde se prefiera, claro y copiar allí la img de raspbian.

$ cd $HOME/sb2_lab
$ /sbin/fdisk -lu 2013-07-26-wheezy-raspbian.img

Disco 2013-07-26-wheezy-raspbian.img: 1939 MB, 1939865600 bytes
255 heads, 63 sectors/track, 235 cylinders, 3788800 sectores en total
Units = sectores of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Identificador del disco: 0x00047c7a

Disposit. Inicio Comienzo Fin Bloques Id Sistema
2013-07-26-wheezy-raspbian.img1 8192 122879 57344 c W95 FAT32 (LBA)
2013-07-26-wheezy-raspbian.img2 122880 3788799 1832960 83 Linux
Veamos, que la partición tipo linux comienza en el sector 122880, y cada sector tiene 512 bytes. Con eso vamos a montar el loop device para copiar los archivos.
Hagamos el cálculo:
$ echo "512 * 122880" | bc
62914560


Para montar el loop device, tenemos que hacerlo como root y le pasamos el offset que calculamos:

# mount -o ro,loop,offset=62914560 -t auto 2013-07-26-wheezy-raspbian.img mnt/

A copiar:

$ cd $HOME/sb2_lab/
$ rsync -avz mnt/ raspbian-rootfs

Ahora en el subdir ./raspbian-rootfs tendremos el rootfs de raspbian

$ ls raspbian-rootfs/
bin boot dev etc home lib lost+found media mnt opt proc root run sbin selinux srv sys tmp usr var


4) Iniciar el ambiente de scratchbox

Ingresamos al directorio donde copiamos el rootfs de raspbian, de otra manera sb2 no podrá iniciar bien el toolchain.
Vamos iniciar el ambiente para Scratchbox2, pasandole la ubicación de gcc en la versión del toolchain para Cross-Compile que habíamos bajado:

$ cd $HOME/sb2_lab/raspbian-rootfs
$ sb2-init raspberrypi_wheezy
/home/matias/raspi_build/rpitools/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-gcc

Al final del proceso aparecerá:

"sb2-init completed successfully, have fun!"

Si no aparece, a desconfiar de algo y revisar.
Con eso ya tenemos el ambiente listo. Ahora, a probarlo.

5) Compilando algo para probar

Creemos el siguiente código, solo para probar. Puede ser en el directorio de trabajo o en cualquier otro:

/* Archivo: teste.c - Solo para probar sb2 */
#include <stdio.h>
int main(){
printf("Hello, #RasPi\n");
return 0;
}

Lo llamé teste.c, vamos a compilarlo dejando que scratchbox haga su magia:

$ sb2 gcc -o teste teste.c

Ahora veamos la info sobre el binario que creó:

$ file teste
teste: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0x2c11b259c80ffd4ceccb9a2a0622fb12571858f7, not stripped

$ readelf -a teste | grep -i "cpu\|mach"
Machine: ARM
Tag_CPU_name: "6"
Tag_CPU_arch: v6
Tag_CPU_unaligned_access: v6

Como se ve, la arquitectura de destino fue, en efecto, arm v6.
Ahora, con la ayuda de qemu scratchbox permite hasta ejecutar el binario arm en el sistema "host" (i386):

$ sb2 ./teste
Hello, #RasPi

6) Probar compilar algo del mundo "real"

Por ahora, todo muy lindo. Pero el código de ejemplo es tan básico que precisa de librarías estándares y mínimas.
Probé lo siguiente, que me estaba dando mucho laburo compilar sin usar scratchbox.
El código siguiente es muy simple, es parte de unas clases que usé y modifiqué un poquito para controlar los leds de "status" de una raspberry. Los leds indican si el sistema completó el boot y monitorea el estadod e ethernet. Es algo bastante útil si se usa una RasPi sin monitor, para tener una idea de su estado si necesidad de ping, conectar por ssh, etc.

$ git clone https://github.com/retux/raspileds_status --depth 0
Cloning into 'raspileds_status'...
remote: Counting objects: 56, done.
remote: Compressing objects: 100% (44/44), done.
remote: Total 56 (delta 11), reused 50 (delta 5)
Unpacking objects: 100% (56/56), done.

Originariamente, había compilado estos binarios directo en la raspi. Porque la verdad el tiempo de compilación es mínimo. Ahora compilarlo con ScratchBox fue muy sencillo:


$ cd raspileds_status
$ sb2 make clean
rm -f *.o
$ sb2 make
g++ -Wall -c -o GPIOClass.o GPIOClass.cpp
g++ -Wall -c -o netifledwatch.o netifledwatch.cpp
g++ -Wall -o netifledwatch GPIOClass.o netifledwatch.o
g++ -Wall -c -o gpiopinctrl.o gpiopinctrl.cpp
g++ -Wall -o gpiopinctrl GPIOClass.o gpiopinctrl.o
g++ -Wall -c -o gpioctrl.o gpioctrl.cpp
g++ -Wall -o gpioctrl GPIOClass.o gpioctrl.o

Veamos alguno de los binarios para confirmar:
$ file netifledwatch
netifledwatch: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0xd7cb48d849ae94101dbd00c1ad28f67f0f56048b, not stripped

Como se ve, la magia de ScratchBox hizo posible que ni siquiera fuera necesario modificar el Makefile.

¿Qué sigue?

Queda por ver cómo se puede usar las maravillas de apt-get en el ambiente scratchbox, por ejemplo, para instalar dependencias necesarias para compilar soft más complejo. No lo probé aún, pero tiene que poder hacerse.

Agregado 27/12/2013:

Con la imagen de raspbian reciente que estoy usando para el ambiente chroot de scratchbox, es necesario comentar la línea del archivo ld.so.preload, porque de lo contrario algo de la emulación con qemu se rompía y no permitía por ejemplo ejecutar un apt-get update dentro del ambiente chroot.

$ cat raspbian-rootfs/etc/ld.so.preload
#/usr/lib/arm-linux-gnueabihf/libcofi_rpi.so

Después de eso, se puede ejecutar apt-get update e instalar paquetes en el ambiente, sin problemas:

$ sb2 -eR apt-get update
Get:1 http://mirrordirector.raspbian.org wheezy Release.gpg [490 B]
Get:2 http://mirrordirector.raspbian.org wheezy Release [14.4 kB]
Get:3 http://raspberrypi.collabora.com wheezy Release.gpg [836 B]
Get:4 http://archive.raspberrypi.org wheezy Release.gpg [490 B]
...

$ sb2 -eR apt-get install vim
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
libgpm2 vim-runtime
...

¿Lindo, no es cierto? De esta forma podrían instalarse con apt-get las libs y dependencias para compilar el soft X e intentar el cross-compile con Scratchbox.

Agregado 30/12/2013:

Correr apt-get y todas sus maravillosas luces y botoncitos dentro del ambiente chroot de scratchbox2, por eso siguen una líneas con el procedimiento usado:

Primero entramos en la emulación de Scratchbox con el siguiente comando:
$ sb2 -eR
[SB2 emulate raspberrypi_wheezy] root@debian-test-kput raspbian-rootfs #

Vemos que estamos en el ambiente chroot, lo que nos informa el prompt.

Segundo, instalamos las claves públicas de los repositorios:

[SB2 emulate raspberrypi_wheezy] root@debian-test-kput raspbian-rootfs # wget http://archive.raspbian.org/raspbian.public.key -O - | apt-key add -
--2013-12-30 15:28:47-- http://archive.raspbian.org/raspbian.public.key
Resolviendo archive.raspbian.org (archive.raspbian.org)... 5.153.225.206, 2001:41c9:1:3ce::10
Conectando con archive.raspbian.org (archive.raspbian.org)[5.153.225.206]:80... conectado.
Petición HTTP enviada, esperando respuesta... 200 OK
Longitud: 1776 (1,7K) [application/octet-stream]
Grabando a: “STDOUT”

100%[======================================================================================>] 1.776
--.-K/s en 0s

2013-12-30 15:28:47 (13,0 MB/s) - escritos a stdout [1776/1776]
OK

[SB2 emulate raspberrypi_wheezy] root@debian-test-kput raspbian-rootfs # wget http://archive.raspberrypi.org/debian/raspberrypi.gpg.key -O - | apt-key add -

2013-12-30 15:52:34 (12,2 MB/s) - escritos a stdout [1719/1719]

OK

[SB2 emulate raspberrypi_wheezy] root@debian-test-kput raspbian-rootfs # wget http://raspberrypi.collabora.com/collabora-raspbian.gpg -O - | apt-key add -
--2013-12-30 16:01:35-- http://raspberrypi.collabora.com/collabora-raspbian.gpg
Resolviendo raspberrypi.collabora.com (raspberrypi.collabora.com)... 93.93.128.223
Conectando con raspberrypi.collabora.com (raspberrypi.collabora.com)[93.93.128.223]:80... conectado.
Petición HTTP enviada, esperando respuesta... 200 OK
Longitud: 3145 (3,1K) [text/plain]
Grabando a: “STDOUT”

100%[======================================================================================>] 3.145 --.-K/s en 0s

2013-12-30 16:01:35 (22,6 MB/s) - escritos a stdout [3145/3145]

OK


Conclusión provisional

Con apt-get completo corriendo, pudiendo instalar dependencias para aquello que vayamos a compilar scratchbox2 es una belleza.
Inclusive, hace hasta innecesario y menos eficaz tener necesidad de emular un raspbian (o cualquier otra distro) en un x86, porque basta hacer:

$ sb2 -eR

Y ya estamos en el ambiente chroot, desde donde podremos ejecutar los binarios compilados para arm, cuyas instrucciones serán reinterpretadas y traducidas por qemu.

2 comentarios: