ci(composition): scope A1d-bind-now to release links, declare fixtures by name

Step 0o (MCPB bundle contract) and Step 0q (release candidate derivation)
hand the composition gate a stub compiled straight from a .c file with the
compiler's defaults. That stub exists to exercise packaging, format detection
and the needle scans; it is not linked with -z now and never ships. A1d
measured eager binding on it anyway, so on a toolchain that binds lazily by
default (test-diag: clang-22) Step 0o died deterministically with

  A1d-bind-now stage/codebase-memory-mcp: GOT section(s) .got.plt fall
  outside PT_GNU_RELRO [15824,16384) — they stay WRITABLE after relocation

The property A1d protects is a property of the release link (-z now in
Makefile.cbm), so the assertion is scoped to it: a caller that feeds the gate
a packaging fixture declares so with CBM_COMPOSITION_FIXTURE=1 and A1d is
reported n/a — printed on every run as a named exemption, never a silent
skip. Every other assertion still runs on the fixture unchanged, and release
derivation never sets the variable.

Verified in the Linux arm64 container (Ubuntu 24.04, gcc 13.3): a stub
linked -z lazy reproduces the CI failure text on the gate without the
variable and reports n/a with it; both contracts pass under a CC wrapper
that links lazily (the shape that was red), and unchanged on macOS.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel committed 2026-09-04 22:02:47 +02:00
1 parent fece72b049
commit 7b92d2a621
3 files changed
+33 -1

No files matched your search

+18 -1
View File
@@ -29,6 +29,11 @@
# meaningful. Exit 0 = every assertion passed, 1 = at least one failed,
# 2 = usage error, missing tool, or nothing checkable was found (a vacuous
# run is a failure, not a pass).
# CBM_COMPOSITION_FIXTURE=1 declares the target a packaging FIXTURE (a stub
# compiled by a contract test, not a release link): A1d-bind-now is then
# reported n/a instead of asserted, because eager binding is a property of
# the release link flags, not of the packaging path the fixture exercises.
# Only contract tests set it; release derivation never does.
set -euo pipefail
case "${1:-}" in
@@ -409,7 +414,19 @@ check_file() {
# writes the GOT after the loader has already re-protected it. A static
# binary has no PT_DYNAMIC and nothing to bind at runtime, so GOT coverage
# is the whole property there — a distinct reported outcome, never a skip.
if [ "$fmt" != elf ]; then
#
# Scope: this is a property of the RELEASE link (-z now in Makefile.cbm).
# Contract tests hand the gate a stub compiled straight from a .c file to
# exercise packaging, format detection and the needle scans; a stub is not
# linked with -z now and never ships, so asserting eager binding on it
# tests the fixture's compiler defaults, not the artifact. Such a caller
# declares itself with CBM_COMPOSITION_FIXTURE=1 and A1d is reported n/a —
# a named exemption, printed on every run, never a silent skip. Every
# other assertion still runs on the fixture unchanged.
if [ "${CBM_COMPOSITION_FIXTURE:-0}" = 1 ]; then
printf 'n/a %-22s %s: packaging fixture (CBM_COMPOSITION_FIXTURE=1) — eager binding is asserted on release links only\n' \
A1d-bind-now "$token"
elif [ "$fmt" != elf ]; then
printf 'n/a %-22s %s: BIND_NOW is an ELF dynamic-section property\n' \
A1d-bind-now "$token"
elif [ "$relro_window" = unsupported ]; then
+9
View File
@@ -14,7 +14,16 @@
# the SQLite OMIT_LOAD_EXTENSION marker — live in .rodata. This test packages
# the same opaque selected bytes under foreign target labels deliberately; this
# boundary proves it never invokes strip/codesign and never changes bytes.
#
# One assertion is scoped out, by name: A1d-bind-now measures eager binding,
# a property of the RELEASE link (-z now in Makefile.cbm). The stub below is
# compiled straight from stub.c with the compiler's defaults, so on an ELF leg
# it would fail A1d for a reason that has nothing to do with packaging.
# CBM_COMPOSITION_FIXTURE=1 tells the gate this target is a fixture; the gate
# prints A1d as n/a and runs everything else unchanged. Release derivation
# never sets it.
set -euo pipefail
export CBM_COMPOSITION_FIXTURE=1
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
FIX="$(mktemp -d "${TMPDIR:-/tmp}/cbm-mcpb-contract.XXXXXX")"
@@ -2,6 +2,12 @@
# Contract: release candidates are two immutable derivations of one linker
# output. This uses the host's real compiler/strip/signing tools.
set -euo pipefail
# The input below is a stub compiled with the compiler's defaults, not a
# release link: it exercises derivation (strip/codesign/hash binding), not the
# link flags. Declare it a fixture so the composition gate reports A1d-bind-now
# (eager binding, a -z now property of Makefile.cbm) as n/a instead of failing
# on the fixture's own link; every other assertion still runs on it.
export CBM_COMPOSITION_FIXTURE=1
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
FIX="$(mktemp -d "${TMPDIR:-/tmp}/cbm-candidate-derive.XXXXXX")"