When you create a closure the function is always the same and known at compile time, what changes instead is a hidden argument of that function carrying the context. This data is also part of the closure (it's the data it closes over, hence the name) which is why you can't always convert them to a fn() pointer (because that's missing the argument for the context and the context itself!). For a similar reason you cannot dynamically generate an extern "C" function item/function pointer in the same way that closures work.
Note that technically you can still generate such functions, but it's pretty hard because it will involve either dynamic code generation (not portable, not always possible) or whatever fuckery GCC uses for implementing nested functions.
C libraries which take function pointers also often take some arbitrary pointer which they'll hold onto and pass back when calling your function — this might be called “user data”, “refcon”, or various other names. You can use this to effectively make your own closure — put the data you need in a Box and call Box::into_raw() on it, then pass that pointer.
If you mean trampolines then it's all explained pretty well in the GCC documentation. It's also a code generation, just function is generated right there, on stack.
GCC's nested functions are also incompatible with any platform that doesn't allow mapping the stack as executable like for example arm64 macOS, iOS, game consoles, SELinux (depending on it's configuration), OpenBSD and NetBSD.