Skip to content

Case 145: Unversioned Export Under a Versioning Scheme (Audit, Pure L0)

Field Value
Verdict ๐ŸŸข COMPATIBLE
Category Quality (Compatible)
Platforms Linux
Flags Bad practice
Detected ChangeKinds unversioned_exported_symbol
Source files examples/case145_audit_unversioned_export/

Category: Quality (Audit) | Verdict: ๐ŸŸข COMPATIBLE (bad practice)

Verdict and consumer impact

Single-release audit: one build's evidence checked against itself, no baseline. abicheck's verdict is COMPATIBLE, but the audit flags an advisory finding: the library defines a symbol-versioning scheme (DEMO_1.0 in .gnu.version_d) for demo_init/demo_run, yet a third export, demo_experimental, ships with no version node at all. Once consumers link against the bare (unversioned) demo_experimental symbol, the library has no way to later ship an incompatible demo_experimental under a new version node the way it can for the versioned symbols โ€” the usual "add DEMO_2.0, keep DEMO_1.0 for old binaries" escape hatch doesn't exist for a symbol that was never versioned in the first place.

What this snapshot contains

snapshot.abi.json is a single, hand-built AbiSnapshot for one build of libdemo.so. This case is pure L0 โ€” everything the finding needs is in the ELF export table's own version metadata, nothing from DWARF or headers:

Source in the snapshot What it records
Binary export table (L0, elf.symbols[].version) demo_init โ†’ "DEMO_1.0", demo_run โ†’ "DEMO_1.0", demo_experimental โ†’ null (no version)
Version-definition scheme (L0, elf.versions_defined) ["DEMO_1.0"] โ€” the library does define a scheme

abicheck command

abicheck scan snapshot.abi.json

Expected abicheck finding

Verdict: COMPATIBLE (exit 0)

crosscheck:unversioned_exported_symbol present   binary exports โ†” version table:
  1 exported symbol(s) with no version under a 1-node scheme

ABI-hygiene catalog (intra-version, advisory)
  [warning] unversioned_exported_symbol: 1

Minimum evidence

min_evidence: L0 โ€” both facts the check needs (the export table and its per-symbol version strings, plus the set of defined version nodes) live entirely in the ELF .dynsym/.gnu.version_d data. No DWARF, no headers, no previous release required.

Why abicheck catches it

unversioned_exported_symbol is a cross-source check within a single artifact: abicheck compares the exported-symbol set against the version-definition scheme recorded in the same binary (binary_exports is the sole provider โ€” no second artifact needed, per provider_assertions). A library that defines any version scheme is expected to version every export consistently; a symbol sitting outside that scheme is what the check flags. Reading the export table alone (without also reading the version metadata attached to each symbol) can't tell "no versioning used" from "versioning used inconsistently" โ€” the check has to look at both.

Why this matters for a real release

Symbol versioning exists precisely so a library can ship an ABI-incompatible change to a symbol while keeping old binaries working against the old version node. An export that slips through unversioned trades away that option the moment a consumer links against it โ€” the next incompatible change to demo_experimental has no compatible path back to old callers, unlike demo_init/demo_run, which do.

Safe redesign

Bring the export into the existing scheme before it ships: add demo_experimental to the version script under a version node (e.g. DEMO_1.1 { global: demo_experimental; } DEMO_1.0;), or, if it isn't meant to be public API yet, move it to the script's local: block so it isn't exported at all.

Cross-tool comparison

unversioned_exported_symbol is a cross-source check unique to abicheck's audit mode โ€” it reconciles two views of the same binary (its export table and its version-definition scheme) rather than diffing two releases, which isn't something abidiff/abi-compliance-checker do.


Source files

  • snapshot.abi.json

See also: Examples overview ยท All COMPATIBLE cases ยท Category: Quality (Compatible).