c - Calling convention for function returning struct -
for following c code:
struct _astruct { int a; int b; float c; float d; int e; }; typedef struct _astruct astruct; astruct test_callee5(); void test_caller5(); void test_caller5() { astruct g = test_callee5(); astruct h = test_callee5(); }
i following disassembly win32:
_test_caller5: 00000000: lea eax,[esp-14h] 00000004: sub esp,14h 00000007: push eax 00000008: call _test_callee5 0000000d: lea ecx,[esp+4] 00000011: push ecx 00000012: call _test_callee5 00000017: add esp,1ch 0000001a: ret
and linux32:
00000000 <test_caller5>: 0: push %ebp 1: mov %esp,%ebp 3: sub $0x38,%esp 6: lea 0xffffffec(%ebp),%eax 9: mov %eax,(%esp) c: call d <test_caller5+0xd> 11: sub $0x4,%esp ;;;;;;;;;; note sub ;;;;;;;;;;;; 14: lea 0xffffffd8(%ebp),%eax 17: mov %eax,(%esp) 1a: call 1b <test_caller5+0x1b> 1f: sub $0x4,%esp ;;;;;;;;;; note sub ;;;;;;;;;;;; 22: leave 23: ret
i trying understand difference in how caller behaves after call. why caller in linux32 these subs?
i assume both targets follow cdecl calling convention. doesn't cdecl define calling convention function returning structure?!
edit:
i added implementation of callee. , sure enough, can see linux32 callee pops argument, while win32 callee not:
astruct test_callee5() { astruct s={0}; return s; }
win32 disassembly:
test_callee5: 00000000: mov eax,dword ptr [esp+4] 00000004: xor ecx,ecx 00000006: mov dword ptr [eax],0 0000000c: mov dword ptr [eax+4],ecx 0000000f: mov dword ptr [eax+8],ecx 00000012: mov dword ptr [eax+0ch],ecx 00000015: mov dword ptr [eax+10h],ecx 00000018: ret
linux32 disassembly:
00000000 <test_callee5>: 0: push %ebp 1: mov %esp,%ebp 3: sub $0x20,%esp 6: mov 0x8(%ebp),%edx 9: movl $0x0,0xffffffec(%ebp) 10: movl $0x0,0xfffffff0(%ebp) 17: movl $0x0,0xfffffff4(%ebp) 1e: movl $0x0,0xfffffff8(%ebp) 25: movl $0x0,0xfffffffc(%ebp) 2c: mov 0xffffffec(%ebp),%eax 2f: mov %eax,(%edx) 31: mov 0xfffffff0(%ebp),%eax 34: mov %eax,0x4(%edx) 37: mov 0xfffffff4(%ebp),%eax 3a: mov %eax,0x8(%edx) 3d: mov 0xfffffff8(%ebp),%eax 40: mov %eax,0xc(%edx) 43: mov 0xfffffffc(%ebp),%eax 46: mov %eax,0x10(%edx) 49: mov %edx,%eax 4b: leave 4c: ret $0x4 ;;;;;;;;;;;;;; note ;;;;;;;;;;;;;;
why caller in linux32 these subs?
the reason use of hidden pointer (named return value optimization), injected compiler, returning struct value. in systemv's abi, page 41, in section "function returning structures or unions", says:
the called function must remove address stack before returning.
that why ret $0x4
@ end of test_callee5()
, compliance abi.
now presence of sub $0x4, %esp
after each test_callee5()
call sites, side-effect of above rule, combined optimized code generated c compiler. local storage stack space pre-reserved entirely by:
3: sub $0x38,%esp
there no need push/pop hidden pointer, written @ bottom of pre-reserved space (pointed @ esp
), using mov %eax,(%esp)
@ lines 9 , 17. stack pointer not decremented, sub $0x4,%esp
there negate effect of ret $0x4
, , keep stack pointer unchanged.
on win32 (using msvc compiler guess), there no such abi rule, simple ret
used (as expected in cdecl), hidden pointer pushed on stack @ line 7 , 11. though, slots not freed after calls, optimization, before callee exits, using add esp,1ch
, freeing hidden pointer stack slots (2 * 0x4 bytes) , local astruct
struct (0x14 bytes).
doesn't cdecl define calling convention function returning structure?!
unfortunately, not, varies c compilers , operating systems
Comments
Post a Comment