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

C言語系/呼び出し規約/x86/thiscall (v4)

C言語系/呼び出し規約/x86/thiscall (v4)

C言語系 / 呼び出し規約 / x86 / thiscall (v4)
id: 621 所有者: msakamoto-sf    作成日: 2010-03-17 10:33:52
カテゴリ: Assembler C言語 UNIX Windows 

thiscall呼び出し規約:

  • C++でクラスのメンバ関数(インスタンスメソッド)で使われる。
  • 引数はスタック上にPUSHする。(主に右から左へPUSH)
  • スタックのクリーンアップは、コンパイラや可変長引数の有無により異なる。
    • 関数(callee)側でスタックをクリーンアップする場合は、アセンブラレベルでは "RET imm16" 命令を使い、戻る時にスタックポインタを"imm16"バイト分だけ戻す。つまり、"imm16"バイト分だけSPが増える(x86ではスタックはアドレスの小さい方へ進んでいくので、「戻る」=SPのアドレス値は大きくなる)。
    • 関数を呼ぶ(caller)側でスタックをクリーンアップする場合は、アセンブラレベルではCALL命令の直後(=関数からRETした直後)に、スタックポインタ(SP)にPUSHした引数のバイト分だけ即値を加算 (ADD)する。(x86ではスタックはアドレスの小さい方へ進んでいくので、「スタックのクリーンアップ」=「戻る」=SPのアドレス値を大きくする)。
  • thiscall呼び出し規約はC++専用のため、C言語での他の呼び出し規約における装飾名のルールは使われず、C++でのmanglingがそのまま適用される。


サンプルコード

foo.cpp (メンバ関数の実装)

#include "foo.hpp"
 
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;
}

foo.hpp (クラス定義ヘッダーファイル)

#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 */

main.cpp (main()関数定義)

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

VC++2008 Express Edtion

可変長引数を使わない時
  • 引数は右から左へスタックにPUSHされる。
  • "this"ポインタはECX経由で渡される。
  • stdcallと同様、関数(callee)側でスタッククリーンアップする。
可変長引数を使う時
  • 引数は右から左へスタックにPUSHされる。
  • "this"ポインタは最後にスタックにPUSHされる。
  • cdeclと同様、関数を呼ぶ(caller)側でスタッククリーンアップする。

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

> 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

foo.cpp のアセンブラ出力(foo.asm)(抜粋)

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

main.cpp のアセンブラ出力(main.asm)(抜粋)

; 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

Borland C++ Compiler

  • 引数は右から左へスタックにPUSHされる。
  • "this"ポインタは最後にスタックにPUSHされる。
  • cdeclと同様、関数を呼ぶ(caller)側でスタッククリーンアップする。

※可変長引数の有無に依らない。

アセンブラコードを生成

> 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

foo.cpp のアセンブラ出力(foo.asm)(抜粋)

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

main.cpp のアセンブラ出力(main.asm)(抜粋)

   ;		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

OpenWatcom Compiler(16bit)

可変長引数を使わない時
  • 左から3つまでの引数は、左から順にDX, BX, CXレジスタ経由で渡される。
  • 左から4つ目以降の引数は、右から左へスタックへ積まれる。
  • "this"ポインタはAXレジスタ経由で渡される。
  • stdcallと同様、関数(callee)側でスタッククリーンアップする。

これは丁度 watcall(C言語系/呼び出し規約/x86/watcall)) 呼び出し規約で、"this"ポインタが暗黙的に第一引数に指定された状況と同じになる。watcallでは左から4つめまでの引数が AX, DX, BX, CX の順で渡されるが、最初のAXの分が"this"ポインタに指定され、以降一つ分ずれた形になる。

可変長引数を使う時
  • 引数は右から左へスタックにPUSHされる。
  • "this"ポインタは最後にスタックにPUSHされる。
  • cdeclと同様、関数を呼ぶ(caller)側でスタッククリーンアップする。

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

foo.cpp のアセンブラ出力(foo.asm)(抜粋)

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

main.cpp のアセンブラ出力(main.asm)(抜粋)

    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 

OpenWatcom Compiler(32bit)

foo.cpp のアセンブラ出力(foo.asm)(抜粋)

main.cpp のアセンブラ出力(main.asm)(抜粋)

Turbo C++ 4.0J

foo.cpp のアセンブラ出力(foo.asm)(抜粋)

main.cpp のアセンブラ出力(main.asm)(抜粋)

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

foo.cpp のアセンブラ出力(foo.asm)(抜粋)

main.cpp のアセンブラ出力(main.asm)(抜粋)



プレーンテキスト形式でダウンロード
表示中のバージョン : 4
現在のバージョン : 7
更新者: msakamoto-sf
更新日: 2010-03-27 13:00:27
md5:d063a2a1cf34469613fadcf169ff8d50
sha1:060badea4a1a340397a2283a70cbbd37fa6217f1
コメント
コメントを投稿するにはログインして下さい。