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

@@ -3352,6 +3352,33 @@ fn loadarchive(l: *lnk, path: *u8, buf: *u8, len: u64) i32 = {
m = m.mnext;
};
};
// Pass 3 (#31, task #62): catch a dup the selective pull MASKED. A
// member pass 2 left unloaded yet defining a name some other object
// already `defined` is exactly the cross-package collision archive
// selective-pull would silently skip. lookup (never intern) keeps
// the pull machinery untouched → byte-id-neutral on every clean
// link. Pulled-member and direct-`.o` dups stay caught in loadimage.
// ww has no weak symbols → a genuine dup. (The bare message matches
// loadimage's; the w6l path+name text divergence is #61, separate.)
let dm: *armember = head;
for (dm != nil) {
if (dm.loaded == 0) {
let de: *defent = dm.defs;
for (de != nil) {
let s: *lsym = lookup(l, de.name);
if (s != nil) {
if (s.defined != 0) {
let msg: str = "w6l: duplicate symbol\n";
os.write(2, msg.ptr, msg.len: u64);
l.errs += 1;
};
};
de = de.dnext;
};
};
dm = dm.mnext;
};
return 0;
};

View File

@@ -376,6 +376,33 @@ fn loadarchive(l: *lnk, path: *u8, buf: *u8, len: u64) i32 = {
m = m.mnext;
};
};
// Pass 3 (#31, task #62): catch a dup the selective pull MASKED. A
// member pass 2 left unloaded yet defining a name some other object
// already `defined` is exactly the cross-package collision archive
// selective-pull would silently skip. lookup (never intern) keeps
// the pull machinery untouched → byte-id-neutral on every clean
// link. Pulled-member and direct-`.o` dups stay caught in loadimage.
// ww has no weak symbols → a genuine dup. (The bare message matches
// loadimage's; the w6l path+name text divergence is #61, separate.)
let dm: *armember = head;
for (dm != nil) {
if (dm.loaded == 0) {
let de: *defent = dm.defs;
for (de != nil) {
let s: *lsym = lookup(l, de.name);
if (s != nil) {
if (s.defined != 0) {
let msg: str = "w6l: duplicate symbol\n";
os.write(2, msg.ptr, msg.len: u64);
l.errs += 1;
};
};
de = de.dnext;
};
};
dm = dm.mnext;
};
return 0;
};