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

C言語系/呼び出し規約/x86/cdecl (v3)

C言語系/呼び出し規約/x86/cdecl (v3)

C言語系 / 呼び出し規約 / x86 / cdecl (v3)
id: 617 所有者: msakamoto-sf    作成日: 2010-03-17 10:31:36
カテゴリ: Assembler C言語 UNIX Windows 

__cdecl呼び出し規約:

  • 引数はスタック上にPUSHする。(主に右から左へPUSH)
  • 関数を呼ぶ(caller)側でスタックをクリーンアップする。
    • アセンブラレベルではCALL命令の直後(=関数からRETした直後)に、スタックポインタ(SP)にPUSHした引数のバイト分だけ即値を加算(ADD)する。(x86ではスタックはアドレスの小さい方へ進んでいくので、「スタックのクリーンアップ」=「戻る」=SPのアドレス値を大きくする)。
  • 可変長引数を使う場合はこの呼び出し規約を使う必要がある。
    • 通常、引数がいくつPUSHされたのかを正確に判断出来るのは関数を呼ぶ側(caller)だけ。よってスタックのクリーンアップも関数を呼ぶ側(caller)で行う "__cdecl" を使う必要がある。
    • 装飾名に引数のスタック上のバイト数が含まれないのも、可変長引数を意識しているため。
  • UNIX系のシステムの場合は、基本的に "__cdecl" が使われる。
example:
PUSH arg3  ; 4byte
PUSH arg2  ; 2byte
PUSH arg1  ; 1byte
CALL _foobar
ADD ESP, 7 ; スタックポインタを7バイト戻す


サンプルコード

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 __cdecl foobar();
typedef int (__cdecl *ptr)();
コンパイラオプション
"/Gd",C言語で他の呼び出し規約が指定されていない場合のデフォルト
装飾名
"_" + 関数名

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

> cl /FAs /TC /Od /nologo /c /Gd /Focallee_cdecl.obj callee.c
> cl /FAs /TC /Od /nologo /c /Gd /Focaller_cdecl.obj caller.c
> link /SUBSYSTEM:CONSOLE /NOLOGO /OUT:cdecl.exe caller_cdecl.obj callee_cdecl.obj
> cdecl.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
; Function compile flags: /Odtp
; File c:\in_vitro\c\calling_conventions\callee.c
_TEXT	SEGMENT
_a$ = 8							; size = 4
_foo1	PROC

; 1    : int foo1(int a) { return a * 2; }

	push	ebp
	mov	ebp, esp
	mov	eax, DWORD PTR _a$[ebp]
	shl	eax, 1
	pop	ebp
	ret	0
_foo1	ENDP
_TEXT	ENDS
PUBLIC	_foo2
; Function compile flags: /Odtp
_TEXT	SEGMENT
_a$ = 8							; size = 4
_b$ = 12						; size = 4
_foo2	PROC

; 2    : int foo2(int a, int b) { return a + b; }

	push	ebp
	mov	ebp, esp
	mov	eax, DWORD PTR _a$[ebp]
	add	eax, DWORD PTR _b$[ebp]
	pop	ebp
	ret	0
_foo2	ENDP
_TEXT	ENDS
PUBLIC	_foo3
; Function compile flags: /Odtp
_TEXT	SEGMENT
_a$ = 8							; size = 4
_b$ = 12						; size = 4
_c$ = 16						; size = 4
_foo3	PROC

; 3    : int foo3(int a, int b, int c) { return a + b + c; }

	push	ebp
	mov	ebp, esp
	mov	eax, DWORD PTR _a$[ebp]
	add	eax, DWORD PTR _b$[ebp]
	add	eax, DWORD PTR _c$[ebp]
	pop	ebp
	ret	0
_foo3	ENDP
_TEXT	ENDS
PUBLIC	_foo4
; Function compile flags: /Odtp
_TEXT	SEGMENT
_a$ = 8							; size = 4
_b$ = 12						; size = 4
_c$ = 16						; size = 4
_d$ = 20						; size = 4
_foo4	PROC

; 4    : int foo4(int a, int b, int c, int d) { return a + b + c + d; }

	push	ebp
	mov	ebp, esp
	mov	eax, DWORD PTR _a$[ebp]
	add	eax, DWORD PTR _b$[ebp]
	add	eax, DWORD PTR _c$[ebp]
	add	eax, DWORD PTR _d$[ebp]
	pop	ebp
	ret	0
_foo4	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:PROC
EXTRN	_foo3:PROC
EXTRN	_foo2:PROC
EXTRN	_printf:PROC
EXTRN	_foo1: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));

	push	10					; 0000000aH
	call	_foo1
	add	esp, 4
	push	eax
	push	OFFSET $SG2496
	call	_printf
	add	esp, 8

; 11   : 	printf("foo2() = %d\n", foo2(10, 20));

	push	20					; 00000014H
	push	10					; 0000000aH
	call	_foo2
	add	esp, 8
	push	eax
	push	OFFSET $SG2497
	call	_printf
	add	esp, 8

; 12   : 	printf("foo3() = %d\n", foo3(10, 20, 30));

	push	30					; 0000001eH
	push	20					; 00000014H
	push	10					; 0000000aH
	call	_foo3
	add	esp, 12					; 0000000cH
	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
	push	20					; 00000014H
	push	10					; 0000000aH
	call	_foo4
	add	esp, 16					; 00000010H
	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

指定方法
int __cdecl foobar();
typedef int (__cdecl *ptr)();
コンパイラオプション
"-p-" or "-pc",C言語で他の呼び出し規約が指定されていない場合のデフォルト
装飾名
"_" + 関数名

アセンブラコードを生成

> bcc32 -Od -pc -S callee.c
> bcc32 -Od -pc -S caller.c

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

> bcc32 -Od -pc -c -ocallee_cdecl.obj callee.c
> bcc32 -Od -pc -c -ocaller_cdecl.obj caller.c
> ilink32 /c /ap /Tpe c0x32 caller_cdecl callee_cdecl, cdecl, , cw32 import32
> cdecl.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
@1:
	mov       eax,dword ptr [ebp+8]
	add       eax,eax
@3:
@2:
	pop       ebp
	ret 
_foo1	endp
_foo2	proc	near
?live1@32:
   ;	
   ;	int foo2(int a, int b) { return a + b; }
   ;	
	push      ebp
	mov       ebp,esp
@4:
	mov       eax,dword ptr [ebp+8]
	add       eax,dword ptr [ebp+12]
@6:
@5:
	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
@7:
	mov       eax,dword ptr [ebp+8]
	add       eax,dword ptr [ebp+12]
	add       eax,dword ptr [ebp+16]
@9:
@8:
	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
@10:
	mov       eax,dword ptr [ebp+8]
	add       eax,dword ptr [ebp+12]
	add       eax,dword ptr [ebp+16]
	add       eax,dword ptr [ebp+20]
@12:
@11:
	pop       ebp
	ret 
_foo4	endp
_TEXT	ends
	public	_foo1
	public	_foo2
	public	_foo3
	public	_foo4
	?debug	D "callee.c" 15473 24064
	end

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

※引数が一つの"foo1()"の呼び出しのみ、スタックのクリーンアップが

add       esp,4

ではなく、

pop       ecx

となっている。結果としては4バイトスタックポインタが巻戻り、ECXレジスタも他で特に使っていない為副作用も無い。最適化がONになっているためかと思い、"-Od"(最適化を無効)にしても変わらなかった。

	.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:
	push      10
	call      _foo1
	pop       ecx
	push      eax
	push      offset s@
	call      _printf
	add       esp,8
   ;	
   ;		printf("foo2() = %d\n", foo2(10, 20));
   ;	
	push      20
	push      10
	call      _foo2
	add       esp,8
	push      eax
	push      offset s@+13
	call      _printf
	add       esp,8
   ;	
   ;		printf("foo3() = %d\n", foo3(10, 20, 30));
   ;	
	push      30
	push      20
	push      10
	call      _foo3
	add       esp,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
	push      20
	push      10
	call      _foo4
	add       esp,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: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

MinGW/MSYS

OpenWatcom Compiler(16bit)

OpenWatcom Compiler(32bit)

Turbo C++ 4.0J

GCC4.1.2 + binutils-2.7.50.0.6 (CentOS5.2)



プレーンテキスト形式でダウンロード
表示中のバージョン : 3
現在のバージョン : 7
更新者: msakamoto-sf
更新日: 2010-03-19 17:30:32
md5:0984770484bfba81872db90e2c14a1f0
sha1:8101c970bc9113150caf8f175a17a006340bb65c
コメント
コメントを投稿するにはログインして下さい。