Sommaire
Suite à une discussion avec un collègue, je me suis lancé dans un correctif du noyau pour remplacer le shebang (#!
) par un caractère bien plus adapté : 🍻. Il faut savoir que le shebang est lu par le noyau. Quand on demande d’exécuter un fichier à ce dernier, il essaye toutes les méthodes qu’il connait (script, ELF) jusqu’à ce qu’il y en ait une qui fonctionne. Une de ces méthodes est le script où il exécute le binaire donné par le shebang et passe le script en paramètre.
Mise en place
Compilation d’un noyau vanilla
Déjà, la première chose, c’est de vérifier que j’arrive encore à compiler un noyau. Et surtout que j’arrive à l’amorcer. Comme j’ai un pressentiment que ça peut poser problème dans l’amorçage d’un système classique, je préfère utiliser une machine virtuelle.
Donc, première étape, make oldconfig
et make -j16
. Bon, depuis la dernière fois que j’ai compilé un noyau, il y a des modules signés. Comme je ne compte pas utiliser de module, je désactive la fonctionnalité de signature avec make menuconfig
dans le menu « Enable loadable module support » et les clefs dans « security options → Trusted keys ». Bien, ça compile.
Deuxième étape comment compiler un noyau sans l’installer sur le système (ce que 98,7 % des tutoriels sur le sujet font) ? Après quelques recherches, il suffit de faire un make bzImage
. Le noyau généré se trouve dans arch/x86/boot/bzImage
.
QEMU permet d’amorcer directement un noyau sans devoir passer par un gestionnaire d’amorçage (bootloader). C’est pratique, ça va me simplifier mes tests. Je peux donc lancer la commande suivante :
qemu-system-x86_64 -kernel arch/x86/boot/bzImage
Et ça démarre, avec un kernel panic parce qu’il ne trouve pas le système de fichiers racine (rootfs), ce qui est plutôt rassurant vu que je ne lui en ai pas donné.
QEMU permet d’utiliser un initrd aussi, mais vu ma configuration assez simple, je préfère m’en passer. Et comme je n’oublie jamais rien, je me dis que je compilerai les modules dont j’ai besoin en statique.
Amorçage avec BusyBox
Donc, il me faut un programme d’initialisation, de préférence simple et sans script Shell. Il me faut aussi un système minimaliste avec un Shell et quelques binaires comme ls
et cat
pour pouvoir déboguer. Le job parfait pour BusyBox. Donc, je crée une image qcow2 avec virt-make-fs
, qui contient BusyBox et un script shell basique pour vérifier que ça fonctionne, et un lien symbolique pour avoir un /bin/sh
.
cat > hostdir/shebang.sh <<EOF
#!/bin/sh
echo test
EOF
cp -i /bin/busybox hostdir/
mkdir hostdir/bin
cd hostdir/bin
ln -s ../busybox sh
cd ../..
virt-make-fs --partition --format=qcow2 --size=+200M hostdir hostimg.qcow2
N. D. M. : commandes modifiées suites aux remarques dans les commentaires.
Je peux donc amorcer avec :
qemu-system-x86_64 -kernel arch/x86/boot/bzImage -append "root=/dev/sda1 init=/busybox sh" -hda hostimg.qcow2
Attention à bien mettre le sh
à la fin de la ligne d’amorçage, sinon BusyBox affiche l’aide et quitte, et le noyau panique parce que le processus numéro 1 (PID 1) est parti.
Après quelques essais pour trouver les modules à compiler en statique pour avoir le pilote pour le disque (et le pilote pour ext4 que j’avais oublié), j’ai donc un système qui démarre avec un shell et mon script de test fonctionne (mais ce n’est pas surprenant).
Correctif
Je décide d’abord de modifier un seul caractère du shebang pour vérifier que c’est bien possible, mon shebang sera donc ##
. Je trouve le fichier où le shebang est défini grâce à Google : fs/binfmt_script.c
. Pratique, on dirait qu’il suffit de changer un caractère. Je fais donc la modification suivante :
--- a/fs/binfmt_script.c
+++ b/fs/binfmt_script.c
@@ -39,7 +39,7 @@ static int load_script(struct linux_binprm *bprm)
int retval;
/* Not ours to exec if we don't start with "#!". */
- if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
+ if ((bprm->buf[0] != '#') || (bprm->buf[1] != '#'))
return -ENOEXEC;
/*
Et je peux tester avec un nouveau script qui comment par ##/bin/sh
. Il fonctionne bien et un script avec un #!/bin/sh
renvoie une erreur. Je peux donc passer à l’étape suivante, un shebang sur quatre caractères, vu que l’émoji prend quatre octets en UFT-8 : f0 9f 8d bb
. Comme il y a des opérations sur le tampon qui contient la ligne qui sont faites en décalant le démarrage en se basant sur la longueur de deux octets, on remplace ça par un quatre :
--- a/fs/binfmt_script.c
+++ b/fs/binfmt_script.c
@@ -39,7 +39,8 @@ static int load_script(struct linux_binprm *bprm)
int retval;
/* Not ours to exec if we don't start with "#!". */
- if ((bprm->buf[0] != '#') || (bprm->buf[1] != '#'))
+ // 🍻 f0 9f 8d bb
+ if ((bprm->buf[0] != '\xf0') || (bprm->buf[1] != '\x9f') || (bprm->buf[2] != '\x8d' || bprm->buf[3] != '\xbb'))
return -ENOEXEC;
/*
@@ -73,7 +74,7 @@ static int load_script(struct linux_binprm *bprm)
buf_end = bprm->buf + sizeof(bprm->buf) - 1;
cp = strnchr(bprm->buf, sizeof(bprm->buf), '\n');
if (!cp) {
- cp = next_non_spacetab(bprm->buf + 2, buf_end);
+ cp = next_non_spacetab(bprm->buf + 4, buf_end);
if (!cp)
return -ENOEXEC; /* Entire buf is spaces/tabs */
/*
@@ -93,7 +94,7 @@ static int load_script(struct linux_binprm *bprm)
else
break;
}
- for (cp = bprm->buf+2; (*cp == ' ') || (*cp == '\t'); cp++);
+ for (cp = bprm->buf+4; (*cp == ' ') || (*cp == '\t'); cp++);
if (*cp == '\0')
return -ENOEXEC; /* No interpreter name found */
i_name = cp;
On recompile, relance la machine virtuelle et l’on peut tester avec un script qui contient le nouveau shebang.
Vous pouvez retrouver les deux correctifs ici : https://github.com/claudex/linux-shebang/commits/master.
Et ça marche ?
Premièrement, le sh de BusyBox (et sans doute tous les shells) n’aime pas que la première ligne ne commence pas par #
. On se retrouve avec l’erreur « ./beerbang.sh: line 1: 🍻/bin/sh not found.
» car, vu que la ligne ne commence pas par #
, il ne la traite pas comme un commentaire et donc essaye de l’exécuter et, évidemment, il ne trouve pas de commande pareille à exécuter. Mais il exécute le reste du script sans problème.
Le contenu du beerbang.sh
:
🍻/bin/sh
echo "beers!"
Un autre point, mais qui relève plus du détail. Comme on fait de l’Unicode, il faudrait normalement canoniser la séquence de caractères, car il y a peut‐être d’autres façons de l’écrire. Je laisse l’exercice au lecteur.
Et l’intérêt de la chose dans tout ça ?
Strictement aucun !
# Tout ça pour rien
Posté par claudex . Évalué à 10.
En fait, c'est déjà possible via binfmt-misc https://en.m.wikipedia.org/wiki/Binfmt_misc#Registration et un programme userland qui fait le tri pour envoyer au bon interpréteur.
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
[^] # Re: Tout ça pour rien
Posté par Benoît Sibaud (site web personnel) . Évalué à 10.
Avec des 🍻 et des |, tu pourrais faire un biéroduc peut-être ? Ou une version pour Wine ? Par contre ça serait plus sympa de faire un shell qui dégrade peu à peu les affichages, en les rendant flous, puis mouvants, et de remplacer les bips par des burps. Mais ça n'aurait pas plus d'intérêt.
# Sympa !
Posté par barmic 🦦 . Évalué à 10.
Je ne suis pas d'accord. La démarche est très cool. Jouer avec ce qui nous entour c'est le meilleur moyen de les comprendre et de se les approprier. C'est une excellente démarche en info comme dans tous les domaines (prendre le temps de regarder un peu plus en détail notre moyen de locomotion par exemple).
Après je ne suis pas très bière donc je ne cautionnerais pas :p
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
[^] # Re: Sympa !
Posté par claudex . Évalué à 0. Dernière modification le 12 octobre 2019 à 22:47.
Ça aurait été une partie obscure du kernel, ça aurait pu être intéressant pour comprendre son fonctionnement. Là, on se rend juste compte que ça correspond à ce qui est connu et documenté un peu partout.
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
[^] # Re: Sympa !
Posté par Thomas Debesse (site web personnel) . Évalué à 10.
Oui et non, quand je vois le journal je vois que l’auteur a du savoir faire tout ça :
Typiquement cela pourrait être un projet de fin de formation pour valider que les personnes formées sont prêtes à rejoindre une équipe qui fait du développement de système embarqué, étant considéré que les compétences « développement en tel ou tel langage » et « connaissance approfondie de tel ou tel noyau » n’entrent pas dans le cadre de cette formation (cela peut faire l’objet d’autres formations).
ce commentaire est sous licence cc by 4 et précédentes
[^] # Re: Sympa !
Posté par Jarvis . Évalué à 7.
On apprend aussi que ce n'est pas un hasard si le shebang commence par un dièse et non par 🍻.
[^] # Re: Sympa !
Posté par claudex . Évalué à 3. Dernière modification le 13 octobre 2019 à 08:21.
Je ne suis pas sûr que ce soit pour ça qu'il commence par un dièse. Mais comme ça fait tellement longtemps que c'est comme ça, les interpréteurs s'attendent à se comportement et pas un autre.
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
[^] # Re: Sympa !
Posté par claudex . Évalué à 4.
D'ailleurs, il faudrait trouver un langage interprété qui n'a pas le dièse comme début de commentaire pour voir comment il gère ce cas là.
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
[^] # Re: Sympa !
Posté par barmic 🦦 . Évalué à 1.
lisp par exemple.
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
[^] # Re: Sympa !
Posté par claudex . Évalué à 4.
Du coup, il y a bien un méchannisme spécifique pour le shebang https://clisp.sourceforge.io/impnotes/quickstart.html#quickstart-unix (mais je ne connais pas assez le lisp pour lire de code relatif à ça).
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
[^] # Re: Sympa !
Posté par wismerhill . Évalué à 4.
Java, depuis la version 11:
https://openjdk.java.net/jeps/330
ils ont prévu un cas particulier pour le shebang
[^] # Re: Sympa !
Posté par Oliver (site web personnel) . Évalué à 6.
L'interpréteur PHP sait que le
#!
en tout début d'un fichier correspond au shebang et qu'il faut ignorer cette première ligne. D'après la documentation cela ne fonctionnerait pas sous Windows…La documentation en français :
https://www.php.net/manual/fr/features.commandline.usage.php
Exemple :
On modifie la ligne du shebang :
On remplace le shebang
#!
par un commentaire shell#
sans le caractère!
. Cette fois-ci l'interpréteur PHP ne reconnaît pas le shebang et afficher la ligne :Commentaire sous licence Creative Commons Zero CC0 1.0 Universal (Public Domain Dedication)
[^] # Re: Sympa !
Posté par barmic 🦦 . Évalué à 4.
Hé hé tu joue les blasé. Si tu n'y voyais si peux d'intérêt, tu n'aurais pas pris le temps d'en faire un journal ;)
Ton journal montre beaucoup de compétences utiles. Les partager au travers d'un cadre trivial permet à ceux qui s'y connaissent moins de découvrir et de pouvoir reproduire.
Ça a beaucoup plus d'intérêt que de passer le triple de temps pour savoir quel type de personne est RMS. En tout cas c'est mon point de vue
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
[^] # Re: Sympa !
Posté par deuzene (site web personnel) . Évalué à 10.
Toi t'as abusé du shebang ;)
« Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. »
[^] # Re: Sympa !
Posté par WrathOfThePixel . Évalué à 10.
Je n'avais aucune idée que le shebang remontait jusqu'au noyau. je pensais que c'était du ressort du shell. Du coup j'ai appris un truc. Donc interet. Meme si ça me sert pas à grand chose de savoir ça au fond…
[^] # Re: Sympa !
Posté par claudex . Évalué à 10.
Ça a quand même une importance. Cela veut dire que quelque soit le shell que tu utilise, tu auras ce même comportement (vu que ça ne dépend pas du shell). Mais ça veut aussi dire que si tu utilise autre chose qu'un shell pour lancer ton script (comme le lancer depuis un programme C, Python, Java), tu auras ce comportement. Et même si tu ne programme pas, si un programme te demande un binaire à exécuter, tu peux lui donner un script exécutable avec un shebang et ça fonctionnera.
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
# Erreurs ?
Posté par Jean-Philippe Garcia Ballester (site web personnel) . Évalué à 4. Dernière modification le 13 octobre 2019 à 06:22.
Dans la partie « Boot avec busybox », deux choses m'intriguent.
Tu crée un fichier
/tmp/test
mais tu ne l'utilise jamais. J'imagine que tu voulais le créer danshostimg
?Quand tu utilises
virt-make-fs
tu l'appelles avec pour paramètrehostdir
que tu n'as jamais utilisé avant. J'imagine que tu voulais utiliserhostimg
?[^] # Re: Erreurs ?
Posté par claudex . Évalué à 3.
Effectivement
En fait, je voulais tout faire dans un dossier hostdir. J'ai fourché quand j'ai réécris les commandes de tête.
Je vais corriger ça, merci.
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
# Intérêt de la chose
Posté par flan (site web personnel) . Évalué à 10.
Il faut se préparer à l’hypothèse d’un monde dans lequel tous les # auront été consommés sans qu’on sache en reconstruire, mais avec des 🍻 en abondance !
# Aller plus vite !
Posté par _kaos_ . Évalué à 7.
Salut,
Juste une petite remarque sur le
make oldconfig
, qui j'espère est toujours d'actualité (pas vérifié).Du temps où je compilais mon noyau, plutôt que de faire un certain nombre de fois
y
aux nouvelles questions posées, j'utilisais la commandeyes
. Ce qui donne :Et hop, plus de questions !
Et si ça ne boote pas, retour en arrière pour inspecter les questions plus précisément.
Matricule 23415
# Sondage
Posté par aiolos . Évalué à 7.
C'est malin, maintenant il faudrait rajouter une entrée au sondage :
[ ] je voulais changer mon shebang
:D
(Même si techniquement, ce pourrait être
[x] je voulais tester ou appliquer un patch, pour le commun des mortels
[x] le noyau précompilé de ma distribution ne répondait pas à mes besoins, pour Xavier Claude)
Plus sérieusement, j'ai beaucoup aimé le journal, merci.
# Mais pourquoi pas le #
Posté par J Avd . Évalué à 3.
Bon ton journal est TRèS intéressant
Mais pourquoi tant de N envers ce pauvre # ?
Tu pourrais très bien mettre #🍻 comme shebang et ça marcherait sans aucun problèmes…
# pour le commentaire
🍻 pour signifier que tu veux consommer … un script
"Gentoo" is an ancient african word, meaning "Read the F*ckin' Manual". "Gentoo" also means "I am what I am because you all are freaky n3rdz"
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.