c - GCC/x86 inline asm: How do you tell gcc that inline assembly section will modify %esp? -
while trying make old code work again (https://github.com/chaos4ever/chaos/blob/master/libraries/system/system_calls.h#l387, fwiw) discovered of semantics of gcc
seem have changed in quite subtle still dangerous way during latest 10-15 years... :p
the code used work older versions of gcc
, 2.95. anyway, here code:
static inline return_type system_call_service_get(const char *protocol_name, service_parameter_type *service_parameter, tag_type *identification) { return_type return_value; asm volatile("pushl %2\n" "pushl %3\n" "pushl %4\n" "lcall %5, $0" : "=a" (return_value), "=g" (*service_parameter) : "g" (identification), "g" (service_parameter), "g" (protocol_name), "n" (system_call_service_get << 3)); return return_value; }
the problem code above gcc
(4.7 in case) compile following asm code (at&t syntax):
# 392 "../system/system_calls.h" 1 pushl 68(%esp) # pointer (%esp + 0x68) valid when inline asm entered. pushl %eax pushl 48(%esp) # ...but 1 not (%esp + 0x48), since 2 dwords have been pushed onto stack, %esp not compiler expects lcall $456, $0 # restoration of %esp @ point done in called method (i.e. lret $12)
the problem: variables (identification
, protocol_name
) on stack in calling context. gcc
(with optimizations turned out, unsure if matters) values there , hand on inline asm section. but since i'm pushing stuff on stack, offsets gcc
calculate off 8 in third call (pushl 48(%esp)
). :)
this took me long time figure out, wasn't obvious me @ first.
the easiest way around of course use r
input constraint, ensure value in register instead. there another, better way? 1 obvious way of course rewrite whole system call interface not push stuff on stack in first place (and use registers instead, e.g. linux), that's not refactoring feel doing tonight...
is there way tell gcc
inline asm "the stack volatile"? how have guys been handling stuff in past?
update later same evening: did found relevant gcc
ml thread (https://gcc.gnu.org/ml/gcc-help/2011-06/msg00206.html) didn't seem help. seems specifying %esp
in clobber list should make offsets %ebp
instead, doesn't work , suspect -o2 -fomit-frame-pointer
has effect here. have both of these flags enabled.
what works , doesn't:
i tried omitting
-fomit-frame-pointer
. no effect whatsoever. included%esp
,esp
,sp
in list of clobbers.i tried omitting
-fomit-frame-pointer
,-o3
. produces code works, since relies on%ebp
rather%esp
.pushl 16(%ebp) pushl 12(%ebp) pushl 8(%ebp) lcall $456, $0
i tried having
-o3
, not-fomit-frame-pointer
specified in command line. creates bad, broken code (relies on%esp
being constant within whole assembly block, i.e. no stack frame).i tried skipping
-fomit-frame-pointer
, using-o2
. broken code, no stack frame.i tried using `-o1. broken code, no stack frame.
i tried adding
cc
clobber. no can do, doesn't make difference whatsoever.i tried changing input constraints
ri
, giving input & output code below. of course works less elegant had hoped. again, perfect enemy of good maybe have live now.
input c code:
static inline return_type system_call_service_get(const char *protocol_name, service_parameter_type *service_parameter, tag_type *identification) { return_type return_value; asm volatile("pushl %2\n" "pushl %3\n" "pushl %4\n" "lcall %5, $0" : "=a" (return_value), "=g" (*service_parameter) : "ri" (identification), "ri" (service_parameter), "ri" (protocol_name), "n" (system_call_service_get << 3)); return return_value; }
output asm code. can seen, using registers instead should safe (but maybe less performant since compiler has move stuff around):
#app # 392 "../system/system_calls.h" 1 pushl %esi pushl %eax pushl %ebx lcall $456, $0
Comments
Post a Comment