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¶
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 abicheckdoes not installcastxml. Any command that takes headers (--old-header/--new-header/-H) needscastxmlon yourPATH— without it those commands fail withcastxml not found. Install it with the system/conda packages below (the conda-forge package pulls it in automatically). If you have nocastxml, run binary-only mode by omitting the header flags — abicheck falls back to DWARF/symbols analysis (weaker, but works).
Option A: system packages¶
# Windows (PowerShell, as administrator)
choco install castxml
# plus MSVC Build Tools (cl.exe) for PE/PDB debug-info analysis
Option B: conda-forge (recommended for reproducible envs)¶
# 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¶
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:
# 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 withcastxml 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:
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:
You can also pass a header directory (recursive scan for *.h, *.hpp, ...):
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 L0–L4 — 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):
JSON — machine-readable, includes precise verdict field:
SARIF — for GitHub Code Scanning:
HTML — standalone human-readable report:
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++):
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¶
5) Application compatibility check¶
Check whether your application is affected by a library update — filtering out irrelevant changes:
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):
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) switchescompareto 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 —0passes,4is worst — but1then 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:
- Library maintainer → Verdicts, Policy Profiles
- App developer → Application Compatibility
- SDK / package maintainer → Multi-Binary Releases, Baseline Management
- CI owner → GitHub Action, Severity Configuration, Output Formats
- Plugin author → Plugin Systems
- Distro / package maintainer → Multi-Binary Releases
- Migrating from ABICC / libabigail → from ABICC, from libabigail
Background reading:
- ABI/API Handling & Recommendations — real-world ABI/API break scenarios and how to prevent them
- Limitations — what abicheck does not catch