Martin Uecker, 2026-08-29
We discussed last time how one can use nested functions on GCC 17 and Clang for callbacks with requiring an executable stack. But what if ones needs to support older versions of GCC? Of course, one can simply accept an executable stack (it is not quite as terrible as some people claim), but it is also possible to avoid this with a hack.
Let's discuss first how GCC supports taking the address of a nested function. Our toy example without the use of the new macros is shown below (Godbolt Example).
typedef int cb_f(int y);
int baz(cb_f p, int x)
{
return p(x);
}
int foo(int k)
{
int bar(int x) { return k + x; }
return baz(bar, 2 * k);
}
On x86_64, the generated assembly is the following.
bar.0:
movl %edi, %eax
addl (%r10), %eax
ret
foo:
subq $56, %rsp
leaq 64(%rsp), %rax
movq %rax, 32(%rsp)
movl %edi, (%rsp)
leaq 4(%rsp), %rax
movw $-17591, 4(%rsp)
movabsq $bar.0, %rcx
movq %rcx, 6(%rsp)
movw $-17847, 14(%rsp)
movq %rsp, 16(%rsp)
movl $-1864106167, 24(%rsp)
addl %edi, %edi
call *%rax
addq $56, %rsp
ret
This code places a trampoline on the stack and immediately
invokes it via the inlined baz function. The trampoline
is a short code sequence that loads the static frame register to a
structure on the stack that contains the captured variables from the
parent function and then jumps to the local function. If one
translates the constants -17591, -17847, and -1864106167 back to
assembly instructions one obtains the following x86_64 code.
movq $bar.0, %r11
movq $frame, %r10
jump *%r11
Both, the static chain and the code address are immediate constants
used by move instructions in the code of the trampoline.
Instead of using the address of the trampoline to call the function,
we can extract the code address and the static chain from the trampoline
and use them with the __builtin_call_with_static_chain
built-in function to call the local function directly. For example,
using noplate's peek and
array_slice macros,
this could be done in the following way for x64_64 (and a large memory
model).
unsigned char (*tramp)[24] = (void*)bar;
void *code = peek(uint64_t, &array_slice(tramp, 2, 10));
void *chain = peek(uint64_t, &array_slice(tramp, 12, 20));
These pointers are then exactly the same information that can be obtained
on the yet to-be-released GCC 17 with the new built-ins
__builtin_call_static_chain and __builtin_call_code_adress
and can be used to call the local function with as discussed previously.
__builtin_call_with_static_chain(((typeof(bar)*)code)(arg), chain);
Thus, we can use reading of these two pointer values from a trampoline as a fallback mechanism in older versions of GCC. The downsides are that a trampoline is still created, the compiler can still not devirtualize the indirect call, and the stack will still be marked executable. So what was gained? Since we never actually invoke the trampoline, we can make the stack non-executable again with the following command, which at least addresses the security concerns of this feature.
patchelf --clear-execstack program
This idea is implemented in my experimental library, noplate, where a wide pointer is constructed from the code address and the static chain.
There is another idea I find worth exploring: One could also use the trampoline
itself as a function descriptor. Instead of extracting the static chain
and code pointer where the trampoline is created we just pass on the address of
the trampoline as usually. But everywhere where we might call the trampoline,
we first check whether the pointer points to a trampoline, and then extract code
address and static chain to call the nested function directly using
__builtin_call_with_static_chain. In some sense we could say that
instead of invoking the trampoline, we are interpreting the code of the
trampoline at the call site using a super simple interpreter that only can
interpret this specific code sequence and that is so simple that it can be inlined
(Godbolt Example).