Skip to content

Getting Started

abicheck compares two versions of a C/C++ shared library and tells you whether existing binaries will break. It supports ELF (Linux), PE/COFF (Windows), and Mach-O (macOS) binaries.

On all platforms it provides binary metadata analysis (exports, imports, dependencies) and header AST analysis (via castxml). Debug info cross-check uses DWARF (Linux, macOS) and PDB (Windows).

In CI already? Skip straight to the GitHub Action — it installs everything and runs the check in a few lines of YAML.


What question are you asking?

abicheck ships several commands; pick the one that matches your question. If you're unsure, start with abicheck compare — it's the default workflow.

Your question Command See
Did my library break? — does upgrading it break existing consumers? abicheck compare §2 below
Does my application still work with the new library version? abicheck appcompat §5 below
Did my whole package / release break? abicheck compare Multi-Binary Releases
Will this binary load and resolve correctly in this sysroot — and does its dependency tree have unresolved symbols? abicheck deps tree (--sysroot /rootfs for a specific root) CLI Usage
Did anything in the dependency stack change between two sysroots / images? abicheck deps compare --baseline … --candidate … CLI Usage
I'm migrating from abi-compliance-checker and want the same flags. abicheck compat Migrating from ABICC
Save a reusable ABI baseline for CI. abicheck dump §4 below

For the full decision matrix — every artifact layout, accuracy tier, and CI policy — see Choose Your Workflow.


1) Install abicheck

pip install abicheck
# or
conda install -c conda-forge abicheck

Requirements

  • Python 3.10+
  • castxml + a C/C++ compiler — required for header AST analysis (all platforms)

All Python dependencies (pyelftools, pefile, macholib) come with the abicheck install.

Important: pip install abicheck does not install castxml. Any command that takes headers (--old-header / --new-header / -H) needs castxml on your PATH — without it those commands fail with castxml not found. Install it with the system/conda packages below (the conda-forge package pulls it in automatically). If you have no castxml, run binary-only mode by omitting the header flags — abicheck falls back to DWARF/symbols analysis (weaker, but works).

Option A: system packages

# Ubuntu / Debian
sudo apt-get update && sudo apt-get install -y castxml gcc g++
# macOS
brew install castxml
# plus Xcode Command Line Tools for clang
# Windows (PowerShell, as administrator)
choco install castxml
# plus MSVC Build Tools (cl.exe) for PE/PDB debug-info analysis
# create env and install abicheck (recipe includes required analysis deps)
# Python >= 3.10 is required; any supported version works
conda create -n abicheck -c conda-forge python=3.12 abicheck
conda activate abicheck

No extra manual dependency installation is required when using the conda-forge package.

Install from source

git clone https://github.com/abicheck/abicheck.git
cd abicheck
pip install -e .

2) First check (using repo examples)

Best first run: compare two shared libraries with their public headers — it gives abicheck the most evidence to work with (see the input-quality ladder below).

The repo includes 152 ABI scenario examples. Most are single-library cases with paired v1/v2 sources and headers; bundle/release-level cases use release-style layouts. Browse the generated single-library pages in the Examples & Case Encyclopedia, or pick one and run it locally:

cd examples/case01_symbol_removal
# Build v1 and v2 shared libraries
gcc -shared -fPIC -g v1.c -o libv1.so
gcc -shared -fPIC -g v2.c -o libv2.so
# Compare (header-aware — needs castxml; see Requirements above)
abicheck compare libv1.so libv2.so --old-header v1.h --new-header v2.h
# Verdict: BREAKING (symbol 'helper' was removed)

No castxml? The command above will fail with castxml not found. Either install castxml (see Requirements), or run the same comparison in binary-only mode by dropping the header flags — it still catches the removed symbol from the ELF/DWARF metadata:

abicheck compare libv1.so libv2.so   # binary-only fallback, no castxml needed

For your own library:

abicheck compare libfoo.so.1 libfoo.so.2 \
  --old-header include/v1/foo.h --new-header include/v2/foo.h

If the header is the same for both versions:

abicheck compare libfoo.so.1 libfoo.so.2 -H include/foo.h

You can also pass a header directory (recursive scan for *.h, *.hpp, ...):

abicheck compare libfoo.so.1 libfoo.so.2 -H include/

If no headers are provided for ELF inputs, abicheck falls back to symbols-only mode and prints a warning (weaker analysis: may miss type/signature ABI breaks).

Input quality: the five evidence layers (L0–L4)

How much abicheck can prove depends on what you give it. It overlays up to five independent, additive sources — labelled L0L4 — and lets the strongest evidence win. Start at the layer your artifacts allow and add more when you need more confidence:

Layer Inputs (flags) Confidence What it newly catches
L0 Binary only Low Symbol add/remove, SONAME, visibility, basic metadata
L1 + debug info (-g build / sidecar) Medium Struct/class layout, field offsets, enum values, vtable slots, calling convention
L2 + headers (-H include/) High Public API surface: signatures, overloads, access, noexcept, templates, public/internal scoping
L3 + build data (-p build/) Higher The flags the library was actually built with: -std, _GLIBCXX_USE_CXX11_ABI, -fvisibility, sysroot, export maps
L4 + sources (build/source pack via compare --old-build-info/--new-build-info) Best Facts that never reach the binary: macro/constexpr values, default-argument values, uninstantiated templates

The layers are additive, not a fallback chain: artifact-backed evidence (L0/L1/L2) is authoritative for the shipped-ABI verdict, while build/source evidence (L3/L4) explains, localizes, and scopes a finding (and can raise its own source-level findings) but never silently deletes an artifact-proven break. With less input, abicheck degrades gracefully down the staircase rather than failing — a stripped binary with no headers collapses toward symbol-only checking.

Run abicheck dump libfoo.so --show-data-sources to see which layers abicheck found for a binary. For the full picture see Evidence & Detectability and the per-layer Tool Modes reference; build data (L3) and source build/source packs (L4) are documented under CLI Usage → Evidence packs. For stripped production builds, point abicheck at separate debug files (--debug-root1/2) or fetch them with --debuginfod — see CLI Usage.


3) Output formats

abicheck supports five output formats: markdown (default), json, sarif, html, and junit (plus a compact review digest). See Output Formats for the full reference.

Markdown (default, printed to stdout):

abicheck compare libfoo.so.1 libfoo.so.2 -H foo.h

JSON — machine-readable, includes precise verdict field:

abicheck compare libfoo.so.1 libfoo.so.2 -H foo.h --format json -o result.json

SARIF — for GitHub Code Scanning:

abicheck compare libfoo.so.1 libfoo.so.2 -H foo.h --format sarif -o abi.sarif

HTML — standalone human-readable report:

abicheck compare libfoo.so.1 libfoo.so.2 -H foo.h --format html -o report.html

4) Snapshot workflow (for CI baselines)

Save a snapshot once per release, then compare against new builds without re-dumping:

# Save baseline (header is baked into the snapshot)
abicheck dump libfoo.so -H include/foo.h --version 1.0 -o baseline.json
# Compare saved baseline against current build
abicheck compare baseline.json ./build/libfoo.so \
  --new-header include/foo.h --new-version 2.0-dev
# Or compare two snapshots (no headers needed — already baked in)
abicheck compare old.json new.json

compare auto-detects each input: .so files are dumped on-the-fly, .json snapshots are loaded directly. You can mix them freely.

Language mode

Use --lang c for pure C libraries (default is c++):

abicheck dump libfoo.so -H foo.h --lang c -o snap.json

Cross-compilation

When analysing libraries built for a different architecture:

abicheck dump libfoo.so -H include/foo.h \
  --gcc-prefix aarch64-linux-gnu- \
  --sysroot /opt/sysroots/aarch64 \
  -o snap.json

Available flags: --gcc-path, --gcc-prefix, --gcc-options, --sysroot, --nostdinc.

Verbose output

abicheck compare old.json new.json -v

5) Application compatibility check

Check whether your application is affected by a library update — filtering out irrelevant changes:

abicheck appcompat ./myapp libfoo.so.1 libfoo.so.2 -H include/foo.h

This parses your application binary to find which library symbols it actually uses, then shows only the changes that matter. If the library removed a function your app never calls, it won't appear in the report.

Quick symbol availability check (no old library needed):

abicheck appcompat ./myapp --check-against libfoo.so.2

See Application Compatibility for the full reference.


6) Exit codes and CI

By default, abicheck compare exits with the verdict:

Exit code Verdict Meaning
0 NO_CHANGE / COMPATIBLE / COMPATIBLE_WITH_RISK Safe — no binary ABI break
1 Tool/runtime error
2 API_BREAK Source-level API break (binary still works)
4 BREAKING Binary ABI break

Note: passing any --severity-* flag (as the recipes below do) switches compare to severity-aware exit codes: 0 = no error-level findings, 1 = error-level findings in addition/quality categories, 2 = in potential-breaking, 4 = in ABI-breaking. The shape stays the same — 0 passes, 4 is worst — but 1 then means a finding, not a tool error.

Full reference (including compat mode): Exit Codes

Policy recipes — what should fail the build?

abicheck separates what fails CI (severity → exit code) from what shows up in the report (display filtering). These three recipes cover the common cases; the Choose Your Workflow → policy recipes and Severity Configuration pages have the rest.

# Breakage-only gate: report everything, fail ONLY on binary ABI breaks
abicheck compare baseline.json build/libfoo.so \
  --new-header include/ \
  --severity-preset info-only \
  --severity-abi-breaking error

# Strict API-surface governance: also fail on new public ABI/API additions
abicheck compare baseline.json build/libfoo.so \
  --new-header include/ \
  --severity-addition error

# Show only additions in a review report — verdict and exit code unchanged
abicheck compare baseline.json build/libfoo.so \
  --new-header include/ \
  --show-only compatible,added

The first maps to "just alert me on breakages"; the second to "fail when new public ABI/API appears." The third is display-only--show-only filters what the report renders without changing the verdict or exit code.

GitHub Actions — the easy way

The fastest way to gate ABI in CI is the first-class GitHub Action. It installs Python, castxml, and abicheck for you, runs the comparison, sets the step exit code, and can upload SARIF — all in a few lines of YAML:

- uses: abicheck/abicheck@v0.3.0
  with:
    old-library: abi-baseline.json   # committed or downloaded baseline
    new-library: build/libfoo.so
    new-header: include/foo.h
    upload-sarif: true

See the GitHub Action reference for every input, baseline workflows, package/compare-release mode, and multi-platform matrices.

GitHub Actions — raw CLI

If you prefer to drive the CLI directly, save a baseline once at release time, then compare every new build:

# Release step — save baseline as an artifact
abicheck dump ./build/libfoo.so -H include/foo.h \
  --version 1.0 -o abi-baseline.json
# Upload abi-baseline.json as a release artifact
# CI step — compare new build against saved baseline
steps:
  - name: Download ABI baseline
    uses: actions/download-artifact@v4
    with:
      name: abi-baseline

  - name: Compare ABI
    run: |
      abicheck compare abi-baseline.json ./build/libfoo.so \
        --new-header include/foo.h \
        --format sarif -o abi.sarif

  - uses: github/codeql-action/upload-sarif@v3
    if: always()
    with:
      sarif_file: abi.sarif

Next steps

Find your workflow: Choose Your Workflow maps your artifacts and CI policy to the exact command. Or jump straight to your persona:

Background reading: