URL: https://linuxfr.org/users/claudex/journaux/comment-rendre-le-shebang-plus-festif Title: Comment rendre le shebang plus festif Authors: claudex Date: 2019年10月12日T18:44:58+02:00 License: CC By-SA Tags: shebang Score: 69 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 <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 : ```diff --- 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 : . # 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. ![Screenshot busybox](https://www.claudex.be/owncloud/index.php/apps/files_sharing/publicpreview/M8N5M8x8RiT287M?x=1904&y=551&a=true&file=Screenshot_20191012_182447.png&scalingup=0) Le contenu du `beerbang.sh` : ~~~bash 🍻/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 !

AltStyle によって変換されたページ (->オリジナル) /