HeadlinesBriefing favicon HeadlinesBriefing.com

Optimización pila trampolín funciones anidadas GCC

Hacker News •
×

Optimización de Pila Trampolín para Funciones Anidadas en GCC. Llamada Indirecta de Funciones Anidadas en GCC Sin Pila Ejecutable. Martin Uecker, 2026-08-29.

Discutimos la última vez cómo se pueden usar funciones anidadas en GCC y Clang para callbacks sin requerir una pila ejecutable. Pero, ¿qué pasa si se necesita soportar versiones antiguas de GCC? Por supuesto, se puede simplemente aceptar una pila ejecutable, pero también es posible evitarlo con un truco. GCC: Funciones Anidadas y Trampolines.

Primero discutamos cómo GCC soporta tomar la dirección de una función anidada. Nuestro ejemplo de juguete sin el uso de las nuevas macros se muestra a continuación. En x86_64, el ensamblador generado coloca un trampolín en la pila e inmediatamente lo invoca a través de la función baz inlineada.

El trampolín es una secuencia de código corta que carga el registro de marco estático a una estructura en la pila que contiene las variables capturadas de la función padre y luego salta a la función local. Si se traducen las constantes -17591, -17847 y -1864106167 de nuevo a instrucciones de ensamblador, se obtiene el siguiente código x86_64: movq $bar.0, %r11, movq $frame, %r10, jump *%r11. Tanto la cadena estática como la dirección de código son constantes inmediatas usadas por instrucciones de movimiento en el código del trampolín.

En lugar de usar la dirección del trampolín para llamar a la función, podemos extraer la dirección de código y la cadena estática del trampolín y usarlas con la función incorporada __builtin_call_with_static_chain para llamar a la función local directamente. Así, podemos usar la lectura de estos dos valores de puntero desde un trampolín como mecanismo de respaldo en versiones antiguas de GCC. Las desventajas son que aún se crea un trampolín, el compilador aún no puede desvirtualizar la llamada indirecta, y la pila seguirá marcada como ejecutable. ¿Qué se ganó entonces? Como nunca invocamos realmente el trampolín, podemos hacer que la pila no sea ejecutable de nuevo con el siguiente comando, lo que al menos aborda las preocupaciones de seguridad de esta característica. patchelf --clear-execstack program.

Esta idea está implementada en mi biblioteca experimental, noplate, donde se construye un puntero ancho a partir de la dirección de código y la cadena estática. Trampolines como Descriptores de Función. Hay otra idea que encuentro digna de explorar: También se podría usar el propio trampolín como descriptor de función.

En lugar de extraer la cadena estática y el puntero de código donde se crea el trampolín, simplemente pasamos la dirección del trampolín como de costumbre. Pero en todos los lugares donde podríamos llamar al trampolín, primero verificamos si el puntero apunta a un trampolín, y luego extraemos la dirección de código y la cadena estática para llamar a la función anidada directamente usando __builtin_call_with_static_chain. En cierto sentido, podríamos decir que en lugar de invocar el trampolín, estamos interpretando el código del trampolín en el sitio de llamada usando un intérprete super simple que solo puede interpretar esta secuencia de código específica y que es tan simple que...