- Define
initrd como una unidad de programa que el kernel interpreta y ejecuta directamente, reinterpretando Linux como una especie de intérprete
- Usa
kexec, base64 y cpio para construir una distribución Linux recursiva que se reinicia a sí misma, donde el initrd se vuelve a ejecutar solo
- Si el script
/init hace que imprima su propia imagen cpio, se forma un initrd autorreplicante con forma de Quine
- Explica, a través de la estructura de ejecución ELF,
ld.so y binfmt_misc, una arquitectura donde las capas de intérpretes se extienden hasta el kernel
- Con
kexec o QEMU, es posible ejecutar otro Linux sobre Linux de forma recursiva en cola, ampliando de manera experimental los límites entre kernel, virtualización e intérpretes
Ingeniería inversa de rkx.gz y estructura de initrd autorrecursiva
- El comando
curl https://astrid.tech/rkx.gz | gunzip | sudo sh descarga y ejecuta un script de shell codificado en base64 de 20 MB
- El script verifica permisos de root y comprueba la existencia de
kexec, base64 y cpio
- Decodifica los datos base64 para crear un archivo
cpio llamado r, y extrae de él una imagen de kernel llamada k
- Usa
kexec para cargar y ejecutar k como kernel y r como ramdisk
- Dentro de
r.cpio están incluidos /bin, /init y el archivo k; k es una imagen del kernel Linux 6.18.18, y /init tiene forma de script de shell
/init monta /proc, empaqueta el sistema de archivos actual como cpio en /r, y luego vuelve a ejecutar /k y /r con kexec
- El resultado es una distribución Linux recursiva que se reinicia continuamente a sí misma
La perspectiva de ver el kernel Linux como un intérprete
initrd no es solo un ramdisk de arranque, sino que puede verse como un programa que el kernel Linux interpreta y ejecuta
- Igual que
curl | sh o python3 script.py, initrd también es una forma de programa de entrada ejecutado por el kernel
- Por lo tanto, el kernel Linux funciona como un intérprete que interpreta
initrd
- Esta estructura se parece a la optimización de recursión de cola (tail-call optimization)
kexec no sobrescribe el kernel anterior, sino que carga y ejecuta uno nuevo en un espacio de memoria nuevo
- Cada kernel no conserva el estado previo, sino que es reemplazado por un nuevo “stack frame”
Quine y autorreplicación de initrd
- Un Quine es un programa que imprime su propio código
- Si el script
/init ejecuta cat /r al final, imprimirá un cpio idéntico a sí mismo
- En este caso, se forma un Quine del intérprete
initrd de Linux
- Como todos los archivos existen en
tmpfs en RAM, no se produce I/O real de disco
ELF, ld.so y las capas del intérprete
- Un ejecutable ELF incluye en su encabezado la ruta del intérprete (
ld-linux-x86-64.so.2)
- Al ejecutarse, el kernel lanza primero
ld.so, y ld.so carga las bibliotecas dinámicas del ELF antes de ejecutar el programa
- Por eso, ELF también puede verse como una especie de lenguaje interpretado
/bin/sh es interpretado por ld.so, y ld.so es interpretado directamente por el kernel
- Como
ld.so es un ELF enlazado estáticamente, el kernel puede ejecutarlo directamente
- Así se forma el caso base de la jerarquía de intérpretes
Ejecutar CPIO mediante binfmt_misc
- Con
binfmt_misc se puede ejecutar con un intérprete definido archivos con bytes mágicos específicos
- Se puede registrar como intérprete un script que ejecute CPIO como
initrd usando QEMU
- QEMU arranca una máquina virtual usando el kernel y el
initrd especificados
- Como resultado, el intérprete del archivo CPIO pasa a ser el kernel Linux ejecutado por QEMU
Intérpretes recursivos y “el bucle más extraño”
- Un intérprete basado en QEMU crea un nuevo stack frame de entorno Linux
- Es una estructura donde Linux ejecuta otro Linux, y puede anidarse hasta el límite de memoria
- Si se reemplaza por un intérprete basado en
kexec, se hace posible una ejecución recursiva de Linux optimizada como llamada de cola
- Si en
/init se registra binfmt_misc y se configura para ejecutar /r,
se completa un initrd que se ejecuta a sí mismo
/r es el siguiente proceso init en formato CPIO y, al ejecutarse, vuelve a interpretarse a sí mismo
Conclusión
initrd no es solo una herramienta de arranque, sino una unidad de programa que el kernel Linux interpreta
- Con
kexec y binfmt_misc, es posible ejecutar Linux de forma recursiva como si fuera su propio intérprete
- Esta estructura es un concepto experimental que difumina los límites entre kernel, virtualización, intérpretes y programas autorreplicantes
- El código fuente relacionado está publicado en el repositorio de GitHub ifd3f/rekexec
2 comentarios
Dicen que la ignorancia da valentía... Ojalá se evitaran este tipo de artículos.
Comentarios en Hacker News
Me dolió leer este artículo por la cantidad de malentendidos que tiene
Un archivo cpio no es un sistema de archivos. El autor usa initramfs, que está basado en tmpfs. Linux puede extraer cpio a tmpfs. Un archivo de archivos y directorios no es, por sí mismo, un programa
Que dos cosas se parezcan no significa que sean lo mismo. Un programa binario se ejecuta en la CPU, y si hay un intérprete, está oculto en el entorno de hardware. Eso queda fuera del alcance del kernel
Para ejecutar un script de shell hace falta un shell que interprete ese script. El autor omite esa parte y confunde el kernel con el programa shell
Linux puede compilarse sin initramfs ni ramdisk, y aun así puede ejecutar un userland desde el sistema de archivos
La expresión “Linux initrd interpreter” es una descripción realmente equivocada
¿No hacen todos los OS el papel de intérprete de código máquina con privilegios de kernel?
Este artículo funciona si se toma como un modelo mental de “Linux es un intérprete”, pero es incorrecto si se toma literalmente
Tiene más sentido verlo no como interpretación al nivel de instrucciones de CPU, sino como el papel del kernel coordinando formatos ejecutables como ELF, scripts con shebang e initramfs. La confusión parece venir de mezclar dos significados distintos de “intérprete”
El punto central no es si la analogía es correcta, sino mostrar hasta qué punto el concepto de ejecución depende del entorno
“¿Todo es un intérprete?”
El Theta Combinator de Turing
En una entrada anterior de la serie, el autor dijo que no quería usar el object storage de Contabo, así que se armó sus propias imágenes de VPS
Creo que hay un punto de equilibrio entre gastar 50 horas para ahorrarte 1.50 dólares al mes y gastar 250 mil dólares en tokens.
Si no puedes cubrir los costos de infraestructura, quizá el problema sea más de factores sociales que de capacidad técnica. Obsesionarse con correr Doom desde curl no me parece productivo
Si lees
man ld.so, se indica explícitamente que se ejecuta el enlazador dinámico almacenado en la sección.interpde ELF. El nombre mismo de la sección es interesanteLinux es muy útil como interfaz programable. Windows también puede serlo, pero siento que Linux se presta más para eso
Creo que Windows es mejor en GUI, pero GNOME y KDE también me resultan incómodos. Por eso uso fluxbox, icewm y a veces xfce o mate-desktop. Últimamente prefiero entornos simples y rápidos. La mayor parte del trabajo la hago desde la línea de comandos y editando código