__fastcall呼び出し規約:
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; }
#include <stdio.h> 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; }
int __fastcall foobar(); typedef int (__fastcall *ptr)();
コンパイル&リンク&実行
> 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
; 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
; 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
int __fastcall foobar(); typedef int (__fastcall *ptr)();
アセンブラコードを生成
> bcc32 -Od -pr -S callee.c > bcc32 -Od -pr -S caller.c
コンパイル&リンク&実行
> 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
.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
.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
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全体:
.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
装飾名や引数渡しに使うレジスタ、スタックPUSH方向などはVC++2008と同じ。
int __msfastcall foobar(); typedef int (__msfastcall *ptr)();
.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
.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
※サンプルコードは C言語系/呼び出し規約/x86/fortran の callee.c, caller.c を使用。
int __fastcall foobar(); typedef int (__fastcall *ptr)();
コンパイル&リンク&実行
> 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
アセンブラ生成
> wdis -a -l=callee.asm callee.obj > wdis -a -l=caller.asm caller.obj
.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
.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
※サンプルコードは C言語系/呼び出し規約/x86/fortran の callee.c, caller.c を使用。
int __fastcall foobar(); typedef int (__fastcall *ptr)();
コンパイル&リンク&実行
> 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
アセンブラ生成
> wdis -a -l=callee.asm callee.obj > wdis -a -l=caller.asm caller.obj
.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
.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
TC4Jで、コマンドラインからの呼び出し規約指定オプションが不明だった為、gcc系列と同様、CALLINGCONVを"-D"でdefineすることにより切り替える方式を採った。
なお、TC4Jについてはリンク&実行までは行っていない。
int __fastcall foobar(); typedef int (__fastcall *ptr)();
アセンブラコードを生成
> tcc -DCALLINGCONV=__fastcall -S callee.c > tcc -DCALLINGCONV=__fastcall -S caller.c
.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
.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
MinGWおよびLinuxのgcc用のサンプルコード。"CALLINGCONV"をコンパイルオプションで実行時に指定して、呼び出し規約を切り替える。
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; }
#include <stdio.h> 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; }
"__attribute__" を使うか、"__fastcall" を使う。なお "__fastcall" はプリプロセス時に "__attribute__" 版に変換される。
int __fastcall foo(); →プリプロセス後 int __attribute__((fastcall)) foo();
コンパイル&リンク&実行
$ 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
.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
.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
int __attribute__((fastcall)) foo();
コンパイル&リンク&実行
$ 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
.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
.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