2 ms·
I've spent far too much time reading this while I'm supposed to be working - and I still don't understand it. Hopefully someone can help. __static_call(nam
by gpm 6y ago
I've spent far too much time reading this while I'm supposed to be working - and I still don't understand it. Hopefully someone can help.
__static_call(name)
is a macro that expands to (name inlined):
({ \
__ADDRESSABLE(__SCK__x86_pmu_add); \
&__SCT__x86_pmu_add_tramp; \
})
It is used in this context
#define static_call(name) __static_call(name)
Which is used in this context
static_call(name)(args...);
But... this (expanding the last version) isn't valid syntax?
({ \
__ADDRESSABLE(__SCK__x86_pmu_add); \
&__SCT__x86_pmu_add_tramp; \
})(args...)
A block isn't callable in C?
---
It might help if I understood what the final product (assembly) is supposed to look like, but I don't. For instance, at the end of the article the final assembly produced is discussed, but it's not clear to me what code is producing that assembly/what the surrounding assembly is supposed to look like. Taking the last example
call __SCK__x86_pmu_add
; in call + "trampoline"
ret ; "trampoline" returns, completing the call
Is this code generated inside a trampoline function (so that last ret is actually a ret)? Is this code called inside the caller, and someone played with the return address? Something else?
- nikita2206 6y agoI believe in GCC C a block can return a value. In this case it returns a pointer to __SCT__x86_pmu_add_tramp. And that pointer is then treated as a function.
- woodruffw 6y agoAuthor here: It's a GCC extension[1]! > Is this code generated inside a trampoline function (so that last ret is actually a ret)? Is this code called inside the caller, and someone played with the return address? Something else? The first: the "trampoline" in this case is just a ret. Calling the trampoline causes the compiler to emit a call instruction (which does the normal stack ops associated with one), so our "trampoline" just cleans that call up, completing the behavior of `static_call` in the case where we don't actually have a function registered. [1]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
- gpm 6y agoGood to know about that gcc extension, that's cool :) Ok, I think I understand what's going on now. To write it down in a bit of a different way for anyone else who is confused Start with some user written code like this ... usercode(...) { ... static_call(x86_pmu_add)(args..); ... } That code is expanded to (desugaring the gcc extension as well) ... usercode(...) { ... __ADDRESSABLE(__SCK__x86_pmu_add); // Just making sure the compiler doesn't think this is unused (&__SCT__x86_pmu_add_tramp)(args...); ... } __SCT__x86_pmu_add_tramp is where we've "jitted" the function address to jump to. If we've written a function pointer, that assembly is just a "jmp" instruction to the function we wrote (note that one function jumping to another is just a tail call), and in the other case it's just a "ret" instruction because we don't want to do anything. Thanks woodruffw! This is some really Interesting (and intricate) code
- woodruffw 6y agoYou're welcome, and thanks for breaking it out like that! The kernel developers, of course, deserve the majority of the praise -- I just summarized my understanding as I read through it :-)