An application binary interface, or ABI, is the set of rules that compiled code follows so that it can be combined with other compiled code: a program with the libraries it calls, C with hand-written assembly, the output of one compiler with that of another. The C standard leaves these details to each platform, and the x86-64 and x86 platforms settled on a handful of ABIs. Each has its own page here, which names every platform that uses it.

What an ABI decides

Data layout
The size and alignment of each type, and so how structs are padded. long has 8 bytes on Linux and 4 on Windows, so one struct declaration can describe two different layouts.
Calling convention
Which registers or stack slots carry the arguments and the result, which registers a called function must give back unchanged, how the stack is aligned at a call, and how a variadic function such as printf finds its arguments.
Stack frames and unwinding
How debuggers, profilers and exception handlers walk from a function back to its callers.
System calls
Which instruction enters the kernel, and where the call number and its arguments go.
Object files and linking
The file format (ELF, PE/COFF or Mach-O), relocations, how symbol names are decorated, and how code reaches thread-local variables.
C++
Name mangling, the layout of virtual tables, how this is passed and how exceptions unwind the stack.

For ELF systems, the processor supplements of the System V ABI, the psABIs, define most of this: the x86-64 psABI goes from type sizes to ELF relocations and unwind tables, and has an informative chapter on Linux system calls. Microsoft documents its conventions for Windows, and Apple documents where macOS departs from the x86-64 psABI.

The same C code shows the difference. Listing 1 has two small functions compiled for three ABIs.

The same two functions compiled for System V x86-64, Microsoft x64 and System V i386.

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 syntax 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
        ret

Intel syntax 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
        ret

Clang 23.1.2 x86_64-pc-windows-msvc

AT&T syntax 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
        retq

Intel syntax 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
        ret

GCC 14.2.0 (Debian 14.2.0-19) i386-linux-gnu

AT&T syntax 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)
        ret

Intel syntax 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]
        ret

On System V x86-64, the two long arguments of diff arrive in RDI and RSI, and the result leaves in RAX. Microsoft x64 passes them in RCX and RDX, and since long has only 32 bits there, the code works on ECX, EDX and EAX. The 32-bit i386 ABI passes both on the stack, just above the return address. The three double arguments of axpy arrive in XMM0 to XMM2 on both 64-bit ABIs, and the result returns in XMM0; i386 passes them on the stack as well and returns the result on the x87 register stack, in ST0.

Every listing on these pages is the output of a real compiler: Clang 23.1.2, GCC 14.2.0 (Debian 14.2.0-19) and MinGW-w64 GCC 14.2.0 (Debian 14.2.0-19+27+b1), each pinned to an exact version and shown with the command that produced it. The AT&T and Intel buttons switch the syntax of every listing, and the choice is remembered. The site’s tests compile each listing again and fail if the output has changed.

Which platform uses which ABI

Platforms and their ABIs. The data model says how wide long and pointers are; Table 2 explains the names.
PlatformABIData modelPage
Linux, Android, FreeBSD, NetBSD, OpenBSD, DragonFly BSD, illumos and Solaris, HaikuSystem V x86-64 psABILP64System V x86-64
macOS on Intel processorsx86-64 psABI, with differences that Apple documentsLP64System V x86-64: macOS
Linux x32x86-64 psABI with 32-bit pointersILP32System V x86-64: x32
Windows with MSVC, clang-cl or MinGW-w64, and MSYS2’s UCRT64 and CLANG64 environmentsMicrosoft x64LLP64Microsoft x64
UEFI firmware on x64Microsoft x64LLP64 with MSVC or Clang’s UEFI targetMicrosoft x64
Cygwin, MSYS2’s MSYS environmentMicrosoft x64 calling conventionLP64Microsoft x64
32-bit Linux, the BSDs and AndroidSystem V i386 psABIILP32System V i386
32-bit Windows with MSVC, clang-cl or MinGW-w64cdecl, stdcall, fastcall, thiscall and vectorcallILP32Windows x86

A few rows need a word of explanation:

  • UEFI firmware calls its services with the Microsoft x64 convention. EDK II, the reference implementation, marks every interface function with GCC’s and Clang’s ms_abi attribute when it builds for x64 with them, and Clang’s x86_64-unknown-uefi target compiles with the Microsoft registers and type sizes.
  • Cygwin, and MSYS2’s MSYS environment, which is built on Cygwin, keep the Windows calling convention but use the LP64 model of Linux, so long has 64 bits. MSYS2’s other environments, such as UCRT64 and CLANG64, use Microsoft’s C runtimes and the Windows data model.
  • macOS on Intel processors follows the x86-64 psABI except for a few differences that Apple documents, listed on the System V page. Apple has not supported 32-bit Intel code since macOS Catalina, so the table has no 32-bit row for it.

Data models

The data models of x86 and x86-64 platforms: the width in bits of the integer types and pointers.
Modelintlonglong longPointersUsed by
LP6432646464System V x86-64, Cygwin, MSYS2’s MSYS environment
LLP6432326464Microsoft x64
ILP3232326432Linux x32 and every 32-bit x86 platform

The names of LP64 and LLP64 list the types that have 64 bits: long and pointers, or long long and pointers. ILP32 lists those with 32: int, long and pointers. long long has 64 bits everywhere. Code that keeps a pointer in an integer should use intptr_t or uintptr_t, which fit on every platform, rather than long, which does not on 64-bit Windows.

Sizes of C types

Tables 3 and 4 give the size and alignment of the basic C types as the compilers of each platform see them. On every platform in them, _Bool and char take 1 byte, short 2, and int and float 4, and char is signed; the tables leave these types out.

Sizes of C types in bytes on 64-bit platforms. Where the alignment is smaller than the size, it follows after the slash.
TypeSystem V x86-64AndroidLinux x32WindowsMinGW-w64Cygwin, MSYS2
Data modelLP64LP64ILP32LLP64LLP64LP64
long884448
long long888888
__int128161616161616
void *884888
double888888
long double16161681616
long double formatx87 80-bitbinary128x87 80-bitbinary64x87 80-bitx87 80-bit
wchar_t444222
va_list24 / 824 / 816 / 4888
Platforms and compilers of each column
System V x86-64
Linux, FreeBSD, NetBSD, OpenBSD, DragonFly BSD, illumos and Solaris, Haiku, macOS. Measured with GCC 14.2.0 (Debian 14.2.0-19) for x86_64-linux-gnu; Clang 23.1.2 for 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 and x86_64-apple-macos11.
Android
Android on x86-64. Measured with Clang 23.1.2 for x86_64-linux-android.
Linux x32
Linux x32. Measured with GCC 14.2.0 (Debian 14.2.0-19) for x86_64-linux-gnux32; Clang 23.1.2 for x86_64-linux-gnux32.
Windows
Windows with MSVC or clang-cl, UEFI. Measured with Clang 23.1.2 for x86_64-pc-windows-msvc and x86_64-unknown-uefi.
MinGW-w64
Windows with MinGW-w64 GCC or Clang. Measured with MinGW-w64 GCC 14.2.0 (Debian 14.2.0-19+27+b1) for x86_64-w64-mingw32; Clang 23.1.2 for x86_64-w64-windows-gnu.
Cygwin, MSYS2
Cygwin and the MSYS2 msys environment. Measured with Clang 23.1.2 for x86_64-pc-cygwin and x86_64-pc-msys.
Sizes of C types in bytes on 32-bit platforms, written like Table 3.
TypeSystem V i386AndroidWindowsMinGW-w64
Data modelILP32ILP32ILP32ILP32
long4444
long long8 / 48 / 488
__int128––––
void *4444
double8 / 48 / 488
long double12 / 48 / 4812 / 4
long double formatx87 80-bitbinary64binary64x87 80-bit
wchar_t4422
va_list4444
Platforms and compilers of each column
System V i386
Linux and FreeBSD on i386. Measured with GCC 14.2.0 (Debian 14.2.0-19) for i386-linux-gnu; Clang 23.1.2 for i686-linux-gnu and i686-unknown-freebsd14.
Android
Android on x86. Measured with Clang 23.1.2 for i686-linux-android.
Windows
Windows with MSVC or clang-cl. Measured with Clang 23.1.2 for i686-pc-windows-msvc.
MinGW-w64
Windows with MinGW-w64 GCC or Clang. Measured with MinGW-w64 GCC 14.2.0 (Debian 14.2.0-19+27+b1) for i686-w64-mingw32; Clang 23.1.2 for i686-w64-windows-gnu.

The numbers come from compiling sizeof and _Alignof of each type with the compilers of each platform and reading the results back from the assembly. Each column was measured for all the targets listed under its table, and the site’s build fails if they disagree.

long double
It differs most between platforms. The System V ABIs, MinGW-w64 and Cygwin use the x87 80-bit format, stored in 16 bytes on x86-64 and in 12 bytes aligned to 4 on 32-bit x86. Android on x86-64 uses the 128-bit IEEE format instead, while MSVC, UEFI and Android on 32-bit x86 make long double the same as double.
double and long long on 32-bit x86
System V aligns them to 4 bytes and Windows to 8, so a struct of an int and a double takes 12 bytes on 32-bit Linux and 16 on 32-bit Windows.
wchar_t
It has 4 bytes on the System V platforms and 2 on Windows and Cygwin.
va_list
A pointer on Windows and on 32-bit x86, but a 24-byte structure on System V x86-64 (16 bytes in x32), because a variadic function’s arguments may still be in the registers they arrived in.
__int128
Available on every 64-bit platform here, x32 included, and on none of the 32-bit ones. On Windows it is Clang’s: MSVC’s own integer types end at 64 bits.

The pages

One page for each ABI, naming every platform that uses it:

Pages that compare the ABIs:

Sources

  1. System V Application Binary Interface, AMD64 Architecture Processor Supplement, version 1.0: commit e1ce098331da, 2025-03-12
  2. System V Application Binary Interface, Intel386 Architecture Processor Supplement, version 1.2: commit 20ec676cd56d, 2025-08-24
  3. Microsoft Learn: Overview of x64 ABI conventions: commit f70d88cd5da7, 2026-09-24
  4. Microsoft Learn: Data type ranges: commit f70d88cd5da7, 2026-09-24
  5. Microsoft Learn: Argument passing and naming conventions: commit f70d88cd5da7, 2026-09-24
  6. Apple Developer: Writing 64-bit Intel code for Apple Platforms: as published on 2026-09-25
  7. Cygwin User's Guide: Building applications for 64 bit Cygwin: as published on 2026-09-25
  8. MSYS2 documentation: Environments: commit 9879735eb93f, 2026-05-14
  9. EDK II: BaseTools/Conf/tools_def.template: commit 2970e5699ba6, 2026-08-12
  10. Clang 23.1.2: clang/lib/CodeGen/Targets/X86.cpp: commit 85ac56026243, 2026-09-20