ADR-0078: Exact GNU version requirement-to-definition compatibility admission
Status: accepted
Date: 2026-08-10
Decision owners: NAUX architecture and x86-64 symbol-version boundary
Context
ADR-0076 independently inventories the ordered version names each reviewed object requests from each direct provider. ADR-0077 independently inventories the version namespaces those exact providers define. Neither boundary compares the two sides. A later linker cannot safely inspect dynamic symbols until NAUX has first proved that every strong requested namespace exists uniquely in the provider already admitted by the sealed dependency closure.
Decision drivers
- Replay ADR-0076 and ADR-0077 in full before trusting either evidence vector.
- Match only the exact provider ordinal already bound by ADR-0076; never search the host, global closure, filesystem, loader cache, or alternative providers.
- Bind exact version names and sovereign ELF hashes while preserving requester, requirement, auxiliary, provider, and definition order/identity.
- Require one and only one definition for every strong requirement.
- Fail closed on weak requirements until weak-version fallback semantics have a separate decision and oracle.
- Produce a proof-only compatibility ledger. Do not inspect
.dynsym,.gnu.version, relocations, symbol visibility, or symbol types.
Considered options
Let the host loader decide compatibility
Rejected. Host search paths, cache state, replacement objects, and loader policy would become unsealed semantic authority.
Match by name across the complete closure
Rejected. It could satisfy a requirement from the wrong provider and erase the
direct DT_NEEDED binding proven by ADR-0076.
Treat weak requirements as ordinary strong requirements
Rejected. That would be stricter than GNU weak-version semantics without recording the semantic difference, and it could later be misrepresented as loader equivalence.
Implement weak fallback immediately
Rejected. Weak fallback interacts with dynamic-symbol and unversioned-symbol selection, which remain outside this boundary.
Combine compatibility with dynamic-symbol inventory
Rejected. Namespace existence and per-symbol binding are different judgments. Combining them would obscure the authority ceiling and enlarge adversarial state before either layer is independently stable.
Emit an exact proof-only compatibility ledger
Selected. Strong requirement names are joined only to primary definition names in their exact providers. Both source record hashes and every selected target field are sealed into an independently regenerable ledger.
Decision
Introduce an ordered compatibility evidence vector with one object record per
canonical requester and one binding per ADR-0076 Vernaux auxiliary. Before
emission, replay ADR-0077, which itself replays the complete ADR-0073 through
ADR-0076 chain. For each requirement auxiliary:
- require
vna_flags = 0in policy version 1.0; - select only the exact provider ordinal already sealed in its parent ADR-0076 requirement;
- find exactly one ADR-0077 definition whose primary name and sovereign ELF hash equal the requested name and hash;
- seal requester/requirement/auxiliary ordinals and evidence hashes, provider ordinal and evidence hash, definition ordinal/index/flags/name/hash and evidence hash, plus one domain-separated binding hash;
- preserve complete requester and requirement order without sorting or deduplication.
Verification preflights bounded shape and all predecessor roots, then regenerates the complete ledger from independently verified predecessors and requires exact equality.
Frozen limits
- at most 65 requester/provider objects;
- at most 64 requirement records per requester;
- at most 64 auxiliaries per requirement;
- at most 4,096 total compatibility bindings;
- exact UTF-8/ASCII names inherited from ADR-0076/0077, at most 256 bytes;
- weak requirements are refused in policy version 1.0;
- accepted ADR-0076 policy root
7c278cc0cdf40cc415cea91839434dfb4bcbd5305d2a6b73eb2cc41c22e45b32; - accepted ADR-0077 policy root
f447533303533aca4f761a202aafecdff11a428b48aa3b51fa7bd81cda9172ce; - proof-only admission; encoder policy remains
1.4.0.
Consequences
- Positive: every strong GNU version requirement is proven present in the exact reviewed provider before NAUX inspects any symbol table.
- Positive: disagreement between the independent requirement and definition decoders becomes an explicit fail-closed compatibility error.
- Positive: the cross-proof is path-free, byte-free, deterministic, bounded, and cheap relative to the predecessor ELF decoders.
- Negative: objects containing weak requirements remain unsupported even when a host loader could execute them.
- Negative: verification replays the full predecessor chain before joining evidence.
- Risk: namespace compatibility does not prove that any requested dynamic symbol exists under that namespace.
- Open: weak fallback,
.dynsym,.gnu.version, symbol visibility/type, relocation, mapping, initialization, and execution remain outside.
Acceptance gates
- The accepted fixture produces exactly twelve bindings in requester and requirement order: eight from libgcc and four from libc.
- Every binding selects the expected loader or libc definition ordinal/index, exact primary name/hash/flags, and both predecessor record hashes.
- Weak, missing, duplicate, wrong-provider, wrong-name/hash/index/flag/order, stale-root, count, record, and locally resealed evidence mutations fail closed.
- Production source imports no filesystem, path, process, loader, external ELF parser, native emitter, standalone/raw decoder, mapping, relocation, or measurement authority.
- Focused, workspace, strict lint, governance, refinement, format, shell, link, parity, and static-authority gates pass before acceptance.
Related decisions
- ADR-0075 supplies exact unique provider identity.
- ADR-0076 supplies ordered strong/weak requirement facts and exact provider ordinals.
- ADR-0077 supplies independent provider definition namespaces.
Acceptance record
Accepted on 2026-08-10 under dependency-compatibility policy root
017832c19b76fc994f2840423fdee0411893a5c9c21e808f1ee5979e39f7c84e.
The exact ordered ledger contains twelve strong bindings: eight requested by
libgcc and four requested by libc. Libgcc binds GLIBC_2.3 to loader
definition index 3 and its remaining seven names to libc indices 44, 7, 36,
18, 35, 5, and 2 in requirement order. Libc binds GLIBC_2.2.5,
GLIBC_2.3, GLIBC_2.35, and GLIBC_PRIVATE to loader indices 2, 3, 6,
and 7. Every binding retains exact requester, requirement, auxiliary,
provider, and definition ordinals; both predecessor object hashes; exact
primary names, ELF hashes, indices, flags; and domain-separated local seals.
Join probes reject weak requirements, absent definitions, wrong primary
hashes, ambiguous matches, and exact-name matches found only in a different
provider. The locked fixture intentionally contains GLIBC_2.3 in more than
one provider, proving that provider ordinal is semantic rather than a search
hint. Evidence probes reject stale policy/version/definition roots, count,
object, binding, and order drift. A deep mutation substitutes a different
valid definition from the same provider and recomputes the binding, object,
and aggregate seals; independent regeneration still rejects it. Canonical
ADR-0074 through ADR-0078 replay succeeds after deleting the source bundle.
The focused ADR-0078 carrier passes in 360.42 seconds. The all-feature
workspace exits successfully: the library suite reports 408 passed, zero
failed, and six intentionally ignored tests; the ADR-0073 through ADR-0078
carrier reports two passed and zero failed in 323.64 seconds. Strict Clippy,
cargo check, format/diff, 22/22 governance, 8/8 strict refinement, and all
nine shell syntax checks pass. All 334 local Markdown targets across 145
project files resolve with zero missing. Production source imports no filesystem,
path, process, object-byte, native execution, standalone/raw image,
host-loader, symbol-selection, or relocation authority.
Acceptance grants no weak-version fallback, dynamic-symbol or .gnu.version
inventory, symbol selection/binding, host resolution, mapping, relocation,
initialization, execution, sandbox, timing, performance claim, or global
encoder-policy authority.