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
25 changes: 25 additions & 0 deletions Makefile
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,31 @@ IP65_BUILD := ip65-build
IP65_BIN := $(IP65_BUILD)/ip65-c64.bin

CA65FLAGS := -I src -I src/inc -I src/crypto/shared -I src/net/$(BACKEND) -I build --debug-info
# Binary-include search roots for `.incbin` (issue #116).
#
# `-I` above does NOT feed `.incbin` — that is a separate search path in ca65,
# and pointing `-I` at a blob directory fails with "Cannot open include file"
# even though the file is right there (measured, ca65 V2.18). Binary includes
# need `--bin-include-dir`.
#
# Why this exists at all: ca65 resolves a relative `.incbin` operand against
# the CURRENT DIRECTORY FIRST, falling back to the including source file's
# directory only if that misses. Every `.incbin` here used to be spelled
# `../../../<dir>/<file>` on the belief that source-relative was the rule. From
# the repo root those two interpretations coincide, so it worked — but an agent
# worktree lives at `.claude/worktrees/<name>/`, exactly three levels down, so
# `../../../` climbed straight out into the PRIMARY checkout and every worktree
# build silently embedded the primary's blob. The worktree's own freshly-built
# copy was never read, by anything, while make's dependency graph tracked it.
#
# ABSOLUTE paths, not relative: a relative root would be cwd-relative and would
# re-acquire the same class of bug the moment any recipe assembles from a
# subdirectory. `$(abspath ...)` pins these to make's own working directory.
# The operands in the sources are then written WITHOUT any `../`, so there is
# no escape hatch left to resolve. A missing blob now fails the assemble
# loudly instead of quietly resolving somewhere else.
CA65FLAGS += --bin-include-dir $(abspath $(IP65_BUILD))
CA65FLAGS += --bin-include-dir $(abspath build)
# Which backend is selected, as a ca65 define. Sources that must name a
# cfg-level symbol need this, because the two cfgs give the same region
# different names: the crypto code+rodata region is CRYPTO_HOT under UCI and
Expand Down
17 changes: 13 additions & 4 deletions src/crypto/shared/p256_overlay_blobs.s
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,13 +36,22 @@
; P-256 verify overlay image (REU_OVERLAY_P256_VERIFY source)
;
; Loads into the live CRYPTO_OVERLAY slot at $4200-$5FFF at PRG load
; time, then boot DMAs it to REU bank 2 slot $22100. The .incbin path
; is resolved by ca65 relative to this source file: from
; src/crypto/shared/ the build/ tree is two levels up.
; time, then boot DMAs it to REU bank 2 slot $22100.
;
; Path is relative to ca65's BINARY include path, set by the Makefile to
; an absolute $(abspath build). The `../../../` this used to carry was
; an escape hatch, not a path: ca65 resolves .incbin against the CURRENT
; DIRECTORY first and only falls back to this file's directory, so from a
; git worktree (three levels down, at .claude/worktrees/<name>/) it
; climbed into the PRIMARY checkout and embedded that tree's overlay
; image. Worse here than for the ip65 blob, which is deterministic:
; build/ is branch- and flag-dependent, so the borrowed image could have
; come from a different pin entirely, with no diagnostic. See issue #116
; and the longer note in src/net/ip65/ip65_blob.s.
; -----------------------------------------------------------------------------
.segment "OVERLAY_BLOB_P256"
p256_overlay_verify_blob:
.incbin "../../../build/lib/nistcurves-p256-verify.bin"
.incbin "lib/nistcurves-p256-verify.bin"
p256_overlay_verify_blob_end:

.endif ; .ifdef USE_OVERLAY_P256_EMBED
13 changes: 8 additions & 5 deletions src/crypto/shared/p384_overlay_blobs.s
Original file line numberDiff line numberDiff line change
Expand Up@@ -87,13 +87,16 @@
; SHA-384 overlay image (REU bank 6 source)
;
; Loads into the live CRYPTO_OVERLAY slot at $4200-$5FFF at PRG load
; time, then boot DMAs it to REU bank 6. The .incbin path is resolved
; by ca65 relative to this source file: from src/crypto/shared/ the
; build/ tree is two levels up.
; time, then boot DMAs it to REU bank 6.
;
; Path is relative to ca65's BINARY include path (absolute $(abspath
; build), set by the Makefile). The former `../../../` escaped the
; checkout entirely when assembled from a git worktree — see issue #116
; and the note in src/net/ip65/ip65_blob.s.
; -----------------------------------------------------------------------------
.segment "OVERLAY_BLOB_SHA384"
p384_overlay_sha384_blob:
.incbin "../../../build/lib/overlay-p384-sha384.bin"
.incbin "lib/overlay-p384-sha384.bin"
p384_overlay_sha384_blob_end:

; -----------------------------------------------------------------------------
Expand All@@ -104,7 +107,7 @@ p384_overlay_sha384_blob_end:
; -----------------------------------------------------------------------------
.segment "OVERLAY_BLOB_CURVE"
p384_overlay_curve_blob:
.incbin "../../../build/lib/overlay-p384-curve.bin"
.incbin "lib/overlay-p384-curve.bin"
p384_overlay_curve_blob_end:

.endif ; .ifdef USE_OVERLAY_P384_EMBED
53 changes: 37 additions & 16 deletions src/net/ip65/ip65_blob.s
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,20 +17,41 @@

.segment "NET_CODE"

; Three levels up from this file's directory (src/net/ip65/) is the repo
; root, where ip65-build/ lives.
;
; CAUTION: ca65 does NOT resolve .incbin relative to the including source
; file only — it also tries the path relative to the current directory,
; which is the repo root when make runs. From there `../../../` escapes
; three levels ABOVE the checkout. A git worktree under
; <repo>/.claude/worktrees/<name>/ sits at exactly that depth, so if its
; own ip65-build/ip65-c64.bin is missing this silently picks up the parent
; checkout's blob and the build looks fine. Measure blob behaviour in a
; real clone, never in a nested worktree.
;
; The build order is guaranteed by an explicit dependency edge in the
; Makefile (build/net/ip65/ip65_blob.o: $(IP65_BIN)) — make cannot see
; through .incbin, so without it this object could be assembled before
; The blob is found through ca65's BINARY include path, set by the
; Makefile to an absolute $(abspath $(IP65_BUILD)). Hence the bare
; filename: there is deliberately no `../` here to resolve.
;
; It used to read `../../../ip65-build/ip65-c64.bin`, on the belief that
; ca65 resolves .incbin relative to the including source file. It does
; not — or rather, not first. **ca65 tries the CURRENT DIRECTORY first**
; and falls back to the source file's directory only on a miss. From the
; repo root the two coincide, which is why the old spelling worked and
; why the wrong rule survived in this comment for so long.
;
; They stop coinciding inside a git worktree. One lives at
; <repo>/.claude/worktrees/<name>/ — exactly three levels down — so
; `../../../` climbed out of the worktree and into the PRIMARY checkout,
; and every worktree build embedded the primary's blob while make's
; dependency graph tracked the worktree's. Nothing diagnosed it: the two
; copies are normally byte-identical, so it stayed invisible until
; someone bumped the ip65 submodule on a branch. Measured, ca65 V2.18,
; with distinguishable bytes planted in each copy (issue #116):
;
; .incbin "../../../ip65-build/…" -> PRIMARY's bytes
; --bin-include-dir <abs> + .incbin "ip65-c64.bin" -> worktree's
;
; Note `-I` does NOT feed .incbin — that is the source-include path, and
; pointing it at a blob directory fails with "Cannot open include file"
; even though the file is sitting in it. Binary includes have their own
; search path, hence --bin-include-dir.
;
; Two properties worth preserving if this is ever respelled: an absent
; blob now FAILS the assemble instead of quietly resolving elsewhere, and
; adding the flag while leaving a `../` operand in place would silently
; keep the old behaviour — the operand is the load-bearing half.
;
; The build ORDER is guaranteed separately, by an explicit dependency edge
; in the Makefile (build/net/ip65/ip65_blob.o: $(IP65_BIN)) — make cannot
; see through .incbin, so without it this object could be assembled before
; the blob exists (issue #89).
.incbin "../../../ip65-build/ip65-c64.bin"
.incbin "ip65-c64.bin"