sábado, 7 de diciembre de 2013

Compilación cruzada con Buildroot

En este post quiero mostrar el uso de una herramienta de compilación cruzada llamada "Buildroot" para poder escribir programas en C que se ejecuten sistemas Linux embebidos, como los que hay por ejemplo en routers, camaras ip, DVRs, etc.

Muchos de estos dispositivos permiten conectar por SSH o Telnet y obtener una shell, otros tienen vulnerabilidades de inyección de comandos (como este router o este otro) y a otros se puede acceder por puerto serial con algo de hardware hacking. De una u otra manera, lo primero que debes conseguir es una linea de comandos en el dispositivo.


¿Qué es Buildroot?

Traducción monse: Buildrrot es un conjunto de "Makefiles" y parches que facilitan la creación de sistemas Linux embebidos completos. Buildroot puede generar las herramientas de compilación cruzada, un sistema de ficheros raiz, una imagen del kernel y una imagen de bootloader.


Nosotros lo utilizaremos para crear el "Toolchain" de compilación cruzada y con él poder compilar nuestros programas para la arquitectura del cacharro en cuestión.

El primer paso será descargar Buildroot:

$ curl http://buildroot.uclibc.org/downloads/buildroot-2013.11.tar.gz -o buildroot-2013.11.tar.gz

Luego lo desempaquetamos:

$ tar xvzf buildroot-2013.11.tar.gz

Y por lo pronto ya está. Antes de continuar asegurate de tener instaladas todas las dependencias (o por lo menos las de la sección 2.1.1)

http://buildroot.uclibc.org/downloads/manual/manual.html#requirement


Obteniendo información

Antes de continuar con la configuración de Buildroot debemos obtener algo de información sobre la arquitectura objetivo. Para ello podemos hacer lo siguiente:

En la terminal del dispositivo, ejecutamos:

$ cat /proc/version

Fig. 1 - Obtener versión de Linux y GCC

Algo que ayuda bastante es analizar algún binario que venga con el sistema embebido. Podemos escoger, por ejemplo, cualquiera de los que esté en "/bin" y descargarlo a nuestro PC usando netcat o por ftp (o de cualquier otra forma). Una vez tengas el binario, analizalo con el comando "readelf".

$ readelf -h [BINARIO]

Fig. 2 - Analizando binario.


Configurando Buildroot

Ahora toca setear los parametros que hemos obtenido en la configuración de Buildroot. Para ello primero ubicate en la carpeta de Buildroot:

$ cd buildroot-2013.11

Y ejecuta:

$ make menuconfig

Esto iniciará el asistente de configuración de Buildroot.

Fig. 3 - Configuración de Buildroot

En "Target options" > "Target Architecture" seleccionamos el tipo de arquitectura. En mi caso "MIPS (big endian)".

Fig. 4 - Configurar arquitectura

Luego, si es necesario, ajustamos las opciones de la variante de MIPS. En mi caso no es necesario.

Fig. 5 - Variante (mips 32) ABI (o32)

Ahora en "Toolchain" configuramos la versión del kernel Linux. En mi caso "2.6.30". Si no está en la lista, podemos asignar la versión manualmente.

Fig. 6 - Configurar versión de Linux

En "Toolchain" también podemos configurar la versión del compilador GCC. En mi caso "4.4.x".

Fig. 7 - Configurar versión de GCC.

Si nuestro código requiere librerías adicionales, en "Target packages" > "Libraries" podremos buscarlas por categorías y seleccionarlas.

Fig. 8 - Configuración de librerías

Finalmente guardamos la configuración con la opción "Save". Es importante guardar la configuración en el mismo directorio y con el nombre ".config". En otro caso no reconocerá la configuración.

Fig. 9 - Guardar configuración

Y ya podemos cerrar el asistente de configuración.


Construyendo el Toolchain

Esto es muy fácil. Simplemente nos ubicamos en la carpeta de buildroot y ejecutamos "make".

$ make

Buildroot leerá la configuración e iniciará la descarga de todos los paquetes requeridos para finalmente compilarlos y organizarlos. Esto va a demorar un buen rato, así que ve a comer algo, duerme, comunícate con otros mortales... vamos, deja la maquina trabajando y busca otra cosa que hacer.

Cuando termine, tendremos los binarios para compilación cruzada en el directorio "output/host/usr/bin/". Para acceder fácilmente a ellos configuramos las variables de entorno:

export TOOLCHAIN_MIPS=[RUTA AL DIRECTORIO BUILDROOT]
export PATH=$TOOLCHAIN_MIPS/output/host/usr/bin:$PATH
export AR=$TOOLCHAIN_MIPS/output/host/usr/bin/mips-linux-ar
export AS=$TOOLCHAIN_MIPS/output/host/usr/bin/mips-linux-as
export CC=$TOOLCHAIN_MIPS/output/host/usr/bin/mips-linux-gcc
export CPP=$TOOLCHAIN_MIPS/output/host/usr/bin/mips-linux-cpp
export CXX=$TOOLCHAIN_MIPS/output/host/usr/bin/mips-linux-g++
export LD=$TOOLCHAIN_MIPS/output/host/usr/bin/mips-linux-ld
export GCC=$TOOLCHAIN_MIPS/output/host/usr/bin/mips-linux-gcc
export NM=$TOOLCHAIN_MIPS/output/host/usr/bin/mips-linux-nm


Puedes añadir las lineas anteriores al ".bashrc" de tu home para no tener que hacer la configuración cada vez.


Compilando un "Hola Mundo"

Siguiendo con la tradición, haremos nuestro clásico "Hola Mundo"  y lo compilaremos para MIPS big endian (o la arquitectura que hayas configurado).

holamundo.c
#include <stdio.h>

int main(int argc, char **argv) {
 printf("Hola Mundo!\n");
 return 0;
}

Colocamos el código anterior en "holamundo.c" y lo compilamos con el siguiente comando:

$ mips-linux-gcc holamundo.c -o holamundo

Y ya está.

Ahora subimos el binario compilado al cacharro, le damos permisos de ejecución y lo corremos:

Fig. 10 - Hola Mundo en MIPS big endian

Es todo por ahora. Hasta la próxima.

Un saludo.

No hay comentarios:

Publicar un comentario