ほかのページの C ABI は、関数が引数をどう受け取るかを定めますが、C++ プログラムが加えるもの、つまり隠れた this、オーバーロードされた関数の名前、仮想関数テーブル、メンバーへのポインター、実行時型情報 (run-time type information)、例外については定めていません。これらの規則が C++ ABI を構成し、x86 には 2 つの C++ ABI があります。

x86 の 2 つの C++ ABI
C++ ABI使用するもの
Itanium C++ ABIGCC、および Linux、BSD 系、macOS 上と MinGW-w64 向けの Clang
Microsoft C++ ABIMSVC、および MSVC ABI の Windows ターゲット向けの Clang (clang-cl を含む)

Itanium C++ ABI は Intel の Itanium 向けに書かれましたが、その大部分はプロセッサーに依存しません。GCC の C++ は GCC 3.2 以降これに基づいており、ほかのコンパイラーも、特に GNU/Linux と BSD 系でこれを実装しています。Microsoft C++ ABI にはそのような仕様書がありません。Microsoft は装飾名やメンバーポインターの形式などその一部を文書化しており、Clang は Windows ターゲット向けにこれを実装しています。どちらもそれぞれのプラットフォームの C の呼び出し規約の上に成り立っているため、MinGW-w64 のコードは Microsoft x64 のレジスターを Itanium C++ ABI と組み合わせて使います。このページのリストはすべて、両方の ABI 向けにコンパイルできる Clang によるものです。

this

非静的メンバー関数は、オブジェクトのアドレスを隠し引数として受け取ります。Itanium C++ ABI はこれを第 1 引数として渡すため、x86-64 では RDI に、i386 では最初のスタックスロットに届きます。Microsoft x64 もこれを第 1 引数として RCX で渡します。32 ビット Windows では、メンバー関数は独自の呼び出し規約 __thiscall を使います。this は ECX に入り、ほかの引数はスタックに置かれ、それを呼び出し先が取り除きます。

各 ABI での Counter::add。this は RDI、RCX、4(%esp)、ECX にあります。32 ビット Windows 版は retl $4 で引数をスタックから取り除きます。

cxx-this.cpp

// A member function: this is an argument that the source does not
// write.
struct Counter {
	int n;
	int add(int k);
};

int Counter::add(int k)
{
	return n += k;
}

Clang 23.1.2 x86_64-linux-gnu

AT&T 構文 clang --target=x86_64-linux-gnu -O2 -fno-asynchronous-unwind-tables -S cxx-this.cpp

_ZN7Counter3addEi:
        movl    %esi, %eax
        addl    (%rdi), %eax
        movl    %eax, (%rdi)
        retq

Intel 構文 clang --target=x86_64-linux-gnu -O2 -fno-asynchronous-unwind-tables -masm=intel -S cxx-this.cpp

_ZN7Counter3addEi:
        mov     eax, esi
        add     eax, dword ptr [rdi]
        mov     dword ptr [rdi], eax
        ret

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S cxx-this.cpp

"?add@Counter@@QEAAHH@Z":
        movl    %edx, %eax
        addl    (%rcx), %eax
        movl    %eax, (%rcx)
        retq

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S cxx-this.cpp

"?add@Counter@@QEAAHH@Z":
        mov     eax, edx
        add     eax, dword ptr [rcx]
        mov     dword ptr [rcx], eax
        ret

Clang 23.1.2 i686-linux-gnu

AT&T 構文 clang --target=i686-linux-gnu -march=i686 -mtune=generic -O2 -fno-asynchronous-unwind-tables -S cxx-this.cpp

_ZN7Counter3addEi:
        movl    4(%esp), %ecx
        movl    (%ecx), %eax
        addl    8(%esp), %eax
        movl    %eax, (%ecx)
        retl

Intel 構文 clang --target=i686-linux-gnu -march=i686 -mtune=generic -O2 -fno-asynchronous-unwind-tables -masm=intel -S cxx-this.cpp

_ZN7Counter3addEi:
        mov     ecx, dword ptr [esp + 4]
        mov     eax, dword ptr [ecx]
        add     eax, dword ptr [esp + 8]
        mov     dword ptr [ecx], eax
        ret

Clang 23.1.2 i686-pc-windows-msvc

AT&T 構文 clang --target=i686-pc-windows-msvc -march=pentium4 -mtune=generic -O2 -fno-asynchronous-unwind-tables -S cxx-this.cpp

"?add@Counter@@QAEHH@Z":
        movl    (%ecx), %eax
        addl    4(%esp), %eax
        movl    %eax, (%ecx)
        retl    $4

Intel 構文 clang --target=i686-pc-windows-msvc -march=pentium4 -mtune=generic -O2 -fno-asynchronous-unwind-tables -masm=intel -S cxx-this.cpp

"?add@Counter@@QAEHH@Z":
        mov     eax, dword ptr [ecx]
        add     eax, dword ptr [esp + 4]
        mov     dword ptr [ecx], eax
        ret     4

名前修飾

C++ では、名前空間やクラスが異なる、あるいはパラメーターが異なる同じ名前の関数を複数定義できるため、リンカーが見る名前はそれらすべてをエンコードします。2 つの ABI はこの名前修飾 (name mangling) を異なる方法で行い、一方の名前がもう一方の名前と一致することはありません。

3 つの呼び出しは、それぞれ別の関数を指します。Itanium の名前は _Z で始まり (macOS では、すべての C の名前と同様にアンダースコアがもう 1 つ付きます)、N3geo4distE は geo::dist、RK は const への参照を表し、S_ と S2_ はすでに書かれた部分を参照します。Microsoft の名前は ? で始まり、名前、続いてスコープが並び、@Z で終わります。

cxx-mangling.cpp

// Overloads, a namespace and a template: each call names a different
// function, and the mangled names tell them apart.
namespace geo {
struct Point { int x, y; };
int dist(const Point &a, const Point &b);
int dist(int a, int b);
template <typename T> T scale(T v, int k);
}

int use(const geo::Point &p)
{
	return geo::dist(p, p) + geo::dist(1, 2) + geo::scale<long>(3, 4);
}

Clang 23.1.2 x86_64-linux-gnu

AT&T 構文 clang --target=x86_64-linux-gnu -O2 -fno-asynchronous-unwind-tables -S cxx-mangling.cpp

_Z3useRKN3geo5PointE:
        pushq   %rbp
        pushq   %rbx
        pushq   %rax
        movq    %rdi, %rsi
        callq   _ZN3geo4distERKNS_5PointES2_@PLT
        movl    %eax, %ebx
        movl    $1, %edi
        movl    $2, %esi
        callq   _ZN3geo4distEii@PLT
        movl    %eax, %ebp
        addl    %ebx, %ebp
        movl    $3, %edi
        movl    $4, %esi
        callq   _ZN3geo5scaleIlEET_S1_i@PLT
        addl    %ebp, %eax
        addq    $8, %rsp
        popq    %rbx
        popq    %rbp
        retq

Intel 構文 clang --target=x86_64-linux-gnu -O2 -fno-asynchronous-unwind-tables -masm=intel -S cxx-mangling.cpp

_Z3useRKN3geo5PointE:
        push    rbp
        push    rbx
        push    rax
        mov     rsi, rdi
        call    _ZN3geo4distERKNS_5PointES2_@PLT
        mov     ebx, eax
        mov     edi, 1
        mov     esi, 2
        call    _ZN3geo4distEii@PLT
        mov     ebp, eax
        add     ebp, ebx
        mov     edi, 3
        mov     esi, 4
        call    _ZN3geo5scaleIlEET_S1_i@PLT
        add     eax, ebp
        add     rsp, 8
        pop     rbx
        pop     rbp
        ret

Clang 23.1.2 x86_64-apple-macos11

AT&T 構文 clang --target=x86_64-apple-macos11 -O2 -fno-asynchronous-unwind-tables -S cxx-mangling.cpp

__Z3useRKN3geo5PointE:
        pushq   %rbp
        movq    %rsp, %rbp
        pushq   %r14
        pushq   %rbx
        movq    %rdi, %rsi
        callq   __ZN3geo4distERKNS_5PointES2_
        movl    %eax, %ebx
        movl    $1, %edi
        movl    $2, %esi
        callq   __ZN3geo4distEii
        movl    %eax, %r14d
        addl    %ebx, %r14d
        movl    $3, %edi
        movl    $4, %esi
        callq   __ZN3geo5scaleIlEET_S1_i
        addl    %r14d, %eax
        popq    %rbx
        popq    %r14
        popq    %rbp
        retq

Intel 構文 clang --target=x86_64-apple-macos11 -O2 -fno-asynchronous-unwind-tables -masm=intel -S cxx-mangling.cpp

__Z3useRKN3geo5PointE:
        push    rbp
        mov     rbp, rsp
        push    r14
        push    rbx
        mov     rsi, rdi
        call    __ZN3geo4distERKNS_5PointES2_
        mov     ebx, eax
        mov     edi, 1
        mov     esi, 2
        call    __ZN3geo4distEii
        mov     r14d, eax
        add     r14d, ebx
        mov     edi, 3
        mov     esi, 4
        call    __ZN3geo5scaleIlEET_S1_i
        add     eax, r14d
        pop     rbx
        pop     r14
        pop     rbp
        ret

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S cxx-mangling.cpp

"?use@@YAHAEBUPoint@geo@@@Z":
        pushq   %rsi
        pushq   %rdi
        subq    $40, %rsp
        movq    %rcx, %rdx
        callq   "?dist@geo@@YAHAEBUPoint@1@0@Z"
        movl    %eax, %esi
        movl    $1, %ecx
        movl    $2, %edx
        callq   "?dist@geo@@YAHHH@Z"
        movl    %eax, %edi
        addl    %esi, %edi
        movl    $3, %ecx
        movl    $4, %edx
        callq   "??$scale@J@geo@@YAJJH@Z"
        addl    %edi, %eax
        addq    $40, %rsp
        popq    %rdi
        popq    %rsi
        retq

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S cxx-mangling.cpp

"?use@@YAHAEBUPoint@geo@@@Z":
        push    rsi
        push    rdi
        sub     rsp, 40
        mov     rdx, rcx
        call    "?dist@geo@@YAHAEBUPoint@1@0@Z"
        mov     esi, eax
        mov     ecx, 1
        mov     edx, 2
        call    "?dist@geo@@YAHHH@Z"
        mov     edi, eax
        add     edi, esi
        mov     ecx, 3
        mov     edx, 4
        call    "??$scale@J@geo@@YAJJH@Z"
        add     eax, edi
        add     rsp, 40
        pop     rdi
        pop     rsi
        ret

通常の関数の Itanium の名前は、パラメーターをエンコードしますが戻り値の型はエンコードしません。戻り値の型を含むのはテンプレート関数の名前だけで、_ZN3geo5scaleIlEET_S1_i の T_ がそれにあたります。ここで IlE はテンプレート引数 long です。Microsoft の名前は、すべての関数について戻り値の型と呼び出し規約をエンコードします。ツールを使うと名前を宣言に戻せます。Itanium の名前には c++filt または llvm-cxxfilt、Microsoft の名前には Microsoft の undname を使います。

仮想関数テーブル

仮想関数を持つクラスのオブジェクトは、そのクラスの仮想関数テーブルへのポインターで始まり、仮想呼び出しは関数をそこから読み込みます。2 つの ABI ではテーブルのレイアウトが異なります。

2 つの ABI の仮想関数テーブル
Itanium C++ ABIMicrosoft C++ ABI
関数の前オブジェクトの先頭までのオフセット、続いて型情報型情報につながる完全オブジェクトロケーター (complete object locator) へのポインター
オブジェクトが指す先この 2 つのエントリの後にある最初の関数最初の関数
仮想デストラクター2 つのエントリ (完全オブジェクトデストラクターと削除デストラクター)1 つのエントリ (フラグを受け取るベクトル削除デストラクター)
出力先キー関数がある場合、それを定義するオブジェクトファイルそれを必要とするすべてのオブジェクトファイル (リンカーが 1 つにまとめる)
Itanium: _ZTV6Square は 0、型情報 _ZTI6Square、2 つのデストラクター、area、sides を保持します。make_square は _ZTV6Square+16 を格納し、area_of は 3 番目の関数である 16(%rax) を通じて呼び出します。Microsoft: ??_7Square@@6B@ は、最初のワードが完全オブジェクトロケーター ??_R4Square@@6B@ を指すブロックの、先頭から 8 バイトの位置に定義されています。area は削除デストラクターに続く 2 番目のエントリで、8(%rax) にあります。

cxx-vtable.cpp

// A class with virtual functions, one of them defined here, an object
// of it made here, and a virtual call.
struct Shape {
	virtual ~Shape();
	virtual int area() const = 0;
	virtual int sides() const { return 0; }
};

struct Square : Shape {
	int side = 1;
	int area() const override;
	int sides() const override { return 4; }
};

int Square::area() const
{
	return side * side;
}

Shape *make_square()
{
	return new Square;
}

int area_of(const Shape &s)
{
	return s.area();
}

Clang 23.1.2 x86_64-linux-gnu

AT&T 構文 clang --target=x86_64-linux-gnu -O2 -fno-asynchronous-unwind-tables -S cxx-vtable.cpp

_ZNK6Square4areaEv:
        movl    8(%rdi), %eax
        imull   %eax, %eax
        retq

_Z11make_squarev:
        pushq   %rax
        movl    $16, %edi
        callq   _Znwm@PLT
        leaq    _ZTV6Square+16(%rip), %rcx
        movq    %rcx, (%rax)
        movl    $1, 8(%rax)
        popq    %rcx
        retq

_Z7area_ofRK5Shape:
        movq    (%rdi), %rax
        jmpq    *16(%rax)

_ZN6SquareD0Ev:
        pushq   %rbx
        movq    %rdi, %rbx
        callq   _ZN5ShapeD2Ev@PLT
        movl    $16, %esi
        movq    %rbx, %rdi
        popq    %rbx
        jmp     _ZdlPvm@PLT

_ZNK6Square5sidesEv:
        movl    $4, %eax
        retq

_ZTV6Square:
        .quad   0
        .quad   _ZTI6Square
        .quad   _ZN5ShapeD2Ev
        .quad   _ZN6SquareD0Ev
        .quad   _ZNK6Square4areaEv
        .quad   _ZNK6Square5sidesEv

_ZTI6Square:
        .quad   _ZTVN10__cxxabiv120__si_class_type_infoE+16
        .quad   _ZTS6Square
        .quad   _ZTI5Shape

_ZTS6Square:
        .asciz  "6Square"

Intel 構文 clang --target=x86_64-linux-gnu -O2 -fno-asynchronous-unwind-tables -masm=intel -S cxx-vtable.cpp

_ZNK6Square4areaEv:
        mov     eax, dword ptr [rdi + 8]
        imul    eax, eax
        ret

_Z11make_squarev:
        push    rax
        mov     edi, 16
        call    _Znwm@PLT
        lea     rcx, [rip + _ZTV6Square+16]
        mov     qword ptr [rax], rcx
        mov     dword ptr [rax + 8], 1
        pop     rcx
        ret

_Z7area_ofRK5Shape:
        mov     rax, qword ptr [rdi]
        jmp     qword ptr [rax + 16]

_ZN6SquareD0Ev:
        push    rbx
        mov     rbx, rdi
        call    _ZN5ShapeD2Ev@PLT
        mov     esi, 16
        mov     rdi, rbx
        pop     rbx
        jmp     _ZdlPvm@PLT

_ZNK6Square5sidesEv:
        mov     eax, 4
        ret

_ZTV6Square:
        .quad   0
        .quad   _ZTI6Square
        .quad   _ZN5ShapeD2Ev
        .quad   _ZN6SquareD0Ev
        .quad   _ZNK6Square4areaEv
        .quad   _ZNK6Square5sidesEv

_ZTI6Square:
        .quad   _ZTVN10__cxxabiv120__si_class_type_infoE+16
        .quad   _ZTS6Square
        .quad   _ZTI5Shape

_ZTS6Square:
        .asciz  "6Square"

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S cxx-vtable.cpp

"?area@Square@@UEBAHXZ":
        movl    8(%rcx), %eax
        imull   %eax, %eax
        retq

"?make_square@@YAPEAUShape@@XZ":
        subq    $40, %rsp
        movl    $16, %ecx
        callq   "??2@YAPEAX_K@Z"
        leaq    "??_7Square@@6B@"(%rip), %rcx
        movq    %rcx, (%rax)
        movl    $1, 8(%rax)
        addq    $40, %rsp
        retq

"?area_of@@YAHAEBUShape@@@Z":
        movq    (%rcx), %rax
        rex64 jmpq      *8(%rax)

"?sides@Square@@UEBAHXZ":
        movl    $4, %eax
        retq

"?sides@Shape@@UEBAHXZ":
        xorl    %eax, %eax
        retq

"??_GShape@@UEAAPEAXI@Z":
        ud2

"??_GSquare@@UEAAPEAXI@Z":
        pushq   %rsi
        pushq   %rdi
        subq    $40, %rsp
        movl    %edx, %edi
        movq    %rcx, %rsi
        callq   "??1Shape@@UEAA@XZ"
        testb   $1, %dil
        je      .LBB6_2
        movl    $16, %edx
        movq    %rsi, %rcx
        callq   "??3@YAXPEAX_K@Z"
.LBB6_2:
        movq    %rsi, %rax
        addq    $40, %rsp
        popq    %rdi
        popq    %rsi
        retq

.L__unnamed_1:
        .quad   "??_R4Square@@6B@"
        .quad   "??_ESquare@@UEAAPEAXI@Z"
        .quad   "?area@Square@@UEBAHXZ"
        .quad   "?sides@Square@@UEBAHXZ"

"??_R4Square@@6B@":
        .long   1
        .long   0
        .long   0
        .long   "??_R0?AUSquare@@@8"@IMGREL
        .long   "??_R3Square@@8"@IMGREL
        .long   "??_R4Square@@6B@"@IMGREL

"??_R0?AUSquare@@@8":
        .quad   "??_7type_info@@6B@"
        .quad   0
        .asciz  ".?AUSquare@@"
        .zero   3

"??_R3Square@@8":
        .long   0
        .long   0
        .long   2
        .long   "??_R2Square@@8"@IMGREL

"??_R2Square@@8":
        .long   "??_R1A@?0A@EA@Square@@8"@IMGREL
        .long   "??_R1A@?0A@EA@Shape@@8"@IMGREL
        .long   0

"??_R1A@?0A@EA@Square@@8":
        .long   "??_R0?AUSquare@@@8"@IMGREL
        .long   1
        .long   0
        .long   4294967295
        .long   0
        .long   64
        .long   "??_R3Square@@8"@IMGREL

"??_R1A@?0A@EA@Shape@@8":
        .long   "??_R0?AUShape@@@8"@IMGREL
        .long   0
        .long   0
        .long   4294967295
        .long   0
        .long   64
        .long   "??_R3Shape@@8"@IMGREL

"??_R0?AUShape@@@8":
        .quad   "??_7type_info@@6B@"
        .quad   0
        .asciz  ".?AUShape@@"
        .zero   4

"??_R3Shape@@8":
        .long   0
        .long   0
        .long   1
        .long   "??_R2Shape@@8"@IMGREL

"??_R2Shape@@8":
        .long   "??_R1A@?0A@EA@Shape@@8"@IMGREL
        .long   0

"??_R4Shape@@6B@":
        .long   1
        .long   0
        .long   0
        .long   "??_R0?AUShape@@@8"@IMGREL
        .long   "??_R3Shape@@8"@IMGREL
        .long   "??_R4Shape@@6B@"@IMGREL
"??_7Square@@6B@" = .L__unnamed_1+8
"??_ESquare@@UEAAPEAXI@Z" = "??_GSquare@@UEAAPEAXI@Z"

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S cxx-vtable.cpp

"?area@Square@@UEBAHXZ":
        mov     eax, dword ptr [rcx + 8]
        imul    eax, eax
        ret

"?make_square@@YAPEAUShape@@XZ":
        sub     rsp, 40
        mov     ecx, 16
        call    "??2@YAPEAX_K@Z"
        lea     rcx, [rip + "??_7Square@@6B@"]
        mov     qword ptr [rax], rcx
        mov     dword ptr [rax + 8], 1
        add     rsp, 40
        ret

"?area_of@@YAHAEBUShape@@@Z":
        mov     rax, qword ptr [rcx]
        rex64 jmp       qword ptr [rax + 8]

"?sides@Square@@UEBAHXZ":
        mov     eax, 4
        ret

"?sides@Shape@@UEBAHXZ":
        xor     eax, eax
        ret

"??_GShape@@UEAAPEAXI@Z":
        ud2

"??_GSquare@@UEAAPEAXI@Z":
        push    rsi
        push    rdi
        sub     rsp, 40
        mov     edi, edx
        mov     rsi, rcx
        call    "??1Shape@@UEAA@XZ"
        test    dil, 1
        je      .LBB6_2
        mov     edx, 16
        mov     rcx, rsi
        call    "??3@YAXPEAX_K@Z"
.LBB6_2:
        mov     rax, rsi
        add     rsp, 40
        pop     rdi
        pop     rsi
        ret

.L__unnamed_1:
        .quad   "??_R4Square@@6B@"
        .quad   "??_ESquare@@UEAAPEAXI@Z"
        .quad   "?area@Square@@UEBAHXZ"
        .quad   "?sides@Square@@UEBAHXZ"

"??_R4Square@@6B@":
        .long   1
        .long   0
        .long   0
        .long   "??_R0?AUSquare@@@8"@IMGREL
        .long   "??_R3Square@@8"@IMGREL
        .long   "??_R4Square@@6B@"@IMGREL

"??_R0?AUSquare@@@8":
        .quad   "??_7type_info@@6B@"
        .quad   0
        .asciz  ".?AUSquare@@"
        .zero   3

"??_R3Square@@8":
        .long   0
        .long   0
        .long   2
        .long   "??_R2Square@@8"@IMGREL

"??_R2Square@@8":
        .long   "??_R1A@?0A@EA@Square@@8"@IMGREL
        .long   "??_R1A@?0A@EA@Shape@@8"@IMGREL
        .long   0

"??_R1A@?0A@EA@Square@@8":
        .long   "??_R0?AUSquare@@@8"@IMGREL
        .long   1
        .long   0
        .long   4294967295
        .long   0
        .long   64
        .long   "??_R3Square@@8"@IMGREL

"??_R1A@?0A@EA@Shape@@8":
        .long   "??_R0?AUShape@@@8"@IMGREL
        .long   0
        .long   0
        .long   4294967295
        .long   0
        .long   64
        .long   "??_R3Shape@@8"@IMGREL

"??_R0?AUShape@@@8":
        .quad   "??_7type_info@@6B@"
        .quad   0
        .asciz  ".?AUShape@@"
        .zero   4

"??_R3Shape@@8":
        .long   0
        .long   0
        .long   1
        .long   "??_R2Shape@@8"@IMGREL

"??_R2Shape@@8":
        .long   "??_R1A@?0A@EA@Shape@@8"@IMGREL
        .long   0

"??_R4Shape@@6B@":
        .long   1
        .long   0
        .long   0
        .long   "??_R0?AUShape@@@8"@IMGREL
        .long   "??_R3Shape@@8"@IMGREL
        .long   "??_R4Shape@@6B@"@IMGREL
"??_7Square@@6B@" = .L__unnamed_1+8
"??_ESquare@@UEAAPEAXI@Z" = "??_GSquare@@UEAAPEAXI@Z"

Itanium C++ ABI でのクラスのキー関数 (key function) は、クラスが定義される場所で純粋仮想でもインラインでもない最初の仮想関数で、ここでは Square::area です。テーブルはそれを定義するオブジェクトファイルに、プログラム全体で 1 回だけ置かれます。Microsoft の vftable は、そのようなオブジェクトを作成するすべてのオブジェクトファイルに COMDAT セクションとして置かれ、リンカーが 1 つだけ残します。

メンバー関数ポインター

メンバー関数へのポインターは、アドレスがオブジェクトに依存する仮想関数と、this の調整が必要になる複数の基底クラスを持つクラスを扱えなければなりません。Itanium C++ ABI は常に 2 ワードを使います。1 つ目は関数のアドレス、または仮想関数テーブル内のエントリのオフセットに 1 を加えた値、2 つ目は this の調整値です。最初のワードが奇数なら仮想関数を表します。この ABI はメンバー関数が偶数アドレスから始まることを前提としているためです。

Microsoft の表現はクラスの継承によって変わります。単一継承では、メンバー関数へのポインターは単なる関数のアドレスです。仮想関数の場合、そのアドレスはサンク (thunk) のもので、サンクが仮想呼び出しを行います。多重継承と仮想継承ではフィールドが加わり、まだ定義されていないクラスには最大の形式が使われます。

Itanium: invoke は RDX にある調整値を this に加え、最初のワードのビット 0 を調べ、セットされていればテーブルから関数を読み込みます。pick はペアを RAX と RDX で返し、size に対しては 1 (エントリ 0 に 1 を加えた値) です。このようなポインターは、どのクラスでも 16 バイトです。Microsoft: invoke はポインターへジャンプするだけで、pick はエントリ 0 を呼び出すサンク ??_9Widget@@$BA@AA を返し、ポインターは 8 バイトです。未定義のクラス Later では 24 バイトです。

cxx-member-pointer.cpp

// Pointers to member functions: calling one, taking one of a virtual
// and of a non-virtual function, and their sizes.
struct Widget {
	int value;
	int get();
	virtual int size();
};

typedef int (Widget::*Method)();

int invoke(Widget *w, Method m)
{
	return (w->*m)();
}

Method pick(bool virt)
{
	return virt ? &Widget::size : &Widget::get;
}

struct Later;
int method_size = sizeof(Method);
int later_size = sizeof(int (Later::*)());

Clang 23.1.2 x86_64-linux-gnu

AT&T 構文 clang --target=x86_64-linux-gnu -O2 -fno-asynchronous-unwind-tables -S cxx-member-pointer.cpp

_Z6invokeP6WidgetMS_FivE:
        addq    %rdx, %rdi
        testb   $1, %sil
        je      .LBB0_2
        movq    (%rdi), %rax
        movq    -1(%rax,%rsi), %rsi
.LBB0_2:
        jmpq    *%rsi

_Z4pickb:
        movl    $1, %eax
        testl   %edi, %edi
        jne     .LBB1_2
        movq    _ZN6Widget3getEv@GOTPCREL(%rip), %rax
.LBB1_2:
        xorl    %edx, %edx
        retq

method_size:
        .long   16

later_size:
        .long   16

Intel 構文 clang --target=x86_64-linux-gnu -O2 -fno-asynchronous-unwind-tables -masm=intel -S cxx-member-pointer.cpp

_Z6invokeP6WidgetMS_FivE:
        add     rdi, rdx
        test    sil, 1
        je      .LBB0_2
        mov     rax, qword ptr [rdi]
        mov     rsi, qword ptr [rax + rsi - 1]
.LBB0_2:
        jmp     rsi

_Z4pickb:
        mov     eax, 1
        test    edi, edi
        jne     .LBB1_2
        mov     rax, qword ptr [rip + _ZN6Widget3getEv@GOTPCREL]
.LBB1_2:
        xor     edx, edx
        ret

method_size:
        .long   16

later_size:
        .long   16

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S cxx-member-pointer.cpp

"?invoke@@YAHPEAUWidget@@P81@EAAHXZ@Z":
        rex64 jmpq      *%rdx

"?pick@@YAP8Widget@@EAAHXZ_N@Z":
        leaq    "??_9Widget@@$BA@AA"(%rip), %rdx
        leaq    "?get@Widget@@QEAAHXZ"(%rip), %rax
        testb   %cl, %cl
        cmovneq %rdx, %rax
        retq

"??_9Widget@@$BA@AA":
        movq    (%rcx), %rax
        movq    (%rax), %rax
        rex64 jmpq      *%rax

"?method_size@@3HA":
        .long   8

"?later_size@@3HA":
        .long   24

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S cxx-member-pointer.cpp

"?invoke@@YAHPEAUWidget@@P81@EAAHXZ@Z":
        rex64 jmp       rdx

"?pick@@YAP8Widget@@EAAHXZ_N@Z":
        lea     rdx, [rip + "??_9Widget@@$BA@AA"]
        lea     rax, [rip + "?get@Widget@@QEAAHXZ"]
        test    cl, cl
        cmovne  rax, rdx
        ret

"??_9Widget@@$BA@AA":
        mov     rax, qword ptr [rcx]
        mov     rax, qword ptr [rax]
        rex64 jmp       rax

"?method_size@@3HA":
        .long   8

"?later_size@@3HA":
        .long   24

例外

Itanium C++ ABI では、throw は __cxa_allocate_exception で例外を確保し、それを型情報とともに __cxa_throw に渡します。__cxa_throw はそれを psABI のアンワインドライブラリに渡し、このライブラリがフレームをたどって各関数のパーソナリティルーチン (personality routine) を呼び出します。C++ のパーソナリティルーチンは __gxx_personality_v0 です。このルーチンは関数の呼び出し箇所と catch 節の表、つまり言語固有データ領域 (language-specific data area、LSDA) を読み、その関数が例外を捕捉するかどうかを判断します。catch ブロックは例外オブジェクトに対して __cxa_begin_catch を呼び出し、処理が終わると __cxa_end_catch を呼び出します。プログラムが try ブロックに入るときには何も実行されません。

.cfi_personality と .cfi_lsda は、safe_parse のアンワインド情報をパーソナリティルーチンとその LSDA に結び付けます。LSDA には .Ltmp0 から .Ltmp1 までの呼び出し箇所が 1 つあり、その着地先は .Ltmp2、アクションは 1 で、型 _ZTIi つまり int を捕捉します。MinGW-w64 向けでは、Clang は同じランタイム関数と表を使いますが、例外は Windows のアンワインドを通って伝わります。__gxx_personality_seh0 が関数の .xdata のハンドラーです。

cxx-exceptions.cpp

// Throwing an int, and catching it around a call.
int parse(const char *s);

int safe_parse(const char *s)
{
	try {
		return parse(s);
	} catch (int code) {
		return -code;
	}
}

void fail(int code)
{
	throw code;
}

Clang 23.1.2 x86_64-linux-gnu

AT&T 構文 clang --target=x86_64-linux-gnu -O2 -fno-asynchronous-unwind-tables -fasynchronous-unwind-tables -S cxx-exceptions.cpp

_Z10safe_parsePKc:
.Lfunc_begin0:
        .cfi_startproc
        .cfi_personality 155, DW.ref.__gxx_personality_v0
        .cfi_lsda 27, .Lexception0
        pushq   %rbx
        .cfi_def_cfa_offset 16
        .cfi_offset %rbx, -16
.Ltmp0:
        callq   _Z5parsePKc@PLT
.Ltmp1:
        popq    %rbx
        .cfi_def_cfa_offset 8
        retq
        .cfi_def_cfa_offset 16
.Ltmp2:
        movq    %rax, %rdi
        callq   __cxa_begin_catch@PLT
        xorl    %ebx, %ebx
        subl    (%rax), %ebx
        callq   __cxa_end_catch@PLT
        movl    %ebx, %eax
        popq    %rbx
        .cfi_def_cfa_offset 8
        retq
.Lfunc_end0:
        .cfi_endproc

GCC_except_table0:
.Lexception0:
        .byte   255
        .byte   155
        .uleb128 .Lttbase0-.Lttbaseref0
.Lttbaseref0:
        .byte   1
        .uleb128 .Lcst_end0-.Lcst_begin0
.Lcst_begin0:
        .uleb128 .Ltmp0-.Lfunc_begin0
        .uleb128 .Ltmp1-.Ltmp0
        .uleb128 .Ltmp2-.Lfunc_begin0
        .byte   1
        .uleb128 .Ltmp1-.Lfunc_begin0
        .uleb128 .Lfunc_end0-.Ltmp1
        .byte   0
        .byte   0
.Lcst_end0:
        .byte   1
        .byte   0
.Ltmp3:
        .long   .L_ZTIi.DW.stub-.Ltmp3
.Lttbase0:
_Z4faili:
        .cfi_startproc
        pushq   %rbx
        .cfi_def_cfa_offset 16
        .cfi_offset %rbx, -16
        movl    %edi, %ebx
        movl    $4, %edi
        callq   __cxa_allocate_exception@PLT
        movl    %ebx, (%rax)
        movq    _ZTIi@GOTPCREL(%rip), %rsi
        movq    %rax, %rdi
        xorl    %edx, %edx
        callq   __cxa_throw@PLT
        .cfi_endproc

.L_ZTIi.DW.stub:
        .quad   _ZTIi

DW.ref.__gxx_personality_v0:
        .quad   __gxx_personality_v0

Intel 構文 clang --target=x86_64-linux-gnu -O2 -fno-asynchronous-unwind-tables -fasynchronous-unwind-tables -masm=intel -S cxx-exceptions.cpp

_Z10safe_parsePKc:
.Lfunc_begin0:
        .cfi_startproc
        .cfi_personality 155, DW.ref.__gxx_personality_v0
        .cfi_lsda 27, .Lexception0
        push    rbx
        .cfi_def_cfa_offset 16
        .cfi_offset rbx, -16
.Ltmp0:
        call    _Z5parsePKc@PLT
.Ltmp1:
        pop     rbx
        .cfi_def_cfa_offset 8
        ret
        .cfi_def_cfa_offset 16
.Ltmp2:
        mov     rdi, rax
        call    __cxa_begin_catch@PLT
        xor     ebx, ebx
        sub     ebx, dword ptr [rax]
        call    __cxa_end_catch@PLT
        mov     eax, ebx
        pop     rbx
        .cfi_def_cfa_offset 8
        ret
.Lfunc_end0:
        .cfi_endproc

GCC_except_table0:
.Lexception0:
        .byte   255
        .byte   155
        .uleb128 .Lttbase0-.Lttbaseref0
.Lttbaseref0:
        .byte   1
        .uleb128 .Lcst_end0-.Lcst_begin0
.Lcst_begin0:
        .uleb128 .Ltmp0-.Lfunc_begin0
        .uleb128 .Ltmp1-.Ltmp0
        .uleb128 .Ltmp2-.Lfunc_begin0
        .byte   1
        .uleb128 .Ltmp1-.Lfunc_begin0
        .uleb128 .Lfunc_end0-.Ltmp1
        .byte   0
        .byte   0
.Lcst_end0:
        .byte   1
        .byte   0
.Ltmp3:
        .long   .L_ZTIi.DW.stub-.Ltmp3
.Lttbase0:
_Z4faili:
        .cfi_startproc
        push    rbx
        .cfi_def_cfa_offset 16
        .cfi_offset rbx, -16
        mov     ebx, edi
        mov     edi, 4
        call    __cxa_allocate_exception@PLT
        mov     dword ptr [rax], ebx
        mov     rsi, qword ptr [rip + _ZTIi@GOTPCREL]
        mov     rdi, rax
        xor     edx, edx
        call    __cxa_throw@PLT
        .cfi_endproc

.L_ZTIi.DW.stub:
        .quad   _ZTIi

DW.ref.__gxx_personality_v0:
        .quad   __gxx_personality_v0

Clang 23.1.2 x86_64-w64-windows-gnu

AT&T 構文 clang --target=x86_64-w64-windows-gnu -O2 -fno-asynchronous-unwind-tables -fasynchronous-unwind-tables -S cxx-exceptions.cpp

_Z10safe_parsePKc:
.Lfunc_begin0:
.seh_proc _Z10safe_parsePKc
        .seh_handler __gxx_personality_seh0, @unwind, @except
        pushq   %rsi
        .seh_pushreg %rsi
        subq    $32, %rsp
        .seh_stackalloc 32
        .seh_endprologue
.Ltmp0:
        callq   _Z5parsePKc
        nop
.Ltmp1:
.LBB0_2:
        .seh_startepilogue
        addq    $32, %rsp
        popq    %rsi
        .seh_endepilogue
        retq
.Ltmp2:
        movq    %rax, %rcx
        callq   __cxa_begin_catch
        xorl    %esi, %esi
        subl    (%rax), %esi
        callq   __cxa_end_catch
        movl    %esi, %eax
        jmp     .LBB0_2
.Lfunc_end0:
        .seh_handlerdata
        .seh_endproc

GCC_except_table0:
.Lexception0:
        .byte   255
        .byte   0
        .uleb128 .Lttbase0-.Lttbaseref0
.Lttbaseref0:
        .byte   1
        .uleb128 .Lcst_end0-.Lcst_begin0
.Lcst_begin0:
        .uleb128 .Ltmp0-.Lfunc_begin0
        .uleb128 .Ltmp1-.Ltmp0
        .uleb128 .Ltmp2-.Lfunc_begin0
        .byte   1
        .uleb128 .Ltmp1-.Lfunc_begin0
        .uleb128 .Lfunc_end0-.Ltmp1
        .byte   0
        .byte   0
.Lcst_end0:
        .byte   1
        .byte   0
        .quad   _ZTIi
.Lttbase0:
_Z4faili:
.seh_proc _Z4faili
        pushq   %rsi
        .seh_pushreg %rsi
        subq    $32, %rsp
        .seh_stackalloc 32
        .seh_endprologue
        movl    %ecx, %esi
        movl    $4, %ecx
        callq   __cxa_allocate_exception
        movl    %esi, (%rax)
        movq    .refptr._ZTIi(%rip), %rdx
        movq    %rax, %rcx
        xorl    %r8d, %r8d
        callq   __cxa_throw
        int3
        .seh_endproc

.refptr._ZTIi:
        .quad   _ZTIi

Intel 構文 clang --target=x86_64-w64-windows-gnu -O2 -fno-asynchronous-unwind-tables -fasynchronous-unwind-tables -masm=intel -S cxx-exceptions.cpp

_Z10safe_parsePKc:
.Lfunc_begin0:
.seh_proc _Z10safe_parsePKc
        .seh_handler __gxx_personality_seh0, @unwind, @except
        push    rsi
        .seh_pushreg rsi
        sub     rsp, 32
        .seh_stackalloc 32
        .seh_endprologue
.Ltmp0:
        call    _Z5parsePKc
        nop
.Ltmp1:
.LBB0_2:
        .seh_startepilogue
        add     rsp, 32
        pop     rsi
        .seh_endepilogue
        ret
.Ltmp2:
        mov     rcx, rax
        call    __cxa_begin_catch
        xor     esi, esi
        sub     esi, dword ptr [rax]
        call    __cxa_end_catch
        mov     eax, esi
        jmp     .LBB0_2
.Lfunc_end0:
        .seh_handlerdata
        .seh_endproc

GCC_except_table0:
.Lexception0:
        .byte   255
        .byte   0
        .uleb128 .Lttbase0-.Lttbaseref0
.Lttbaseref0:
        .byte   1
        .uleb128 .Lcst_end0-.Lcst_begin0
.Lcst_begin0:
        .uleb128 .Ltmp0-.Lfunc_begin0
        .uleb128 .Ltmp1-.Ltmp0
        .uleb128 .Ltmp2-.Lfunc_begin0
        .byte   1
        .uleb128 .Ltmp1-.Lfunc_begin0
        .uleb128 .Lfunc_end0-.Ltmp1
        .byte   0
        .byte   0
.Lcst_end0:
        .byte   1
        .byte   0
        .quad   _ZTIi
.Lttbase0:
_Z4faili:
.seh_proc _Z4faili
        push    rsi
        .seh_pushreg rsi
        sub     rsp, 32
        .seh_stackalloc 32
        .seh_endprologue
        mov     esi, ecx
        mov     ecx, 4
        call    __cxa_allocate_exception
        mov     dword ptr [rax], esi
        mov     rdx, qword ptr [rip + .refptr._ZTIi]
        mov     rcx, rax
        xor     r8d, r8d
        call    __cxa_throw
        int3
        .seh_endproc

.refptr._ZTIi:
        .quad   _ZTIi

Microsoft の C++ 例外は、Windows の構造化例外処理 (structured exception handling) の上に作られています。throw は、オブジェクトと、その型および捕捉されうる型の記述を渡して _CxxThrowException を呼び出します。x64 では、try を持つ関数のハンドラーである __CxxFrameHandler3 が、関数のコードを状態に、状態を try ブロックとその catch 節に対応付ける表を読みます。各 catch ブロックは、それぞれ独立した小さな関数としてコンパイルされます。32 ビット Windows では、同じハンドラーが関数の入口で FS:0 のチェーンに登録されます。

fail は _TI1H を渡します。これは _CTA1H と _CT??_R0H@84 を経て、int の型記述子 ??_R0H@8 につながります。safe_parse の表は $cppxdata$ から始まり、try ブロック、ファンクレット (funclet) ?catch$2@... が処理する ??_R0H@8 の catch 節、そしてコードアドレスから状態への対応表が続きます。ファンクレットは safe_parse が処理を続けるアドレス .LBB0_1 を返します。

cxx-exceptions.cpp

// Throwing an int, and catching it around a call.
int parse(const char *s);

int safe_parse(const char *s)
{
	try {
		return parse(s);
	} catch (int code) {
		return -code;
	}
}

void fail(int code)
{
	throw code;
}

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -fasynchronous-unwind-tables -S cxx-exceptions.cpp

"?safe_parse@@YAHPEBD@Z":
.Lfunc_begin0:
.seh_proc "?safe_parse@@YAHPEBD@Z"
        .seh_handler __CxxFrameHandler3, @unwind, @except
        pushq   %rbp
        .seh_pushreg %rbp
        subq    $64, %rsp
        .seh_stackalloc 64
        leaq    64(%rsp), %rbp
        .seh_setframe %rbp, 64
        .seh_endprologue
        movq    $-2, -16(%rbp)
.Ltmp0:
        callq   "?parse@@YAHPEBD@Z"
        movl    %eax, -20(%rbp)
.Ltmp1:
.LBB0_1:
        movl    -20(%rbp), %eax
        .seh_startepilogue
        addq    $64, %rsp
        popq    %rbp
        .seh_endepilogue
        retq
        .seh_handlerdata
        .long   "$cppxdata$?safe_parse@@YAHPEBD@Z"@IMGREL
        .seh_endproc

"?catch$2@?0??safe_parse@@YAHPEBD@Z@4HA":
.seh_proc "?catch$2@?0??safe_parse@@YAHPEBD@Z@4HA"
        .seh_handler __CxxFrameHandler3, @unwind, @except
        movq    %rdx, 16(%rsp)
        pushq   %rbp
        .seh_pushreg %rbp
        subq    $32, %rsp
        .seh_stackalloc 32
        leaq    64(%rdx), %rbp
        .seh_endprologue
        xorl    %eax, %eax
        subl    -4(%rbp), %eax
        movl    %eax, -20(%rbp)
        leaq    .LBB0_1(%rip), %rax
        .seh_startepilogue
        addq    $32, %rsp
        popq    %rbp
        .seh_endepilogue
        retq
        .seh_handlerdata
        .long   "$cppxdata$?safe_parse@@YAHPEBD@Z"@IMGREL
        .seh_endproc

"$cppxdata$?safe_parse@@YAHPEBD@Z":
        .long   429065506
        .long   2
        .long   "$stateUnwindMap$?safe_parse@@YAHPEBD@Z"@IMGREL
        .long   1
        .long   "$tryMap$?safe_parse@@YAHPEBD@Z"@IMGREL
        .long   4
        .long   "$ip2state$?safe_parse@@YAHPEBD@Z"@IMGREL
        .long   48
        .long   0
        .long   1
"$stateUnwindMap$?safe_parse@@YAHPEBD@Z":
        .long   -1
        .long   0
        .long   -1
        .long   0
"$tryMap$?safe_parse@@YAHPEBD@Z":
        .long   0
        .long   0
        .long   1
        .long   1
        .long   "$handlerMap$0$?safe_parse@@YAHPEBD@Z"@IMGREL
"$handlerMap$0$?safe_parse@@YAHPEBD@Z":
        .long   0
        .long   "??_R0H@8"@IMGREL
        .long   60
        .long   "?catch$2@?0??safe_parse@@YAHPEBD@Z@4HA"@IMGREL
        .long   56
"$ip2state$?safe_parse@@YAHPEBD@Z":
        .long   .Lfunc_begin0@IMGREL
        .long   -1
        .long   .Ltmp0@IMGREL
        .long   0
        .long   .Ltmp1@IMGREL
        .long   -1
        .long   "?catch$2@?0??safe_parse@@YAHPEBD@Z@4HA"@IMGREL
        .long   1

"?fail@@YAXH@Z":
.seh_proc "?fail@@YAXH@Z"
        subq    $40, %rsp
        .seh_stackalloc 40
        .seh_endprologue
        movl    %ecx, 36(%rsp)
        leaq    _TI1H(%rip), %rdx
        leaq    36(%rsp), %rcx
        callq   _CxxThrowException
        int3
        .seh_endproc

"??_R0H@8":
        .quad   "??_7type_info@@6B@"
        .quad   0
        .asciz  ".H"
        .zero   5

"_CT??_R0H@84":
        .long   1
        .long   "??_R0H@8"@IMGREL
        .long   0
        .long   4294967295
        .long   0
        .long   4
        .long   0

_CTA1H:
        .long   1
        .long   "_CT??_R0H@84"@IMGREL

_TI1H:
        .long   0
        .long   0
        .long   0
        .long   _CTA1H@IMGREL

Intel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -fasynchronous-unwind-tables -masm=intel -S cxx-exceptions.cpp

"?safe_parse@@YAHPEBD@Z":
.Lfunc_begin0:
.seh_proc "?safe_parse@@YAHPEBD@Z"
        .seh_handler __CxxFrameHandler3, @unwind, @except
        push    rbp
        .seh_pushreg rbp
        sub     rsp, 64
        .seh_stackalloc 64
        lea     rbp, [rsp + 64]
        .seh_setframe rbp, 64
        .seh_endprologue
        mov     qword ptr [rbp - 16], -2
.Ltmp0:
        call    "?parse@@YAHPEBD@Z"
        mov     dword ptr [rbp - 20], eax
.Ltmp1:
.LBB0_1:
        mov     eax, dword ptr [rbp - 20]
        .seh_startepilogue
        add     rsp, 64
        pop     rbp
        .seh_endepilogue
        ret
        .seh_handlerdata
        .long   "$cppxdata$?safe_parse@@YAHPEBD@Z"@IMGREL
        .seh_endproc

"?catch$2@?0??safe_parse@@YAHPEBD@Z@4HA":
.seh_proc "?catch$2@?0??safe_parse@@YAHPEBD@Z@4HA"
        .seh_handler __CxxFrameHandler3, @unwind, @except
        mov     qword ptr [rsp + 16], rdx
        push    rbp
        .seh_pushreg rbp
        sub     rsp, 32
        .seh_stackalloc 32
        lea     rbp, [rdx + 64]
        .seh_endprologue
        xor     eax, eax
        sub     eax, dword ptr [rbp - 4]
        mov     dword ptr [rbp - 20], eax
        lea     rax, [rip + .LBB0_1]
        .seh_startepilogue
        add     rsp, 32
        pop     rbp
        .seh_endepilogue
        ret
        .seh_handlerdata
        .long   "$cppxdata$?safe_parse@@YAHPEBD@Z"@IMGREL
        .seh_endproc

"$cppxdata$?safe_parse@@YAHPEBD@Z":
        .long   429065506
        .long   2
        .long   "$stateUnwindMap$?safe_parse@@YAHPEBD@Z"@IMGREL
        .long   1
        .long   "$tryMap$?safe_parse@@YAHPEBD@Z"@IMGREL
        .long   4
        .long   "$ip2state$?safe_parse@@YAHPEBD@Z"@IMGREL
        .long   48
        .long   0
        .long   1
"$stateUnwindMap$?safe_parse@@YAHPEBD@Z":
        .long   -1
        .long   0
        .long   -1
        .long   0
"$tryMap$?safe_parse@@YAHPEBD@Z":
        .long   0
        .long   0
        .long   1
        .long   1
        .long   "$handlerMap$0$?safe_parse@@YAHPEBD@Z"@IMGREL
"$handlerMap$0$?safe_parse@@YAHPEBD@Z":
        .long   0
        .long   "??_R0H@8"@IMGREL
        .long   60
        .long   "?catch$2@?0??safe_parse@@YAHPEBD@Z@4HA"@IMGREL
        .long   56
"$ip2state$?safe_parse@@YAHPEBD@Z":
        .long   .Lfunc_begin0@IMGREL
        .long   -1
        .long   .Ltmp0@IMGREL
        .long   0
        .long   .Ltmp1@IMGREL
        .long   -1
        .long   "?catch$2@?0??safe_parse@@YAHPEBD@Z@4HA"@IMGREL
        .long   1

"?fail@@YAXH@Z":
.seh_proc "?fail@@YAXH@Z"
        sub     rsp, 40
        .seh_stackalloc 40
        .seh_endprologue
        mov     dword ptr [rsp + 36], ecx
        lea     rdx, [rip + _TI1H]
        lea     rcx, [rsp + 36]
        call    _CxxThrowException
        int3
        .seh_endproc

"??_R0H@8":
        .quad   "??_7type_info@@6B@"
        .quad   0
        .asciz  ".H"
        .zero   5

"_CT??_R0H@84":
        .long   1
        .long   "??_R0H@8"@IMGREL
        .long   0
        .long   4294967295
        .long   0
        .long   4
        .long   0

_CTA1H:
        .long   1
        .long   "_CT??_R0H@84"@IMGREL

_TI1H:
        .long   0
        .long   0
        .long   0
        .long   _CTA1H@IMGREL

MSVC の /EH オプションは、catch (...) が何を捕捉するかを選びます。/EHa では、アクセス違反などのプロセッサーとシステムの構造化例外も捕捉し、/EHsc では C++ の例外だけを捕捉します。

まとめ

2 つの C++ ABI の比較
Itanium C++ ABIMicrosoft C++ ABI
this第 1 引数第 1 引数、32 ビット x86 では ECX
名前_Z...、テンプレート以外は戻り値の型なし?...@Z、戻り値の型と呼び出し規約を含む
仮想関数テーブル関数の前に先頭までのオフセットと型情報、キー関数関数の前にロケーター、COMDAT
メンバー関数へのポインター常に 2 ワード継承に応じて 1 から 4 フィールド
例外__cxa_throw、パーソナリティルーチンと LSDA_CxxThrowException、__CxxFrameHandler3 とその表

出典

  1. Itanium C++ ABI: コミット fae5e46c8614、2026-01-20
  2. Itanium C++ ABI: Exception Handling: コミット fae5e46c8614、2026-01-20
  3. GCC 14.2 manual: Binary Compatibility: 2026-09-25 の公開版
  4. System V Application Binary Interface, AMD64 Architecture Processor Supplement, version 1.0: コミット e1ce098331da、2025-03-12
  5. Microsoft Learn: Decorated names: コミット f70d88cd5da7、2026-09-24
  6. Microsoft Learn: __thiscall: コミット f70d88cd5da7、2026-09-24
  7. Clang 23.1.2: clang/lib/CodeGen/MicrosoftCXXABI.cpp: コミット 85ac56026243、2026-09-20
  8. Microsoft Learn: Inheritance keywords: コミット f70d88cd5da7、2026-09-24
  9. Microsoft Learn: /vmb, /vmg (Representation method): コミット f70d88cd5da7、2026-09-24
  10. Microsoft Learn: /EH (Exception handling model): コミット f70d88cd5da7、2026-09-24
  11. Microsoft Learn: x64 exception handling: コミット f70d88cd5da7、2026-09-24
  12. LLVM 23.1.2: llvm/lib/Target/X86/X86WinEHState.cpp: コミット 85ac56026243、2026-09-20