Comment by Dwedit
16 hours ago
What do you need the executable stack for? You call using a function pointer, there's no executable read/write memory involved in using a function pointer.
16 hours ago
What do you need the executable stack for? You call using a function pointer, there's no executable read/write memory involved in using a function pointer.
A nested function requires an extra parameter for the nested stack pointer, which is passed in a dedicated register on most ABIs. You can't spell this kind of function type in C. To make a C-callable function pointer, the compiler needs to generate a little bit of code (a trampoline) that stuffs the appropriate stack pointer in the appropriate register.
That trampoline needs to live somewhere. Since the function is inherently noncallable after the stack returns, and C programmers hate it when their compiler sneaks in extra malloc calls under the hood, the compiler decides to stick the trampoline on the stack instead of heap-allocating it.
Nested functions may require a context pointer of some kind, which the caller can't supply. One way of doing this: create a thunk on the stack that provides the context pointer, and use that address as the pointer to the nested function.
But now the stack needs to be executable.
Doesn't x86 actually support nested call pointers using enter to natively support this in Pascal?
It’s not a question of ISA support. If you call via a function pointer, how do you supply the pointer to the data? (It has to either be in a separate place from the function code, or the same place. One requires an ABI change, the other an executable, writable section of memory.)
9 replies →
need to represent the "captured-variables"/closure
C# does closures by creating a class. When you run the function, captured local variables actually live inside the object instead of in the stack. This makes use of an object, but no memory pages need to become read/write/executable to make C#-style closures happen. You just have a combination function/object pointer (a delegate) instead of a single function pointer.