home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

C言語系/呼び出し規約/x86/fastcall

C言語系/呼び出し規約/x86/fastcall

C言語系 / 呼び出し規約 / x86 / fastcall
id: 618 所有者: msakamoto-sf    作成日: 2010-03-17 10:32:13
カテゴリ: Assembler C言語 UNIX Windows 

__fastcall呼び出し規約:

  • 一部の引数はレジスタ経由で渡される。
    • 残りの引数はスタック上にPUSHする。(主に右から左へPUSH)
  • (スタック渡しの引数がある場合は)関数(callee)側でスタックをクリーンアップする。
    • アセンブラレベルではRET imm16命令を使って、戻る時にスタックポインタを"imm16"バイト分だけ戻す。つまり、"imm16"バイト分だけSPが増える(x86ではスタックはアドレスの小さい方へ進んでいくので、「戻る」=SPのアドレス値は大きくなる)。
  • Borland C++ Compilerにおいて、Delphi互換の "__fastcall" と Microsoft互換の "__msfastcall" の二種類がある点に注意すること。("__fastcall"と"__msfastcall"との間には互換性は無い。)


サンプルコード

callee.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() 側)

#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;
}

VC++2008 Express Edtion

指定方法
int __fastcall foobar();
typedef int (__fastcall *ptr)();
コンパイラオプション
"/Gr"
装飾名
"@" + 関数名 + "@" + 引数のバイト数 (レジスタ渡しのバイト数 + スタックにPUSHされたバイト数)
特記事項
  • 2つまでの引数はレジスタ経由(ECX, EDX)で渡される。
  • 3つめ以降の引数はスタック上にPUSHする。(右から左へPUSH)

コンパイル&リンク&実行

> 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)

; 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)

; 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")

指定方法
int __fastcall foobar();
typedef int (__fastcall *ptr)();
コンパイラオプション
"-pr"
装飾名
"@" + 関数名
特記事項
  • 左から3つめまでの引数は左から順に EAX, EDX, ECX レジスタ経由で渡される。
  • 左から4つめ以降の引数はスタック上にPUSHする。(左から右へPUSH)

アセンブラコードを生成

> 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

callee.c のアセンブラ出力(callee.asm)

	.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)

	.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全体:

	.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と同じ。

指定方法
int __msfastcall foobar();
typedef int (__msfastcall *ptr)();
コンパイラオプション
"-pm"
装飾名
"@" + 関数名 + "@" + 引数のバイト数 (レジスタ渡しのバイト数 + スタックにPUSHされたバイト数)
特記事項
  • 2つまでの引数はレジスタ経由(ECX, EDX)で渡される。
  • 3つめ以降の引数はスタック上にPUSHする。(右から左へPUSH)

callee.c のアセンブラ出力(callee.asm)

	.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)

	.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)

※サンプルコードは C言語系/呼び出し規約/x86/fortran の callee.c, caller.c を使用。

指定方法
int __fastcall foobar();
typedef int (__fastcall *ptr)();
コンパイラオプション
"-ecf"
装飾名
"@" + 関数名
特記事項
  • 左から3つめ引数は左から順に AX, DX, BX レジスタ経由で渡される。
  • 左から4つめ以降の引数は右から左へスタック上にPUSHする。

コンパイル&リンク&実行

> 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

callee.c のアセンブラ出力(callee.asm)

.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)

.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)

※サンプルコードは C言語系/呼び出し規約/x86/fortran の callee.c, caller.c を使用。

指定方法
int __fastcall foobar();
typedef int (__fastcall *ptr)();
コンパイラオプション
"-ecf"
装飾名
"@" + 関数名 + "@" + 引数のバイト数 (レジスタ渡しのバイト数 + スタックにPUSHされたバイト数)
特記事項
  • 2つまでの引数はレジスタ経由(ECX, EDX)で渡される。
  • 3つめ以降の引数はスタック上にPUSHする。(右から左へPUSH)

コンパイル&リンク&実行

> 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

callee.c のアセンブラ出力(callee.asm)

.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)

.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についてはリンク&実行までは行っていない。

指定方法
int __fastcall foobar();
typedef int (__fastcall *ptr)();
装飾名
"@" + 関数名
特記事項
  • 左から3つめ引数は左から順に AX, DX,BXレジスタ経由で渡される。
  • 左から4つめ以降の引数はスタック上にPUSHする。(左から右へPUSH)

アセンブラコードを生成

> tcc -DCALLINGCONV=__fastcall -S callee.c
> tcc -DCALLINGCONV=__fastcall -S caller.c

callee.c のアセンブラ出力(callee.asm)

	.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)

	.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 (呼ばれる関数側)

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() 側)

#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;
}

GCC-3.4.5, MinGW-5.1.6, MSYS 1.0

指定方法

"__attribute__" を使うか、"__fastcall" を使う。なお "__fastcall" はプリプロセス時に "__attribute__" 版に変換される。

int __fastcall foo();
→プリプロセス後
int __attribute__((fastcall)) foo();
装飾名
"@" + 関数名 + "@" + 引数のバイト数 (レジスタ渡しのバイト数 + スタックにPUSHされたバイト数)
特記事項
  • 2つまでの引数はレジスタ経由(ECX, EDX)で渡される。
  • 3つめ以降の引数はスタック上にPUSHする。(右から左へPUSH)
    • 生成されたアセンブラ(caller.s)上はESPの相対アドレスを使ってMOVしているが、結果としてメモリレイアウト上は右から左へPUSHされた状態になる。
  • なおgcc側の仕様か、呼び出し側(main())で使用するローカル領域分、冒頭でまとめてalloca()でスタック上に確保してしまい、以降ESPは動かさずにESPからの相対アドレスを使ってスタック上に値を置いていく。
    • これにより、fastcallのサブルーチンの "RET imm16" で戻されたESPをわざわざSUB命令を使って、サブルーチンを呼ぶ前の位置に進めている点に注意。

コンパイル&リンク&実行

$ 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)

	.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)

	.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)

指定方法
int __attribute__((fastcall)) foo();
装飾名
装飾無し
特記事項
  • 2つまでの引数はレジスタ経由(ECX, EDX)で渡される。
  • 3つめ以降の引数はスタック上にPUSHする。(右から左へPUSH)
    • 生成されたアセンブラ(caller.s)上はESPの相対アドレスを使ってMOVしているが、結果としてメモリレイアウト上は右から左へPUSHされた状態になる。
  • なおgcc側の仕様か、呼び出し側(main())で使用するローカル領域分、冒頭でまとめてalloca()でスタック上に確保してしまい、以降ESPは動かさずにESPからの相対アドレスを使ってスタック上に値を置いていく。
    • これにより、fastcallのサブルーチンの "RET imm16" で戻されたESPをわざわざSUB命令を使って、サブルーチンを呼ぶ前の位置に進めている点に注意。

コンパイル&リンク&実行

$ 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)

	.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)

	.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


プレーンテキスト形式でダウンロード
現在のバージョン : 8
更新者: msakamoto-sf
更新日: 2010-03-19 17:07:07
md5:872e21c7b1f16514590a19e6a2fea7e7
sha1:ea3dabfc74086fb26a9262d646492cd50b326ea7
コメント
コメントを投稿するにはログインして下さい。