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

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

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

C言語系 / 呼び出し規約 / x86 / thiscall (v3)
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

MinGW/MSYS

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

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

OpenWatcom Compiler(16bit)

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

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

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)(抜粋)



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