__cdecl呼び出し規約:
example: PUSH arg3 ; 4byte PUSH arg2 ; 2byte PUSH arg1 ; 1byte CALL _foobar ADD ESP, 7 ; スタックポインタを7バイト戻す
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 __cdecl foobar(); typedef int (__cdecl *ptr)();
コンパイル&リンク&実行
> 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
; 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
; 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
int __cdecl foobar(); typedef int (__cdecl *ptr)();
アセンブラコードを生成
> 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
.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
※引数が一つの"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
※サンプルコードは C言語系/呼び出し規約/x86/fortran の callee.c, caller.c を使用。
int __cdecl foobar(); typedef int (__cdecl *ptr)();
コンパイル&リンク&実行
> wcc -od -d0 -ecc callee.c > wcc -od -d0 -ecc caller.c > wcl -fe=cdecl16.exe caller.obj callee.obj > cdecl16.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:
mov ax,4
call near ptr __STK
push bp
mov bp,sp
mov ax,word ptr 4[bp]
shl ax,1
pop bp
ret
_foo2:
mov ax,4
call near ptr __STK
push bp
mov bp,sp
mov ax,word ptr 4[bp]
add ax,word ptr 6[bp]
pop bp
ret
_foo3:
mov ax,4
call near ptr __STK
push bp
mov bp,sp
mov ax,word ptr 4[bp]
add ax,word ptr 6[bp]
add ax,word ptr 8[bp]
pop bp
ret
_foo4:
mov ax,4
call near ptr __STK
push bp
mov bp,sp
mov ax,word ptr 4[bp]
add ax,word ptr 6[bp]
add ax,word ptr 8[bp]
add ax,word ptr 0aH[bp]
pop bp
ret
_foo5:
mov ax,4
call near ptr __STK
push bp
mov bp,sp
mov ax,word ptr 4[bp]
add ax,word ptr 6[bp]
add ax,word ptr 8[bp]
add ax,word ptr 0aH[bp]
add ax,word ptr 0cH[bp]
pop bp
ret
_foo6:
mov ax,4
call near ptr __STK
push bp
mov bp,sp
mov ax,word ptr 4[bp]
add ax,word ptr 6[bp]
add ax,word ptr 8[bp]
add ax,word ptr 0aH[bp]
add ax,word ptr 0cH[bp]
add ax,word ptr 0eH[bp]
pop bp
ret
_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,12H
call near ptr __STK
pop ax
push bx
push cx
mov ax,0aH
push ax
call near ptr _foo1
add sp,2
push ax
mov ax,offset DGROUP:L$1
push ax
call near ptr printf_
add sp,4
mov ax,14H
push ax
mov ax,0aH
push ax
call near ptr _foo2
add sp,4
push ax
mov ax,offset DGROUP:L$2
push ax
call near ptr printf_
add sp,4
mov ax,1eH
push ax
mov ax,14H
push ax
mov ax,0aH
push ax
call near ptr _foo3
add sp,6
push ax
mov ax,offset DGROUP:L$3
push ax
call near ptr printf_
add sp,4
mov ax,28H
push ax
mov ax,1eH
push ax
mov ax,14H
push ax
mov ax,0aH
push ax
call near ptr _foo4
add sp,8
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 ax,1eH
push ax
mov ax,14H
push ax
mov ax,0aH
push ax
call near ptr _foo5
add sp,0aH
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 ax,1eH
push ax
mov ax,14H
push ax
mov ax,0aH
push ax
call near ptr _foo6
add sp,0cH
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 __cdecl foobar(); typedef int (__cdecl *ptr)();
コンパイル&リンク&実行
> wcc386 -od -d0 -ecc callee.c > wcc386 -od -d0 -ecc caller.c > wcl386 -fe=cdecl32.exe caller.obj callee.obj > cdecl32.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
PUBLIC _foo2
PUBLIC _foo3
PUBLIC _foo4
PUBLIC _foo5
PUBLIC _foo6
EXTRN __CHK:BYTE
DGROUP GROUP CONST,CONST2,_DATA
_TEXT SEGMENT BYTE PUBLIC USE32 'CODE'
ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP
_foo1:
push 4
call near ptr FLAT:__CHK
mov eax,dword ptr 4[esp]
add eax,eax
ret
_foo2:
push 4
call near ptr FLAT:__CHK
mov eax,dword ptr 4[esp]
add eax,dword ptr 8[esp]
ret
_foo3:
push 4
call near ptr FLAT:__CHK
mov eax,dword ptr 4[esp]
add eax,dword ptr 8[esp]
add eax,dword ptr 0cH[esp]
ret
_foo4:
push 4
call near ptr FLAT:__CHK
mov eax,dword ptr 4[esp]
add eax,dword ptr 8[esp]
add eax,dword ptr 0cH[esp]
add eax,dword ptr 10H[esp]
ret
_foo5:
push 4
call near ptr FLAT:__CHK
mov eax,dword ptr 4[esp]
add eax,dword ptr 8[esp]
add eax,dword ptr 0cH[esp]
add eax,dword ptr 10H[esp]
add eax,dword ptr 14H[esp]
ret
_foo6:
push 4
call near ptr FLAT:__CHK
mov eax,dword ptr 4[esp]
add eax,dword ptr 8[esp]
add eax,dword ptr 0cH[esp]
add eax,dword ptr 10H[esp]
add eax,dword ptr 14H[esp]
add eax,dword ptr 18H[esp]
ret
_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:BYTE
EXTRN printf_:BYTE
EXTRN _foo2:BYTE
EXTRN _foo3:BYTE
EXTRN _foo4:BYTE
EXTRN _foo5:BYTE
EXTRN _foo6: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 20H
call near ptr FLAT:__CHK
push ecx
push 0aH
call near ptr FLAT:_foo1
add esp,4
push eax
push offset FLAT:L$1
call near ptr FLAT:printf_
add esp,8
push 14H
push 0aH
call near ptr FLAT:_foo2
add esp,8
push eax
push offset FLAT:L$2
call near ptr FLAT:printf_
add esp,8
push 1eH
push 14H
push 0aH
call near ptr FLAT:_foo3
add esp,0cH
push eax
push offset FLAT:L$3
call near ptr FLAT:printf_
add esp,8
push 28H
push 1eH
push 14H
push 0aH
call near ptr FLAT:_foo4
add esp,10H
push eax
push offset FLAT:L$4
call near ptr FLAT:printf_
add esp,8
push 32H
push 28H
push 1eH
push 14H
push 0aH
call near ptr FLAT:_foo5
add esp,14H
push eax
push offset FLAT:L$5
call near ptr FLAT:printf_
add esp,8
push 3cH
push 32H
push 28H
push 1eH
push 14H
push 0aH
call near ptr FLAT:_foo6
add esp,18H
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 __cdecl foobar(); typedef int (__cdecl *ptr)();
アセンブラコードを生成
> tcc -DCALLINGCONV=__cdecl -S callee.c > tcc -DCALLINGCONV=__cdecl -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
push bp
mov bp,sp
mov ax,word ptr [bp+4]
add ax,ax
pop bp
ret
pop bp
ret
_foo1 endp
;
; int CALLINGCONV foo2(int a, int b) { return a + b; }
;
assume cs:_TEXT,ds:DGROUP
_foo2 proc near
push bp
mov bp,sp
mov ax,word ptr [bp+4]
add ax,word ptr [bp+6]
pop bp
ret
pop bp
ret
_foo2 endp
;
; int CALLINGCONV foo3(int a, int b, int c) { return a + b + c; }
;
assume cs:_TEXT,ds:DGROUP
_foo3 proc near
push bp
mov bp,sp
mov ax,word ptr [bp+4]
add ax,word ptr [bp+6]
add ax,word ptr [bp+8]
pop bp
ret
pop bp
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
push bp
mov bp,sp
mov ax,word ptr [bp+4]
add ax,word ptr [bp+6]
add ax,word ptr [bp+8]
add ax,word ptr [bp+10]
pop bp
ret
pop bp
ret
_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));
;
push 10
call near ptr _foo1
pop cx
push ax
push offset DGROUP:s@
call near ptr _printf
add sp,4
;
; printf("foo2() = %d\n", foo2(10, 20));
;
push 20
push 10
call near ptr _foo2
add sp,4
push ax
push offset DGROUP:s@+13
call near ptr _printf
add sp,4
;
; printf("foo3() = %d\n", foo3(10, 20, 30));
;
push 30
push 20
push 10
call near ptr _foo3
add sp,6
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
push 30
push 20
push 10
call near ptr _foo4
add sp,8
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; }
GCCの場合は、特に指定しない場合のデフォルトがcdecl呼び出し規約となる。
明示したい場合は "__attribute__" を使うか、"__cdecl" を使う。なお "__cdecl" はプリプロセス時に "__attribute__" 版に変換される。
int __cdecl foo(); →プリプロセス後 int __attribute__((cdecl)) foo();
コンパイル&リンク&実行
$ gcc -O0 -Wall --save-temps -DCALLINGCONV=cdecl -c callee.c $ gcc -O0 -Wall --save-temps -DCALLINGCONV=cdecl -c caller.c $ gcc -o cdecl caller.o callee.o $ cdecl.exe foo1() = 20 foo2() = 30 foo3() = 60 foo4() = 100 foo5() = 150
.file "callee.c" .text .globl _foo1 .def _foo1; .scl 2; .type 32; .endef _foo1: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax addl %eax, %eax popl %ebp ret .globl _foo2 .def _foo2; .scl 2; .type 32; .endef _foo2: pushl %ebp movl %esp, %ebp movl 12(%ebp), %eax addl 8(%ebp), %eax popl %ebp ret .globl _foo3 .def _foo3; .scl 2; .type 32; .endef _foo3: pushl %ebp movl %esp, %ebp movl 12(%ebp), %eax addl 8(%ebp), %eax addl 16(%ebp), %eax popl %ebp ret .globl _foo4 .def _foo4; .scl 2; .type 32; .endef _foo4: pushl %ebp movl %esp, %ebp movl 12(%ebp), %eax addl 8(%ebp), %eax addl 16(%ebp), %eax addl 20(%ebp), %eax popl %ebp ret .globl _foo5 .def _foo5; .scl 2; .type 32; .endef _foo5: pushl %ebp movl %esp, %ebp movl 12(%ebp), %eax addl 8(%ebp), %eax addl 16(%ebp), %eax addl 20(%ebp), %eax addl 24(%ebp), %eax popl %ebp ret
.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, (%esp) call _foo1 movl %eax, 4(%esp) movl $LC0, (%esp) call _printf movl $20, 4(%esp) movl $10, (%esp) call _foo2 movl %eax, 4(%esp) movl $LC1, (%esp) call _printf movl $30, 8(%esp) movl $20, 4(%esp) movl $10, (%esp) call _foo3 movl %eax, 4(%esp) movl $LC2, (%esp) call _printf movl $40, 12(%esp) movl $30, 8(%esp) movl $20, 4(%esp) movl $10, (%esp) call _foo4 movl %eax, 4(%esp) movl $LC3, (%esp) call _printf movl $50, 16(%esp) movl $40, 12(%esp) movl $30, 8(%esp) movl $20, 4(%esp) movl $10, (%esp) call _foo5 movl %eax, 4(%esp) movl $LC4, (%esp) call _printf movl $0, %eax leave ret .def _foo5; .scl 2; .type 32; .endef .def _foo4; .scl 2; .type 32; .endef .def _foo3; .scl 2; .type 32; .endef .def _foo2; .scl 2; .type 32; .endef .def _foo1; .scl 2; .type 32; .endef .def _printf; .scl 2; .type 32; .endef
GCCの場合は、特に指定しない場合のデフォルトがcdecl呼び出し規約となる。
int __attribute__((cdecl)) foo();
コンパイル&リンク&実行
$ gcc -O0 -Wall --save-temps -DCALLINGCONV=cdecl -c callee.c $ gcc -O0 -Wall --save-temps -DCALLINGCONV=cdecl -c caller.c $ gcc -o cdecl caller.o callee.o $ ./cdecl 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 movl 8(%ebp), %eax addl %eax, %eax popl %ebp ret .size foo1, .-foo1 .globl foo2 .type foo2, @function foo2: pushl %ebp movl %esp, %ebp movl 12(%ebp), %eax addl 8(%ebp), %eax popl %ebp ret .size foo2, .-foo2 .globl foo3 .type foo3, @function foo3: pushl %ebp movl %esp, %ebp movl 12(%ebp), %eax addl 8(%ebp), %eax addl 16(%ebp), %eax popl %ebp ret .size foo3, .-foo3 .globl foo4 .type foo4, @function foo4: pushl %ebp movl %esp, %ebp movl 12(%ebp), %eax addl 8(%ebp), %eax addl 16(%ebp), %eax addl 20(%ebp), %eax popl %ebp ret .size foo4, .-foo4 .globl foo5 .type foo5, @function foo5: pushl %ebp movl %esp, %ebp movl 12(%ebp), %eax addl 8(%ebp), %eax addl 16(%ebp), %eax addl 20(%ebp), %eax addl 24(%ebp), %eax popl %ebp ret .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, (%esp) call foo1 movl %eax, 4(%esp) movl $.LC0, (%esp) call printf movl $20, 4(%esp) movl $10, (%esp) call foo2 movl %eax, 4(%esp) movl $.LC1, (%esp) call printf movl $30, 8(%esp) movl $20, 4(%esp) movl $10, (%esp) call foo3 movl %eax, 4(%esp) movl $.LC2, (%esp) call printf movl $40, 12(%esp) movl $30, 8(%esp) movl $20, 4(%esp) movl $10, (%esp) call foo4 movl %eax, 4(%esp) movl $.LC3, (%esp) call printf movl $50, 16(%esp) movl $40, 12(%esp) movl $30, 8(%esp) movl $20, 4(%esp) movl $10, (%esp) call foo5 movl %eax, 4(%esp) movl $.LC4, (%esp) call printf movl $0, %eax addl $20, %esp popl %ecx popl %ebp 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