An application binary interface (ABI) defines how application programs can interact with the operating system or other programs on a computer system. It specifies the low-level binary interface between an application and the operating system or between applications. An ABI ensures binary compatibility so that an application built for one system can run on another system of the same architecture without modifications.
The ABI covers details like data type sizes, calling conventions, register usage, system call numbers, and binary file formats. Maintaining a stable ABI allows developers to write code that works across different versions of an operating system and hardware. In this article, we will look at some examples of ABIs used on major platforms.
Linux ABI
The Linux operating system defines a UNIX-style ABI that is implemented across many CPU architectures like x86, ARM, and RISC-V. The Linux ABI covers aspects like:
- System calls – All Linux system calls have a stable number that programs can use to invoke them.
- Binary formats – Executables and libraries use the ELF binary format.
- C data types – Defined C data types like int, long have a fixed size.
- Calling conventions – Functions use registers and the stack in a defined way.
- Name mangling – Encoding of function names into a standard form.
The Linux ABI maintains backward compatibility heavily so that old binaries continue to work on newer versions. But it also steadily evolves by adding some new features and APIs over time. The Linux kernel ABI is separated from the GNU C library ABI, so they can be updated independently.
Windows ABI
The Windows ABI covers how Windows applications interact with the kernel and system libraries. Some key aspects include:
- Win32 API – Primary native C/C++ interface for Windows apps.
- COM – Binary standard for component interaction.
- .NET Framework – Managed code ABI for .NET languages.
- DLLs – Dynamic link libraries that programs can load at runtime.
- Calling conventions – _cdecl, _stdcall, and _fastcall.
- Data types – int, long defined for 32/64-bit Windows.
Microsoft maintains backward compatibility for the core Win32 ABI across Windows versions. But they have also created more modern ABIs like .NET Framework that target specific development stacks. The Windows ABI is designed to be very portable across hardware platforms.
macOS ABI
macOS defines an ABI above the underlying hardware so that developers don’t have to write hardware-specific code. It covers aspects like:
- Mach-O binary format – Used for executables and libraries.
- System calls – UNIX-style calls with a fixed number.
- C runtime – Defines C data types, math functions, etc.
- Objective-C runtime – Interface for Objective-C dynamic features.
- DriverKit – Framework for writing device drivers.
- Apple frameworks – Interface for using Apple frameworks.
Apple maintains extensive backward compatibility with older Mac software through emulation layers. Newer versions of macOS deprecate older ABI features gradually while adding support for newer APIs and hardware capabilities.
Java ABI
The Java platform defines a virtual machine and language ABI that allows Java code to run across operating systems and hardware. The key elements of Java’s ABI include:
- JVM instruction set – Common interpreted bytecode.
- Java Native Interface – Interface for linking native code.
- Java class file format – Binary layout for compiled Java classes.
- Java API – Libraries that make up the standard Java API.
- Java language spec – Defines language structures, variables, etc.
Oracle maintains extensive backward compatibility for older Java applications. Code compiled for older versions of Java will run on newer JVMs without changes. New language features get added regularly while keeping the core ABI stable.
Android ABI
The Android operating system for mobile devices defines its own ABI for application compatibility across device vendors. The Android ABI includes:
- Dalvik bytecode – Android’s register-based VM instruction set.
- .dex file format – Android binary format for compiled bytecode.
- Android Framework API – Native Android app frameworks.
- NDK – Interface for linking native code.
- Bionic C library – Optimized C library for Android.
Google maintains extensive backward compatibility for older Android apps using app emulation layers. New Android releases add support for new instruction sets and hardware capabilities.
Go ABI
The Go programming language defines its own ABI that allows compiled Go code to work across operating systems and CPU architectures. Go’s ABI covers aspects like:
- Calling convention – Defines how to call between Go and C code.
- Object file format – Encapsulates Go package metadata and code.
- Type information – Encodes type shape, alignment, and size info.
- Go standard library – Implements common functions and data structures.
The Go project maintains extensive ABI compatibility policy so that old code can run on new Go releases. There are plans to formalize the ABI for future compatibility.
Rust ABI
Although Rust does not yet have a fixed ABI, there are efforts to define a stable ABI that allows Rust code compiled separately to link together. Proposed elements include:
- Mangling scheme – Encodes Rust identifiers into symbols.
- Data layout – Defines how Rust data types are laid out.
- Calling convention – Standardizes function calls between modules.
- Binary format – Standard way to package Rust libraries/executables.
The Rust ABI working group is working on proposals to create a stable ABI while retaining Rust’s flexibility and avoiding C++’s ABI fragility issues.
WebAssembly ABI
WebAssembly aims to define a portable ABI for running sandboxes code on the web. Its ABI covers aspects like:
- Instruction set – Low-level stack-based opcode format.
- Module format – Standard binary encoding for WASM code and data.
- JS API – Interface for interacting with JS environment.
- Browser integration – DOM and other web platform access.
The WebAssembly ABI provides portability across browsers and devices. There are ongoing proposals to enhance the ABI with new features like garbage collection integration.
In summary, ABIs allow compiled code to work across implementations of an operating system, platform, or language. Standardizing the ABI enables portability and interoperability between components in a ecosystem. But ABIs also need to evolve over time to take advantage of new hardware and software capabilities.