Bienvenido a mi primera publicación técnica en español. Hoy nos sumergimos en el mundo práctico de la compilación cruzada de módulos del kernel de Linux, una habilidad esencial en desarrollo embebido. Si trabajas con Raspberry Pi, placas ARM personalizadas o entornos virtualizados, entender este proceso es clave.
Antes de tocar código, aclaremos conceptos. En un IDE compilas con un botón y no ves el detalle. En Linux, herramientas como gcc y make son las protagonistas y el proceso completo consta de cuatro etapas: preprocesado para expandir macros e includes, compilación a lenguaje ensamblador, ensamblado a código máquina en archivos objeto y enlazado para producir binarios ejecutables.
Estrategias de compilación. Compilación nativa: compilar en la misma arquitectura y sistema donde se ejecutará. Compilación cruzada: compilar en una arquitectura distinta a la de destino usando una toolchain de cross-compiling para generar binarios que correrán en otra máquina. La razón principal para compilar en cruz es que el objetivo suele tener recursos limitados y la compilación exige memoria y CPU considerables.
Toolchain de compilación cruzada. Una toolchain agrupa compiladores, enlazadores y bibliotecas. Para ARM64 usaremos aarch64-linux-gnu, con el compilador aarch64-linux-gnu-gcc, binutils como aarch64-linux-gnu-ld y aarch64-linux-gnu-objcopy, y bibliotecas C para ARM64. Esta toolchain se ejecuta en nuestro host x86-64 pero genera binarios ARM64.
Preparación del entorno. Instala las herramientas necesarias con: sudo apt install -y qemu-system-arm gcc-aarch64-linux-gnu build-essential libncurses-dev bison flex libssl-dev libelf-dev cpio kmod. Clona el proyecto de ejemplo con: git clone https://github.com/samar12lassoued/kernel-module-arm-qemu.
Descarga y preparación del kernel. Obtén Linux 6.6 con: wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.tar.xz; descomprime con: tar xf linux-6.6.tar.xz; entra al directorio: cd linux-6.6. Define variables de entorno: export ARCH=arm64; export CROSS_COMPILE=aarch64-linux-gnu-.
Configuración y compilación del kernel. Genera una configuración por defecto con: make defconfig. De forma opcional personaliza con: make menuconfig y navega con flechas, Enter para submenús, Y para activar, N para desactivar, M para compilar como módulo y la barra para buscar. Verifica opciones críticas con: grep -E CONFIG_SERIAL_AMBA_PL011\|CONFIG_BLK_DEV_INITRD\|CONFIG_MODULES\|CONFIG_DEVTMPFS\|CONFIG_PROC_FS\|CONFIG_SYSFS .config. Compila la imagen y módulos con: make -j$(nproc) Image; make -j$(nproc) modules. Comprueba el resultado con: file arch/arm64/boot/Image y deberías ver Linux kernel ARM64 boot executable Image.
BusyBox y rootfs mínimo. El kernel requiere un sistema de archivos raíz con utilidades de usuario e init. Usaremos BusyBox por su ligereza y porque podemos enlazar estáticamente para evitar dependencias de bibliotecas compartidas. Descarga y compila: cd ..; wget https://busybox.net/downloads/busybox-1.36.0.tar.bz2; tar xf busybox-1.36.0.tar.bz2; cd busybox-1.36.0; make defconfig; make menuconfig y habilita Build static binary no shared libs; compila e instala con: make CROSS_COMPILE=aarch64-linux-gnu- install. Verifica la arquitectura con: file _install/bin/busybox.
Construcción del rootfs. Crea la estructura: cd ..; mkdir -p rootfs/bin rootfs/dev rootfs/etc rootfs/proc rootfs/sys rootfs/tmp rootfs/usr/bin. Copia BusyBox con: cp -r busybox-1.36.0/_install/* rootfs/. Crea nodos de dispositivo esenciales: sudo mknod rootfs/dev/console c 5 1; sudo mknod rootfs/dev/null c 1 3. Emplearemos initramfs para arrancar en memoria volátil y simplificar el flujo de pruebas.
Script init. Crea un archivo rootfs/init con el contenido: shebang de sh; mount -t proc none /proc; mount -t sysfs none /sys; mount -t devtmpfs none /dev; export PATH=/bin:/sbin:/usr/bin:/usr/sbin; export SHELL=/bin/sh; echo Minimal Linux environment ready; exec /bin/sh. Marca como ejecutable con: chmod +x rootfs/init.
Compilación e integración del módulo. En la raíz del proyecto compila el módulo con: make KDIR=linux-6.6 cross-compile. Verifica con: file hello_module.ko. Copia al rootfs: cp hello_module.ko rootfs/. Genera el initramfs: cd rootfs; find . | cpio -o -H newc | gzip > ../initramfs.cpio.gz; cd ..
Prueba en QEMU. Arranca el sistema emulado con: qemu-system-aarch64 -M virt -cpu cortex-a53 -smp 2 -m 1G -kernel linux-6.6/arch/arm64/boot/Image -initrd initramfs.cpio.gz -append console=ttyAMA0 earlycon=pl011,0x9000000 init=/init -nographic -no-reboot. Esta orden levanta una VM ARM64 genérica virt con 2 cores y 1 GB de RAM, usa PL011 para consola y ejecuta init como primer proceso en modo texto.
Pruebas en tiempo de ejecución del módulo. Carga y valida con: insmod hello_module.ko; dmesg | tail -3. Desinstala con: rmmod hello_module; dmesg | tail -3.
Logros del ejercicio. Hemos compilado en cruz un kernel ARM64, construido un rootfs mínimo con BusyBox, creado un initramfs funcional, escrito y compilado un módulo externo, iniciado una VM ARM64 con QEMU y cargado el módulo personalizado. Embedded Linux no es tan temible cuando sigues un flujo ordenado.
Si necesitas llevar estos procesos a producción, integrar pipelines CI en servicios cloud aws y azure o crear herramientas internas, en Q2BSTUDIO desarrollamos aplicaciones a medida y software a medida de extremo a extremo. Descubre cómo planificamos, diseñamos y construimos soluciones robustas aquí: desarrollo de aplicaciones y software multiplataforma, y cómo desplegarlas de forma escalable con nuestros servicios cloud en Azure y AWS.
Además, somos especialistas en inteligencia artificial, ia para empresas, agentes IA, ciberseguridad, servicios inteligencia de negocio y power bi, para que tus soluciones embebidas se conecten con analítica avanzada, detección de anomalías y protección extremo a extremo dentro de ecosistemas modernos.
Recursos recomendados: documentación del kernel de Linux, BusyBox, emulación ARM en QEMU, guía de módulos externos, guía de programación de módulos y gcc cross compiler para ARM.