wcc/ww: self-hosted deterministic ar-writer + archive dup-detect (M3-tail c5a, #62)

Per-package .a archives are written by a self-hosted deterministic ar
writer (zeroed mtime/uid/gid, fixed mode 100644, stable member order)
so cstage and wwstage emit byte-identical archives. The linker
force-loads the root .o positionally and pulls deps from .a; a
post-pull PASS-3 over unloaded members reports duplicate symbols
through the archive (#31). Both stages mirrored (cmd/ + selfhost/).

USER-ruled D2 (self-hosted ar writer); pike P1/P2 link model. Gate
989_separchive_run proves cs.a==ww.a byte-identity, 3x-determinism,
link-consumes-.a (exit 7), and the masked-dup-through-.a loud fire.
This commit is contained in:
2026-06-16 14:54:19 +09:00
parent 9cceb4ca8d
commit 62c7771594
8 changed files with 689 additions and 24 deletions

View File

@@ -227,6 +227,27 @@ load_archive(Lnk *l, const char *path, u8 *buf, u64 len)
}
}
/* Pass 3 (#31, task #62): catch a dup the selective pull MASKED.
* A member that pass 2 left unloaded yet defines a name some other
* object already `defined` is exactly the cross-package collision
* archive selective-pull would silently skip (obj.c:218). Lookup
* (never intern) so the pull predicate / selective-pull is wholly
* untouched → byte-id-neutral on every clean link. Pulled-member
* and direct-`.o` dups stay caught at load by the defined-check
* below in load_image. ww has no weak symbols → a genuine dup. */
for (ArMember *m = head; m; m = m->next) {
if (m->loaded || m->defs == NULL) continue;
for (int i = 0; m->defs[i]; i++) {
Lsym *s = l_lookup(l, m->defs[i]);
if (s != NULL && s->defined) {
fprintf(stderr,
"w6l: %s: duplicate symbol %s\n",
path, m->defs[i]);
l->errs++;
}
}
}
/* Free unloaded members; loaded ones had their bytes consumed
* by load_image (which took the copy). */
while (head) {