From e407d5d2f273669ea613c0abab170a78c847c3c2 Mon Sep 17 00:00:00 2001 From: Christopher Williams Date: Thu, 24 Sep 2026 01:57:36 -0400 Subject: [PATCH] =?UTF-8?q?phase9:=20merge=20C=203=20=E2=80=94=20361=20reg?= =?UTF-8?q?ions=20/=20352=20distinct=20bodies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Worker C closed 0x80031F2C (a row the coordinator had recorded as a negative) via the F23 struct-assignment lever (second confirmation) — stale negative removed. Also 0x800129C8, 0x8009132C (F22: unsigned char local re-masks an already-zero-extended lbu — width-of-DESTINATION tell). C's link-error finding recorded: 'small-data section too large' on a region that built fine = a duplicate gp symbol (name mismatch), not a -G problem. New negative 0x80042E68 pairs with 0x80042DD4; named pointer lever untried. Gate MATCH whole-binary SHA-1 e173426c157384ebf1b6caf8c6fea18a85a14af9. --- config/near_match_negatives.tsv | 1 - config/regions.tsv | 3 ++ src/func_800129C8.c | 48 +++++++++++++++++++++++++++++ src/func_80031F2C.c | 52 ++++++++++++++++++++++++++++++++ src/func_8009132C.c | 53 +++++++++++++++++++++++++++++++++ 5 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 src/func_800129C8.c create mode 100644 src/func_80031F2C.c create mode 100644 src/func_8009132C.c diff --git a/config/near_match_negatives.tsv b/config/near_match_negatives.tsv index 1043d90..cd5e76f 100644 --- a/config/near_match_negatives.tsv +++ b/config/near_match_negatives.tsv @@ -28,7 +28,6 @@ 0x800266A8 68 near-match byte-replication alloc (first sll register; fresh-context re-spelling 3B; named-locals regresses) 0x8002D014 - near-match - 0x8002D060 72 near-match - -0x80031F2C 76 near-match cross-join/load-hoist (3 coordinator spellings: 92/92/68; original loads 4 words then stores, shared a3=a1 delay binding; finding-F6 family) 0x80039308 - near-match - 0x80042DD4 - near-match - 0x80042E10 - near-match - diff --git a/config/regions.tsv b/config/regions.tsv index f6b64b4..225e034 100644 --- a/config/regions.tsv +++ b/config/regions.tsv @@ -15,6 +15,7 @@ 0x8001281C 0x80012834 src/func_8001281C.c 0x8001289C 0x800128E4 src/func_8001289C.c 0x800128E4 0x80012918 src/func_800128E4.c +0x800129C8 0x80012A10 src/func_800129C8.c 0x80012D8C 0x80012DBC src/func_80012D8C.c 0x80012DBC 0x80012DE8 src/func_80012DBC.c 0x80013C88 0x80013C90 src/func_80013C88.c @@ -100,6 +101,7 @@ 0x8002F404 0x8002F450 src/func_8002F404.c 0x800301FC 0x8003022C src/func_800301FC.c 0x80030358 0x80030390 src/func_80030358.c +0x80031F2C 0x80031F78 src/func_80031F2C.c 0x80031F78 0x80031FC4 src/func_80031F78.c 0x800321EC 0x800321F8 src/func_800321EC.c 0x80034A80 0x80034ABC src/func_80034A80.c @@ -209,6 +211,7 @@ 0x8009107C 0x800910B0 src/func_8009107C.c 0x800912D4 0x800912FC src/func_800912D4.c 0x800912FC 0x8009132C src/func_800912FC.c +0x8009132C 0x80091370 src/func_8009132C.c 0x80092068 0x80092088 src/func_80092068.c 0x80092088 0x800920BC src/func_80092088.c 0x800920BC 0x800920DC src/func_800920BC.c diff --git a/src/func_800129C8.c b/src/func_800129C8.c new file mode 100644 index 0000000..e452e1a --- /dev/null +++ b/src/func_800129C8.c @@ -0,0 +1,48 @@ +/* + * func_800129C8 — 72 bytes at 0x800129C8..0x80012A10 + * + * Calls a teardown routine with a fixed displacement from the first argument and + * a value read through the second, then clears that value. Both null cases return + * zero through the SAME epilogue, so the source uses guarded blocks rather than + * early returns. + * + * The observed instructions are: + * addiu sp,sp,-24 + * sw s0,16(sp) + * move s0,a1 ; s0 = flag pointer + * beqz s0,0x800129F8 ; if (flag == 0) goto return-zero + * sw ra,20(sp) ; save ra (delay slot) + * lw a1,0(s0) ; a1 = *flag + * nop + * beqz a1,0x800129FC ; if (*flag == 0) return 0 + * move v0,zero ; return value (delay slot) + * jal 0x80026560 + * addiu a0,a0,152 ; first argument = p + 152 (delay slot) + * sw zero,0(s0) ; *flag = 0 + * F8: move v0,zero ; return 0 + * FC: lw ra,20(sp) + * lw s0,16(sp) + * addiu sp,sp,24 + * jr ra + * nop + * + * The second argument is kept in `s0` across the call, which is why the frame + * saves both `ra` and `s0`. The value read through the pointer is passed as the + * callee's second argument and the pointer is cleared afterwards. + * + * LIMITS: the displacement (152) and the parameter types are hypotheses read from + * the instruction shape; what the pointer and the callee mean is unknown and is + * not guessed here. Only the compiled bytes are evidence. + */ + +int func_800129C8(char *p, int *flag) { + if (flag == 0) + return 0; + + if (*flag != 0) { + func_80026560(p + 152, *flag); + *flag = 0; + } + + return 0; +} diff --git a/src/func_80031F2C.c b/src/func_80031F2C.c new file mode 100644 index 0000000..d5a4da9 --- /dev/null +++ b/src/func_80031F2C.c @@ -0,0 +1,52 @@ +/* + * func_80031F2C — 76 bytes at 0x80031F2C..0x80031F78 + * + * Copies either one word or four words out of a structure reached through the + * first argument, depending on a flag word: the single-word case jumps over the + * four-word case, and both converge on one epilogue. + * + * The observed instructions are: + * lw a2,164(a0) ; q = p->ptr_a4 + * nop + * lw v0,1300(a2) ; q->word_514 (the flag) + * nop + * bnez v0,0x80031F50 ; if (flag != 0) take the four-word arm + * move a3,a1 ; out = a1 (delay slot) + * lw v0,1308(a2) ; q->word_51c + * j 0x80031F70 + * sw v0,0(a3) ; out[0] = v0 (delay slot) + * 50: lw v0,1308(a2) + * lw v1,1312(a2) + * lw a0,1316(a2) + * lw a1,1320(a2) + * sw v0,0(a3) ; out[0..3] = four words + * sw v1,4(a3) + * sw a0,8(a3) + * sw a1,12(a3) + * 70: jr ra + * nop + * + * Note the four-word arm re-reads the SAME first field that the one-word arm + * reads (offset 0x51c), so the two arms differ only in how many words follow. + * + * LIMITS: the pointer offset (0xa4) and the five field offsets (0x514, 0x51c, + * 0x520, 0x524, 0x528) are hypotheses read from the instruction shape; what the + * structure holds is unknown and is not guessed here. Only the compiled bytes are + * evidence. + */ + +typedef struct { + int word_00; + int word_04; + int word_08; + int word_0c; +} func_80031F2C_block; + +void func_80031F2C(char *p, int *out) { + char *q = *(char **)(p + 164); + + if (*(int *)(q + 1300) == 0) + out[0] = *(int *)(q + 1308); + else + *(func_80031F2C_block *)out = *(func_80031F2C_block *)(q + 1308); +} diff --git a/src/func_8009132C.c b/src/func_8009132C.c new file mode 100644 index 0000000..f94c6f2 --- /dev/null +++ b/src/func_8009132C.c @@ -0,0 +1,53 @@ +/* + * func_8009132C — 68 bytes at 0x8009132C..0x80091370 + * + * Reads a gp-relative mode byte, clears a gp-relative state word to -1, and when + * the mode is exactly 1 makes a two-argument call before a final no-argument call. + * The mode byte is read BEFORE the state word is cleared. + * + * The observed instructions are: + * addiu sp,sp,-24 + * lbu v1,3388(gp) ; mode = D_80122674 <- UNSIGNED byte + * li v0,-1 + * sw v0,1468(gp) ; D_80121EF4 = -1 + * li v0,1 + * bne v1,v0,0x80091358 ; if (mode != 1) skip the first call + * sw ra,16(sp) ; save ra (delay slot) + * lw a0,3368(gp) ; D_80122660 + * lw a1,3384(gp) ; D_80122670 + * jal 0x8008FCD8 + * nop + * 58: jal 0x800912D4 + * nop + * lw ra,16(sp) + * addiu sp,sp,24 + * jr ra + * nop + * + * Two source-shape requirements: the mode byte must be loaded before the state + * store (reading it inline in the condition lets cc1 sink the load below the + * store), and the local it is read into must be an `int`, not an `unsigned char` + * — an `unsigned char` local makes cc1 re-mask the already-zero-extended `lbu` + * result with `andi v0,v0,0xff`, costing 4 bytes. + * + * LIMITS: the four gp offsets (3388 = 0xD3C, 1468 = 0x5BC, 3368 = 0xD28, + * 3384 = 0xD38) and the two callees are hypotheses read from the instruction + * shape; the offsets are facts about this executable's gp layout (gp = + * 0x80121938). What the mode and the state mean is unknown and is not guessed + * here. The `lbu` shows the mode is an unsigned byte. Only the compiled bytes are + * evidence. + */ + +extern unsigned char D_80122674; +extern int D_80121EF4; +extern int D_80122660; +extern int D_80122670; + +void func_8009132C(void) { + int mode = D_80122674; + + D_80121EF4 = -1; + if (mode == 1) + func_8008FCD8(D_80122660, D_80122670); + func_800912D4(); +}