; #include <stdlib.h>
;
; void __stdcall leaf_call(int b, int c, int d, int e, int f, int g, int h)
; {
; }
;
; void __stdcall nonleaf_call(int a, int b, int c, int d, int e, int f, int g, int h)
; {
; /* use some local data */
; *(char*)alloca(220) = 'L';
; leaf_call(b, c, d, e, f, g, h);
; }
;
; int main()
; {
; nonleaf_call(0, 1, 2, 3, 4, 5, 6, 7);
; return 0;
; }
; output from godbolt compiler explorer w/ msvc 19.14 (w/ /GS- for simplicity)
_leaf_call@28 PROC
push ebp
mov ebp, esp
pop ebp
ret 28
_leaf_call@28 ENDP
_b$ = 12
_c$ = 16
_d$ = 20
_e$ = 24
_f$ = 28
_g$ = 32
_h$ = 36
_nonleaf_call@32 PROC
push ebp
mov ebp, esp
push 220
call _alloca
add esp, 4
mov BYTE PTR [eax], 76
mov eax, DWORD PTR _h$[ebp]
push eax
mov ecx, DWORD PTR _g$[ebp]
push ecx
mov edx, DWORD PTR _f$[ebp]
push edx
mov eax, DWORD PTR _e$[ebp]
push eax
mov ecx, DWORD PTR _d$[ebp]
push ecx
mov edx, DWORD PTR _c$[ebp]
push edx
mov eax, DWORD PTR _b$[ebp]
push eax
call _leaf_call@28
pop ebp
ret 32
_nonleaf_call@32 ENDP
_main PROC
push ebp
mov ebp, esp
push 7
push 6
push 5
push 4
push 3
push 2
push 1
push 0
call _nonleaf_call@32
xor eax, eax
pop ebp
ret 0
_main ENDP
; ---------- structs by value, struct in first call on reg arg boundary ---------->
;
; struct A { int x; short y; char z; long long t; };
;
; struct A __stdcall leaf_call(struct A a, short b, long long c, char d, int e, int f, int g, long long h)
; {
; a.x += 1;
; return a;
; }
;
; int main()
; {
; struct A a = {9, 99, 23, 12LL};
; leaf_call(a, 1, 2, 3, 4, 5, 6, 7LL);
; return 0;
; }
; output from godbolt compiler explorer w/ msvc 19.14 (w/ /GS- for simplicity)
$T1 = 8
_a$ = 12
_leaf_call@52 PROC
push ebp ; | prolog
mov ebp, esp ; /
mov eax, DWORD PTR _a$[ebp] ; \
add eax, 1 ; | get struct's x (from stack args), add 1 and write back
mov DWORD PTR _a$[ebp], eax ; /
mov ecx, DWORD PTR $T1[ebp] ; get ptr to retval struct passed as hidden arg (+8 to skip retval and saved ebp)
mov edx, DWORD PTR _a$[ebp] ; |
mov DWORD PTR [ecx], edx ; |
mov eax, DWORD PTR _a$[ebp+4] ; |
mov DWORD PTR [ecx+4], eax ; | copy modified (b/c of x+=1) struct arg to space of retval
mov edx, DWORD PTR _a$[ebp+8] ; |
mov DWORD PTR [ecx+8], edx ; |
mov eax, DWORD PTR _a$[ebp+12] ; |
mov DWORD PTR [ecx+12], eax ; |
mov eax, DWORD PTR $T1[ebp] ; return value (= ptr to struct that was passed-in as hidden arg)
pop ebp ; |
ret 56 ; | epilog (56 = stack cleanup of stdcall)
_leaf_call@52 ENDP
$T1 = -32
_a$ = -16
_main PROC
push ebp ; |
mov ebp, esp ; | prolog
sub esp, 32 ; / 32 = 16b local area for struct + 16b space used for retval struct
mov DWORD PTR _a$[ebp], 9 ; \ int x
mov eax, 99 ; | |
mov WORD PTR _a$[ebp+4], ax ; | struct values (local area) | short y
mov BYTE PTR _a$[ebp+6], 23 ; | char z
mov DWORD PTR _a$[ebp+8], 12 ; | |
mov DWORD PTR _a$[ebp+12], 0 ; / | long long t
push 0 ; \
push 7 ; | arg 7
push 6 ; arg 6
push 5 ; arg 5
push 4 ; arg 4
push 3 ; arg 3
push 0 ; |
push 2 ; arg 2
push 1 ; arg 1
sub esp, 16 ; |
mov ecx, esp ; |
mov edx, DWORD PTR _a$[ebp] ; |
mov DWORD PTR [ecx], edx ; |
mov eax, DWORD PTR _a$[ebp+4] ; |
mov DWORD PTR [ecx+4], eax ; | arg 0 (struct), "pushed" onto stack (fetched from local area)
mov edx, DWORD PTR _a$[ebp+8] ; |
mov DWORD PTR [ecx+8], edx ; |
mov eax, DWORD PTR _a$[ebp+12] ; |
mov DWORD PTR [ecx+12], eax ; /
lea ecx, DWORD PTR $T1[ebp] ; \ ptr to space used for struct retval (pushed as hidden first arg)
push ecx ; |
call _leaf_call@52 ; push return address and call
; (note: cdecl would clean up stack, here)
xor eax, eax ; return value
mov esp, ebp ; |
pop ebp ; | epilog
ret 0 ; |
_main ENDP
; ---------- C++ trivial and non-trivial aggrs passed to C funcs ---------->
;
; struct Trivial { int a; };
; struct NonTrivial {
; int a;
; __attribute__((stdcall)) NonTrivial() : a(0) {}
; __attribute__((stdcall)) NonTrivial(const NonTrivial& rhs) : a(rhs.a) { }
; };
;
; extern "C" {
;
; void __attribute__((stdcall)) f1(struct Trivial s) { }
; void __attribute__((stdcall)) f2(struct NonTrivial s) { }
;
; void __attribute__((stdcall)) f()
; {
; struct Trivial t;
; struct NonTrivial n;
; int a=1;
; a += 123;
; f1(t);
; a -= 123;
; f2(n);
; a -= 12;
; }
; }
; output from alpine_linux-3.11.3-x86 w/ gcc 9.2.0
00001215 <f1>:
1215: 55 push %ebp
1216: 89 e5 mov %esp,%ebp
1218: e8 f0 ff ff ff call 120d <__x86.get_pc_thunk.ax>
121d: 05 af 2d 00 00 add $0x2daf,%eax
1222: 90 nop
1223: 5d pop %ebp
1224: c2 04 00 ret $0x4
00001227 <f2>:
1227: 55 push %ebp
1228: 89 e5 mov %esp,%ebp
122a: e8 de ff ff ff call 120d <__x86.get_pc_thunk.ax>
122f: 05 9d 2d 00 00 add $0x2d9d,%eax
1234: 90 nop
1235: 5d pop %ebp
1236: c2 04 00 ret $0x4
00001239 <f>:
1239: 55 push %ebp ;
123a: 89 e5 mov %esp,%ebp ;
123c: 83 ec 28 sub $0x28,%esp ;
123f: e8 c9 ff ff ff call 120d <__x86.get_pc_thunk.ax> ;
1244: 05 88 2d 00 00 add $0x2d88,%eax ;
1249: 65 a1 14 00 00 00 mov %gs:0x14,%eax ;
124f: 89 45 f4 mov %eax,-0xc(%ebp) ;
1252: 31 c0 xor %eax,%eax ;
1254: 83 ec 0c sub $0xc,%esp ;
1257: 8d 45 e8 lea -0x18(%ebp),%eax ;
125a: 50 push %eax ;
125b: e8 6e 00 00 00 call 12ce <_ZN10NonTrivialC1Ev> ; NonTrivial::NonTrivial() / ctor
1260: 83 c4 0c add $0xc,%esp ;
1263: c7 45 f0 01 00 00 00 movl $0x1,-0x10(%ebp) ;
126a: 83 45 f0 7b addl $0x7b,-0x10(%ebp) ;
126e: 83 ec 0c sub $0xc,%esp ;
1271: ff 75 e4 pushl -0x1c(%ebp) ;
1274: e8 9c ff ff ff call 1215 <f1> ; call f1(struct Trivial)
1279: 83 c4 0c add $0xc,%esp ;
127c: 83 6d f0 7b subl $0x7b,-0x10(%ebp) ;
1280: 83 ec 08 sub $0x8,%esp ;
1283: 8d 45 e8 lea -0x18(%ebp),%eax ; | | ptr to n
1286: 50 push %eax ; | /
1287: 8d 45 ec lea -0x14(%ebp),%eax ; | \
128a: 50 push %eax ; | copy n | ptr to dest of copy of n
128b: e8 5a 00 00 00 call 12ea <_ZN10NonTrivialC1ERKS_> ; | NonTrivial::NonTrivial(const NonTrivial&) / copy ctor
1290: 83 c4 08 add $0x8,%esp ;
1293: 83 ec 0c sub $0xc,%esp ;
1296: 8d 45 ec lea -0x14(%ebp),%eax ; |
1299: 50 push %eax ; | f2 arg 0 (ptr to copy of struct NonTrivial), via ptr as non-trivial
129a: e8 88 ff ff ff call 1227 <f2> ; call f2(struct NonTrivial)
129f: 83 c4 0c add $0xc,%esp ;
12a2: 83 6d f0 0c subl $0xc,-0x10(%ebp) ;
12a6: 90 nop ;
12a7: 8b 45 f4 mov -0xc(%ebp),%eax ;
12aa: 65 33 05 14 00 00 00 xor %gs:0x14,%eax ;
12b1: 74 05 je 12b8 <f+0x7f> ;
12b3: e8 4e 00 00 00 call 1306 <__stack_chk_fail_local> ;
12b8: c9 leave ;
12b9: c3 ret ;
; ... snip, removed code of ctor and copy ctor ...
; ---------- C++ trivial and non-trivial aggrs as return values ---------->
;
; struct Trivial { int a; };
; struct NonTrivial {
; int a;
; __attribute__((stdcall)) NonTrivial() : a(0) {}
; __attribute__((stdcall)) NonTrivial(const NonTrivial& rhs) : a(rhs.a) { }
; };
;
; extern "C" {
; struct Trivial __attribute__((stdcall)) f1() { return Trivial(); }
; }
;
; struct NonTrivial __attribute__((stdcall)) f2() { return NonTrivial(); }
;
; extern "C" {
; void __attribute__((stdcall)) f()
; {
; int a=1;
; a += 123;
; struct Trivial t = f1();
; a -= 123;
; struct NonTrivial n = f2();
; a -= 12;
; }
; }
; output from alpine_linux-3.11.3-x86 w/ gcc 9.2.0 (w/ -O0 --no-stack-protector for simplicity)
00001205 <f1>:
1205: 55 push %ebp ; | prolog
1206: 89 e5 mov %esp,%ebp ; |
1208: e8 f0 ff ff ff call 11fd <__x86.get_pc_thunk.ax> ;
120d: 05 c3 2d 00 00 add $0x2dc3,%eax ;
1212: 8b 45 08 mov 0x8(%ebp),%eax ; fetch ptr to retval space -> eax
1215: c7 00 00 00 00 00 movl $0x0,(%eax) ; write retval
121b: 8b 45 08 mov 0x8(%ebp),%eax ; re-fetch ptr to retval space -> eax, to return it in eax (a bit pointless)
121e: 5d pop %ebp ; |
121f: c2 04 00 ret $0x4 ; | epilog
00001222 <_Z2f2v>:
1222: 55 push %ebp
1223: 89 e5 mov %esp,%ebp
1225: 83 ec 08 sub $0x8,%esp
1228: e8 d0 ff ff ff call 11fd <__x86.get_pc_thunk.ax>
122d: 05 a3 2d 00 00 add $0x2da3,%eax
1232: 83 ec 0c sub $0xc,%esp
1235: ff 75 08 pushl 0x8(%ebp)
1238: e8 65 00 00 00 call 12a2 <_ZN10NonTrivialC1Ev>
123d: 83 c4 0c add $0xc,%esp
1240: 8b 45 08 mov 0x8(%ebp),%eax
1243: c9 leave
1244: c2 04 00 ret $0x4
00001247 <f>:
1247: 55 push %ebp ;
1248: 89 e5 mov %esp,%ebp ;
124a: 83 ec 18 sub $0x18,%esp ;
124d: e8 ab ff ff ff call 11fd <__x86.get_pc_thunk.ax> ;
1252: 05 7e 2d 00 00 add $0x2d7e,%eax ;
1257: c7 45 f4 01 00 00 00 movl $0x1,-0xc(%ebp) ; a = 1
125e: 83 45 f4 7b addl $0x7b,-0xc(%ebp) ; a += 123
1262: 8d 45 f0 lea -0x10(%ebp),%eax ; ptr to space (top of stack) to hold aggr retval -> eax ...
1265: 50 push %eax ; ... as hidden first arg
1266: e8 9a ff ff ff call 1205 <f1> ; call f1()
126b: 83 6d f4 7b subl $0x7b,-0xc(%ebp) ; a -= 123
126f: 8d 45 ec lea -0x14(%ebp),%eax ; ptr to space to hold aggr retval -> eax ...
1272: 83 ec 0c sub $0xc,%esp ; grow stack by 12
1275: 50 push %eax ; ... as hidden first arg
1276: e8 a7 ff ff ff call 1222 <_Z2f2v> ; call f2()
127b: 83 c4 0c add $0xc,%esp ; shrink stack back by 12
127e: 83 6d f4 0c subl $0xc,-0xc(%ebp) ; a -= 12
1282: 90 nop ;
1283: c9 leave ;
1284: c3 ret ;
; vim: ft=asm