The second part is just a function pointer with an initializer (casting &puts
).
The first part is more interesting:
It gets %esp
into a C variable, and masks off the low 24 bits. i.e. rounds down to a 16MiB boundary. IDK what this is for, but a 4B offset from a non-inlined function call might or might not matter (if %esp
was very near a 16MiB boundary, or that's explicitly what you're trying to detect.)
The version posted in the question will break your code in non-obvious ways if the compiler ever gets a chance to inline it (e.g. with cross-file inlining). Instead of properly declaring an output operand from the asm
statement, it just modifies %eax
inside a function with no return statement. This is really dumb and has zero advantages compared to telling the compiler about what you're returning.
/********* Safe version of the function *************/
// Actually unsigned long was fine, since this asm only works on 32bit anyway
static inline uintptr_t get_sp(void) {
uintptr_t result;
__asm__("movl %%esp, %0\n\t"
: "=g" (result)
);
result &= 0xff000000; // do this outside the inline asm so the compiler knows that the low 24b are always zero.
return result;
}
This compiles to the same asm when not inlined, but can be safely inlined. (e.g. put it in a header with static inline
). It also of course avoids compiler warnings about functions with missing return values.
As Michael Petch points out in comments, making this an always-inline function, or even a macro, is probably a good idea for consistency. (Although optimized vs. un-optimized builds will consume different amounts of stack space anyway.)
See the inline-assembly tag wiki for more about how to write GNU C inline asm that doesn't suck.