How libdav1d Ensures Cross-Platform ABI Stability

This article explores how libdav1d, the open-source AV1 video decoder, maintains consistent Application Binary Interface (ABI) stability across different operating systems and hardware architectures. We will examine its use of opaque data structures, controlled symbol exports, standardized assembly calling conventions, and automated testing pipelines that prevent breaking changes in downstream applications.

Opaque Data Structures and API Design

The primary mechanism libdav1d uses to guarantee ABI stability is the strict use of opaque data structures. In the public headers, critical structures—such as decoder contexts and picture buffers—are declared as incomplete types (pointers to structs).

Because the internal layout, field sizes, and alignments of these structures are hidden from the public API, client applications cannot access them directly. Instead, clients must interact with the library using designated getter and setter functions. This separation allows developers to modify, add, or reorder internal struct fields in newer versions of libdav1d without altering the binary layout expected by compiled applications.

Strict Symbol Export Control

To prevent name clashes and accidental dependency binding, libdav1d strictly controls which symbols are visible to the operating system’s linker.

The library utilizes compiler attributes (such as __attribute__((visibility("default"))) on GCC/Clang and __declspec(dllexport) on MSVC) mapped to a unified DAV1D_API macro. Only functions explicitly decorated with this macro are exported in the final shared library or DLL. All internal helper functions, especially those used for platform-specific hardware acceleration, are kept private, preventing external applications from linking against volatile internal APIs.

Fixed-Width Integer Types and Standardized Layouts

Cross-platform compatibility requires consistent data sizes regardless of the compiler, operating system, or CPU architecture. libdav1d achieves this by strictly avoiding native C types whose sizes vary across platforms (such as long, which is 32-bit on 64-bit Windows but 64-bit on 64-bit Linux).

Instead, the library relies entirely on standard fixed-width types defined in <stdint.h> (e.g., int32_t, uint64_t, intptr_t). This ensures that function arguments and structure alignments remain identical whether compiled on Windows, macOS, Linux, Android, or iOS.

Unified Assembly Calling Conventions

Because a significant portion of libdav1d is written in assembly for x86 (using NASM) and ARM (using GAS) to maximize decoding speed, managing CPU-level calling conventions is critical. Different operating systems utilize different registers and stack layouts for function calls (for example, the Windows x64 calling convention versus the System V AMD64 ABI used by Linux and macOS).

libdav1d resolves this by utilizing a robust assembly preprocessing layer. This layer abstracts the underlying calling convention, automatically mapping function arguments to the correct CPU registers and handling stack allocation based on the target platform at compile time. This ensures that the hand-optimized assembly code seamlessly adheres to the host platform’s ABI.

Automated ABI Verification in CI Pipelines

To prevent human error from introducing ABI regressions, the libdav1d development workflow integrates automated static analysis tools into its continuous integration (CI) pipeline.

Tools like abi-compliance-checker compare the public header files and compiled shared libraries of new pull requests against established stable releases. If a change inadvertently alters a function signature, changes an exported symbol, or modifies an exposed data structure, the CI pipeline flags the build as failed, ensuring that ABI compatibility is verified before any code is merged into the main branch.