HeadlinesBriefing favicon HeadlinesBriefing.com

Optimisation pile trampoline fonctions imbriquées GCC

Hacker News •
×

Optimisation de la Pile Trampoline pour Fonctions Imbriquées GCC. Appel Indirect de Fonctions Imbriquées sur GCC Sans Pile Exécutable. Martin Uecker, 2026-08-29.

Nous avons discuté la dernière fois de la façon dont on peut utiliser des fonctions imbriquées sur GCC et Clang pour les rappels sans nécessiter de pile exécutable. Mais que faire si l'on doit prendre en charge d'anciennes versions de GCC ? Bien sûr, on peut simplement accepter une pile exécutable, mais il est également possible de l'éviter avec un piratage. GCC : Fonctions Imbriquées et Trampolines.

Discutons d'abord de la façon dont GCC prend en charge la prise d'adresse d'une fonction imbriquée. Notre exemple jouet sans l'utilisation des nouvelles macros est présenté ci-dessous. Sur x86_64, l'assemblage généré place un trampoline sur la pile et l'invoque immédiatement via la fonction baz inline.

Le trampoline est une courte séquence de code qui charge le registre de frame statique vers une structure sur la pile qui contient les variables capturées de la fonction parente et saute ensuite vers la fonction locale. Si l'on traduit les constantes -17591, -17847 et -1864106167 en instructions d'assemblage, on obtient le code x86_64 suivant : movq $bar.0, %r11, movq $frame, %r10, jump *%r11. La chaîne statique et l'adresse de code sont toutes deux des constantes immédiates utilisées par les instructions de déplacement dans le code du trampoline.

Au lieu d'utiliser l'adresse du trampoline pour appeler la fonction, nous pouvons extraire l'adresse de code et la chaîne statique du trampoline et les utiliser avec la fonction intégrée __builtin_call_with_static_chain pour appeler la fonction locale directement. Ainsi, nous pouvons utiliser la lecture de ces deux valeurs de pointeur à partir d'un trampoline comme mécanisme de repli dans les anciennes versions de GCC. Les inconvénients sont qu'un trampoline est toujours créé, le compilateur ne peut toujours pas dévirtualiser l'appel indirect, et la pile sera toujours marquée comme exécutable.

Alors, qu'a-t-on gagné ? Puisque nous n'invoquons jamais réellement le trampoline, nous pouvons rendre la pile non-exécutable à nouveau avec la commande suivante, ce qui répond au moins aux préoccupations de sécurité de cette fonctionnalité. patchelf --clear-execstack program. Cette idée est implémentée dans ma bibliothèque expérimentale, noplate, où un pointeur large est construit à partir de l'adresse de code et de la chaîne statique. Trampolines en tant que Descripteurs de Fonction.

Il y a une autre idée que je trouve digne d'exploration : on pourrait également utiliser le trampoline lui-même comme descripteur de fonction. Au lieu d'extraire la chaîne statique et le pointeur de code là où le trampoline est créé, nous transmettons simplement l'adresse du trampoline comme d'habitude. Mais partout où nous pourrions appeler le trampoline, nous vérifions d'abord si le pointeur pointe vers un trampoline, puis extrayons l'adresse de code et la chaîne statique pour appeler la fonction imbriquée directement en utilisant __builtin_call_with_static_chain.

En un sens, on pourrait dire qu'au lieu d'invoquer le trampoline, nous interprétons le code du trampoline au site d'appel en utilisant un interpréteur super simple qui ne peut interpréter que cette séquence de code spécifique et qui est si simple que...