From 2c972af35e2fd30735d2da6c5543ea4b99e89932 Mon Sep 17 00:00:00 2001 From: Christopher Williams Date: Thu, 24 Sep 2026 02:18:56 -0400 Subject: [PATCH] =?UTF-8?q?phase9:=20merge=20B=202=20=E2=80=94=20383=20reg?= =?UTF-8?q?ions=20/=20374=20distinct=20bodies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Worker B: 28 new this cycle. 0x800B6C14's inverted-polarity guard (if (test() == 0) — nonzero skips the work) recorded as the 3rd same-count-wrong-reading this cycle. Two pure-allocation negatives (0x8007374C 7B reg swap, 0x800A86B4 pointer roles). Gate MATCH whole-binary SHA-1 e173426c157384ebf1b6caf8c6fea18a85a14af9. --- config/regions.tsv | 6 +++++ src/func_8003ABB4.c | 55 ++++++++++++++++++++++++++++++++++++++++++ src/func_800B6C14.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 src/func_8003ABB4.c create mode 100644 src/func_800B6C14.c diff --git a/config/regions.tsv b/config/regions.tsv index 5ee2a11..7270f4a 100644 --- a/config/regions.tsv +++ b/config/regions.tsv @@ -123,6 +123,7 @@ 0x80038788 0x80038790 src/func_80038788.c 0x80038790 0x8003879C src/func_80038790.c 0x8003AAE8 0x8003AB20 src/func_8003AAE8.c +0x8003ABB4 0x8003AC00 src/func_8003ABB4.c 0x8003B2F0 0x8003B320 src/func_8003B2F0.c 0x8003B320 0x8003B34C src/func_8003B320.c 0x8003CB8C 0x8003CBE4 src/func_8003CB8C.c @@ -202,6 +203,7 @@ 0x80085B80 0x80085B90 src/func_80085B80.c 0x80089314 0x80089338 src/func_80089314.c 0x800893E8 0x80089434 src/func_800893E8.c +0x80089B30 0x80089B88 src/func_80089B30.c 0x80089C4C 0x80089C54 src/func_80042088.c 0x80089C54 0x80089C64 src/func_80089C54.c 0x80089C64 0x80089C74 src/func_80089C64.c @@ -246,6 +248,7 @@ 0x8009E8D0 0x8009E95C src/func_8009E8D0.c 0x8009F0E8 0x8009F120 src/func_8009F0E8.c 0x800A2F20 0x800A2F44 src/func_800A2F20.c +0x800A34E8 0x800A3540 src/func_800A34E8.c 0x800A45E0 0x800A466C src/func_8009E8D0.c 0x800A5CC8 0x800A5CEC src/func_800A5CC8.c 0x800A5CEC 0x800A5D24 src/func_800A5CEC.c @@ -256,6 +259,7 @@ 0x800A74BC 0x800A74D0 src/func_800A74BC.c 0x800A8B48 0x800A8B8C src/func_800A8B48.c 0x800A9D58 0x800A9D90 src/func_800A9D58.c +0x800A9F7C 0x800A9FD4 src/func_800A9F7C.c 0x800A9FD4 0x800AA01C src/func_800A9FD4.c 0x800AA2B4 0x800AA2F8 src/func_800AA2B4.c 0x800AA56C 0x800AA59C src/func_800AA56C.c @@ -276,6 +280,7 @@ 0x800B5AF0 0x800B5AF8 src/func_80042088.c 0x800B5CB4 0x800B5CF8 src/func_800B5CB4.c 0x800B6BDC 0x800B6C14 src/func_800B6BDC.c +0x800B6C14 0x800B6C60 src/func_800B6C14.c 0x800B7230 0x800B7264 src/func_800B7230.c 0x800B74D0 0x800B7524 src/func_800B74D0.c 0x800BBDEC 0x800BBDF8 src/func_800BBDEC.c @@ -353,6 +358,7 @@ 0x80103B54 0x80103B60 src/func_80103B54.c 0x80103B60 0x80103B6C src/func_80103B60.c 0x80103B6C 0x80103B8C src/func_80103B6C.c +0x80103B8C 0x80103BE4 src/func_80103B8C.c 0x80103C7C 0x80103CA0 src/func_800F7FB4.c 0x80103F84 0x80103FA8 src/func_800F7FB4.c 0x80103FCC 0x80103FDC src/func_80103FCC.c diff --git a/src/func_8003ABB4.c b/src/func_8003ABB4.c new file mode 100644 index 0000000..3c31d28 --- /dev/null +++ b/src/func_8003ABB4.c @@ -0,0 +1,55 @@ +/* func_8003ABB4 — 0x8003ABB4..0x8003AC00 (76 bytes). + * + * Original words: + * 27BDFFD8 addiu sp,sp,-40 + * AFBF0024 sw ra,36(sp) + * AFB00020 sw s0,32(sp) + * 8C820008 lw v0,8(a0) v0 = obj->8 + * 8C50000C lw s0,12(v0) s0 = v0->0xc + * 0C00EAC8 jal 0x8003AB20 + * 27A60010 _addiu a2,sp,16 (delay slot) third argument = &local + * 00002821 move a1,zero + * 27A60010 addiu a2,sp,16 + * 0C009635 jal 0x800258D4 + * 02002021 _move a0,s0 (delay slot) + * 0C004421 jal 0x80011084 + * 02002021 _move a0,s0 (delay slot) + * 8FBF0024 lw ra,36(sp) + * 8FB00020 lw s0,32(sp) + * 27BD0028 addiu sp,sp,40 + * 03E00008 jr ra + * 00000000 nop + * + * A three-call sequence over a two-hop pointer walk, with one stack object shared + * between the first two calls. + * + * The first call receives the routine's own `a0` and `a1` unchanged (neither is set) + * plus the local's address, so `a1` is a **pass-through** — the routine must declare + * it or the register count is wrong. The second call supplies `s0` as its first + * argument and the zero as its second, and the third supplies `s0` alone. + * + * The `nop` after the first `lw` is the load-delay fill; the frame is 40 bytes with + * `ra` at 36 and `s0` at 32, leaving the local at 0x10 to extend to 0x1f, so the + * object is at most 16 bytes. + * + * LIMITS: the displacements 8 and 0xc are read from the bytes; the two hops are + * typed `int` here purely so the second hop can be dereferenced, and nothing + * establishes what either object is. The local's declared size (16) is chosen to + * make the frame come out and is not evidence — only the first two calls use it and + * neither call's required size is visible. The three callees are named for their + * addresses. + */ + +int func_8003AB20(int a0, int a1, char *buf); +void func_800258D4(int a0, int a1, char *buf); +void func_80011084(int a0); + +void func_8003ABB4(int obj, int arg) +{ + char buf[16]; + int inner = *(int *)(*(int *)(obj + 8) + 0xc); + + func_8003AB20(obj, arg, buf); + func_800258D4(inner, 0, buf); + func_80011084(inner); +} diff --git a/src/func_800B6C14.c b/src/func_800B6C14.c new file mode 100644 index 0000000..eb78610 --- /dev/null +++ b/src/func_800B6C14.c @@ -0,0 +1,59 @@ +/* func_800B6C14 — 0x800B6C14..0x800B6C60 (76 bytes). + * + * Original words: + * 27BDFFE8 addiu sp,sp,-24 + * AFB00010 sw s0,16(sp) + * AFBF0014 sw ra,20(sp) + * 0C02DAF7 jal 0x800B6BDC + * 00808021 _move s0,a0 (delay slot) keep the argument + * 14400008 bnez v0,0x800B6C4C + * 00000000 _nop (delay slot) + * 278406FC addiu a0,gp,1788 a0 = &D_80122034 (gp + 0x6FC) + * 0C009918 jal 0x80026460 + * 02002821 _move a1,s0 (delay slot) + * 02002021 move a0,s0 + * 24050001 li a1,1 + * 0C02DA25 jal 0x800B6894 + * 00003021 _move a2,zero (delay slot) + * 8FBF0014 lw ra,20(sp) <- 0x800B6C4C + * 8FB00010 lw s0,16(sp) + * 27BD0018 addiu sp,sp,24 + * 03E00008 jr ra + * 00000000 nop + * + * A guard-with-a-report: if the test routine answers non-zero nothing happens, + * otherwise a message is emitted from a global descriptor and a second routine is + * called with a fixed middle argument. + * + * The guard's polarity is worth stating because it is the opposite of the usual + * shape: **`bnez` skips the work**, so non-zero means "handled, do nothing" and zero + * means "report". Writing the C as `if (!test())` is therefore required; an + * `if (test())` would invert the region. + * + * `addiu a0,gp,1788` takes the descriptor's address **gp-relatively** with no `lui`, + * so the symbol is within ±32K of `gp` (cookbook finding 10): `gp` is 0x80121938 and + * the address is **0x80122034**, which needs a `gp` marker row in the registry. + * + * The second call's third argument is cleared (`move a2,zero`) in the call's delay + * slot, and the first argument is the routine's own — so the signature of the callee + * here is (arg, 1, 0). + * + * LIMITS: the gp offset 0x6FC is read from the bytes; that the address is a message + * descriptor is inferred from its being passed to a routine alongside the object, not + * from anything in this body. The two callees are named for their addresses and the + * test routine's return type is inferred only from its use in a zero test. + */ + +extern char D_80122034; + +int func_800B6BDC(int arg); +void func_80026460(char *desc, int arg); +void func_800B6894(int arg, int one, int zero); + +void func_800B6C14(int arg) +{ + if (func_800B6BDC(arg) == 0) { + func_80026460(&D_80122034, arg); + func_800B6894(arg, 1, 0); + } +}