4 ms·
Using GCC's Nested Functions with Wide Pointers and No Trampolines II
- mananaysiempre 2mo agoWhat about your older patch where -fno-trampolines meant a function pointer could either be a code pointer or a closure (descriptor) pointer, distinguished by a tag?
- uecker 2mo agoMy old patch from 2018? This was not accepted to GCC because it relied on function pointers being aligned and there were concerns with this. But I prefer this approach anyhow, as it does not impose any run-time cost for checking the tag, and is easier to optimize.
- tpoacher 2mo agoWhat's a "trampoline"?
- monster_truck 2mo agoIt's where you jump and then get immediately bounced back. Basically GOTOs with params
- eru 2mo agoIf you have proper tail call optimisation, then tail calls are GOTOs with params. Trampolines allow you to simulate that, even when your compiler / language doesn't handle tail calls properly.
- mananaysiempre 2mo agoCould be a number of things depending on context. In this case it’s a short function that adjusts some things and jumps to the actual functions (a “thunk” is another term for this). Specifically, if in GCC you write int f(int x) { int g(int y) { ... use x and y ... } ... h(&g); ... } then what the compiled code for f does is construct on the stack a short piece of machine code: mov <well-known register>, <frame pointer> jmp <start of g’s code> and &g points to the start not of g’s code but of this snippet on the stack, which has the parent function’s frame pointer compiled into it as a literal constant. The snippet is called a trampoline.
- dgellow 2mo agohttps://en.wikipedia.org/wiki/Trampoline_(computing) https://en.wikipedia.org/wiki/Trampoline_(computing)
- jcranmer 2mo agoIn this context: Nested functions have a different ABI from regular C functions, due to the invisible static chain register that needs to be set up. C has no way of indicating this different ABI, so GCC happily lets you cast a nested function to a C function pointer by creating a little tiny function that puts the right value in the static chain register before calling the nested function. This little tiny function is the trampoline. Since the trampoline needs to live somewhere, GCC puts it on the stack, requiring the stack to be executable and consequently a whole lot of people hate the feature because it's a walking security nightmare.
- uecker 2mo agoCorrect (although the nightmare part is a bit exaggerated since return-oriented programming showed that non-executable stack does not help a lot). GCC can also put the trampoline on the heap, but this also has downsides. For me the main downside of trampolines is that the optimizer can not de-virtualize the trampoline again. This could be implemented, but avoiding the creation of the trampoline in the first place is much better.
- Gibbon1 2mo agoThat's what it felt to me like. With non split stacks you're mixing return addresses with potentially entrusted data. You're already living in sin. With split stacks a trampoline wouldn't be a problem.
- kccqzy 2mo agoC++ solves this problem by simply not allowing a nested function (lambda) to be converted to a function pointer, and thereby avoids this problem of trampolines and executable stack altogether. I think that’s a better design.
- uecker 2mo agoC++ has the same solution as I propose here: A wide function pointer type. In C++ it is called std::function, but this comes with a bit of baggage. C++ 26 has std::function_ref which would be the exact equivalent to my wide pointer. https://godbolt.org/z/GaP9jb5rE https://godbolt.org/z/GaP9jb5rE
- mbeavitt 2mo agoWhy would someone want to use a nested function, practically speaking?
- kloop 2mo agoSo that you can name a section of code without polluting the namespace.
- anta40 2mo agoSay to strictly enforce modularity, e.g helper functions that can only be accessed within its function. Pascal supports it (at least Turbo Pascal, no idea about ISO Pascal).
- Joker_vD 2mo agoFor a counterpoint, see David R. Hanson's "Is block structure necessary?" (1981) [0] — back in those days, "block structure" meant nested routines with nested scopes — which argues that having instead a proper module system, with explicit control over what's being exported from a module, not only gives a better modularity, decomposition, and encapsulation, but also simplifies both the language's implementation, and the run-time structures it needs (remember displays, and the hardware support for them e.g. x86's ENTER?). Block structure is traditionally considered an a priori requirement for algorithmic program- ming languages. Most new languages since Algol-60 have block structure. Reasons exist, however, to omit the general form of block structure — nested procedure definitions in which references to identifiers defined in outer procedures are permitted — from programming languages, especially those intended for systems programming applications. This paper reviews the concept of block structure and considers its advantages and disadvantages. It concludes that, in many cases, a module facility is superior to block structure and should be considered in lieu of block structure in future languages. [0] https://drh.github.io/documents/blockstructure.pdf https://drh.github.io/documents/blockstructure.pdf
- kccqzy 2mo agoThe traditional way of doing this in C is simply static functions. Every .c file has exactly one non-static function and all the other helper functions are static.