D 1.0   D 2.0
About Japanese Translation

Last update Tue Nov 9 13:50:05 2010

D言語 アプリケーションバイナリインターフェイス

D ABI(アプリケーション・バイナリインターフェイス) に従ったDの実装は、 他の実装によって生成されたライブラリや DLL などと相互利用することが可能なバイナリを出力します。

C ABI

この仕様で C ABI として参照するのは、対象とするシステムの C言語のアプリケーション・バイナリインターフェイスです。 C と D のコードは制限無くリンクでき、特に、D のコードからは C の ABI を持ったランタイムライブラリには完全にアクセス可能であるべきです。

エンディアン

データレイアウトの エンディアン (バイトオーダー) は、 対象マシンのエンディアンに従います。 Intel x86 CPU では、 値 0x0A0B0C0D がメモリ上で 0D 0C 0B 0A と表現される リトルエンディアン になります。

基本型

bool
8 bit byte。0 が false、1 が true を表す
byte
8 bit signed value
ubyte
8 bit unsigned value
short
16 bit signed value
ushort
16 bit unsigned value
int
32 bit signed value
uint
32 bit unsigned value
long
64 bit signed value
ulong
64 bit unsigned value
cent
128 bit signed value
ucent
128 bit unsigned value
float
32 bit IEEE 754 浮動小数点数
double
64 bit IEEE 754 浮動小数点数
real
実装定義の浮動小数点数。x86 では 80 bit IEEE 754 拡張実数

delegate

delegate は2つの部分から構成される fat pointer です:

delegate
オフセット プロパティ 内容
0 .ptr コンテキストポインタ
ptrsize .funcptr 関数ポインタ

コンテキストポインタ は、クラスの this 参照か構造体の this ポインタ、あるいは クロージャ、 または周囲の関数のスタックフレーム(ネスト関数の場合)へのポインタです。

構造体

対象環境の C ABI の構造体レイアウトに従います。

クラス

オブジェクトのメモリ構成は次のようになっています:

クラスオブジェクトのレイアウト
サイズ プロパティ 内容
ptrsize .__vptr vtableへのポインタ
ptrsize .__monitor モニタ
... ... 基底クラスの非staticメンバとinterfaceのvtableへのポインタ。継承のルートから順に。
... 名前のついたフィールド 非staticメンバ
ptrsize...   このクラスの実装するinterfaceのvtableへのポインタ。左から右、直近の派生元から遠くの派生元、の順で。

vtableの構成は次の通り:

仮想関数ポインタテーブルのレイアウト
サイズ 内容
ptrsize ClassInfoのインスタンスへのポインタ
ptrsize... 仮想メンバ関数へのポインタ

クラスオブジェクトのインターフェイスへのキャストは、 オブジェクトのベースアドレスに、インターフェイスのvptrへのオフセットを加算することで行われます。 インターフェイスをクラス型へキャストし直すには、 引き算する正しいオフセットを求めるために、vtbl[0] の object.Interface エントリを参照します。 vtbl[] の中には、 thisポインタが正しくオブジェクトのインスタンスを参照するように調整するための サンク が生成され格納されます。

サンクは以下のようなコードになります:

    ADD EAX,offset
    JMP method

継承関係グラフの左端は、全てのインターフェイスが vptr を共有する、 単一継承モデルです。 (インターフェイスの多重継承によって)継承グラフが分岐する度に、 新し vptr が作られクラスのインスタンスに格納されます。 また、virtual メソッドがオーバーライドされるたびに、新しく vtbl[] が作られ新しいメソッドポインタをそこに格納する必要があります。

クラス定義:

class XXXX
{
    ....
};

は、以下のコードを生成します:

インターフェイス

インターフェイスは vtbl[] へのポインタです。 vtbl[0] は、object.Interface クラスの対応するインスタンスへのポインタです。 残りのエントリ vtbl[1..$] は、 そのインターフェイスで実装される仮想関数へのポインタが、 宣言された順番に入ります。

COM インターフェイスは、通常のインターフェイスと異なり、 vtbl[0] に object.Interface は入りません。 vtbl[0..$] のエントリ全てが仮想関数ポインタで、 宣言された順番に格納されます。 これは Windows の COM オブジェクトのレイアウトに合わせています。

C++ インターフェイスは通常のインターフェイスと異なり、 ターゲット環境での C++ の単一継承のクラスレイアウトと同じ形式に合わせた実装になります。

配列

動的な配列の構成は:

動的配列のレイアウト
オフセット プロパティ 内容
0 .length 配列のサイズ
size_t .ptr 配列データへのポインタ

動的な配列は次のように宣言されますが:

type[] array;

静的な配列の宣言は次のようになります:

type[dimension] array;

従って、静的な配列のサイズは常に型の一部として静的に得ることが出来ます。 このため、静的な配列についてはC言語と同様に実装されています。 静的な配列と動的な配列は簡単に相互変換が可能です。

連想配列

連想配列は、 実装ごとに定義される実体へのポインタ一つで構成されます。 現在の実装は internal/aaA.d から参照できます。

参照型

Dには参照型がありますが、明示的に表には出てきません。例えば、クラスは常に 参照によってアクセスされます。これはすなわち、クラスのインスタンスそのものは決して スタックに置かれたり関数のパラメタとして渡したりできないことを意味します。

静的な配列を関数へ渡すと、例え静的な配列として宣言されていても、 結果は実際には静的配列への参照となります。例をあげると:

int[3] abc;

関数へ abc を渡す際には、次のような暗黙の変換が引き起こされます:

void func(int[3] array); // 実際には <intの><サイズ3配列><への参照>
void func(int* p);       // abc は
			 // 先頭要素へのポインタへ変換される
void func(int[] array);	 // abc は動的配列へ変換される

名前マングリング

Dでは、型安全なリンクを実現するために、Dの識別子名を mangling してスコープと型の情報を埋め込んでいます。

MangledName:
    _D QualifiedName Type
    _D QualifiedName M Type

QualifiedName:
    SymbolName
    SymbolName QualifiedName

SymbolName:
    LName
    TemplateInstanceName

M は、そのシンボルが this ポインタを必要とする関数であることを表しています。

テンプレートのインスタンス名は、 そのパラメタ型と値をエンコードして保持します:

TemplateInstanceName:
     __T LName TemplateArgs Z

TemplateArgs:
    TemplateArg
    TemplateArg TemplateArgs

TemplateArg:
    T Type
    V Type Value
    S LName

Value:
    n
    Number
    i Number
    N Number
    e HexFloat
    c HexFloat c HexFloat
    A Number Value...

HexFloat:
    NAN
    INF
    NINF
    N HexDigits P Exponent
    HexDigits P Exponent

Exponent:
    N Number
    Number

HexDigits:
    HexDigit
    HexDigit HexDigits

HexDigit:
    Digit
    A
    B
    C
    D
    E
    F
n
null 引数を表します
Number
は正の数値リテラルです (文字リテラルを含みます)
N Number
は負の数値リテラルです
e HexFloat
は実数および虚数の浮動小数点数リテラルです。
c HexFloat c HexFloat
は複素数の浮動小数点数リテラルです。
Width Number _ HexDigits
Width は文字列中の文字が 1 byte (a), 2 bytes (w), 4 bytes (d) のいずれであるかを示します。 Number は文字列中の文字の数です。 HexDigits は文字列データの16進表現です。
A Number Value...
配列リテラル。ValueNumber 回繰り返す。
Name:
    Namestart
    Namestart Namechars

Namestart:
    _
    Alpha

Namechar:
    Namestart
    Digit

Namechars:
    Namechar
    Namechar Namechars

Name は標準的なDの識別子です。

LName:
    Number Name

Number:
    Digit
    Digit Number

Digit:
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9

LName は、先頭に文字数を表す Number のついた Name です。

型名のマングリング

型の名前は以下のように単純な一列の文字列になります:

Type:
    Shared
    Const
    Invariant
    Wild
    TypeArray
    TypeStaticArray
    TypeAssocArray
    TypePointer
    TypeFunction
    TypeIdent
    TypeClass
    TypeStruct
    TypeEnum
    TypeTypedef
    TypeDelegate
    TypeNone
    TypeVoid
    TypeByte
    TypeUbyte
    TypeShort
    TypeUshort
    TypeInt
    TypeUint
    TypeLong
    TypeUlong
    TypeFloat
    TypeDouble
    TypeReal
    TypeIfloat
    TypeIdouble
    TypeIreal
    TypeCfloat
    TypeCdouble
    TypeCreal
    TypeBool
    TypeChar
    TypeWchar
    TypeDchar
    TypeTuple

Shared:
    O Type

Const:
    x Type

Invariant:
    y Type

Wild:
    Ng Type

TypeArray:
    A Type

TypeStaticArray:
    G Number Type

TypeAssocArray:
    H Type Type

TypePointer:
    P Type

TypeFunction:
    CallConvention Arguments ArgClose Type

CallConvention:
    F       // D
    U       // C
    W       // Windows
    V       // Pascal
    R       // C++


Arguments:
    Argument
    Argument Arguments

Argument:
    Type
    J Type     // out
    K Type     // ref
    L Type     // lazy

ArgClose
    X     // variadic (T t,...) 形式
    Y     // variadic (T t...) 形式
    Z     // variadic ではない

TypeIdent:
    I LName

TypeClass:
    C LName

TypeStruct:
    S LName

TypeEnum:
    E LName

TypeTypedef:
    T LName

TypeDelegate:
    D TypeFunction

TypeNone:
    n

TypeVoid:
    v

TypeByte:
    g

TypeUbyte:
    h

TypeShort:
    s

TypeUshort:
    t

TypeInt:
    i

TypeUint:
    k

TypeLong:
    l

TypeUlong:
    m

TypeFloat:
    f

TypeDouble:
    d

TypeReal:
    e

TypeIfloat:
    o

TypeIdouble:
    p

TypeIreal:
    j

TypeCfloat:
    q

TypeCdouble:
    r

TypeCreal:
    c

TypeBool:
    b

TypeChar:
    a

TypeWchar:
    u

TypeDchar:
    w

TypeTuple:
    B Number Arguments

関数呼び出し規約

extern (C) と宣言された関数の呼び出し規約は、 ホスト環境で対応するCコンパイラの関数呼び出し規約と一致します。 extern (D) と宣言された関数の x86 での呼び出し規約をここで説明します。

レジスター使用規約

返値

引数

可変個引数でない関数の引数:

	foo(a1, a2, ..., an);

は、以下のように渡されます

a1
a2
...
an
hidden
this

hidden は、 構造体を返す必要があるときに現れます。 this は、メンバ関数に使うthisポインタや、 ネストした関数で使うコンテキストポインタを渡すときに現れます。

次の条件が満たされているときには、最後の引数は、 スタックではなくEAXレジスタで渡されます:

引数は、 常に4バイト境界に切り上げてスタックに push されます。 上位ワードが先にpushされます。 out 引数と ref 引数はポインタが渡されます。 静的配列は先頭へのポインタが渡されます。 Windowsでは、real型は10バイトの量として、 creal型は20バイトの量としてpushされます。 Linuxでは、realは12バイト、 crealは二つの12バイトの量としてpushされます。 パディングの2バイトは'most significant'側に埋められます。 (訳注:ところどころ訳が怪しいかも)

呼び出された関数側が、スタックを復元します。

以下の形式の可変個引数関数の場合は、

	void foo(int p1, int p2, int[] p3...)
	foo(a1, a2, ..., an);

このように引数が渡されます:

p1
p2
a3
hidden
this

可変個部分は動的配列に変換され、 あとは通常の関数と全く同様です。

以下の形式の可変個引数関数の場合は、

	void foo(int p1, int p2, ...)
	foo(a1, a2, a3, ..., an);

このように引数が渡されます:

an
...
a3
a2
a1
_arguments
hidden
this

呼び出し側がスタックを復元します。 _argptr は直接は渡されません。 呼び出された関数側が計算します。

関数属性

Na
pure
Nb
nothrow

例外処理

Windows

Microsoft Windows の構造化例外処理の規約に従う。

Linux and OSX

静的な範囲/ハンドラ対応表を用いる。 これはELFの例外ハンドラテーブルとは互換性がありません。 EBPでスタックフレームを保持しているとの仮定の下で、 スタックをたどります。例外ハンドラテーブル(EHテーブル)を持つ全ての関数で、 このEBPの規約が満たされている必要があります。

例外ハンドラのあるそれぞれの関数に対して、 EHテーブルのエントリが生成されます。

EHテーブルエントリ
フィールド 説明
void* 関数の開始アドレス
DHandlerTable* 対応するEHデータへのポインタ
uint 関数のサイズ。バイト単位

EHテーブルは、以下の特別なセグメントに配置され、 リンカによって一つに結合されます。

EHテーブルセグメント
OS セグメント名
Windows FI
Linux .deh_eh
OSX __deh_eh, __DATA

例外ハンドラのそのほかのデータは任意の箇所に配置されます。 書き換えは不可能です。

DHandlerTable
フィールド 説明
void* 関数の開始アドレス
uint ESPのEBPからのオフセット
uint 関数の開始アドレスからreturn codeまでのオフセット
uint DHandlerInfo[] の要素数
DHandlerInfo[] ハンドラ情報の配列

DHandlerInfo
フィールド 説明
uint 関数の開始アドレスからtry区間の先頭までのオフセット
uint 同、tryの終端までのオフセット
int 一つ前のテーブルのインデックス
uint 0で内場合、テーブルの先頭から DCatchInfo までのオフセット
void* nullでない場合、finallyのコード

DCatchInfo
フィールド 説明
uint DCatchBlock[] の要素数
DCatchBlock[] catch 情報の配列

DCatchBlock
フィールド 説明
ClassInfo catchする例外の型
uint catch変数の EBP からのオフセット
void* catch ハンドラのコード

ガベージコレクション

インターフェイスが phobos/internal/gc に存在する

実行時補助関数

phobos/internal に存在する。

モジュール初期化と終了

モジュール内の静的コンストラクタは1つの関数へとまとめられます。 その関数へのポインタが、そのモジュールの ModuleInfo インスタンスの ctor メンバに格納されます。

モジュール内の静的コンストラクタは1つの関数へとまとめられます。 その関数へのポインタが、そのモジュールの ModuleInfo インスタンスの dtor メンバに格納されます。

単体テスト

モジュール内の静的コンストラクタは1つの関数へとまとめられます。 その関数へのポインタが、そのモジュールの ModuleInfo インスタンスの unitTest メンバに格納されます。

シンボリック・デバッグ

D には、既存のC/C++用デバッガでは表現できない型があります。 動的配列、連想配列、delegate がそれにあたります。 しかし、一般には関数呼び出し規約が構造体とは異なるために、 これらを構造体として表現すると C/C++ デバッガの動作ミスを引き起こしてしまいます。 これらデバッガへの対策として、現状では、 呼び出し規約がマッチするような型を選んでそれで置き換えて表現するようになっています。 -gc スイッチを指定した場合、dmd コンパイラはこのような C 形式シンボル情報のみを出力します。

Cデバッガ向け型情報
D の型 C 表現
動的配列 unsigned long long
連想配列 void*
delegate long long
dchar unsigned long

新しい型を追加できるようなタイプのデバッガでは、 Dの型を完全サポートするために以下の情報を利用してください。

Codeview デバッガ拡張

D の dchar 型は特別なプリミティブ型 0x78 と表現されます。

D は、LF_OEM (0x0015) で示される Codeview OEM 汎用型情報レコードを使います。 形式は以下の通りです:

D の Codeview OEM 拡張
フィールドサイズ 2 2 2 2 2 2
D の型 Leaf Index OEM Identifier recOEM num indices type index type index
動的配列 LF_OEM OEM 1 2 @index @element
連想配列 LF_OEM OEM 2 2 @key @element
delegate LF_OEM OEM 3 2 @this @function


OEM 0x42
index 配列の添え字のtype index
key 連想配列のkeyのtype index
element 連想配列のelementのtype index
this コンテキストポインタのtype index
function 関数のtype index

これらの拡張は obj2asm を使うと綺麗に表示できます。

Ddbg デバッガがこの拡張に対応しています。

Dwarf デバッガ拡張

以下のleaf typeが追加されます:

D の Dwarf 拡張
D の型 Identifier Value Format
動的配列 DW_TAG_darray_type 0x41 DW_AT_type が要素の型
associative array DW_TAG_aarray_type 0x42 DW_AT_type, が要素の型, DW_AT_containing_type がkeyの型
delegate DW_TAG_delegate_type 0x43 DW_AT_type, が関数の型, DW_AT_containing_type が'this'の型

これらの拡張は dumpobj を使うと綺麗に表示できます。

ZeroBUGS デバッガがこの拡張に対応しています。