Hacker News new | past | comments | ask | show | jobs | submit login

GNU lambdas are incredibly evil. In order for lambdas to capture their environment and yet still be available as a function pointer, the compiler makes the stack executable and stores the trampoline code on the stack.

C++ lambdas don't suffer from this problem.




GNU doesn't really have lambdas. This is an abuse of the compound statement macro.

IIRC compound statements, like I've presented, don't use trampolines. Nested functions definitely do, but that isn't quite what we're doing here.

GCC _should_ compile using descriptors for the compound statements that lambda is expanding to instead of using trampolines.

    gcc -fno-trampolines -I. examples/lambda.c
Works. There's no trampoline present.


  #define lambda(ret_type, _body) ({ ret_type _ _body _; })
I found this one really fascinating, they're using both statement expression and nested functions. Their (reformatted) example of:

  int (*max)(int, int) =
    lambda(int, (int x, int y) { return x > y ? x : y; });
macro-expands to

  int (*max)(int, int) =
    ({ int _(int x, int y) { return x > y ? x : y;}; });
So they have a statement-expression with just one statement in it - the value of that statement is the value of the whole statement-expression. And the one statement inside the statement expression is a declaration of a nested function named _. Statement-expressions decay their return types, so that function gets converted to a function pointer. And thus your "lambda" is a pointer to a GCC nested function.


It's slightly different.

    { ret_type _ _body _; }
Notice the second underscore at the end? The compound statement contains a nested function definition followed by an expression statement which "returns" the function just defined. The function definition alone wouldn't work.


A lambda that does not close over its environment is not really a true lambda. It's just a normal function with function scope. I suspect we have some difference in terminology difference: what I call lambda is what you call nested functions. I hate it when there are those kind of trivial terminology differences.


You can do that with this. Because it does technically contain a nested function called _ it's just GCC usually makes that function a normal function if it contains no variables from the parent's scope.

If you reference the outer scope inwards, however, you will end up with an executable stack. And you won't have a borrow checker to tell you the the value that the lambda mentions is no longer at the same address. In fact, you have no flags to warn you of that event.

C++ has lambdas. GNU-C has a hack that is truly terrifying to behold if you use it to the full power.


What's `readelf -l a.out` say for GNU_STACK flags?

I'd try myself but my gcc doesn't recognize -fno-trampolines.


RW


clang blocks https://en.wikipedia.org/wiki/Blocks_(C_language_extension) also don't suffer from this problem :D




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: