Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 46 additions & 19 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,10 @@ drops a symbol fails the link by name on both backends. Surface:
`net_dhcp` (alias), and `net_print_ip` — IP printing is consumer UI and
is now `print_local_ip` in `boot.s`, one copy for both backends.
- Byte accounting on ip65 (the tight one): LOADER went from 16 B free to
58 B **at the time of #142**; it is **21 B** at the v0.14.0 pin, which
is the number the Memory layout section carries and the one to use.
58 B **at the time of #142**; it is **17 B** at the v0.14.0 pin with
#211's fix in (21 B before it — the 4 B is `http.o`'s `CODE` growth in
that fix, measured), which is the number the
Memory layout section carries and the one to use.
`print_local_ip` rides LOADER_OVERFLOW, so the NET_CODE tail that is
`HTTPS_HOST`/`HTTPS_PATH`'s ip65 budget shrank from 170 to ~60 B beyond
the default strings (56 B measured; wikipedia's +46 B still builds on
Expand Down Expand Up @@ -556,9 +558,10 @@ already refused a step later, as `DF_ERR_TYPE = $04`). Test:
(server-name validation took the comb tail from 714 to 223 B and broke
`rig_https_wiki.py`, which now drives the menu instead; that tail is
**126 B today** — see the margin note under Known issues, and do not
quote the 223 as current). Those are the
numbers from that episode; the comb tail is **153 B** at the v0.14.0
pin — see the memory map below, and never size rig scratch from this
quote the 223 as current). The 714 and the 223 are the
numbers from that episode; the comb tail is **126 B** at the v0.14.0
pin, re-measured off `build/c64-https.map` — see the memory map
below, and never size rig scratch from this
bullet's historical figures. The harness write guard raises
`MemoryPolicyError` before the wire.
- `CRYPTO_HOT` margin under UCI is **per profile, and the one number this
Expand Down Expand Up @@ -608,8 +611,16 @@ already refused a step later, as `DF_ERR_TYPE = $04`). Test:
`LOADER_OVERFLOW`.** #211's verdict routine (~27 B) was placed twice
before it landed, and both wrong homes linked cleanly on the default
target:
- inline in `CODE` took ip65's LOADER from 21 B free to **zero** —
it fit exactly, so the *next* byte anyone added would not link;
- inline in `CODE` took ip65's LOADER from the 21 B free it had
*then* to **zero** — it fit exactly, so the *next* byte anyone
added would not link. The routine landed in `HTTP_AUX_CODE2`
instead, and LOADER measures **17 B** free today. The 4 B is
`http.o`'s own `CODE` growth in that same fix: rebuilding
ip65-onchip with only `src/http.s` reverted to `60022de` moves
`http.o`'s `CODE` from $35E to $35A and the whole `CODE` segment
from $17E2 to $17DE, putting LOADER back at 21 B. Attribution is
to the module and the commit; nobody has disassembled *which*
four bytes;
- `LOADER_OVERFLOW` lands in `NET_CODE`, whose tail is a **joint
budget with `HTTPS_TARGET_RODATA`**, and 27 B there took the
wikipedia-target margin from 14 B to **−13 B**, breaking
Expand Down Expand Up @@ -752,11 +763,15 @@ UCI (`cfg/c64-https-uci.cfg`, W1 hot/cold split — the reference):
build: TLS_DEFRAME_CODE (~1.4 KB),
CERT_BUF_BSS (2,048 B), HTTPS_TARGET_RODATA,
x509_name; comb adds RODATA/LIMLEE_BSS
(**153 B** tail free measured at the
(**126 B** tail free, re-measured at the
v0.14.0 pin with the P-384 objects gated
out; it was 120 B before gating returned
33 B of CRYPTO_RODATA here, and the
~223 B this file used to claim was stale
out: last tenant
`LIB_NISTCURVES_MUL_CODE` ends $5F81,
region ends $5FFF, $5FFF-$5F81 = $7E.
It was 153 B before #211's fix, and
120 B before gating returned 33 B of
`CRYPTO_RODATA` here; the ~223 B this
file used to claim was stale
— it was 159 B at v0.11.2). Also the slot
for the
(broken) overlay-embed flags.
Expand Down Expand Up @@ -795,11 +810,17 @@ deriving the others by arithmetic — the deltas are not uniform, because a
segment recovered in one profile can land in a different region in another
(comb's `CRYPTO_RODATA` is the worked example: see the 33 B note above).

ip65 is essentially full. Measured at the v0.14.0 pin, with the P-384
objects gated out of the link: 56 B NET_CODE tail (on top of the 20 B the
default target strings already use), 145 B CRYPTO_RESIDENT, 152 B
CRYPTO_OVERLAY, 21 B LOADER, and a 43 B hole below TABLES_BSS in
CRYPTO_COLD_SHADOW. PRG size is not a headroom gauge. **CRYPTO_OVERLAY and
ip65 is essentially full. Measured on **ip65-onchip** — the shipped
product — at the v0.14.0 pin, with the P-384 objects gated out of the
link and #211's fix in: 56 B NET_CODE tail (on top of the 20 B the
default target strings already use), 145 B CRYPTO_RESIDENT, 125 B
CRYPTO_OVERLAY, 17 B LOADER, and a 43 B hole
below TABLES_BSS in
CRYPTO_COLD_SHADOW. The unshipped ip65 REU profile matches on three of
those (LOADER, CRYPTO_OVERLAY, NET_CODE) but **not** on CRYPTO_RESIDENT,
where it has 231 B free: `LIB_NISTCURVES_MUL_CODE` is $27 there against
$A2 onchip, so the onchip figure is the conservative one and the one to
size against. PRG size is not a headroom gauge. **CRYPTO_OVERLAY and
CRYPTO_RESIDENT are ADJACENT ($4F8C-$5FFF and $6000-$9FFF), so they are one
pool of 20,596 B, and no amount of shuffling segments between them creates
space** — that is why the contiguous-region cfg restructure is only worth
Expand All @@ -812,9 +833,15 @@ CRYPTO_OVERLAY only rebalances the two halves of the pool, and does it to
match what both UCI cfgs already do. The other free blocks are not
reachable from the pool: NET_CODE's tail is the `HTTPS_HOST`/`HTTPS_PATH`
budget, and the smallest segment in the pool is `LIB_NISTCURVES_MUL_CODE`
at 162 B (not `HTTP_AUX_CODE2`'s 169 B, which this file used to name).

- `LOADER_OVERFLOW` carries ~125 B of `http.s` that outgrew LOADER.
at 162 B (not `HTTP_AUX_CODE2`, which this file used to name; that one
is 196 B today, and was 169 B before #211).

- `LOADER_OVERFLOW` is 332 B and `http.s` is **not** in it. Measured
tenants (ip65-onchip map, per module): `boot.o` 110 B, `vic.o` 18 B,
`crypto_swap.o` 204 B. W4 moved `http.s`'s share out to
`HTTP_AUX_CODE` (`src/http.s`, grep "W4: moved from LOADER_OVERFLOW"),
so the segment has no `http.s` tenant at all and this line's old
"~125 B of `http.s`" named a module that had already left.
- `src/loadaddr.s` (PRG load address) and `src/exports.s` (promotes
equates to `labels.txt`, incl. `cert_buf_size` — rigs must read it, not
hardcode 2048/1536) are intentional stubs; ip65-only exports live in
Expand Down
74 changes: 68 additions & 6 deletions tools/integration/build_nistcurves_p256.sh
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,13 @@ lib_preflight_fail() {
local what="$1"
local head; head="$(git -C "$LIB_DIR" rev-parse HEAD 2>/dev/null || true)"
local found; found="$(git -C "$LIB_DIR" describe --tags --always 2>/dev/null || echo '<unknown>')"
local pin; pin="$(git -C "$PROJECT_ROOT" ls-tree HEAD libs/nistcurves 2>/dev/null | awk '{print $3}')"
# `|| true` is load-bearing, exactly as on the two lines above: under
# `set -eo pipefail` a non-repo PROJECT_ROOT makes `git ls-tree` exit 128,
# pipefail carries that through `awk`, and the shell dies here — killing
# the very diagnostic this function exists to print (#217). The caller
# then sees a bare `Error 128`. An empty `pin` is a state the message
# below already handles.
local pin; pin="$(git -C "$PROJECT_ROOT" ls-tree HEAD libs/nistcurves 2>/dev/null | awk '{print $3}' || true)"
local diagnosis
# The tag for the PINNED sha, resolved rather than hardcoded: this line
# used to carry a literal "(v0.11.2)" that had to be edited by hand at
Expand All @@ -160,7 +166,29 @@ lib_preflight_fail() {
# the checkout cannot resolve it (which is itself one of the states this
# message is printed in), and the sha still identifies the pin.
local pintag; pintag="$(git -C "$LIB_DIR" describe --tags --exact-match "$pin" 2>/dev/null || true)"
if [ -z "$head" ]; then
# An empty $pin gets its own branch, and it must come FIRST. Without
# it, a non-repo PROJECT_ROOT whose libs/nistcurves IS populated (a
# symlink into a real checkout, as the test farms use) leaves head
# non-empty and pin empty, so `[ "$head" != "$pin" ]` is trivially true
# and the message blames a stale submodule and prescribes a
# `git submodule update` that cannot run there. Say what is actually
# unknown instead — and only that. The reachable shapes are not just a
# non-repo root: a real checkout whose HEAD has no libs/nistcurves
# gitlink reaches this branch too (an unborn HEAD, a commit predating
# the submodule, or another repo entirely as PROJECT_ROOT), and there
# `.git` is present. Naming a cause we did not establish is the defect
# this branch exists to fix, one layer up.
local remedy="submodule-update"
if [ -z "$pin" ]; then
remedy="no-gitlink"
diagnosis="This repo's gitlink for libs/nistcurves could not be read, so there is
nothing to compare the checkout against: either $PROJECT_ROOT is not a git
checkout, or its HEAD carries no libs/nistcurves gitlink."
# Independent condition, and the actionable one in the #217 Farm
# case where both are true. Do not let the pin branch swallow it.
[ -z "$head" ] && diagnosis="$diagnosis
The submodule is not checked out at all, either."
elif [ -z "$head" ]; then
diagnosis="The submodule is not checked out at all."
elif [ "$head" != "$pin" ]; then
diagnosis="The submodule working tree is NOT the commit this repo pins."
Expand All @@ -180,7 +208,20 @@ ERROR: libs/nistcurves checkout is unusable for this build (c64-https#124).
submodule checkout : $found
this repo pins : ${pin:0:12}${pintag:+ ($pintag)}

$diagnosis Fix it with:
$diagnosis
EOF
if [ "$remedy" = "no-gitlink" ]; then
cat >&2 <<EOF
Fix it wherever this tree's libs/nistcurves comes from, with:

git submodule update --init --recursive

Running that against THIS root will not help: there is no gitlink here to
update, whatever the reason.
EOF
else
cat >&2 <<EOF
Fix it with:

git submodule update --init --recursive

Expand All @@ -191,11 +232,13 @@ If that reports nothing and the build still fails, force it:
Note that tools/check_upstream_pins.py will NOT show this by default -- it
reads the pinned gitlink, not your working checkout. Use its --worktree mode.
EOF
fi
exit 1
}

[ -f "$LIB_SRC/zp_config.s" ] || lib_preflight_fail \
"libs/nistcurves/src/zp_config.s is missing — the submodule is not checked out,\n or that file was removed from it."
"libs/nistcurves/src/zp_config.s is missing — the submodule is not checked out,
or that file was removed from it."

grep -qE '^[[:space:]]*\.ifndef[[:space:]]+nistcurves_zp_ptr2[[:space:]]*$' "$LIB_SRC/zp_config.s" \
|| lib_preflight_fail \
Expand Down Expand Up @@ -387,7 +430,23 @@ mkdir -p "$OUT_DIR"
# that reads a different artifact than the one shipped is the wrong shape
# regardless, and a variant-gated slot could someday differ between variants'
# ZP TUs. Listing an archive is not extracting it, so §6.1 stays clean.
ZP_MEMBER="$( "$AR65" t "$UPSTREAM_ARCHIVE" | grep '^zp_config' )"
#
# `|| true` on the ZP_MEMBER assignment below is load-bearing for the same
# reason as in lib_preflight_fail (#217): under `set -eo pipefail` a grep that matches
# nothing exits 1, the assignment/substitution dies, and the `0)` arm written
# for exactly that case never runs — the caller gets a bare exit 1 with no
# message. A silently-dropped zp_config member is the #124 runtime-corruption
# class (zp_ptr2 reverts to $fd and collides with zp_temp/zp_count, with no
# link error), so this is the one diagnostic here that most needs to survive.
#
# The `case` word below needs NO such guard, and that asymmetry is the point:
# `grep -c .` also exits 1 on a zero count, but a command substitution in a
# `case` word has its status discarded, so `set -e` never sees it. Measured
# both ways -- with the ZP_MEMBER assignment guarded and this line bare, the
# `0)` arm still runs and prints. It is the ASSIGNMENT that is fatal, not the pipeline
# shape. Do not add `|| true` here on the strength of the line above, and do
# not copy this line's bare form into an assignment.
ZP_MEMBER="$( "$AR65" t "$UPSTREAM_ARCHIVE" | grep '^zp_config' || true )"
case "$(printf '%s\n' "$ZP_MEMBER" | grep -c .)" in
1) ;;
0) echo "ERROR: no zp_config member in $(basename "$UPSTREAM_ARCHIVE")" >&2; exit 1 ;;
Expand All @@ -397,8 +456,11 @@ esac

check_zp_slot() {
local name="$1" want="$2" got
# `|| true`: without it a failing od65 kills the script under
# `set -eo pipefail` and the `${got:-<absent>}` empty case below — the
# message written for precisely that state — is unreachable (#217).
got=$("${OD65:-od65}" --dump-exports "$LIB_BUILD/$ZP_MEMBER" \
| awk -v n="\"$name\"" '$1=="Name:" && $2==n {f=1; next} f && $1=="Value:" {print $2; exit}')
| awk -v n="\"$name\"" '$1=="Name:" && $2==n {f=1; next} f && $1=="Value:" {print $2; exit}' || true)
if [ "$got" != "$want" ]; then
echo "ERROR: $ZP_MEMBER exports $name = ${got:-<absent>}, expected $want (CONTRACT_ZP_DEFINES did not take)" >&2
exit 1
Expand Down
Loading