6 points par GN⁺ 2026-03-30 | 2 commentaires | Partager sur WhatsApp
  • Définit l’initrd comme une unité de programme que le noyau interprète et exécute directement, et réinterprète Linux comme une sorte d’interpréteur
  • Construit une distribution Linux récursive qui redémarre sur elle-même à l’aide de kexec, base64 et cpio, l’initrd se réexécutant lui-même
  • Si le script /init est configuré pour afficher sa propre image cpio, on obtient un initrd auto-répliquant de type Quine
  • Explique, via la structure d’exécution ELF, ld.so et binfmt_misc, une hiérarchie d’interpréteurs qui se prolonge jusqu’au noyau
  • En utilisant kexec ou QEMU, il devient possible d’exécuter récursivement un autre Linux au-dessus de Linux de manière tail-récursive, étendant expérimentalement les frontières entre noyau, virtualisation et interpréteur

Rétro-ingénierie de rkx.gz et structure d’un initrd auto-récursif

  • La commande curl https://astrid.tech/rkx.gz | gunzip | sudo sh télécharge et exécute un script shell de 20 Mo encodé en base64
    • Le script vérifie les privilèges root et la présence de kexec, base64 et cpio
    • Il décode les données base64 pour créer une archive cpio nommée r, puis en extrait une image de noyau nommée k
    • Il utilise kexec pour charger et exécuter k comme noyau et r comme ramdisk
  • À l’intérieur de r.cpio se trouvent /bin, /init et le fichier k, où k est une image du noyau Linux 6.18.18 et /init un script shell
    • /init monte /proc, regroupe ensuite le système de fichiers courant en cpio dans /r, puis relance /k et /r via kexec
    • On obtient ainsi une distribution Linux récursive qui redémarre continuellement sur elle-même

Le noyau Linux vu comme un interpréteur

  • L’initrd n’est pas qu’un simple ramdisk de démarrage, mais peut être vu comme un programme que le noyau Linux interprète et exécute
    • Comme curl | sh ou python3 script.py, l’initrd est lui aussi une forme de programme d’entrée exécuté par le noyau
    • Le noyau Linux fonctionne donc comme un interpréteur de l’initrd
  • Cette structure ressemble à une optimisation de récursion terminale (tail-call optimization)
    • kexec ne remplace pas le noyau précédent en l’écrasant, mais charge et exécute le nouveau dans un nouvel espace mémoire
    • Chaque noyau ne conserve pas l’état précédent et est remplacé par une nouvelle « stack frame »

Quine et auto-réplication de l’initrd

  • Un Quine désigne un programme qui affiche sa propre source
    • Si le script /init exécute cat /r à la fin, il affiche un cpio identique à lui-même
    • Dans ce cas, on obtient un Quine de l’interpréteur initrd de Linux
    • Tous les fichiers existent en RAM sur tmpfs, donc aucune E/S disque réelle n’a lieu

ELF, ld.so et la hiérarchie des interpréteurs

  • Un exécutable ELF inclut dans son en-tête le chemin de l’interpréteur (ld-linux-x86-64.so.2)
    • Lors de l’exécution, le noyau lance d’abord ld.so, puis ld.so charge les bibliothèques dynamiques de l’ELF avant d’exécuter le programme
    • On peut donc aussi voir ELF comme une sorte de langage interprété
  • /bin/sh est interprété par ld.so, et ld.so est lui-même interprété directement par le noyau
    • ld.so étant un ELF lié statiquement, le noyau peut l’exécuter directement
    • Cela forme ainsi le cas de base de la hiérarchie des interpréteurs

Exécution de CPIO via binfmt_misc

  • Avec binfmt_misc, il est possible d’exécuter via un interpréteur désigné des fichiers possédant des octets magiques spécifiques
    • Exemple de commande d’enregistrement :
      echo ':cpio:M::\x30\x37\x30\x37\x30\x31::/path/to/my/script.sh:' > /proc/sys/fs/binfmt_misc/register
      
    • Avec ce réglage, un fichier CPIO marqué avec chmod +x peut être exécuté directement
  • On peut enregistrer comme interpréteur un script qui exécute un CPIO comme initrd via QEMU
    • QEMU démarre une machine virtuelle à l’aide du noyau et de l’initrd spécifiés
    • En conséquence, l’interpréteur du fichier CPIO devient le noyau Linux lancé par QEMU

Interpréteurs récursifs et « la boucle la plus étrange »

  • Un interpréteur fondé sur QEMU crée une nouvelle stack frame d’environnement Linux
    • Dans cette structure, Linux en exécute un autre, et l’imbrication peut se poursuivre jusqu’aux limites de la mémoire
    • En le remplaçant par un interpréteur fondé sur kexec, on peut obtenir une exécution récursive de Linux avec optimisation d’appel terminal
  • Si l’on enregistre binfmt_misc dans /init puis qu’on configure l’exécution de /r, on obtient un initrd qui s’exécute lui-même
    • /r est le prochain processus init au format CPIO et, lors de son exécution, il se réinterprète à nouveau

Conclusion

  • L’initrd n’est pas un simple outil de démarrage, mais une unité de programme interprétée par le noyau Linux
  • Avec kexec et binfmt_misc, il devient possible d’exécuter Linux lui-même récursivement comme un interpréteur
  • Cette structure est un concept expérimental qui brouille les frontières entre noyau, virtualisation, interpréteur et programme auto-répliquant
  • Le code source associé est publié dans le dépôt GitHub ifd3f/rekexec

2 commentaires

 
github88 2026-03-31

L’ignorance donne du courage, dit-on… J’aimerais qu’on évite ce genre d’articles.

 
GN⁺ 2026-03-30
Réactions sur Hacker News
  • J’ai souffert en lisant ce billet à cause du trop grand nombre de malentendus
    Une archive cpio n’est pas un système de fichiers. L’auteur utilise initramfs, qui repose sur tmpfs. Linux peut extraire un cpio vers tmpfs. Une archive de fichiers et de répertoires n’est pas, en soi, un programme
    Ce n’est pas parce que deux choses se ressemblent qu’elles sont identiques. Un programme binaire s’exécute sur le CPU et, s’il y a un interpréteur, il est caché dans l’environnement matériel. Cela sort du périmètre du noyau
    Pour exécuter un script shell, il faut un shell qui l’interprète. L’auteur passe ce point sous silence et confond le noyau avec le programme shell
    Linux peut être compilé sans initramfs ni ramdisk, et peut quand même exécuter un userland situé sur un système de fichiers
    L’expression « Linux initrd interpreter » est une description vraiment erronée

    • Un fichier ELF non plus n’est pas forcément un programme en soi. Certains ELF sont des bibliothèques dynamiques sans point d’entrée. De même que certains ELF sont exécutables, on peut considérer que certains CPIO le sont aussi. Au fond, le fait que ld.so charge un ELF en mémoire puis exécute son point d’entrée, et le fait que le noyau décompresse un initramfs puis exécute son point d’entrée, relèvent d’une idée assez similaire
    • Le fichier init dans le cpio est le vrai programme interprété, et les autres fichiers servent de mémoire que ce programme utilise
    • Un programme binaire s’exécute sur le CPU, mais le fichier programme lui-même est une structure d’archive composée de plusieurs sections. Le CPU ne comprend pas directement le fichier programme. Linux met en place l’espace d’adressage dans lequel le programme va s’exécuter, puis saute à l’adresse pointée par le compteur ordinal. Les sections de métadonnées d’ELF définissent ce processus
    • Au moins, on peut se consoler en se disant que ce n’est pas un texte écrit par une IA
  • Tous les OS ne jouent-ils pas le rôle d’interpréteurs de code machine avec les privilèges du noyau ?

    • Je ne pense pas. L’OS n’interprète pas directement chaque instruction ; il les transmet au CPU pour exécution
    • L’OS est une interface qui permet d’utiliser les ressources système. Le CPU interprète le code machine, et l’OS peut indiquer au CPU quoi exécuter
    • Dans ce cas, on peut le voir comme un interpréteur pour les fichiers CPIO
  • Ce billet fonctionne si on prend « Linux est un interpréteur » comme un modèle mental, mais c’est faux si on le prend au pied de la lettre
    Il est plus juste d’y voir le rôle du noyau comme une orchestration des formats exécutables comme ELF, les scripts shebang ou initramfs, plutôt qu’une interprétation au niveau des instructions CPU. La confusion semble venir du mélange entre deux sens du mot « interpréteur »

  • L’essentiel n’est pas de savoir si la métaphore est correcte, mais de montrer à quel point la notion d’« exécution » dépend de son environnement

  • « Tout est interpréteur ? »

    • Oui, mais le compilateur fait exception
  • Le Theta Combinator de Turing

    • Je ne vois pas bien le rapport avec ce billet. Je ne suis pas familier avec les concepts de programmation fonctionnelle
  • Dans un précédent billet de la série, l’auteur disait avoir fabriqué lui-même une image VPS parce qu’il ne voulait pas utiliser le stockage objet de Contabo
    Je pense qu’il existe un juste milieu entre passer 50 heures pour économiser 1,50 $ par mois et dépenser 250 000 $ en tokens.
    Si l’on n’arrive pas à assumer les coûts d’infrastructure, le problème relève peut-être davantage de facteurs sociaux que de compétences techniques. S’acharner à faire tourner Doom avec curl ne me semble pas productif

    • Moi aussi, j’étais comme ça avant. Les 5 € par mois d’un VPS me semblaient trop chers, alors j’arrêtais l’instance et je sauvegardais le système de fichiers racine sur l’ordinateur portable de ma mère jusqu’à avoir assez d’argent. Plus tard, j’ai installé Terminal IDE sur un Kindle pour m’amuser avec busybox et gcc. Merci à Spartacus Rex de m’avoir aidé à lancer ma carrière
    • Ce que disait l’auteur était une plaisanterie. La vraie raison est donnée dans le paragraphe suivant : « Je trouvais ça amusant comme astuce, et je me suis dit qu’en le mettant sur le blog, j’apprendrais quelque chose, les lecteurs apprendraient aussi, et j’y gagnerais des points sur internet : win-win »
    • Même si cela peut sembler improductif à certaines personnes, profiter de ses centres d’intérêt particuliers est important pour la santé mentale. Pour moi qui ai un TDAH, c’est même une activité nécessaire
    • Dire que « si tu ne peux pas payer 1,50 $, tu n’es pas un pro » est étrange. Un professionnel se définit par le fait d’être rémunéré, pas par ses dépenses
  • Dans man ld.so, il est explicitement indiqué que le linker dynamique stocké dans la section .interp d’un ELF est exécuté. Le nom même de la section est intéressant

  • Linux est très utile comme interface programmable. Windows le permet aussi, mais Linux me semble plus adapté
    Je pense que l’interface graphique de Windows est meilleure, mais GNOME ou KDE me mettent aussi mal à l’aise. J’utilise donc fluxbox, icewm, et parfois xfce ou mate-desktop. Ces jours-ci, je préfère les environnements simples et rapides. Je fais l’essentiel de mon travail en ligne de commande et en éditant du code

    • Si tu veux un environnement rapide et simple, la combinaison Sway + foot est excellente. Avec des raccourcis clavier pour organiser les espaces de travail, on peut être très à l’aise même sans bureau classique
    • Je ne suis pas d’accord pour dire que l’interface graphique de Windows est meilleure. GNOME et KDE ne me convainquent pas non plus, mais avec Windows on ne peut pas échapper au WM complexe de Microsoft. Personnellement, je trouve les interfaces de type mpx/mux (par ex. 9wm, cwm, dwm) bien meilleures que celles de la lignée Xerox. Elles sont plus proches de la philosophie d’Engelbart et globalement plus propres