thiscall呼び出し規約:
#include "foo.hpp" // Borland C++ Compilerで必要。VC++2008では有っても無くても影響なし。 // 他のコンパイラは不明。OpenWatcom, TurboC++, GCCでは有りで確認している。 using namespace std; foo::foo(int x) { this->x = x; } int foo::bar(int a, int b, int c, int d, int e) { return a + b + c + d + e + this->x; } int foo::baz(int argn, ...) { va_list ap; int r = this->x; va_start(ap, argn); for (int i = 0; i < argn; i++, va_arg(ap, int)) { r += *(int*)ap; } va_end(ap); return r; }
#include <cstdarg> #ifndef FOO_HPP #define FOO_HPP class foo { protected: int x; public: foo(int x); int bar(int a, int b, int c, int d, int e); int baz(int argn, ...); }; #endif /* FOO_HPP */
#include <iostream> #include "foo.hpp" using namespace std; int main(int argc, char *argv[]) { foo *f; f = new foo(10); int r = f->bar(1, 2, 3, 4, 5); cout << "foo::bar() = " << r << endl; r = f->baz(3, 10, 20, 30); cout << "foo::baz() = " << r << endl; return 0; }
コンパイル&リンク&実行
> cl /c /Od /FAs foo.cpp > cl /c /Od /FAs /EHsc main.cpp > cl main.obj foo.obj > main.exe foo::bar() = 25 foo::baz() = 70
(...)
PUBLIC ??0foo@@QAE@H@Z ; foo::foo
; Function compile flags: /Odtp
; File c:\in_vitro\c\cc_thiscall\foo.cpp
_TEXT SEGMENT
_this$ = -4 ; size = 4
_x$ = 8 ; size = 4
??0foo@@QAE@H@Z PROC ; foo::foo
; _this$ = ecx
; 3 : foo::foo(int x) { this->x = x; }
push ebp
mov ebp, esp
push ecx
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
mov ecx, DWORD PTR _x$[ebp]
mov DWORD PTR [eax], ecx
mov eax, DWORD PTR _this$[ebp]
mov esp, ebp
pop ebp
ret 4
??0foo@@QAE@H@Z ENDP ; foo::foo
_TEXT ENDS
PUBLIC ?bar@foo@@QAEHHHHHH@Z ; foo::bar
; Function compile flags: /Odtp
_TEXT SEGMENT
_this$ = -4 ; size = 4
_a$ = 8 ; size = 4
_b$ = 12 ; size = 4
_c$ = 16 ; size = 4
_d$ = 20 ; size = 4
_e$ = 24 ; size = 4
?bar@foo@@QAEHHHHHH@Z PROC ; foo::bar
; _this$ = ecx
; 5 : int foo::bar(int a, int b, int c, int d, int e) {
push ebp
mov ebp, esp
push ecx
mov DWORD PTR _this$[ebp], ecx
; 6 : return a + b + c + d + e + this->x;
mov eax, DWORD PTR _a$[ebp]
add eax, DWORD PTR _b$[ebp]
add eax, DWORD PTR _c$[ebp]
add eax, DWORD PTR _d$[ebp]
add eax, DWORD PTR _e$[ebp]
mov ecx, DWORD PTR _this$[ebp]
add eax, DWORD PTR [ecx]
; 7 : }
mov esp, ebp
pop ebp
ret 20 ; 00000014H
?bar@foo@@QAEHHHHHH@Z ENDP ; foo::bar
_TEXT ENDS
PUBLIC ?baz@foo@@QAAHHZZ ; foo::baz
; Function compile flags: /Odtp
_TEXT SEGMENT
_i$2881 = -12 ; size = 4
_ap$ = -8 ; size = 4
_r$ = -4 ; size = 4
_this$ = 8 ; size = 4
_argn$ = 12 ; size = 4
?baz@foo@@QAAHHZZ PROC ; foo::baz
; 8 : int foo::baz(int argn, ...) {
push ebp
mov ebp, esp
sub esp, 12 ; 0000000cH
; 9 : va_list ap;
; 10 : int r = this->x;
mov eax, DWORD PTR _this$[ebp]
mov ecx, DWORD PTR [eax]
mov DWORD PTR _r$[ebp], ecx
; 11 : va_start(ap, argn);
lea edx, DWORD PTR _argn$[ebp+4]
mov DWORD PTR _ap$[ebp], edx
; 12 : for (int i = 0; i < argn; i++, va_arg(ap, int)) {
mov DWORD PTR _i$2881[ebp], 0
jmp SHORT $LN3@baz
$LN2@baz:
mov eax, DWORD PTR _i$2881[ebp]
add eax, 1
mov DWORD PTR _i$2881[ebp], eax
mov ecx, DWORD PTR _ap$[ebp]
add ecx, 4
mov DWORD PTR _ap$[ebp], ecx
$LN3@baz:
mov edx, DWORD PTR _i$2881[ebp]
cmp edx, DWORD PTR _argn$[ebp]
jge SHORT $LN1@baz
; 13 : r += *(int*)ap;
mov eax, DWORD PTR _ap$[ebp]
mov ecx, DWORD PTR _r$[ebp]
add ecx, DWORD PTR [eax]
mov DWORD PTR _r$[ebp], ecx
; 14 : }
jmp SHORT $LN2@baz
$LN1@baz:
; 15 : va_end(ap);
mov DWORD PTR _ap$[ebp], 0
; 16 : return r;
mov eax, DWORD PTR _r$[ebp]
; 17 : }
mov esp, ebp
pop ebp
ret 0
?baz@foo@@QAAHHZZ ENDP ; foo::baz
_TEXT ENDS
(...)
; 11 : int r = f->bar(1, 2, 3, 4, 5); push 5 push 4 push 3 push 2 push 1 mov ecx, DWORD PTR _f$[ebp] call ?bar@foo@@QAEHHHHHH@Z ; foo::bar mov DWORD PTR _r$[ebp], eax
; 15 : r = f->baz(3, 10, 20, 30); push 30 ; 0000001eH push 20 ; 00000014H push 10 ; 0000000aH push 3 mov eax, DWORD PTR _f$[ebp] push eax call ?baz@foo@@QAAHHZZ ; foo::baz add esp, 20 ; 00000014H mov DWORD PTR _r$[ebp], eax
※可変長引数の有無に依らない。
アセンブラコードを生成
> bcc32 -Od -S foo.cpp > bcc32 -Od -S main.cpp
コンパイル&リンク&実行
> bcc32 -Od -c foo.cpp > bcc32 -Od -c main.cpp > bcc32 main.obj foo.obj > main.exe foo::bar() = 25 foo::baz() = 70
(...)
_TEXT segment dword public use32 'CODE'
@foo@$bctr$qi segment virtual
@@foo@$bctr$qi proc near
?live16385@0:
;
; foo::foo(int x) { this->x = x; }
;
push ebp
mov ebp,esp
add esp,-36
@1:
mov eax,offset @@_$ECTA$@foo@$bctr$qi
call @__InitExceptBlockLDTC
mov edx,dword ptr [ebp+12]
mov ecx,dword ptr [ebp+8]
mov dword ptr [ecx],edx
mov eax,dword ptr [ebp-36]
mov dword ptr fs:[0],eax
mov eax,dword ptr [ebp+8]
@3:
@2:
mov esp,ebp
pop ebp
ret
@@foo@$bctr$qi endp
@foo@$bctr$qi ends
_TEXT ends
_TEXT segment dword public use32 'CODE'
@foo@bar$qiiiii segment virtual
@@foo@bar$qiiiii proc near
?live16387@0:
;
; int foo::bar(int a, int b, int c, int d, int e) {
;
push ebp
mov ebp,esp
;
; return a + b + c + d + e + this->x;
;
@4:
mov eax,dword ptr [ebp+12]
add eax,dword ptr [ebp+16]
add eax,dword ptr [ebp+20]
add eax,dword ptr [ebp+24]
add eax,dword ptr [ebp+28]
mov edx,dword ptr [ebp+8]
add eax,dword ptr [edx]
;
; }
;
@6:
@5:
pop ebp
ret
@@foo@bar$qiiiii endp
@foo@bar$qiiiii ends
_TEXT ends
_TEXT segment dword public use32 'CODE'
@foo@baz$qie segment virtual
@@foo@baz$qie proc near
?live16388@0:
;
; int foo::baz(int argn, ...) {
;
push ebp
mov ebp,esp
add esp,-12
;
; va_list ap;
; int r = this->x;
;
@7:
mov eax,dword ptr [ebp+8]
mov edx,dword ptr [eax]
mov dword ptr [ebp-8],edx
;
; va_start(ap, argn);
;
lea ecx,dword ptr [ebp+16]
mov dword ptr [ebp-4],ecx
;
; for (int i = 0; i < argn; i++, va_arg(ap, int)) {
;
@8:
xor eax,eax
mov dword ptr [ebp-12],eax
mov edx,dword ptr [ebp-12]
cmp edx,dword ptr [ebp+12]
jge short @10
;
; r += *(int*)ap;
;
@9:
mov ecx,dword ptr [ebp-4]
mov eax,dword ptr [ecx]
add dword ptr [ebp-8],eax
@11:
inc dword ptr [ebp-12]
add dword ptr [ebp-4],4
mov edx,dword ptr [ebp-12]
cmp edx,dword ptr [ebp+12]
jl short @9
;
; }
; va_end(ap);
; return r;
;
@10:
mov eax,dword ptr [ebp-8]
;
; }
;
@14:
@13:
mov esp,ebp
pop ebp
ret
@@foo@baz$qie endp
@foo@baz$qie ends
_TEXT ends
(...)
; int r = f->bar(1, 2, 3, 4, 5); ; push 5 push 4 push 3 push 2 push 1 push dword ptr [ebp-44] call @@foo@bar$qiiiii add esp,24 mov dword ptr [ebp-48],eax
; r = f->baz(3, 10, 20, 30); ; push 30 push 20 push 10 push 3 push dword ptr [ebp-44] call @@foo@baz$qie add esp,20 mov dword ptr [ebp-48],eax
これは丁度 watcall(C言語系/呼び出し規約/x86/watcall)) 呼び出し規約で、"this"ポインタが暗黙的に第一引数に指定された状況と同じになる。watcallでは左から4つめまでの引数が AX, DX, BX, CX の順で渡されるが、最初のAXの分が"this"ポインタに指定され、以降一つ分ずれた形になる。
OpenWatcom Compiler(16bit)では、foo.cppのfoo::baz()メソッドで以下の修正を行う必要があった。
b = *(int*)ap;
→
b = **ap;
コンパイル&リンク&実行
> wpp -od -d0 foo.cpp > wpp -od -d0 main.cpp > wcl -fe=main16.exe main.obj foo.obj > main16.exe foo::bar() = 25 foo::baz() = 70
アセンブラ生成
> wdis -a -l=foo.asm foo.obj > wdis -a -l=main.asm main.obj
(...)
`W?$ct:foo$n(i)_`:
push ax
mov ax,12H
call near ptr __STK
pop ax
push bx
push cx
push si
push di
push bp
mov bp,sp
sub sp,6
mov word ptr -6[bp],ax
mov word ptr -4[bp],dx
mov ax,word ptr -4[bp]
mov bx,word ptr -6[bp]
mov word ptr [bx],ax
mov bx,word ptr -6[bp]
mov word ptr -2[bp],bx
mov ax,word ptr -2[bp]
mov sp,bp
pop bp
pop di
pop si
pop cx
pop bx
ret
`W?bar$:foo$n(iiiii)i`:
push ax
mov ax,12H
call near ptr __STK
pop ax
push si
push di
push bp
mov bp,sp
sub sp,0aH
mov word ptr -0aH[bp],ax
mov word ptr -8[bp],dx
mov word ptr -6[bp],bx
mov word ptr -4[bp],cx
mov bx,word ptr -8[bp]
add bx,word ptr -6[bp]
add bx,word ptr -4[bp]
add bx,word ptr 8[bp]
mov ax,word ptr 0aH[bp]
add ax,bx
mov bx,word ptr -0aH[bp]
mov bx,word ptr [bx]
add bx,ax
mov word ptr -2[bp],bx
mov ax,word ptr -2[bp]
mov sp,bp
pop bp
pop di
pop si
ret 4
`W?baz$:foo$n(ie)i`:
mov ax,16H
call near ptr __STK
push bx
push cx
push dx
push si
push di
push bp
mov bp,sp
sub sp,8
mov bx,word ptr 0eH[bp]
mov ax,word ptr [bx]
mov word ptr -8[bp],ax
mov word ptr -4[bp],0
lea ax,12H[bp]
mov word ptr -6[bp],ax
mov word ptr -2[bp],0
jmp L$2
L$1:
inc word ptr -2[bp]
add word ptr -6[bp],2
L$2:
mov ax,word ptr -2[bp]
cmp ax,word ptr 10H[bp]
jge L$3
mov bx,word ptr -6[bp]
mov al,byte ptr [bx]
xor ah,ah
mov word ptr -4[bp],ax
mov ax,word ptr -4[bp]
add word ptr -8[bp],ax
jmp L$1
L$3:
mov word ptr -6[bp],0
mov ax,word ptr -8[bp]
mov sp,bp
pop bp
pop di
pop si
pop dx
pop cx
pop bx
ret
(...)
mov ax,5
push ax
mov ax,4
push ax
mov cx,3
mov bx,2
mov dx,1
mov ax,word ptr -22H[bp]
call near ptr `W?bar$:foo$n(iiiii)i`
mov ax,1eH
push ax
mov ax,14H
push ax
mov ax,0aH
push ax
mov ax,3
push ax
push word ptr -22H[bp]
call near ptr `W?baz$:foo$n(ie)i`
add sp,0aH
これは丁度 watcall(C言語系/呼び出し規約/x86/watcall)) 呼び出し規約で、"this"ポインタが暗黙的に第一引数に指定された状況と同じになる。watcallでは左から4つめまでの引数が EAX, EDX, EBX, ECX の順で渡されるが、最初のEAXの分が"this"ポインタに指定され、以降一つ分ずれた形になる。
OpenWatcom Compiler(32bit)では、foo.cppのfoo::baz()メソッドで以下の修正を行う必要があった。
b = *(int*)ap;
→
b = **ap;
コンパイル&リンク&実行
> wpp386 -od -d0 foo.cpp > wpp386 -od -d0 main.cpp > wcl386 -fe=main32.exe main.obj foo.obj > main32.exe foo::bar() = 25 foo::baz() = 70
アセンブラ生成
> wdis -a -l=foo.asm foo.obj > wdis -a -l=main.asm main.obj
(...)
`W?bar$:foo$n(iiiii)i`:
push 24H
call near ptr FLAT:__CHK
push esi
push edi
push ebp
mov ebp,esp
sub esp,14H
mov dword ptr -14H[ebp],eax
mov dword ptr -10H[ebp],edx
mov dword ptr -0cH[ebp],ebx
mov dword ptr -8[ebp],ecx
mov eax,dword ptr -10H[ebp]
add eax,dword ptr -0cH[ebp]
add eax,dword ptr -8[ebp]
add eax,dword ptr 10H[ebp]
mov edx,dword ptr 14H[ebp]
add edx,eax
mov eax,dword ptr -14H[ebp]
mov eax,dword ptr [eax]
add eax,edx
mov dword ptr -4[ebp],eax
mov eax,dword ptr -4[ebp]
mov esp,ebp
pop ebp
pop edi
pop esi
ret 8
`W?baz$:foo$n(ie)i`:
push 2cH
call near ptr FLAT:__CHK
push ebx
push ecx
push edx
push esi
push edi
push ebp
mov ebp,esp
sub esp,10H
mov eax,dword ptr 1cH[ebp]
mov eax,dword ptr [eax]
mov dword ptr -10H[ebp],eax
mov dword ptr -8[ebp],0
lea eax,24H[ebp]
mov dword ptr -0cH[ebp],eax
mov dword ptr -4[ebp],0
jmp L$2
L$1:
inc dword ptr -4[ebp]
add dword ptr -0cH[ebp],4
L$2:
mov eax,dword ptr -4[ebp]
cmp eax,dword ptr 20H[ebp]
jge L$3
mov eax,dword ptr -0cH[ebp]
movzx eax,byte ptr [eax]
mov dword ptr -8[ebp],eax
mov eax,dword ptr -8[ebp]
add dword ptr -10H[ebp],eax
jmp L$1
L$3:
mov dword ptr -0cH[ebp],0
mov eax,dword ptr -10H[ebp]
mov esp,ebp
pop ebp
pop edi
pop esi
pop edx
pop ecx
pop ebx
ret
(...)
push 5
push 4
mov ecx,3
mov ebx,2
mov edx,1
mov eax,dword ptr -40H[ebp]
call near ptr FLAT:`W?bar$:foo$n(iiiii)i`
push 1eH
push 14H
push 0aH
push 3
push dword ptr -40H[ebp]
call near ptr FLAT:`W?baz$:foo$n(ie)i`
add esp,14H
※可変長引数の有無に依らない。
※"using namespace"を削除、"cstdarg" → "stdarg.h" に修正して確認。(これらを行わないと、コンパイルエラーになってしまう)
※TC4Jについてはリンク&実行までは行っていない。
アセンブラコードを生成
> tcc -S foo.cpp > tcc -S main.cpp
(...)
;
; int foo::bar(int a, int b, int c, int d, int e) {
;
assume cs:_TEXT,ds:DGROUP
@foo@bar$qiiiii proc near
push bp
mov bp,sp
push si
mov si,word ptr [bp+4]
;
; return a + b + c + d + e + this->x;
;
mov ax,word ptr [bp+6]
add ax,word ptr [bp+8]
add ax,word ptr [bp+10]
add ax,word ptr [bp+12]
add ax,word ptr [bp+14]
add ax,word ptr [si]
jmp short @2@58
@2@58:
;
; }
;
pop si
pop bp
ret
@foo@bar$qiiiii endp
;
; int foo::baz(int argn, ...) {
;
assume cs:_TEXT,ds:DGROUP
@foo@baz$qie proc near
enter 2,0
push si
push di
mov si,word ptr [bp+4]
;
; va_list ap;
; int r = this->x;
;
mov dx,word ptr [si]
;
; int b = 0;
;
xor di,di
;
; va_start(ap, argn);
;
lea ax,word ptr [bp+8]
mov word ptr [bp-2],ax
;
; for (int i = 0; i < argn; i++, va_arg(ap, int)) {
;
xor cx,cx
jmp short @3@114
@3@58:
;
; b = *(int*)ap;
;
mov bx,word ptr [bp-2]
mov di,word ptr [bx]
;
; r += b;
;
add dx,di
inc cx
add word ptr [bp-2],2
mov bx,word ptr [bp-2]
@3@114:
cmp cx,word ptr [bp+6]
jl short @3@58
;
; }
; va_end(ap);
;
;
; return r;
;
mov ax,dx
jmp short @3@170
@3@170:
;
; }
;
pop di
pop si
leave
ret
@foo@baz$qie endp
(...)
; ; ; int r = f->bar(1, 2, 3, 4, 5); ; push 5 push 4 push 3 push 2 push 1 ; この上で、thisポインタがDIレジスタに格納済 push di call near ptr @foo@bar$qiiiii add sp,12
; ; ; r = f->baz(3, 10, 20, 30); ; push 30 push 20 push 10 push 3 ; この上で、thisポインタがDIレジスタに格納済 push di call near ptr @foo@baz$qie add sp,10
※可変長引数の有無に依らない。
コンパイル&リンク&実行
$ g++ -O0 -Wall --save-temps -c foo.cpp $ g++ -O0 -Wall --save-temps -c main.cpp $ g++ -o main main.o foo.o $ ./main foo::bar() = 25 foo::baz() = 70
(...) .globl _ZN3foo3barEiiiii .type _ZN3foo3barEiiiii, @function _ZN3foo3barEiiiii: .LFB5: pushl %ebp .LCFI4: movl %esp, %ebp .LCFI5: movl 16(%ebp), %eax addl 12(%ebp), %eax addl 20(%ebp), %eax addl 24(%ebp), %eax movl %eax, %edx addl 28(%ebp), %edx movl 8(%ebp), %eax movl (%eax), %eax leal (%edx,%eax), %eax popl %ebp ret .LFE5: .size _ZN3foo3barEiiiii, .-_ZN3foo3barEiiiii .align 2 .globl _ZN3foo3bazEiz .type _ZN3foo3bazEiz, @function _ZN3foo3bazEiz: .LFB6: pushl %ebp .LCFI6: movl %esp, %ebp .LCFI7: subl $16, %esp .LCFI8: movl 8(%ebp), %eax movl (%eax), %eax movl %eax, -12(%ebp) movl $0, -8(%ebp) leal 16(%ebp), %eax movl %eax, -16(%ebp) movl $0, -4(%ebp) jmp .L8 .L9: movl -16(%ebp), %eax movl (%eax), %eax movl %eax, -8(%ebp) movl -8(%ebp), %eax addl %eax, -12(%ebp) addl $1, -4(%ebp) movl -16(%ebp), %eax addl $4, %eax movl %eax, -16(%ebp) .L8: movl -4(%ebp), %eax cmpl 12(%ebp), %eax jl .L9 movl -12(%ebp), %eax leave ret .LFE6: .size _ZN3foo3bazEiz, .-_ZN3foo3bazEiz (...)
movl $5, 20(%esp) movl $4, 16(%esp) movl $3, 12(%esp) movl $2, 8(%esp) movl $1, 4(%esp) movl -16(%ebp), %eax movl %eax, (%esp) .LEHB2: call _ZN3foo3barEiiiii
movl $30, 16(%esp) movl $20, 12(%esp) movl $10, 8(%esp) movl $3, 4(%esp) movl -16(%ebp), %eax movl %eax, (%esp) call _ZN3foo3bazEiz