#navi_header|C言語系| ''__fastcall'' 呼び出し規約: - 一部の引数はレジスタ経由で渡される。 -- 残りの引数はスタック上にPUSHする。(主に右から左へPUSH) - (スタック渡しの引数がある場合は)''関数(callee)側でスタックをクリーンアップする。'' -- アセンブラレベルでは ''RET imm16'' 命令を使って、戻る時にスタックポインタを"imm16"バイト分だけ戻す。つまり、"imm16"バイト分だけSPが増える(x86ではスタックはアドレスの小さい方へ進んでいくので、「戻る」=SPのアドレス値は大きくなる)。 - ''Borland C++ Compilerにおいて、Delphi互換の "__fastcall" と Microsoft互換の "__msfastcall" の二種類がある点に注意すること。'' ("__fastcall"と"__msfastcall"との間には互換性は無い。) #more|| ---- #outline|| ---- * サンプルコード ** callee.c (呼ばれる関数側) #code|c|> int foo1(int a) { return a * 2; } int foo2(int a, int b) { return a + b; } int foo3(int a, int b, int c) { return a + b + c; } int foo4(int a, int b, int c, int d) { return a + b + c + d; } ||< ** caller.c (呼ぶ main() 側) #code|c|> #include extern int foo1(int a); extern int foo2(int a, int b); extern int foo3(int a, int b, int c); extern int foo4(int a, int b, int c, int d); int __cdecl main(int argc, char *argv[]) { printf("foo1() = %d\n", foo1(10)); printf("foo2() = %d\n", foo2(10, 20)); printf("foo3() = %d\n", foo3(10, 20, 30)); printf("foo4() = %d\n", foo4(10, 20, 30, 40)); return 0; } ||< * VC++2008 Express Edtion : 指定方法 : #block||> int __fastcall foobar(); typedef int (__fastcall *ptr)(); ||< : コンパイラオプション : "/Gr" : 装飾名 : "@" + 関数名 + "@" + 引数のバイト数 (レジスタ渡しのバイト数 + スタックにPUSHされたバイト数) : 特記事項 : #block||> - 2つまでの引数はレジスタ経由(ECX, EDX)で渡される。 - 3つめ以降の引数はスタック上にPUSHする。(右から左へPUSH) ||< コンパイル&リンク&実行 #pre||> > cl /FAs /TC /Od /nologo /c /Gr /Focallee_fastcall.obj callee.c > cl /FAs /TC /Od /nologo /c /Gr /Focaller_fastcall.obj caller.c > link /SUBSYSTEM:CONSOLE /NOLOGO /OUT:fastcall.exe caller_fastcall.obj callee_fastcall.obj > fastcall.exe foo1() = 20 foo2() = 30 foo3() = 60 foo4() = 100 ||< ** callee.c のアセンブラ出力(callee.asm) #pre||> ; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00.30729.01 TITLE C:\in_vitro\c\calling_conventions\callee.c .686P .XMM include listing.inc .model flat INCLUDELIB LIBCMT INCLUDELIB OLDNAMES PUBLIC @foo1@4 ; Function compile flags: /Odtp ; File c:\in_vitro\c\calling_conventions\callee.c _TEXT SEGMENT _a$ = -4 ; size = 4 @foo1@4 PROC ; _a$ = ecx ; 1 : int foo1(int a) { return a * 2; } push ebp mov ebp, esp push ecx mov DWORD PTR _a$[ebp], ecx mov eax, DWORD PTR _a$[ebp] shl eax, 1 mov esp, ebp pop ebp ret 0 @foo1@4 ENDP _TEXT ENDS PUBLIC @foo2@8 ; Function compile flags: /Odtp _TEXT SEGMENT _b$ = -8 ; size = 4 _a$ = -4 ; size = 4 @foo2@8 PROC ; _a$ = ecx ; _b$ = edx ; 2 : int foo2(int a, int b) { return a + b; } push ebp mov ebp, esp sub esp, 8 mov DWORD PTR _b$[ebp], edx mov DWORD PTR _a$[ebp], ecx mov eax, DWORD PTR _a$[ebp] add eax, DWORD PTR _b$[ebp] mov esp, ebp pop ebp ret 0 @foo2@8 ENDP _TEXT ENDS PUBLIC @foo3@12 ; Function compile flags: /Odtp _TEXT SEGMENT _b$ = -8 ; size = 4 _a$ = -4 ; size = 4 _c$ = 8 ; size = 4 @foo3@12 PROC ; _a$ = ecx ; _b$ = edx ; 3 : int foo3(int a, int b, int c) { return a + b + c; } push ebp mov ebp, esp sub esp, 8 mov DWORD PTR _b$[ebp], edx mov DWORD PTR _a$[ebp], ecx mov eax, DWORD PTR _a$[ebp] add eax, DWORD PTR _b$[ebp] add eax, DWORD PTR _c$[ebp] mov esp, ebp pop ebp ret 4 @foo3@12 ENDP _TEXT ENDS PUBLIC @foo4@16 ; Function compile flags: /Odtp _TEXT SEGMENT _b$ = -8 ; size = 4 _a$ = -4 ; size = 4 _c$ = 8 ; size = 4 _d$ = 12 ; size = 4 @foo4@16 PROC ; _a$ = ecx ; _b$ = edx ; 4 : int foo4(int a, int b, int c, int d) { return a + b + c + d; } push ebp mov ebp, esp sub esp, 8 mov DWORD PTR _b$[ebp], edx mov DWORD PTR _a$[ebp], ecx mov eax, DWORD PTR _a$[ebp] add eax, DWORD PTR _b$[ebp] add eax, DWORD PTR _c$[ebp] add eax, DWORD PTR _d$[ebp] mov esp, ebp pop ebp ret 8 @foo4@16 ENDP _TEXT ENDS END ||< ** caller.cのアセンブラ出力(caller.asm) #pre||> ; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00.30729.01 TITLE C:\in_vitro\c\calling_conventions\caller.c .686P .XMM include listing.inc .model flat INCLUDELIB LIBCMT INCLUDELIB OLDNAMES _DATA SEGMENT $SG2496 DB 'foo1() = %d', 0aH, 00H ORG $+3 $SG2497 DB 'foo2() = %d', 0aH, 00H ORG $+3 $SG2498 DB 'foo3() = %d', 0aH, 00H ORG $+3 $SG2499 DB 'foo4() = %d', 0aH, 00H _DATA ENDS PUBLIC _main EXTRN @foo4@16:PROC EXTRN @foo3@12:PROC EXTRN @foo2@8:PROC EXTRN _printf:PROC EXTRN @foo1@4:PROC ; Function compile flags: /Odtp ; File c:\in_vitro\c\calling_conventions\caller.c _TEXT SEGMENT _argc$ = 8 ; size = 4 _argv$ = 12 ; size = 4 _main PROC ; 9 : { push ebp mov ebp, esp ; 10 : printf("foo1() = %d\n", foo1(10)); mov ecx, 10 ; 0000000aH call @foo1@4 push eax push OFFSET $SG2496 call _printf add esp, 8 ; 11 : printf("foo2() = %d\n", foo2(10, 20)); mov edx, 20 ; 00000014H mov ecx, 10 ; 0000000aH call @foo2@8 push eax push OFFSET $SG2497 call _printf add esp, 8 ; 12 : printf("foo3() = %d\n", foo3(10, 20, 30)); push 30 ; 0000001eH mov edx, 20 ; 00000014H mov ecx, 10 ; 0000000aH call @foo3@12 push eax push OFFSET $SG2498 call _printf add esp, 8 ; 13 : printf("foo4() = %d\n", foo4(10, 20, 30, 40)); push 40 ; 00000028H push 30 ; 0000001eH mov edx, 20 ; 00000014H mov ecx, 10 ; 0000000aH call @foo4@16 push eax push OFFSET $SG2499 call _printf add esp, 8 ; 14 : return 0; xor eax, eax ; 15 : } pop ebp ret 0 _main ENDP _TEXT ENDS END ||< * Borland C++ Compiler (Delphi "register"呼び出し規約互換の "__fastcall") : 指定方法 : #block||> int __fastcall foobar(); typedef int (__fastcall *ptr)(); ||< : コンパイラオプション : "-pr" : 装飾名 : "@" + 関数名 : 特記事項 : #block||> - 左から3つめまでの引数は左から順に EAX, EDX, ECX レジスタ経由で渡される。 - 左から4つめ以降の引数はスタック上にPUSHする。( ''左から右へ'' PUSH) ||< アセンブラコードを生成 > bcc32 -Od -pr -S callee.c > bcc32 -Od -pr -S caller.c コンパイル&リンク&実行 #pre||> > bcc32 -Od -pr -c -ocallee_fastcall.obj callee.c > bcc32 -Od -pr -c -ocaller_fastcall.obj caller.c > ilink32 /c /ap /Tpe c0x32 caller_fastcall callee_fastcall, fastcall, , cw32 import32 > fastcall.exe foo1() = 20 foo2() = 30 foo3() = 60 foo4() = 100 ||< ** callee.c のアセンブラ出力(callee.asm) #pre||> .386p ifdef ??version if ??version GT 500H .mmx endif endif model flat ifndef ??version ?debug macro endm endif ?debug S "callee.c" ?debug T "callee.c" _TEXT segment dword public use32 'CODE' _TEXT ends _DATA segment dword public use32 'DATA' _DATA ends _BSS segment dword public use32 'BSS' _BSS ends DGROUP group _BSS,_DATA _TEXT segment dword public use32 'CODE' @foo1 proc near ?live1@0: ; ; int foo1(int a) { return a * 2; } ; push ebp mov ebp,esp push ecx mov dword ptr [ebp-4],eax @1: mov eax,dword ptr [ebp-4] add eax,eax @3: @2: pop ecx pop ebp ret @foo1 endp @foo2 proc near ?live1@32: ; ; int foo2(int a, int b) { return a + b; } ; push ebp mov ebp,esp add esp,-8 mov dword ptr [ebp-8],edx mov dword ptr [ebp-4],eax @4: mov eax,dword ptr [ebp-4] add eax,dword ptr [ebp-8] @6: @5: pop ecx pop ecx pop ebp ret @foo2 endp @foo3 proc near ?live1@64: ; ; int foo3(int a, int b, int c) { return a + b + c; } ; push ebp mov ebp,esp add esp,-12 mov dword ptr [ebp-12],ecx mov dword ptr [ebp-8],edx mov dword ptr [ebp-4],eax @7: mov eax,dword ptr [ebp-4] add eax,dword ptr [ebp-8] add eax,dword ptr [ebp-12] @9: @8: mov esp,ebp pop ebp ret @foo3 endp @foo4 proc near ?live1@96: ; ; int foo4(int a, int b, int c, int d) { return a + b + c + d; } ; push ebp mov ebp,esp add esp,-12 mov dword ptr [ebp-12],ecx mov dword ptr [ebp-8],edx mov dword ptr [ebp-4],eax @10: mov eax,dword ptr [ebp-4] add eax,dword ptr [ebp-8] add eax,dword ptr [ebp-12] add eax,dword ptr [ebp+8] @12: @11: mov esp,ebp pop ebp ret 4 @foo4 endp _TEXT ends public @foo1 public @foo2 public @foo3 public @foo4 ?debug D "callee.c" 15473 24064 end ||< ** caller.cのアセンブラ出力(caller.asm) #pre||> .386p ifdef ??version if ??version GT 500H .mmx endif endif model flat ifndef ??version ?debug macro endm endif ?debug S "caller.c" ?debug T "caller.c" _TEXT segment dword public use32 'CODE' _TEXT ends _DATA segment dword public use32 'DATA' _DATA ends _BSS segment dword public use32 'BSS' _BSS ends DGROUP group _BSS,_DATA _TEXT segment dword public use32 'CODE' _main proc near ?live1@0: ; ; int __cdecl main(int argc, char *argv[]) ; push ebp mov ebp,esp ; ; { ; printf("foo1() = %d\n", foo1(10)); ; @1: mov eax,10 call @foo1 push eax push offset s@ call _printf add esp,8 ; ; printf("foo2() = %d\n", foo2(10, 20)); ; mov edx,20 mov eax,10 call @foo2 push eax push offset s@+13 call _printf add esp,8 ; ; printf("foo3() = %d\n", foo3(10, 20, 30)); ; mov ecx,30 mov edx,20 mov eax,10 call @foo3 push eax push offset s@+26 call _printf add esp,8 ; ; printf("foo4() = %d\n", foo4(10, 20, 30, 40)); ; push 40 mov ecx,30 mov edx,20 mov eax,10 call @foo4 push eax push offset s@+39 call _printf add esp,8 ; ; return 0; ; xor eax,eax ; ; } ; @3: @2: pop ebp ret _main endp _TEXT ends _DATA segment dword public use32 'DATA' s@ label byte ; s@+0: db "foo1() = %d",10,0 ; s@+13: db "foo2() = %d",10,0 ; s@+26: db "foo3() = %d",10,0 ; s@+39: db "foo4() = %d",10,0 align 4 _DATA ends _TEXT segment dword public use32 'CODE' _TEXT ends public _main extrn __setargv__:near extrn _printf:near extrn @foo1:near extrn @foo2:near extrn @foo3:near extrn @foo4:near ?debug D "C:\in_vitro\apps\borland\bcc55\include\_nfile.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\_null.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\_defs.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\_stddef.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\stdio.h" 10339 10240 ?debug D "caller.c" 15473 24232 end ||< ** 4つめ以降の引数が「左から右に」スタックにPUSHされる事を確認 - foo.c int CALLINGCONV foo(int a, int b, int c, int d, int e) { return a + b + c + d + e; } int bar() { return foo(10, 20, 30, 40, 50); } アセンブラコードを生成 >bcc32 -S -DCALLINGCONV=__fastcall foo.c foo.asmのfoo()呼び出し部分を見てみると次のようになっている。 push 40 push 50 mov ecx,30 mov edx,20 mov eax,10 call @foo 順番を変えれば、「左→右」に EAX, EDX, ECX, スタックの順に詰まれていくのが分かる。 mov eax,10 mov edx,20 mov ecx,30 push 40 push 50 call @foo foo.asm全体: #pre||> .386p ifdef ??version if ??version GT 500H .mmx endif endif model flat ifndef ??version ?debug macro endm endif ?debug S "foo.c" ?debug T "foo.c" _TEXT segment dword public use32 'CODE' _TEXT ends _DATA segment dword public use32 'DATA' _DATA ends _BSS segment dword public use32 'BSS' _BSS ends DGROUP group _BSS,_DATA _TEXT segment dword public use32 'CODE' @foo proc near ?live1@0: ; ; int CALLINGCONV foo(int a, int b, int c, int d, int e) { return a + b + c + d + e; } ; push ebp mov ebp,esp @1: add edx,eax add ecx,edx add ecx,dword ptr [ebp+12] add ecx,dword ptr [ebp+8] mov eax,ecx @3: @2: pop ebp ret 8 @foo endp _bar proc near ?live1@32: ; ; int bar() { ; push ebp mov ebp,esp ; ; return foo(10, 20, 30, 40, 50); ; @4: push 40 push 50 mov ecx,30 mov edx,20 mov eax,10 call @foo ; ; } ; @6: @5: pop ebp ret _bar endp _TEXT ends public @foo public _bar ?debug D "foo.c" 15473 33047 end ||< * Borland C++ Compiler (Microsoft互換の "__msfastcall") 装飾名や引数渡しに使うレジスタ、スタックPUSH方向などはVC++2008と同じ。 : 指定方法 : #block||> int __msfastcall foobar(); typedef int (__msfastcall *ptr)(); ||< : コンパイラオプション : "-pm" : 装飾名 : "@" + 関数名 + "@" + 引数のバイト数 (レジスタ渡しのバイト数 + スタックにPUSHされたバイト数) : 特記事項 : #block||> - 2つまでの引数はレジスタ経由(ECX, EDX)で渡される。 - 3つめ以降の引数はスタック上にPUSHする。(右から左へPUSH) ||< ** callee.c のアセンブラ出力(callee.asm) #pre||> .386p ifdef ??version if ??version GT 500H .mmx endif endif model flat ifndef ??version ?debug macro endm endif ?debug S "callee.c" ?debug T "callee.c" _TEXT segment dword public use32 'CODE' _TEXT ends _DATA segment dword public use32 'DATA' _DATA ends _BSS segment dword public use32 'BSS' _BSS ends DGROUP group _BSS,_DATA _TEXT segment dword public use32 'CODE' @foo1@4 proc near ?live1@0: ; ; int foo1(int a) { return a * 2; } ; push ebp mov ebp,esp push ecx mov dword ptr [ebp-4],ecx @1: mov eax,dword ptr [ebp-4] add eax,eax @3: @2: pop ecx pop ebp ret @foo1@4 endp @foo2@8 proc near ?live1@32: ; ; int foo2(int a, int b) { return a + b; } ; push ebp mov ebp,esp add esp,-8 mov dword ptr [ebp-8],edx mov dword ptr [ebp-4],ecx @4: mov eax,dword ptr [ebp-4] add eax,dword ptr [ebp-8] @6: @5: pop ecx pop ecx pop ebp ret @foo2@8 endp @foo3@12 proc near ?live1@64: ; ; int foo3(int a, int b, int c) { return a + b + c; } ; push ebp mov ebp,esp add esp,-8 mov dword ptr [ebp-8],edx mov dword ptr [ebp-4],ecx @7: mov eax,dword ptr [ebp-4] add eax,dword ptr [ebp-8] add eax,dword ptr [ebp+8] @9: @8: pop ecx pop ecx pop ebp ret 4 @foo3@12 endp @foo4@16 proc near ?live1@96: ; ; int foo4(int a, int b, int c, int d) { return a + b + c + d; } ; push ebp mov ebp,esp add esp,-8 mov dword ptr [ebp-8],edx mov dword ptr [ebp-4],ecx @10: mov eax,dword ptr [ebp-4] add eax,dword ptr [ebp-8] add eax,dword ptr [ebp+8] add eax,dword ptr [ebp+12] @12: @11: pop ecx pop ecx pop ebp ret 8 @foo4@16 endp _TEXT ends public @foo1@4 public @foo2@8 public @foo3@12 public @foo4@16 ?debug D "callee.c" 15473 24064 end ||< ** caller.cのアセンブラ出力(caller.asm) #pre||> .386p ifdef ??version if ??version GT 500H .mmx endif endif model flat ifndef ??version ?debug macro endm endif ?debug S "caller.c" ?debug T "caller.c" _TEXT segment dword public use32 'CODE' _TEXT ends _DATA segment dword public use32 'DATA' _DATA ends _BSS segment dword public use32 'BSS' _BSS ends DGROUP group _BSS,_DATA _TEXT segment dword public use32 'CODE' _main proc near ?live1@0: ; ; int __cdecl main(int argc, char *argv[]) ; push ebp mov ebp,esp ; ; { ; printf("foo1() = %d\n", foo1(10)); ; @1: mov ecx,10 call @foo1@4 push eax push offset s@ call _printf add esp,8 ; ; printf("foo2() = %d\n", foo2(10, 20)); ; mov edx,20 mov ecx,10 call @foo2@8 push eax push offset s@+13 call _printf add esp,8 ; ; printf("foo3() = %d\n", foo3(10, 20, 30)); ; push 30 mov edx,20 mov ecx,10 call @foo3@12 push eax push offset s@+26 call _printf add esp,8 ; ; printf("foo4() = %d\n", foo4(10, 20, 30, 40)); ; push 40 push 30 mov edx,20 mov ecx,10 call @foo4@16 push eax push offset s@+39 call _printf add esp,8 ; ; return 0; ; xor eax,eax ; ; } ; @3: @2: pop ebp ret _main endp _TEXT ends _DATA segment dword public use32 'DATA' s@ label byte ; s@+0: db "foo1() = %d",10,0 ; s@+13: db "foo2() = %d",10,0 ; s@+26: db "foo3() = %d",10,0 ; s@+39: db "foo4() = %d",10,0 align 4 _DATA ends _TEXT segment dword public use32 'CODE' _TEXT ends public _main extrn __setargv__:near extrn _printf:near extrn @foo1@4:near extrn @foo2@8:near extrn @foo3@12:near extrn @foo4@16:near ?debug D "C:\in_vitro\apps\borland\bcc55\include\_nfile.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\_null.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\_defs.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\_stddef.h" 10339 10240 ?debug D "C:\in_vitro\apps\borland\bcc55\include\stdio.h" 10339 10240 ?debug D "caller.c" 15473 24232 end ||< * OpenWatcom Compiler(16bit) ※サンプルコードは [[620]] の callee.c, caller.c を使用。 : 指定方法 : #block||> int __fastcall foobar(); typedef int (__fastcall *ptr)(); ||< : コンパイラオプション : "-ecf" : 装飾名 : "@" + 関数名 : 特記事項 : #block||> - 左から3つめ引数は ''左から順に AX, DX, BX レジスタ経由で'' 渡される。 - 左から4つめ以降の引数は ''右から左へ'' スタック上にPUSHする。 ||< コンパイル&リンク&実行 #pre||> > wcc -od -d0 -ecf callee.c > wcc -od -d0 -ecf caller.c > wcl -fe=fastcall16.exe caller.obj callee.obj > fastcall16.exe foo1() = 20 foo2() = 30 foo3() = 60 foo4() = 100 foo5() = 150 foo6() = 210 ||< アセンブラ生成 #pre||> > wdis -a -l=callee.asm callee.obj > wdis -a -l=caller.asm caller.obj ||< ** callee.c のアセンブラ出力(callee.asm) #pre||> .387 PUBLIC `@foo1` PUBLIC `@foo2` PUBLIC `@foo3` PUBLIC `@foo4` PUBLIC `@foo5` PUBLIC `@foo6` EXTRN __STK:BYTE EXTRN _small_code_:BYTE DGROUP GROUP CONST,CONST2,_DATA _TEXT SEGMENT BYTE PUBLIC USE16 'CODE' ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP `@foo1`: push ax mov ax,2 call near ptr __STK pop ax shl ax,1 ret `@foo2`: push ax mov ax,2 call near ptr __STK pop ax add ax,dx ret `@foo3`: push ax mov ax,2 call near ptr __STK pop ax add ax,dx add ax,bx ret `@foo4`: push ax mov ax,4 call near ptr __STK pop ax push bp mov bp,sp add ax,dx add ax,bx add ax,word ptr 4[bp] pop bp ret 2 `@foo5`: push ax mov ax,4 call near ptr __STK pop ax push bp mov bp,sp add ax,dx add ax,bx add ax,word ptr 4[bp] add ax,word ptr 6[bp] pop bp ret 4 `@foo6`: push ax mov ax,4 call near ptr __STK pop ax push bp mov bp,sp add ax,dx add ax,bx add ax,word ptr 4[bp] add ax,word ptr 6[bp] add ax,word ptr 8[bp] pop bp ret 6 _TEXT ENDS CONST SEGMENT WORD PUBLIC USE16 'DATA' CONST ENDS CONST2 SEGMENT WORD PUBLIC USE16 'DATA' CONST2 ENDS _DATA SEGMENT WORD PUBLIC USE16 'DATA' _DATA ENDS END ||< ** caller.c のアセンブラ出力(caller.asm) #pre||> .387 PUBLIC main_ EXTRN __STK:BYTE EXTRN `@foo1`:BYTE EXTRN printf_:BYTE EXTRN `@foo2`:BYTE EXTRN `@foo3`:BYTE EXTRN `@foo4`:BYTE EXTRN `@foo5`:BYTE EXTRN `@foo6`:BYTE EXTRN __argc:BYTE EXTRN _small_code_:BYTE EXTRN _cstart_:BYTE DGROUP GROUP CONST,CONST2,_DATA _TEXT SEGMENT BYTE PUBLIC USE16 'CODE' ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP main_: push ax mov ax,0cH call near ptr __STK pop ax push bx push cx mov ax,0aH call near ptr `@foo1` push ax mov ax,offset DGROUP:L$1 push ax call near ptr printf_ add sp,4 mov dx,14H mov ax,0aH call near ptr `@foo2` push ax mov ax,offset DGROUP:L$2 push ax call near ptr printf_ add sp,4 mov bx,1eH mov dx,14H mov ax,0aH call near ptr `@foo3` push ax mov ax,offset DGROUP:L$3 push ax call near ptr printf_ add sp,4 mov ax,28H push ax mov bx,1eH mov dx,14H mov ax,0aH call near ptr `@foo4` push ax mov ax,offset DGROUP:L$4 push ax call near ptr printf_ add sp,4 mov ax,32H push ax mov ax,28H push ax mov bx,1eH mov dx,14H mov ax,0aH call near ptr `@foo5` push ax mov ax,offset DGROUP:L$5 push ax call near ptr printf_ add sp,4 mov ax,3cH push ax mov ax,32H push ax mov ax,28H push ax mov bx,1eH mov dx,14H mov ax,0aH call near ptr `@foo6` push ax mov ax,offset DGROUP:L$6 push ax call near ptr printf_ add sp,4 xor ax,ax pop cx pop bx ret _TEXT ENDS CONST SEGMENT WORD PUBLIC USE16 'DATA' L$1: DB 66H, 6fH, 6fH, 31H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$2: DB 66H, 6fH, 6fH, 32H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$3: DB 66H, 6fH, 6fH, 33H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$4: DB 66H, 6fH, 6fH, 34H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$5: DB 66H, 6fH, 6fH, 35H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$6: DB 66H, 6fH, 6fH, 36H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 CONST ENDS CONST2 SEGMENT WORD PUBLIC USE16 'DATA' CONST2 ENDS _DATA SEGMENT WORD PUBLIC USE16 'DATA' _DATA ENDS END ||< * OpenWatcom Compiler(32bit) ※サンプルコードは [[620]] の callee.c, caller.c を使用。 : 指定方法 : #block||> int __fastcall foobar(); typedef int (__fastcall *ptr)(); ||< : コンパイラオプション : "-ecf" : 装飾名 : "@" + 関数名 + "@" + 引数のバイト数 (レジスタ渡しのバイト数 + スタックにPUSHされたバイト数) : 特記事項 : #block||> - 2つまでの引数はレジスタ経由(ECX, EDX)で渡される。 - 3つめ以降の引数はスタック上にPUSHする。(右から左へPUSH) ||< コンパイル&リンク&実行 #pre||> > wcc386 -od -d0 -ecf callee.c > wcc386 -od -d0 -ecf caller.c > wcl386 -fe=fastcall32.exe caller.obj callee.obj > fastcall32.exe foo1() = 20 foo2() = 30 foo3() = 60 foo4() = 100 foo5() = 150 foo6() = 210 ||< アセンブラ生成 #pre||> > wdis -a -l=callee.asm callee.obj > wdis -a -l=caller.asm caller.obj ||< ** callee.c のアセンブラ出力(callee.asm) #pre||> .387 .386p .model flat PUBLIC `@foo1@4` PUBLIC `@foo2@8` PUBLIC `@foo3@12` PUBLIC `@foo4@16` PUBLIC `@foo5@20` PUBLIC `@foo6@24` EXTRN __CHK:BYTE DGROUP GROUP CONST,CONST2,_DATA _TEXT SEGMENT BYTE PUBLIC USE32 'CODE' ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP `@foo1@4`: push 4 call near ptr FLAT:__CHK lea eax,[ecx+ecx] ret `@foo2@8`: push 4 call near ptr FLAT:__CHK lea eax,[ecx+edx] ret `@foo3@12`: push 4 call near ptr FLAT:__CHK lea eax,[ecx+edx] add eax,dword ptr 4[esp] ret 4 `@foo4@16`: push 4 call near ptr FLAT:__CHK add edx,ecx mov eax,dword ptr 4[esp] add eax,edx add eax,dword ptr 8[esp] ret 8 `@foo5@20`: push 4 call near ptr FLAT:__CHK add edx,ecx mov eax,dword ptr 4[esp] add eax,edx add eax,dword ptr 8[esp] add eax,dword ptr 0cH[esp] ret 0cH `@foo6@24`: push 4 call near ptr FLAT:__CHK add edx,ecx mov eax,dword ptr 4[esp] add eax,edx add eax,dword ptr 8[esp] add eax,dword ptr 0cH[esp] add eax,dword ptr 10H[esp] ret 10H _TEXT ENDS CONST SEGMENT DWORD PUBLIC USE32 'DATA' CONST ENDS CONST2 SEGMENT DWORD PUBLIC USE32 'DATA' CONST2 ENDS _DATA SEGMENT DWORD PUBLIC USE32 'DATA' _DATA ENDS END ||< ** caller.c のアセンブラ出力(caller.asm) #pre||> .387 .386p .model flat PUBLIC main_ EXTRN __CHK:BYTE EXTRN `@foo1@4`:BYTE EXTRN printf_:BYTE EXTRN `@foo2@8`:BYTE EXTRN `@foo3@12`:BYTE EXTRN `@foo4@16`:BYTE EXTRN `@foo5@20`:BYTE EXTRN `@foo6@24`:BYTE EXTRN __argc:BYTE EXTRN _cstart_:BYTE DGROUP GROUP CONST,CONST2,_DATA _TEXT SEGMENT BYTE PUBLIC USE32 'CODE' ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP main_: push 18H call near ptr FLAT:__CHK push ecx mov ecx,0aH call near ptr FLAT:`@foo1@4` push eax push offset FLAT:L$1 call near ptr FLAT:printf_ add esp,8 mov edx,14H mov ecx,0aH call near ptr FLAT:`@foo2@8` push eax push offset FLAT:L$2 call near ptr FLAT:printf_ add esp,8 push 1eH mov edx,14H mov ecx,0aH call near ptr FLAT:`@foo3@12` push eax push offset FLAT:L$3 call near ptr FLAT:printf_ add esp,8 push 28H push 1eH mov edx,14H mov ecx,0aH call near ptr FLAT:`@foo4@16` push eax push offset FLAT:L$4 call near ptr FLAT:printf_ add esp,8 push 32H push 28H push 1eH mov edx,14H mov ecx,0aH call near ptr FLAT:`@foo5@20` push eax push offset FLAT:L$5 call near ptr FLAT:printf_ add esp,8 push 3cH push 32H push 28H push 1eH mov edx,14H mov ecx,0aH call near ptr FLAT:`@foo6@24` push eax push offset FLAT:L$6 call near ptr FLAT:printf_ add esp,8 xor eax,eax pop ecx ret _TEXT ENDS CONST SEGMENT DWORD PUBLIC USE32 'DATA' L$1: DB 66H, 6fH, 6fH, 31H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$2: DB 66H, 6fH, 6fH, 32H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$3: DB 66H, 6fH, 6fH, 33H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$4: DB 66H, 6fH, 6fH, 34H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$5: DB 66H, 6fH, 6fH, 35H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 L$6: DB 66H, 6fH, 6fH, 36H, 28H, 29H, 20H, 3dH DB 20H, 25H, 64H, 0aH, 0 CONST ENDS CONST2 SEGMENT DWORD PUBLIC USE32 'DATA' CONST2 ENDS _DATA SEGMENT DWORD PUBLIC USE32 'DATA' _DATA ENDS END ||< * Turbo C++ 4.0J(TC4J) TC4Jで、コマンドラインからの呼び出し規約指定オプションが不明だった為、gcc系列と同様、CALLINGCONVを"-D"でdefineすることにより切り替える方式を採った。 なお、TC4Jについてはリンク&実行までは行っていない。 : 指定方法 : #block||> int __fastcall foobar(); typedef int (__fastcall *ptr)(); ||< : 装飾名 : "@" + 関数名 : 特記事項 : #block||> - 左から3つめ引数は左から順に AX, DX, ''BX'' レジスタ経由で渡される。 - 左から4つめ以降の引数はスタック上にPUSHする。( ''左から右へ'' PUSH) ||< アセンブラコードを生成 > tcc -DCALLINGCONV=__fastcall -S callee.c > tcc -DCALLINGCONV=__fastcall -S caller.c ** callee.c のアセンブラ出力(callee.asm) #pre||> .286p ifndef ??version ?debug macro endm publicdll macro name public name endm $comm macro name,dist,size,count comm dist name:BYTE:count*size endm else $comm macro name,dist,size,count comm dist name[size]:BYTE:count endm endif ?debug V 301h ?debug S "callee.c" ?debug C E91593723C0863616C6C65652E63 _TEXT segment byte public 'CODE' _TEXT ends DGROUP group _DATA,_BSS assume cs:_TEXT,ds:DGROUP _DATA segment word public 'DATA' d@ label byte d@w label word _DATA ends _BSS segment word public 'BSS' b@ label byte b@w label word _BSS ends _TEXT segment byte public 'CODE' ; ; int CALLINGCONV foo1(int a) { return a * 2; } ; assume cs:_TEXT,ds:DGROUP @foo1 proc near enter 2,0 mov word ptr [bp-2],ax mov ax,word ptr [bp-2] add ax,ax leave ret leave ret @foo1 endp ; ; int CALLINGCONV foo2(int a, int b) { return a + b; } ; assume cs:_TEXT,ds:DGROUP @foo2 proc near enter 2,0 mov word ptr [bp-2],ax mov ax,word ptr [bp-2] add ax,dx leave ret leave ret @foo2 endp ; ; int CALLINGCONV foo3(int a, int b, int c) { return a + b + c; } ; assume cs:_TEXT,ds:DGROUP @foo3 proc near enter 2,0 mov word ptr [bp-2],ax mov ax,word ptr [bp-2] add ax,dx add ax,bx leave ret leave ret @foo3 endp ; ; int CALLINGCONV foo4(int a, int b, int c, int d) { return a + b + c + d; } ; assume cs:_TEXT,ds:DGROUP @foo4 proc near enter 2,0 mov word ptr [bp-2],ax mov ax,word ptr [bp-2] add ax,dx add ax,bx add ax,word ptr [bp+4] leave ret 2 leave ret 2 @foo4 endp ?debug C E9 ?debug C FA00000000 _TEXT ends _DATA segment word public 'DATA' s@ label byte _DATA ends _TEXT segment byte public 'CODE' _TEXT ends _s@ equ s@ public @foo1 public @foo2 public @foo3 public @foo4 end ||< ** caller.c のアセンブラ出力(caller.asm) #pre||> .286p ifndef ??version ?debug macro endm publicdll macro name public name endm $comm macro name,dist,size,count comm dist name:BYTE:count*size endm else $comm macro name,dist,size,count comm dist name[size]:BYTE:count endm endif ?debug V 301h ?debug S "caller.c" ?debug C E9D2A0723C0863616C6C65722E63 ?debug C E900207F1E16433A5C5443345C494E434C5544455C737464696F2E+ ?debug C 68 ?debug C E900207F1E16433A5C5443345C494E434C5544455C5F646566732E+ ?debug C 68 ?debug C E900207F1E17433A5C5443345C494E434C5544455C5F6E66696C65+ ?debug C 2E68 ?debug C E900207F1E16433A5C5443345C494E434C5544455C5F6E756C6C2E+ ?debug C 68 _TEXT segment byte public 'CODE' _TEXT ends DGROUP group _DATA,_BSS assume cs:_TEXT,ds:DGROUP _DATA segment word public 'DATA' d@ label byte d@w label word _DATA ends _BSS segment word public 'BSS' b@ label byte b@w label word _BSS ends _TEXT segment byte public 'CODE' ; ; int __cdecl main(int argc, char *argv[]) ; assume cs:_TEXT,ds:DGROUP _main proc near push bp mov bp,sp ; ; { ; printf("foo1() = %d\n", foo1(10)); ; mov ax,10 call near ptr @foo1 push ax push offset DGROUP:s@ call near ptr _printf add sp,4 ; ; printf("foo2() = %d\n", foo2(10, 20)); ; mov dx,20 mov ax,10 call near ptr @foo2 push ax push offset DGROUP:s@+13 call near ptr _printf add sp,4 ; ; printf("foo3() = %d\n", foo3(10, 20, 30)); ; mov bx,30 mov dx,20 mov ax,10 call near ptr @foo3 push ax push offset DGROUP:s@+26 call near ptr _printf add sp,4 ; ; printf("foo4() = %d\n", foo4(10, 20, 30, 40)); ; push 40 mov bx,30 mov dx,20 mov ax,10 call near ptr @foo4 push ax push offset DGROUP:s@+39 call near ptr _printf add sp,4 ; ; return 0; ; xor ax,ax pop bp ret ; ; } ; pop bp ret _main endp ?debug C E9 ?debug C FA00000000 _TEXT ends _DATA segment word public 'DATA' s@ label byte db 'foo1() = %d' db 10 db 0 db 'foo2() = %d' db 10 db 0 db 'foo3() = %d' db 10 db 0 db 'foo4() = %d' db 10 db 0 _DATA ends _TEXT segment byte public 'CODE' _TEXT ends _s@ equ s@ extrn _printf:near extrn @foo1:near extrn @foo2:near extrn @foo3:near extrn @foo4:near public _main extrn __setargv__:far end ||< * サンプルコード2(gcc用) MinGWおよびLinuxのgcc用のサンプルコード。"CALLINGCONV"をコンパイルオプションで実行時に指定して、呼び出し規約を切り替える。 ** callee.c (呼ばれる関数側) #code|c|> int __attribute__((CALLINGCONV)) foo1(int a) { return a * 2; } int __attribute__((CALLINGCONV)) foo2(int a, int b) { return a + b; } int __attribute__((CALLINGCONV)) foo3(int a, int b, int c) { return a + b + c; } int __attribute__((CALLINGCONV)) foo4(int a, int b, int c, int d) { return a + b + c + d; } int __attribute__((CALLINGCONV)) foo5(int a, int b, int c, int d, int e) { return a + b + c + d + e; } ||< ** caller.c (呼ぶ main() 側) #code|c|> #include extern int __attribute__((CALLINGCONV)) foo1(int a); extern int __attribute__((CALLINGCONV)) foo2(int a, int b); extern int __attribute__((CALLINGCONV)) foo3(int a, int b, int c); extern int __attribute__((CALLINGCONV)) foo4(int a, int b, int c, int d); extern int __attribute__((CALLINGCONV)) foo5(int a, int b, int c, int d, int e); int __attribute__((cdecl)) main(int argc, char *argv[]) { printf("foo1() = %d\n", foo1(10)); printf("foo2() = %d\n", foo2(10, 20)); printf("foo3() = %d\n", foo3(10, 20, 30)); printf("foo4() = %d\n", foo4(10, 20, 30, 40)); printf("foo5() = %d\n", foo5(10, 20, 30, 40, 50)); return 0; } ||< * GCC-3.4.5, MinGW-5.1.6, MSYS 1.0 : 指定方法 : #block||> "__attribute__" を使うか、"__fastcall" を使う。なお "__fastcall" はプリプロセス時に "__attribute__" 版に変換される。 int __fastcall foo(); →プリプロセス後 int __attribute__((fastcall)) foo(); ||< : 装飾名 : "@" + 関数名 + "@" + 引数のバイト数 (レジスタ渡しのバイト数 + スタックにPUSHされたバイト数) : 特記事項 : #block||> - 2つまでの引数はレジスタ経由(ECX, EDX)で渡される。 - 3つめ以降の引数はスタック上にPUSHする。(右から左へPUSH) -- ''生成されたアセンブラ(caller.s)上はESPの相対アドレスを使ってMOVしているが、結果としてメモリレイアウト上は右から左へPUSHされた状態になる。'' - なおgcc側の仕様か、呼び出し側(main())で使用するローカル領域分、冒頭でまとめてalloca()でスタック上に確保してしまい、以降ESPは動かさずにESPからの相対アドレスを使ってスタック上に値を置いていく。 -- これにより、fastcallのサブルーチンの "RET imm16" で戻されたESPを ''わざわざSUB命令を使って、サブルーチンを呼ぶ前の位置に進めている'' 点に注意。 ||< コンパイル&リンク&実行 #pre||> $ gcc -O0 -Wall --save-temps -DCALLINGCONV=fastcall -c callee.c $ gcc -O0 -Wall --save-temps -DCALLINGCONV=fastcall -c caller.c $ gcc -o fastcall caller.o callee.o $ fastcall.exe foo1() = 20 foo2() = 30 foo3() = 60 foo4() = 100 foo5() = 150 ||< ** callee.c のアセンブラ出力(callee.s) #pre||> .file "callee.c" .text .globl @foo1@4 .def @foo1@4; .scl 2; .type 32; .endef @foo1@4: pushl %ebp movl %esp, %ebp subl $4, %esp movl %ecx, -4(%ebp) movl -4(%ebp), %eax addl %eax, %eax leave ret .globl @foo2@8 .def @foo2@8; .scl 2; .type 32; .endef @foo2@8: pushl %ebp movl %esp, %ebp subl $8, %esp movl %ecx, -4(%ebp) movl %edx, -8(%ebp) movl -8(%ebp), %eax addl -4(%ebp), %eax leave ret .globl @foo3@12 .def @foo3@12; .scl 2; .type 32; .endef @foo3@12: pushl %ebp movl %esp, %ebp subl $8, %esp movl %ecx, -4(%ebp) movl %edx, -8(%ebp) movl -8(%ebp), %eax addl -4(%ebp), %eax addl 8(%ebp), %eax leave ret $4 .globl @foo4@16 .def @foo4@16; .scl 2; .type 32; .endef @foo4@16: pushl %ebp movl %esp, %ebp subl $8, %esp movl %ecx, -4(%ebp) movl %edx, -8(%ebp) movl -8(%ebp), %eax addl -4(%ebp), %eax addl 8(%ebp), %eax addl 12(%ebp), %eax leave ret $8 .globl @foo5@20 .def @foo5@20; .scl 2; .type 32; .endef @foo5@20: pushl %ebp movl %esp, %ebp subl $8, %esp movl %ecx, -4(%ebp) movl %edx, -8(%ebp) movl -8(%ebp), %eax addl -4(%ebp), %eax addl 8(%ebp), %eax addl 12(%ebp), %eax addl 16(%ebp), %eax leave ret $12 ||< ** caller.c のアセンブラ出力(caller.s) #pre||> .file "caller.c" .def ___main; .scl 2; .type 32; .endef .section .rdata,"dr" LC0: .ascii "foo1() = %d\12\0" LC1: .ascii "foo2() = %d\12\0" LC2: .ascii "foo3() = %d\12\0" LC3: .ascii "foo4() = %d\12\0" LC4: .ascii "foo5() = %d\12\0" .text .globl _main .def _main; .scl 2; .type 32; .endef _main: pushl %ebp movl %esp, %ebp subl $24, %esp andl $-16, %esp movl $0, %eax addl $15, %eax addl $15, %eax shrl $4, %eax sall $4, %eax movl %eax, -4(%ebp) movl -4(%ebp), %eax call __alloca call ___main movl $10, %ecx call @foo1@4 movl %eax, 4(%esp) movl $LC0, (%esp) call _printf movl $20, %edx movl $10, %ecx call @foo2@8 movl %eax, 4(%esp) movl $LC1, (%esp) call _printf movl $30, (%esp) movl $20, %edx movl $10, %ecx call @foo3@12 subl $4, %esp movl %eax, 4(%esp) movl $LC2, (%esp) call _printf movl $40, 4(%esp) movl $30, (%esp) movl $20, %edx movl $10, %ecx call @foo4@16 subl $8, %esp movl %eax, 4(%esp) movl $LC3, (%esp) call _printf movl $50, 8(%esp) movl $40, 4(%esp) movl $30, (%esp) movl $20, %edx movl $10, %ecx call @foo5@20 subl $12, %esp movl %eax, 4(%esp) movl $LC4, (%esp) call _printf movl $0, %eax leave ret .def @foo5@20; .scl 2; .type 32; .endef .def @foo4@16; .scl 2; .type 32; .endef .def @foo3@12; .scl 2; .type 32; .endef .def @foo2@8; .scl 2; .type 32; .endef .def @foo1@4; .scl 2; .type 32; .endef .def _printf; .scl 2; .type 32; .endef ||< * GCC4.1.2 + binutils-2.7.50.0.6 (CentOS5.2) : 指定方法 : #block||> int __attribute__((fastcall)) foo(); ||< : 装飾名 : 装飾無し : 特記事項 : #block||> - 2つまでの引数はレジスタ経由(ECX, EDX)で渡される。 - 3つめ以降の引数はスタック上にPUSHする。(右から左へPUSH) -- ''生成されたアセンブラ(caller.s)上はESPの相対アドレスを使ってMOVしているが、結果としてメモリレイアウト上は右から左へPUSHされた状態になる。'' - なおgcc側の仕様か、呼び出し側(main())で使用するローカル領域分、冒頭でまとめてalloca()でスタック上に確保してしまい、以降ESPは動かさずにESPからの相対アドレスを使ってスタック上に値を置いていく。 -- これにより、fastcallのサブルーチンの "RET imm16" で戻されたESPを ''わざわざSUB命令を使って、サブルーチンを呼ぶ前の位置に進めている'' 点に注意。 ||< コンパイル&リンク&実行 #pre||> $ gcc -O0 -Wall --save-temps -DCALLINGCONV=fastcall -c callee.c $ gcc -O0 -Wall --save-temps -DCALLINGCONV=fastcall -c caller.c $ gcc -o fastcall caller.o callee.o $ ./fastcall foo1() = 20 foo2() = 30 foo3() = 60 foo4() = 100 foo5() = 150 ||< ** callee.c のアセンブラ出力(callee.s) #pre||> .file "callee.c" .text .globl foo1 .type foo1, @function foo1: pushl %ebp movl %esp, %ebp subl $4, %esp movl %ecx, -4(%ebp) movl -4(%ebp), %eax addl %eax, %eax leave ret .size foo1, .-foo1 .globl foo2 .type foo2, @function foo2: pushl %ebp movl %esp, %ebp subl $8, %esp movl %ecx, -4(%ebp) movl %edx, -8(%ebp) movl -8(%ebp), %eax addl -4(%ebp), %eax leave ret .size foo2, .-foo2 .globl foo3 .type foo3, @function foo3: pushl %ebp movl %esp, %ebp subl $8, %esp movl %ecx, -4(%ebp) movl %edx, -8(%ebp) movl -8(%ebp), %eax addl -4(%ebp), %eax addl 8(%ebp), %eax leave ret $4 .size foo3, .-foo3 .globl foo4 .type foo4, @function foo4: pushl %ebp movl %esp, %ebp subl $8, %esp movl %ecx, -4(%ebp) movl %edx, -8(%ebp) movl -8(%ebp), %eax addl -4(%ebp), %eax addl 8(%ebp), %eax addl 12(%ebp), %eax leave ret $8 .size foo4, .-foo4 .globl foo5 .type foo5, @function foo5: pushl %ebp movl %esp, %ebp subl $8, %esp movl %ecx, -4(%ebp) movl %edx, -8(%ebp) movl -8(%ebp), %eax addl -4(%ebp), %eax addl 8(%ebp), %eax addl 12(%ebp), %eax addl 16(%ebp), %eax leave ret $12 .size foo5, .-foo5 .ident "GCC: (GNU) 4.1.2 20071124 (Red Hat 4.1.2-42)" .section .note.GNU-stack,"",@progbits ||< ** caller.c のアセンブラ出力(caller.s) #pre||> .file "caller.c" .section .rodata .LC0: .string "foo1() = %d\n" .LC1: .string "foo2() = %d\n" .LC2: .string "foo3() = %d\n" .LC3: .string "foo4() = %d\n" .LC4: .string "foo5() = %d\n" .text .globl main .type main, @function main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $20, %esp movl $10, %ecx call foo1 movl %eax, 4(%esp) movl $.LC0, (%esp) call printf movl $20, %edx movl $10, %ecx call foo2 movl %eax, 4(%esp) movl $.LC1, (%esp) call printf movl $30, (%esp) movl $20, %edx movl $10, %ecx call foo3 subl $4, %esp movl %eax, 4(%esp) movl $.LC2, (%esp) call printf movl $40, 4(%esp) movl $30, (%esp) movl $20, %edx movl $10, %ecx call foo4 subl $8, %esp movl %eax, 4(%esp) movl $.LC3, (%esp) call printf movl $50, 8(%esp) movl $40, 4(%esp) movl $30, (%esp) movl $20, %edx movl $10, %ecx call foo5 subl $12, %esp movl %eax, 4(%esp) movl $.LC4, (%esp) call printf movl $0, %eax movl -4(%ebp), %ecx leave leal -4(%ecx), %esp ret .size main, .-main .ident "GCC: (GNU) 4.1.2 20071124 (Red Hat 4.1.2-42)" .section .note.GNU-stack,"",@progbits ||< #navi_footer|C言語系|