Skip to content

Platform Support

abicheck runs on Linux, macOS, and Windows and can analyze binaries from any of these platforms. However, the depth of analysis depends on the host platform and whether headers are available.


Quick Reference: What Works Where

Scanning Linux ELF binaries

Host OS Symbol diff Type/param diff Requires
Linux ✅ ✅ Full ✅ Full castxml, g++/gcc
macOS ✅ Yes ❌ No
Windows ✅ Yes ❌ No

Best results: run on Linux with headers provided.

Scanning Windows PE (DLL) binaries

Host OS Symbol diff Type/param diff Requires
Linux ✅ Yes ❌ No pefile
macOS ✅ Yes ❌ No pefile
Windows ✅ Yes ✅ Yes castxml + cl.exe

Scanning macOS Mach-O (dylib) binaries

Host OS Symbol diff Type/param diff Requires
Linux ✅ Yes ❌ No macholib
macOS ✅ Yes ✅ Yes castxml (Xcode clang)
Windows ✅ Yes ❌ No macholib

What "Symbols Only" Mode Means

When scanning a binary without headers, or scanning a non-native binary cross-platform, abicheck operates in symbol-table mode:

Detected: - Function added / removed (func_added, func_removed) - SONAME changed (soname_changed) - Symbol visibility changed (symbol_visibility_changed) - Variable added / removed

Not detected (requires type information from headers + castxml): - Parameter type changes (func_params_changed) - Return type changes (func_return_type_changed) - Struct/class layout changes (type_size_changed, type_field_*) - vtable changes (type_vtable_changed) - Inline/noexcept changes

Recommendation: for complete ABI analysis, always provide -H <header_dir> and run on the native platform (Linux for ELF, macOS for Mach-O, Windows for PE).


Cross-Platform Examples

Scan a Windows DLL from Linux

# Symbol-level diff (works cross-platform)
abicheck compare mylib_v1.dll mylib_v2.dll

# With headers (only useful if castxml+cl.exe is available)
abicheck compare mylib_v1.dll mylib_v2.dll \
  -H include/

What you get: func_removed, func_added, ordinal changes. What you miss: parameter type changes, struct layout changes.

Scan a macOS dylib from Linux

abicheck compare libmylib.1.dylib libmylib.2.dylib

What you get: exported symbol diff. What you miss: type-level analysis (no DWARF walk cross-platform today).

Scan a Linux .so from macOS

abicheck compare libmylib.so.1 libmylib.so.2

ELF parsing is pure Python — works on macOS. DWARF walk also works. Full type analysis requires castxml and headers available on the host.


GitHub Actions: Multi-Platform CI

To get full analysis on each platform:

jobs:
  abi-check:
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - name: Install abicheck (source)
        run: pip install -e .
      - name: Install castxml (Linux/macOS only)
        if: runner.os != 'Windows'
        run: |
          # sudo apt-get install -y castxml   # Ubuntu
          # brew install castxml              # macOS
      - name: ABI check
        run: |
          abicheck compare -lib mylib \
            -old old/libmylib.so -new new/libmylib.so \
            -H include/

Known Limitations by Platform

Windows host

  • castxml with cl.exe backend is untested in CI — may work but is not validated
  • MSVC vtable layout differs from Itanium ABI; vtable diff results may be inaccurate
  • __stdcall/__cdecl calling-convention changes appear as func_removed + func_added (mangled-name churn) — no dedicated change kind; see #50 below
  • Tracked: abicc upstream issues #9, #50, #56, #121

macOS host

  • ARM64 Apple AAPCS differs from Itanium for small structs (≤16 bytes passed in registers)
  • install_name (LC_ID_DYLIB, macOS SONAME equivalent) changes are tracked and emit SONAME_CHANGED
  • Two-level namespace (LC_LOAD_DYLIB) not fully analyzed
  • Tracked: abicc upstream issues #116, #119

All platforms (symbols-only mode)

  • int → long parameter change: mangled name changes → detected as func_removed + func_added (not func_params_changed) when headers are absent
  • Template inner-type changes (std::vector<T> with changed T) — not detected (tracked: #38)

Dependency Summary

Feature Required tools pip / system install conda-forge install
ELF analysis pyelftools pip install -e . conda install -c conda-forge abicheck
PE analysis pefile pip install -e . conda install -c conda-forge abicheck
Mach-O analysis macholib pip install -e . conda install -c conda-forge abicheck
Type/param analysis (Linux) castxml + C/C++ compiler pip install -e . + apt/yum (castxml, gcc/g++) conda install -c conda-forge abicheck
Type/param analysis (macOS) castxml + Apple toolchain pip install -e . + brew install castxml (+ Xcode CLT) conda install -c conda-forge abicheck
Type/param analysis (Windows) castxml + cl.exe pip install -e . + Visual Studio Build Tools + castxml conda install -c conda-forge abicheck

For conda-based workflows, install only abicheck from conda-forge. Recipe dependencies pull required analysis tooling automatically.


Windows Toolchain Support Matrix

Toolchain castxml backend Type/param diff Calling-convention tracking Status Notes
MinGW (GCC) --castxml-cc-gnu gcc ✅ Yes ⚠️ Partial (__cdecl/__stdcall not a dedicated kind, #50) Experimental Covered by CI smoke tests; full MinGW integration coverage on windows-latest is best-effort and not guaranteed on every run.
MSVC (cl.exe) --castxml-cc-msvc cl.exe ✅ Yes ⚠️ Partial (#9, #50) Untested in CI May work locally with Visual Studio Build Tools; ABI details differ from Itanium assumptions in several detectors.

Known Limitations (Windows)

  • #9 (MSVC headers / Windows SDK edge cases): castxml+cl.exe handles common cases, but complex SDK-specific declarations are not yet validated in project CI.
  • #50 (calling conventions): __stdcall/__cdecl deltas are represented as mangled-name churn (func_removed + func_added) instead of a dedicated calling-convention change kind.
  • #56 (PE visibility semantics): __declspec(dllexport/dllimport) transitions are currently reflected at symbol-level only; no dedicated PE export-visibility change kind.
  • #121 (MinGW-specific behavior): MinGW export/import edge-cases (import libs, ordinals, toolchain flags) are only partially covered by current smoke/integration tests.

macOS ARM64 — Known ABI Differences

ARM64 (Apple Silicon) has a different calling convention from x86-64 that affects how small structs and floating-point aggregates are passed.

Feature x86-64 (System V) ARM64 (Apple AAPCS) Detected by abicheck?
Small struct (≤16 B) Stack or reg pair Passed in GP registers TYPE_SIZE_CHANGED catches size delta
HFA (Homogeneous Floating-point Aggregate ≤4 floats) Stack SIMD/FP registers ⚠️ Size same, registers differ — NOT detected
HVA (Homogeneous Vector Aggregate) Stack SIMD/FP registers ⚠️ Size same, registers differ — NOT detected
Return in registers RDX:RAX (x86-64) x0:x1 (ARM64) ⚠️ Not tracked (no calling-convention change kind)

Tracked: abicc issues #116 (small-struct register passing) · #119 (install_name).

install_name tracking (#119)

install_name (LC_ID_DYLIB) is the macOS equivalent of ELF SONAME. abicheck now tracks this — a change emits SONAME_CHANGED with symbol="LC_ID_DYLIB".

Scenario Status
install_name changes between versions SONAME_CHANGED emitted
install_name absent → set ✅ tracked
No change ✅ no false positive

Support claim

ARM64/macOS: Experimental - Symbol diff: fully supported (export table via macholib) - Type/param diff: requires Xcode clang + castxml (≥ 0.9.0 with Apple toolchain backend) - HFA/HVA calling-convention drift: not directly detected — workaround: always check TYPE_SIZE_CHANGED on structs