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

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

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

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

MinGW/MSYS

OpenWatcom Compiler(16bit)

OpenWatcom Compiler(32bit)

Turbo C++ 4.0J

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



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