x86 と x86-64 の ABI と呼び出し規約
ABI は、別々にコンパイルされたコードを組み合わせて動かすための取り決めです。ここでは、各プラットフォームがどの ABI を使うか、ABI が何を決めるか、その下で C の各型が何バイトになるかを示します。
アプリケーションバイナリインターフェイス (ABI) は、コンパイルされたコードを他のコンパイル済みのコードと組み合わせられるように、そのコードが従う規則の集まりです。組み合わせとは、プログラムとそれが呼び出すライブラリ、C と手書きのアセンブリ、あるコンパイラーの出力と別のコンパイラーの出力などです。C 規格はこうした詳細を各プラットフォームに委ねており、x86-64 と x86 のプラットフォームは少数の ABI に落ち着きました。ABI ごとにこのサイトのページがあり、その ABI を使うすべてのプラットフォームを挙げています。
ABI が決めること
- データレイアウト
- 各型のサイズとアラインメント、したがって構造体がどうパディングされるかです。
longは Linux では 8 バイト、Windows では 4 バイトなので、1 つの構造体宣言が 2 つの異なるレイアウトを表すことがあります。 - 呼び出し規約
- 引数と結果をどのレジスターまたはスタックスロットで渡すか、呼び出された関数がどのレジスターを変更せずに返さなければならないか、呼び出し時にスタックをどうアラインするか、
printfのような可変長引数関数が自分の引数をどう見つけるかです。 - スタックフレームとアンワインド
- デバッガー、プロファイラー、例外ハンドラーが、ある関数からその呼び出し元へどうたどるかです。
- システムコール
- どの命令でカーネルに入るか、システムコール番号とその引数をどこに置くかです。
- オブジェクトファイルとリンク
- ファイル形式 (ELF、PE/COFF または Mach-O)、再配置、シンボル名をどう装飾するか、コードがスレッドローカル変数にどうアクセスするかです。
- C++
- 名前修飾 (name mangling)、仮想関数テーブルのレイアウト、
thisの渡し方、例外がスタックをどうアンワインドするかです。
ELF システムでは、System V ABI のプロセッサー補足 (psABI) がこのほとんどを定めています。x86-64 psABI は型のサイズから ELF の再配置やアンワインドテーブルまでを扱い、Linux のシステムコールについての参考情報の章もあります。Microsoft は Windows 向けの規約を文書化しており、Apple は macOS が x86-64 psABI と異なる点を文書化しています。
同じ C コードを見ると違いがわかります。リスト 1 は、2 つの小さな関数を 3 つの ABI 向けにコンパイルしたものです。
every-abi.c
/* Two functions, compiled for every ABI: where do the arguments
arrive, and where does the result go? */
long diff(long a, long b)
{
return a - b;
}
double axpy(double a, double x, double y)
{
return a * x + y;
}
GCC 14.2.0 (Debian 14.2.0-19) x86_64-linux-gnu
AT&T 構文 gcc-14 -O2 -fno-asynchronous-unwind-tables -S every-abi.c
diff:
movq %rdi, %rax
subq %rsi, %rax
ret
axpy:
mulsd %xmm1, %xmm0
addsd %xmm2, %xmm0
retIntel 構文 gcc-14 -O2 -fno-asynchronous-unwind-tables -masm=intel -S every-abi.c
diff:
mov rax, rdi
sub rax, rsi
ret
axpy:
mulsd xmm0, xmm1
addsd xmm0, xmm2
retClang 23.1.2 x86_64-pc-windows-msvc
AT&T 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -S every-abi.c
diff:
movl %ecx, %eax
subl %edx, %eax
retq
axpy:
mulsd %xmm1, %xmm0
addsd %xmm2, %xmm0
retqIntel 構文 clang --target=x86_64-pc-windows-msvc -O2 -fno-asynchronous-unwind-tables -masm=intel -S every-abi.c
diff:
mov eax, ecx
sub eax, edx
ret
axpy:
mulsd xmm0, xmm1
addsd xmm0, xmm2
retGCC 14.2.0 (Debian 14.2.0-19) i386-linux-gnu
AT&T 構文 gcc-14 -m32 -march=i686 -mtune=generic -O2 -fno-asynchronous-unwind-tables -S every-abi.c
diff:
movl 4(%esp), %eax
subl 8(%esp), %eax
ret
axpy:
fldl 4(%esp)
fmull 12(%esp)
faddl 20(%esp)
retIntel 構文 gcc-14 -m32 -march=i686 -mtune=generic -O2 -fno-asynchronous-unwind-tables -masm=intel -S every-abi.c
diff:
mov eax, DWORD PTR 4[esp]
sub eax, DWORD PTR 8[esp]
ret
axpy:
fld QWORD PTR 4[esp]
fmul QWORD PTR 12[esp]
fadd QWORD PTR 20[esp]
retSystem V x86-64 では、diff の 2 つの long 引数は RDI と RSI で渡され、結果は RAX で返ります。Microsoft x64 はこれらを RCX と RDX で渡し、そこでは long が 32 ビットしかないため、コードは ECX、EDX、EAX を操作します。32 ビットの i386 ABI は両方をスタックで渡し、戻りアドレスのすぐ上に置きます。axpy の 3 つの double 引数は、どちらの 64 ビット ABI でも XMM0 から XMM2 で渡され、結果は XMM0 で返ります。i386 はこれらもスタックで渡し、結果は x87 のレジスタースタックの ST0 で返します。
これらのページのリストはすべて、実際のコンパイラーの出力です。使ったのは Clang 23.1.2、GCC 14.2.0 (Debian 14.2.0-19) と MinGW-w64 GCC 14.2.0 (Debian 14.2.0-19+27+b1) で、それぞれ正確なバージョンに固定し、出力を生成したコマンドとともに示しています。AT&T と Intel のボタンですべてのリストの構文が切り替わり、その選択は記憶されます。サイトのテストは各リストを再びコンパイルし、出力が変わっていれば失敗します。
どのプラットフォームがどの ABI を使うか
long とポインターの幅を表します。名前の説明は表 2 にあります。| プラットフォーム | ABI | データモデル | ページ |
|---|---|---|---|
| Linux、Android、FreeBSD、NetBSD、OpenBSD、DragonFly BSD、illumos と Solaris、Haiku | System V x86-64 psABI | LP64 | System V x86-64 |
| Intel プロセッサーの macOS | x86-64 psABI (Apple が文書化した相違点あり) | LP64 | System V x86-64: macOS |
| Linux x32 | 32 ビットポインターの x86-64 psABI | ILP32 | System V x86-64: x32 |
| MSVC、clang-cl、MinGW-w64 を使う Windows、MSYS2 の UCRT64 環境と CLANG64 環境 | Microsoft x64 | LLP64 | Microsoft x64 |
| x64 の UEFI ファームウェア | Microsoft x64 | MSVC または Clang の UEFI ターゲットでは LLP64 | Microsoft x64 |
| Cygwin、MSYS2 の MSYS 環境 | Microsoft x64 呼び出し規約 | LP64 | Microsoft x64 |
| 32 ビットの Linux、BSD 系、Android | System V i386 psABI | ILP32 | System V i386 |
| MSVC、clang-cl、MinGW-w64 を使う 32 ビット Windows | cdecl、stdcall、fastcall、thiscall、vectorcall | ILP32 | Windows x86 |
いくつかの行には説明が必要です。
- UEFI ファームウェアは、そのサービスを Microsoft x64 規約で呼び出します。リファレンス実装である EDK II は、GCC や Clang で x64 向けにビルドするとき、すべてのインターフェイス関数に GCC と Clang の
ms_abi属性を付けます。また Clang のx86_64-unknown-uefiターゲットは、Microsoft のレジスターと型のサイズでコンパイルします。 - Cygwin と、Cygwin の上に構築された MSYS2 の MSYS 環境は、Windows の呼び出し規約を保ちつつ Linux の LP64 モデルを使うため、
longは 64 ビットです。UCRT64 や CLANG64 など MSYS2 のほかの環境は、Microsoft の C ランタイムと Windows のデータモデルを使います。 - Intel プロセッサーの macOS は、Apple が文書化した少数の相違点を除いて x86-64 psABI に従います。相違点は System V のページに挙げています。Apple は macOS Catalina 以降、32 ビットの Intel コードをサポートしていないため、表には macOS の 32 ビットの行がありません。
データモデル
| モデル | int | long | long long | ポインター | 採用先 |
|---|---|---|---|---|---|
| LP64 | 32 | 64 | 64 | 64 | System V x86-64、Cygwin、MSYS2 の MSYS 環境 |
| LLP64 | 32 | 32 | 64 | 64 | Microsoft x64 |
| ILP32 | 32 | 32 | 64 | 32 | Linux x32 とすべての 32 ビット x86 プラットフォーム |
LP64 と LLP64 の名前は、64 ビットの型を並べたものです。long とポインター、または long long とポインターです。ILP32 は 32 ビットの型、つまり int、long、ポインターを並べています。long long はどこでも 64 ビットです。ポインターを整数に保持するコードは、long ではなく、どのプラットフォームでも収まる intptr_t または uintptr_t を使うべきです。long は 64 ビット Windows では収まりません。
C の型のサイズ
表 3 と表 4 は、C の基本型のサイズとアラインメントを、各プラットフォームのコンパイラーから見たとおりに示します。これらの表のどのプラットフォームでも _Bool と char は 1 バイト、short は 2 バイト、int と float は 4 バイトで、char は符号付きです。表ではこれらの型を省いています。
| 型 | System V x86-64 | Android | Linux x32 | Windows | MinGW-w64 | Cygwin, MSYS2 |
|---|---|---|---|---|---|---|
| データモデル | LP64 | LP64 | ILP32 | LLP64 | LLP64 | LP64 |
long | 8 | 8 | 4 | 4 | 4 | 8 |
long long | 8 | 8 | 8 | 8 | 8 | 8 |
__int128 | 16 | 16 | 16 | 16 | 16 | 16 |
void * | 8 | 8 | 4 | 8 | 8 | 8 |
double | 8 | 8 | 8 | 8 | 8 | 8 |
long double | 16 | 16 | 16 | 8 | 16 | 16 |
long double の形式 | x87 80-bit | binary128 | x87 80-bit | binary64 | x87 80-bit | x87 80-bit |
wchar_t | 4 | 4 | 4 | 2 | 2 | 2 |
va_list | 24 / 8 | 24 / 8 | 16 / 4 | 8 | 8 | 8 |
各列のプラットフォームとコンパイラー
- System V x86-64
- Linux, FreeBSD, NetBSD, OpenBSD, DragonFly BSD, illumos and Solaris, Haiku, macOS。
x86_64-linux-gnu向けの GCC 14.2.0 (Debian 14.2.0-19);x86_64-linux-gnu、x86_64-unknown-freebsd14、x86_64-unknown-netbsd10、x86_64-unknown-openbsd7、x86_64-unknown-dragonfly、x86_64-pc-solaris2.11、x86_64-unknown-haikuとx86_64-apple-macos11向けの Clang 23.1.2 で測定しました。 - Android
- Android on x86-64。
x86_64-linux-android向けの Clang 23.1.2 で測定しました。 - Linux x32
- Linux x32。
x86_64-linux-gnux32向けの GCC 14.2.0 (Debian 14.2.0-19);x86_64-linux-gnux32向けの Clang 23.1.2 で測定しました。 - Windows
- Windows with MSVC or clang-cl, UEFI。
x86_64-pc-windows-msvcとx86_64-unknown-uefi向けの Clang 23.1.2 で測定しました。 - MinGW-w64
- Windows with MinGW-w64 GCC or Clang。
x86_64-w64-mingw32向けの MinGW-w64 GCC 14.2.0 (Debian 14.2.0-19+27+b1);x86_64-w64-windows-gnu向けの Clang 23.1.2 で測定しました。 - Cygwin, MSYS2
- Cygwin and the MSYS2 msys environment。
x86_64-pc-cygwinとx86_64-pc-msys向けの Clang 23.1.2 で測定しました。
| 型 | System V i386 | Android | Windows | MinGW-w64 |
|---|---|---|---|---|
| データモデル | ILP32 | ILP32 | ILP32 | ILP32 |
long | 4 | 4 | 4 | 4 |
long long | 8 / 4 | 8 / 4 | 8 | 8 |
__int128 | – | – | – | – |
void * | 4 | 4 | 4 | 4 |
double | 8 / 4 | 8 / 4 | 8 | 8 |
long double | 12 / 4 | 8 / 4 | 8 | 12 / 4 |
long double の形式 | x87 80-bit | binary64 | binary64 | x87 80-bit |
wchar_t | 4 | 4 | 2 | 2 |
va_list | 4 | 4 | 4 | 4 |
各列のプラットフォームとコンパイラー
- System V i386
- Linux and FreeBSD on i386。
i386-linux-gnu向けの GCC 14.2.0 (Debian 14.2.0-19);i686-linux-gnuとi686-unknown-freebsd14向けの Clang 23.1.2 で測定しました。 - Android
- Android on x86。
i686-linux-android向けの Clang 23.1.2 で測定しました。 - Windows
- Windows with MSVC or clang-cl。
i686-pc-windows-msvc向けの Clang 23.1.2 で測定しました。 - MinGW-w64
- Windows with MinGW-w64 GCC or Clang。
i686-w64-mingw32向けの MinGW-w64 GCC 14.2.0 (Debian 14.2.0-19+27+b1);i686-w64-windows-gnu向けの Clang 23.1.2 で測定しました。
数値は、各型の sizeof と _Alignof を各プラットフォームのコンパイラーでコンパイルし、その結果をアセンブリから読み取ったものです。各列は、その表の下に挙げたすべてのターゲットで測定しており、結果が食い違うとサイトのビルドが失敗します。
long double- プラットフォーム間で最も違いが大きい型です。System V の ABI、MinGW-w64、Cygwin は x87 の 80 ビット形式を使い、x86-64 では 16 バイトに、32 ビット x86 では 4 バイトにアラインされた 12 バイトに格納します。x86-64 の Android は代わりに 128 ビットの IEEE 形式を使い、MSVC、UEFI、32 ビット x86 の Android では
long doubleはdoubleと同じです。 - 32 ビット x86 での
doubleとlong long - System V はこれらを 4 バイトに、Windows は 8 バイトにアラインするため、
intとdoubleからなる構造体は 32 ビット Linux では 12 バイト、32 ビット Windows では 16 バイトになります。 wchar_t- System V のプラットフォームでは 4 バイト、Windows と Cygwin では 2 バイトです。
va_list- Windows と 32 ビット x86 ではポインターですが、System V x86-64 では 24 バイトの構造体 (x32 では 16 バイト) です。可変長引数関数の引数が、渡されたときのレジスターにまだ残っている場合があるためです。
__int128- x32 を含め、ここで挙げたすべての 64 ビットプラットフォームで使え、32 ビットのプラットフォームではどれでも使えません。Windows では Clang の型です。MSVC 独自の整数型は 64 ビットまでです。
ABI ごとのページ
ABI ごとに 1 ページあり、その ABI を使うすべてのプラットフォームを挙げています。
- System V x86-64 (Linux x32 と macOS を含む)
- Microsoft x64
- System V i386
- Windows x86
ABI を比較するページ:
出典
- System V Application Binary Interface, AMD64 Architecture Processor Supplement, version 1.0: コミット
e1ce098331da、2025-03-12 - System V Application Binary Interface, Intel386 Architecture Processor Supplement, version 1.2: コミット
20ec676cd56d、2025-08-24 - Microsoft Learn: Overview of x64 ABI conventions: コミット
f70d88cd5da7、2026-09-24 - Microsoft Learn: Data type ranges: コミット
f70d88cd5da7、2026-09-24 - Microsoft Learn: Argument passing and naming conventions: コミット
f70d88cd5da7、2026-09-24 - Apple Developer: Writing 64-bit Intel code for Apple Platforms: 2026-09-25 の公開版
- Cygwin User's Guide: Building applications for 64 bit Cygwin: 2026-09-25 の公開版
- MSYS2 documentation: Environments: コミット
9879735eb93f、2026-05-14 - EDK II: BaseTools/Conf/tools_def.template: コミット
2970e5699ba6、2026-08-12 - Clang 23.1.2: clang/lib/CodeGen/Targets/X86.cpp: コミット
85ac56026243、2026-09-20