HeadlinesBriefing favicon HeadlinesBriefing.com

GCC verschachtelte Funktionen Trampolin Stack Optimierung

Hacker News •
×

GCC Verschachtelte Funktionen Trampolin Stack Optimierung. Indirekter Aufruf von verschachtelten Funktionen in GCC ohne ausführbaren Stack. Martin Uecker, 2026-08-29.

Wir haben letztes Mal besprochen, wie man verschachtelte Funktionen in GCC und Clang für Callbacks verwenden kann, ohne einen ausführbaren Stack zu benötigen. Aber was ist, wenn man ältere Versionen von GCC unterstützen muss? Natürlich kann man einfach einen ausführbaren Stack akzeptieren, aber es ist auch möglich, dies mit einem Hack zu vermeiden. GCC: Verschachtelte Funktionen und Trampoline.

Lassen Sie uns zuerst besprechen, wie GCC das Nehmen der Adresse einer verschachtelten Funktion unterstützt. Unser Spielzeugbeispiel ohne die Verwendung der neuen Makros ist unten gezeigt. Auf x86_64 platziert der generierte Assembler einen Trampolin auf dem Stack und ruft ihn sofort über die inline-Funktion baz auf.

Der Trampolin ist eine kurze Code-Sequenz, die das statische Frame-Register in eine Struktur auf dem Stack lädt, die die erfassten Variablen der Elternfunktion enthält, und dann zur lokalen Funktion springt. Wenn man die Konstanten -17591, -17847 und -1864106167 zurück in Assembler-Anweisungen übersetzt, erhält man den folgenden x86_64-Code: movq $bar.0, %r11, movq $frame, %r10, jump *%r11. Sowohl die statische Kette als auch die Code-Adresse sind unmittelbare Konstanten, die von Move-Anweisungen im Code des Trampolins verwendet werden.

Anstatt die Adresse des Trampolins zu verwenden, um die Funktion aufzurufen, können wir die Code-Adresse und die statische Kette aus dem Trampolin extrahieren und sie mit der eingebauten Funktion __builtin_call_with_static_chain verwenden, um die lokale Funktion direkt aufzurufen. Somit können wir das Lesen dieser beiden Zeigerwerte aus einem Trampolin als Fallback-Mechanismus in älteren Versionen von GCC verwenden. Die Nachteile sind, dass ein Trampolin immer noch erstellt wird, der Compiler den indirekten Aufruf immer noch nicht devirtualisieren kann und der Stack immer noch als ausführbar markiert wird.

Was wurde also gewonnen? Da wir den Trampolin nie tatsächlich aufrufen, können wir den Stack mit dem folgenden Befehl wieder nicht-ausführbar machen, was zumindest die Sicherheitsbedenken dieser Funktion adressiert. patchelf --clear-execstack program. Diese Idee ist in meiner experimentellen Bibliothek noplate implementiert, wo ein breiter Zeiger aus der Code-Adresse und der statischen Kette konstruiert wird. Trampoline als Funktionsdeskriptoren.

Es gibt eine weitere Idee, die ich für erforschenswert halte: Man könnte auch den Trampolin selbst als Funktionsdeskriptor verwenden. Anstatt die statische Kette und den Code-Zeiger dort zu extrahieren, wo der Trampolin erstellt wird, übergeben wir einfach die Adresse des Trampolins wie gewohnt. Aber an allen Stellen, an denen wir den Trampolin aufrufen könnten, prüfen wir zuerst, ob der Zeiger auf einen Trampolin zeigt, und extrahieren dann die Code-Adresse und die statische Kette, um die verschachtelte Funktion direkt mit __builtin_call_with_static_chain aufzurufen.

In gewissem Sinne könnte man sagen, dass wir anstatt den Trampolin aufzurufen, den Code des Trampolins an der Aufruferstelle mit einem super einfachen Interpreter interpretieren, der nur diese spezifische Code-Sequenz interpretieren kann und so einfach ist, dass...