← Back to context

Comment by tpoacher

2 days ago

What's a "trampoline"?

In 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.

  • Correct (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.

    • That'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.

  • C++ 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.

  • Why "trampoline" though? Is there an obvious allusion to multiple jumping that I'm missing?

    • Because instead of jumping straight to the actual code of the function, you jump to the trampoline first, and then bounce off of it to the function.

Could 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.

It's where you jump and then get immediately bounced back. Basically GOTOs with params

  • If 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.