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¶
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).