Using GCC's Nested Functions with Wide Pointers and No Trampolines II
摘要
作者 Martin Uecker 介绍 GCC 嵌套函数与宽指针的两项进展。其一,GCC 16 保证不访问父函数变量的嵌套函数无需 trampoline,即使不优化也成立,且可安全从父函数返回,编译器会在明显情况下对返回捕获上下文的函数给出警告;此类函数仍可访问静态变量、命名常量或类型,适合写回调。其二,作者此前提交的补丁已合入 GCC 17 开发分支,新增两个内建函数配合既有 builtin call with static chain,可在不生成 trampoline 的情况下实现带捕获的嵌套函数,编译器会自动将其转换为手动传数据指针的等价形式,数据指针经 ABI 已预留的专用寄存器传递;用宏包装可定义通用宽指针类型,编译器能优化为单条汇编指令。文末预告后续讨论如何在 clang 与 GCC 上使用嵌套函数,以及如何在旧版 GCC 上规避 trampoline。
荐读理由
GCC 16 起不捕获父函数变量的嵌套函数不再生成栈上蹦床,可直接安全返回;GCC 17 合并的补丁用内置函数加静态链寄存器实现带捕获的嵌套函数,免蹦床且能优化成单条指令,写回调或闭包时能省掉手工样板代码
原文

Using GCC's Nested Functions with Wide Pointers and no Trampolines II
Martin Uecker, 2026-07-14
Introduction
When the address of a nested function is taken, GCC creates at trampoline at run-time. This trampoline is placed on the stack, which means that the stack has to be executable. This is problematic because a non-executable stack is an important security feature. For some time now, GCC has the option to place the trampoline on the heap, which is better but still not ideal as the allocation has a higher cost and the trampoline could leak when longjmp is used.
Previously, I pointed out how this could be avoided with a new wide pointer type, and also talked about a preliminary patch to GCC that implements some core functionality that would be necessary for this.
Here, I want to give two updates.
GCC 16: Nested Functions Without Capture
First, GCC 16 was released. In GCC 16 it is guaranteed that a nested function that does not access variables of a parent function will not require a trampoline. In practice, this was already ensured when optimizing, but now it is also ensured when not optimizing and documented. This also means that you can safely return such a function from its parent and that you will get a warning when trying to return a function that does access the local context (at least in simple cases where this is obvious to the compiler) as in the following example (Godbolt Example)
typedef int cb_f(int);
cb_f *foo(int x)
{
int worker(int y)
{
return x + y; // capture
}
return worker;
}
cb_f *bar(int _x)
{
static int x;
x = _x;
int worker(int y)
{
return x + y; // no capture
}
return worker;
}
Nested function that do not access variables of the parent function can still access static variables, named constants, or types (if not variably modified). This is useful for writing callback functions (Godbolt Example).
typedef int cb_f(void *, int y);
int process(cb_f *cb, void *data)
{
return cb(data, 5);
}
int foo(int x)
{
struct { int x; } data = { .x = x };
int worker(void *_data, int y)
{
typeof(data) *data = _data;
return data->x + y;
}
return process(worker, &data);
}
Still, the true power of nested functions is being able to directly access the variables of the parent function. Also, the code above seems like we just simulate such functions by manually adding some boilerplate code - the compiler should certainly be able to help us with this. This is what my second update is about.
GCC 17: Nested Functions with Capture
Recently, a version of my patch was merged into the development branch of GCC that implements the two new built-in functions that can be used together with the existing built-in __builtin_call_with_static_chain to write this example (Godbolt Example) without requiring trampolines.
typedef int cb_f(int y);
static int process(cb_f *cb, void *data)
{
return __builtin_call_with_static_chain(cb(5), data);
}
int main()
{
int x = 7;
int worker(int y)
{
return x + y;
}
return process(__builtin_call_code_address(worker),
__builtin_call_static_chain(worker));
}
In fact, with trampolines out of the picture, all that GCC does under the hood is to automatically transform this into the example above! The only difference to the manually written version that the data pointer is passed via a special register. This register is used by many other programming language for passing the static chain and therefor already reserved for this use by the ABI.
Even without proper language support, we can still make this example a bit nicer by defining a generic wide pointer type and wrapping the built-ins via macros. (Godbolt Example)
#define wide(T) struct wide_##T { typeof(T) *code; void *chain; }
typedef int cb_f(int y);
int baz(wide(cb_f) p, int x)
{
return CALL(p, (x));
}
int foo(int k)
{
int bar(int x) { return k + x; }
return baz(CLOSURE(cb_f, bar), 2 * k);
}
The compiler will happily optimize this to a single assembly instruction that implements a multiplication by three.
foo:
lea eax, [rdi+rdi*2]
ret
Outlook
While there are compilers other than GCC that support nested functions, this feature is not really portable. In the future, I will discuss how one can use nested functions with both clang and GCC and how one can, with a hack, avoid creating trampolines even on older versions of GCC.
Literature
这条对你有帮助吗?