Hacker News new | ask | show | jobs
by jgtrosh 2397 days ago
How does this relate to nested functions in C? (And resulting “infectious executable stacks”?)

https://nullprogram.com/blog/2019/11/15/

5 comments

It doesn't. This is just showing the normal way that callbacks are implemented in vanilla C and how you would make that programming pattern interoperate with Rust closures. Neither one relies on the compiler trickery/runtime code generation described in the earlier article.
The executable stack trick is only required if you want to implement closures that can be called as if they were plain C functions, with only a function pointer and no extra (void *) argument.
It doesn't relate to it at all. The issues around linking to problematic object files mentioned in that article will apply to Rust as well, but that's unrelated to the subject of this article, it's a property of the linker you're using and the toolchain used to compile whatever C dependencies you have
The problems there don't apply I believe because Rust closures don't require an executable stack.
That's correct - a Rust closure generally [1] can't be converted to a function pointer as it requires both code and state.

[1] https://github.com/rust-lang/rust/issues/39817

The whole point of jgtrosh's link is that there is a way to hide data behind a function pointer, so Rust could convert any closure to a function pointer. But it requires writable-and-executable memory, so it's a pretty bad idea (in GCC's implementation, that memory is on the stack, which is an extra bad idea, but i don't think it needs to be).
Definitely.

Technically this can also be done via static code trampolines that are mmap'd as well [1]. That approach has been used on iOS in the past to turn blocks into raw function pointers.

If you have a platform that allows W+X on code (yikes!), you can do [2] as well.

[1] https://github.com/plausiblelabs/plblockimp/blob/master/Sour... [2] https://www.mikeash.com/pyblog/friday-qa-2010-02-12-trampoli...

Anything that doesn't require W+X would need an entire page allocated per closure, wouldn't it?
No, you can of course allocate W+X pages from the OS and put multiple closures in them using a standard userspace memory allocator.

Or if the OS doesn't support W+X allocation at all, then you can have a bunch of tightly packed pregenerated trampolines in the binary.

Nope! You'd do something to the effect of:

  clo_code:
  4C8B1501100000  mov r10 [rel clo_code+0x1008]
  FF25F30F0000    jmp [rel clo_code+0x1000]
  0F1F00          nop3
  # one page away...
  struct clo_slot {
    void (*func)(void* _R10,...);
    void* data;
    };
Edit: to use r10 rather than rotating all the argument registers.
For example, every platform that has a virtual machine with JIT compilation support.
C doesn't have nested functions - they are a GCC extension.