ADR-0085: Sealed root dynamic-relocation inventory and selection join

Status: accepted
Date: 2026-08-11
Decision owners: NAUX architecture and x86-64 root relocation-inventory boundary

Context

ADR-0071 seals the root worker’s program headers and dynamic entries. ADR-0082 seals its dynamic symbols and version joins. ADR-0084 independently replays the reviewed provider lookup scope and identifies the exact candidate definition for every currently supported strong versioned requester, while explicitly refusing GNU IFUNC candidates.

The root still has no NAUX-owned relocation ledger. A later loader cannot prove which root locations require base-relative adjustment, which locations refer to dynamic symbols, whether a symbol-bearing relocation names the exact ADR-0082 requester, or whether that requester has an ADR-0084 selection. It must not infer those facts from readelf, the host loader, section headers, or an address returned by dlsym.

This decision inventories and joins relocation records only. It does not calculate a load bias, turn st_value into an address, write a relocation, map a segment, relax permissions, initialize a provider, or execute code.

Decision drivers

  • Decode relocation authority from immutable root bytes and already sealed dynamic tags rather than section headers or ambient loader state.
  • Preserve exact table order, target offsets, symbol indices, relocation types, addends, extents, and dynamic-entry provenance.
  • Separate symbol-free base-relative records from symbol-bearing records.
  • Join every symbol-bearing record to the exact ADR-0082 root symbol and join only supported strong requesters to their ADR-0084 decision evidence.
  • Keep weak requesters and IFUNC outcomes explicit and closed rather than pretending that an inventory is permission to resolve or execute them.
  • Bound all arithmetic, table walks, storage, and diagnostics.
  • Grant no runtime-address, write, mapping, initialization, execution, timing, or performance-claim authority.

Considered options

Trust the host loader’s relocation result

Rejected. It collapses decoding, symbol selection, address calculation, interposition, writes, initialization, and execution into opaque ambient state. It also cannot produce NAUX-owned evidence for the original records.

Decode relocation sections by name

Rejected. Runtime relocation authority comes from the admitted PT_DYNAMIC entries, not optional section headers. Section-name lookup would accept a non-runtime view and would not prove the dynamic table actually authorizes the same bytes.

Inventory only the three PLT records

Rejected. The reviewed root contains a much larger DT_RELA table, including base-relative and symbol-bearing records. Ignoring it would leave most root state outside the proof boundary.

Inventory and apply relocations in one pass

Rejected. Application requires admitted load layouts, load biases, provider symbol values, writable target authority, overflow semantics, permission transitions, IFUNC/TLS policy, and initialization order. Those are separate decisions with materially greater authority.

Admit one proof-only relocation ledger

Selected. The boundary decodes the exact DT_RELA and DT_JMPREL extents, classifies the currently reviewed x86-64 relocation forms, and joins symbolic records to predecessor evidence without calculating or writing an address.

Decision

Introduce one independently replayable root relocation ledger with these rules:

  1. ADR-0071, ADR-0082, and ADR-0084 evidence is fully verified before any dynamic tag, symbol index, or candidate decision is trusted;
  2. table locations and sizes come only from the sealed root PT_DYNAMIC entries for DT_RELA, DT_RELASZ, DT_RELAENT, DT_RELACOUNT, DT_JMPREL, DT_PLTRELSZ, and DT_PLTREL;
  3. the accepted entry width is exactly the ELF64 Rela width, all extent and count arithmetic is checked, each table is fully contained in admitted file-backed load bytes, and the two admitted extents do not overlap;
  4. every record seals its table kind, canonical ordinal, file location, relocation target virtual offset, raw r_info, symbol ordinal, relocation type, signed addend, target-segment identity, and evidence hash;
  5. the DT_RELACOUNT prefix contains exactly symbol-zero R_X86_64_RELATIVE records; any malformed prefix, later unreviewed relative form, nonzero symbol, unsupported type, or out-of-range target fails closed;
  6. the first supported symbol-bearing forms are exactly R_X86_64_GLOB_DAT and R_X86_64_JUMP_SLOT; each symbol ordinal must name one exact undefined ADR-0082 record and preserve that symbol’s evidence identity;
  7. every strong versioned symbol-bearing record must join one exact ADR-0084 decision by requester-symbol evidence, name, hashes, semantic version requirement, and provider/candidate identity;
  8. an ADR-0084 IFUNC refusal remains a refusal in relocation evidence and cannot be converted into a selected address; weak, unversioned, GNU-unique, TLS, root-self, or otherwise unsupported requesters receive an explicit unsupported classification and no binding evidence;
  9. duplicate target offsets, duplicate ordinals, missing records, reordered tables, trailing bytes, contradictory dynamic tags, symbol/type mismatch, and count drift fail closed;
  10. verification preflights the canonical bounded encoding, independently regenerates both tables and every predecessor join from sealed bytes, and requires exact aggregate equality;
  11. production inventory performs no load-bias application, provider-value address calculation, final binding, relocation write, mapping, permission transition, initialization, TLS/IFUNC execution, native call, timing, or performance claim.

Frozen limits and acceptance facts

  • exactly one ADR-0071 root artifact, one ADR-0082 root-symbol inventory, and one ADR-0084 selection ledger;
  • exactly two dynamic relocation extents, both using 24-byte ELF64 Rela records and bounded by independently admitted load-file extents;
  • at most 32,768 total records for the first reviewed root and a separately enforced byte cap derived from that record cap;
  • target offsets and table offsets use checked 64-bit arithmetic; ordinals and counts use canonical fixed-width encodings;
  • names are never decoded from relocation records; all names come through the exact ADR-0082 symbol join;
  • every accepted artifact derives its exact DT_RELASZ, DT_RELACOUNT, and total record count from its own sealed ADR-0071 dynamic entries; Cargo features are not relocation-layout authority;
  • the reviewed default, all-feature, and workspace-built artifacts contain respectively 29,055, 29,056, and 29,054 records; only their relative prefixes vary, with respectively 28,947, 28,948, and 28,946 R_X86_64_RELATIVE records;
  • all three reviewed artifacts preserve exactly 105 R_X86_64_GLOB_DAT and three R_X86_64_JUMP_SLOT records, DT_RELAENT = 24, DT_PLTRELSZ = 72, and DT_PLTREL = RELA;
  • the 108 symbol-bearing records reference 106 distinct root symbol ordinals and partition exactly into 89 selected records, eight IFUNC-refused records, and 11 unsupported-requester records;
  • ADR-0084 contains 90 selected decisions; exactly one selected request has no ADR-0085 relocation record because TLS remains a separate closed boundary;
  • proof-only inventory; encoder policy remains 1.4.0.

Consequences

  • Positive: later address and relocation policies receive exact symbolic records instead of reparsing root bytes or trusting the host loader.
  • Positive: root-relative and symbol-bearing work become separately visible, bounded, and independently auditable.
  • Positive: every supported external relocation is tied to the exact ADR-0084 candidate path that selected its provider definition.
  • Positive: IFUNC and weak/unversioned cases remain explicit refusal or unsupported evidence rather than accidental runtime behavior.
  • Negative: the current root has tens of thousands of relative relocations, so exhaustive evidence and adversarial testing will be expensive.
  • Negative: this decision still produces no runnable loader and makes no performance claim.
  • Open: provider load plans, load biases, symbolic value-to-address binding, relocation writes, RELRO permission transitions, IFUNC/TLS, initialization, mapping, and execution remain later decisions.

Acceptance gates

  1. Freeze the exact dynamic-tag/table extents, entry counts, type partition, target-segment coverage, and symbol-reference partition from sealed bytes.
  2. Every symbol-bearing record joins the exact ADR-0082 symbol; every eligible strong versioned record additionally joins the exact ADR-0084 decision.
  3. Raw-byte, dynamic-tag, extent, width, count, order, offset, info, symbol, type, addend, segment, selection, and evidence mutations fail closed.
  4. Coherently resealed alternate record orders, symbol joins, and selection classifications still fail independent regeneration.
  5. Evidence verifies after all source paths are removed.
  6. Production source has no section-name lookup, host loader, runtime address, write, mapping, permission, initialization, native, timing, or claim authority.
  7. Focused default/all-feature, full workspace, strict lint, governance, refinement, format, shell, link, static-authority, and predecessor replay gates pass before acceptance.
  • ADR-0071 supplies the admitted root program headers and dynamic entries.
  • ADR-0082 supplies exact root symbol and version identities.
  • ADR-0084 supplies exact strong candidate decisions and IFUNC refusals.

Acceptance record

Accepted on 2026-08-11 under policy root 825edbe03bec0ddd1d5dbaa8b26cf052d72ce6a35cbe5aa9aa06b4140a6c3779. The independently replayed root contains exactly 105 R_X86_64_GLOB_DAT and three R_X86_64_JUMP_SLOT records. Those 108 symbol-bearing records reference 106 distinct ADR-0082 symbol ordinals and partition into 89 records joined to selected ADR-0084 definitions, eight records joined to explicit IFUNC refusals, and 11 records classified as unsupported requesters. Of ADR-0084’s 90 selected requests, __tls_get_addr is the sole selected request without an ADR-0085 relocation; this ledger does not treat that observation as TLS resolution authority.

The base-relative prefix is deliberately artifact-local. Focused default and all-feature artifacts contain respectively 28,947 and 28,948 relative records, for 29,055 and 29,056 total records. Their aggregate evidence roots are respectively fac873ea72e95d1e685635a28e53244a69da215059605104b89ecd4423582288 and 1f2c84b7d2bdeda1e07d80a890a8202f32601de43e93491401ef89db7c014506. The final workspace-built artifact contains 28,946 relative records and 29,054 total records under aggregate evidence root e76c456cfaa5d086d175c1c6b2ef6123d7b42fceb76b644442b3a0b0833c8317. All three preserve the exact 105/3 relocation-type and 89/8/11 selection-class topology. The carrier derives layout counts from sealed dynamic entries and locks semantic topology separately; it does not infer layout from Cargo features.

Raw-byte probes reject relocation-type substitution, cleared symbol ordinal, invalid and duplicate targets, addend mutation, and table truncation. Shallow count mutations plus coherently resealed classification, addend, duplicate-target, and record-order mutations fail independent regeneration. Exact evidence continues to verify after all source paths are removed. Production inventory imports no filesystem, path, process, host-loader, runtime-address, mapping, native, timing, or performance-claim authority.

Focused default and all-feature carriers pass in 1472.60 and 1367.24 seconds. The final all-feature workspace exits successfully: the library suite reports 423 passed, zero failed, and six intentionally ignored tests; the ADR-0073 through ADR-0085 carrier reports two passed in 1327.91 seconds; and the independent ADR-0071 carrier reports two passed in 149.66 seconds. Strict Clippy, workspace check, 22/22 governance, 8/8 strict refinement, all nine shell syntax checks, format/diff, static-authority, predecessor regeneration, and documentation-link gates pass.

Acceptance grants no runtime address, final binding, relocation write, mapping, permission transition, initialization, weak fallback, GNU-unique, unversioned or root-self lookup, IFUNC/TLS execution, sandbox, standalone, timing, performance claim, or global encoder-policy authority.