phase9: merge B 4 + C 4 — 346 regions / 337 distinct bodies
Worker B: 0x8002E3A8 bare unused frame (16B of pure stack adjustment from a removed local — reproduced by declaring char unused[16] and nothing else), 0x8010AAC0 pass-through with arg reorder, 0x8008FF58 six-arg rebuild, 0x800F79C0 virtual dispatch. Worker C: 0x800268C4 (D_ names), 0x800697C4, 0x800658FC (F20 displacement-fold lever), 0x800A5CC8 (overlap with the coordinator's claim — C's registration stood). Gate MATCH whole-binary SHA-1 e173426c157384ebf1b6caf8c6fea18a85a14af9.
This commit is contained in:
@@ -67,6 +67,7 @@
|
||||
0x800262EC 0x800262F8 src/func_800262E0.c
|
||||
0x800262F8 0x80026304 src/func_800262F8.c
|
||||
0x800267C0 0x800267CC src/func_800267C0.c
|
||||
0x800268C4 0x800268F4 src/func_800268C4.c
|
||||
0x80026C2C 0x80026C7C src/func_80026C2C.c
|
||||
0x80026F14 0x80026F3C src/func_80026F14.c
|
||||
0x800276B0 0x800276D4 src/func_800276B0.c
|
||||
@@ -86,6 +87,7 @@
|
||||
0x8002D5D0 0x8002D608 src/func_8002D5D0.c
|
||||
0x8002DEB4 0x8002DF1C src/func_8002DEB4.c
|
||||
0x8002E028 0x8002E070 src/func_8002E028.c
|
||||
0x8002E3A8 0x8002E3B8 src/func_8002E3A8.c
|
||||
0x8002E4C8 0x8002E4F0 src/func_8002E4C8.c
|
||||
0x8002E7C4 0x8002E7E4 src/func_8002E7C4.c
|
||||
0x8002F160 0x8002F1A4 src/func_8002F160.c
|
||||
@@ -134,6 +136,7 @@
|
||||
0x80058288 0x800582AC src/func_80058288.c
|
||||
0x8005E3D0 0x8005E3F4 src/func_8005E3D0.c
|
||||
0x8005ED6C 0x8005EDBC src/func_8005ED6C.c
|
||||
0x800658FC 0x80065930 src/func_800658FC.c
|
||||
0x80065930 0x80065980 src/func_80065930.c
|
||||
0x80065B6C 0x80065B8C src/func_80065B6C.c
|
||||
0x800668A8 0x800668F0 src/func_800668A8.c
|
||||
@@ -147,6 +150,7 @@
|
||||
0x80068F6C 0x80068F98 src/func_80068F6C.c
|
||||
0x80068F98 0x80068FA8 src/func_80068F98.c
|
||||
0x800697A4 0x800697C4 src/func_800697A4.c
|
||||
0x800697C4 0x800697FC src/func_800697C4.c
|
||||
0x8006B778 0x8006B7C0 src/func_8006B778.c
|
||||
0x8006BC08 0x8006BC34 src/func_8006BC08.c
|
||||
0x8006BC34 0x8006BC74 src/func_8006BC34.c
|
||||
@@ -187,6 +191,7 @@
|
||||
0x8008F4A0 0x8008F4AC src/func_8008F4A0.c
|
||||
0x8008F4F4 0x8008F508 src/func_8008F4F4.c
|
||||
0x8008F508 0x8008F530 src/func_8008F508.c
|
||||
0x8008FF58 0x8008FF84 src/func_8008FF58.c
|
||||
0x8008FF84 0x8008FFC4 src/func_8008FF84.c
|
||||
0x80090028 0x80090048 src/func_80090028.c
|
||||
0x80090048 0x80090058 src/func_80090048.c
|
||||
@@ -259,6 +264,7 @@
|
||||
0x800F6570 0x800F6594 src/func_800F6570.c
|
||||
0x800F75D0 0x800F760C src/func_800F75D0.c
|
||||
0x800F7990 0x800F79C0 src/func_800F7990.c
|
||||
0x800F79C0 0x800F79F0 src/func_800F79C0.c
|
||||
0x800F79F0 0x800F7A20 src/func_800F79F0.c
|
||||
0x800F7A20 0x800F7A54 src/func_800F7A20.c
|
||||
0x800F7A54 0x800F7A84 src/func_800F7A54.c
|
||||
@@ -349,3 +355,4 @@
|
||||
0x8010A888 0x8010A8B0 src/func_8010A888.c
|
||||
0x8010A8B0 0x8010A8D8 src/func_8010A8B0.c
|
||||
0x8010AAA0 0x8010AABC src/func_8010AAA0.c
|
||||
0x8010AAC0 0x8010AAE4 src/func_8010AAC0.c
|
||||
|
||||
|
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* func_800268C4 — 48 bytes at 0x800268C4..0x800268F4
|
||||
*
|
||||
* Guarded teardown: if a gp-relative state word is set, passes the ADDRESS of an
|
||||
* adjacent gp-relative object to a callee and clears the state word.
|
||||
*
|
||||
* The observed instructions are:
|
||||
* lw v0,540(gp) ; v0 = D_80121B54
|
||||
* addiu sp,sp,-24
|
||||
* beqz v0,0x800268E4 ; if (state == 0) skip
|
||||
* sw ra,16(sp) ; save ra (delay slot)
|
||||
* addiu a0,gp,536 ; a0 = &D_80121B50 <- gp-relative ADDRESS
|
||||
* jal 0x80026E94
|
||||
* nop
|
||||
* sw zero,540(gp) ; D_80121B54 = 0
|
||||
* E4: lw ra,16(sp)
|
||||
* addiu sp,sp,24
|
||||
* jr ra
|
||||
* nop
|
||||
*
|
||||
* The callee receives the ADDRESS of a gp-relative object (`addiu a0,gp,536`),
|
||||
* not a loaded value, so both symbols must be gp-marked in the registry: an
|
||||
* address-shaped name that resolves implicitly loses the marker and the address
|
||||
* materialises absolutely instead. See the F19 finding.
|
||||
*
|
||||
* LIMITS: the two gp offsets (536 = 0x218 and 540 = 0x21C) are hypotheses read
|
||||
* from the instruction shape and are facts about this executable's gp layout.
|
||||
* What the state word and the object mean is unknown and is not guessed here.
|
||||
* Only the compiled bytes are evidence.
|
||||
*/
|
||||
|
||||
extern int D_80121B50;
|
||||
extern int D_80121B54;
|
||||
|
||||
void func_800268C4(void) {
|
||||
if (D_80121B54 != 0) {
|
||||
func_80026E94(&D_80121B50);
|
||||
D_80121B54 = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* func_8002E3A8 — 0x8002E3A8..0x8002E3B8 (16 bytes).
|
||||
*
|
||||
* Original words:
|
||||
* 27BDFFF0 addiu sp,sp,-16
|
||||
* 27BD0010 addiu sp,sp,16
|
||||
* 03E00008 jr ra
|
||||
* 00000000 nop
|
||||
*
|
||||
* An empty function that nonetheless adjusts the stack by 16 bytes and puts it
|
||||
* back — the whole body is a frame that is never used and never addresses
|
||||
* anything.
|
||||
*
|
||||
* This is a **compiler artefact, not a body**: cc1 allocates the frame for a
|
||||
* local object that the optimiser then removes, leaving the `sp` adjustment
|
||||
* behind. A function written as truly empty (`void f(void) {}`) emits only
|
||||
* `jr ra` / `nop`, so the source must have declared something with automatic
|
||||
* storage that GCC dropped after the frame had been assigned — which is why the
|
||||
* C below declares an unused local array rather than being an empty body. There is
|
||||
* deliberately nothing else in it: adding any use of the local would emit a store
|
||||
* and change the bytes.
|
||||
*
|
||||
* The frame is 16 bytes, so the dropped object was 16 bytes — a `char[16]`, an
|
||||
* `int[4]`, or any aggregate of that size. Which one cannot be told from the
|
||||
* bytes, because a removed local leaves no trace beyond its size.
|
||||
*
|
||||
* LIMITS: the reconstruction reproduces a *compiler behaviour* (leave the frame,
|
||||
* drop the object) rather than a program statement. The declared size is chosen to
|
||||
* make the frame come out at 16 and is an inference from the frame size alone, not
|
||||
* evidence about the original's declaration. If a later reader finds a compiler
|
||||
* that does not emit the frame for this spelling, the row is a compiler-behaviour
|
||||
* negative and not a source problem.
|
||||
*/
|
||||
|
||||
void func_8002E3A8(void)
|
||||
{
|
||||
char unused[16];
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* func_800658FC — 52 bytes at 0x800658FC..0x80065930
|
||||
*
|
||||
* Clears two 16-bit fields inside one element of a strided array reached through
|
||||
* the argument, then calls a callee with the routine's own two arguments
|
||||
* unchanged. The element offset is `index * 4 + 68`, computed before the frame is
|
||||
* finished being set up.
|
||||
*
|
||||
* The observed instructions are:
|
||||
* addiu sp,sp,-24
|
||||
* sll v0,a1,0x2 ; index * 4
|
||||
* sw ra,16(sp)
|
||||
* lw v1,32(a0) ; v1 = p->ptr_20 (the array base)
|
||||
* addiu v0,v0,68 ; index * 4 + 68
|
||||
* addu v1,v1,v0 ; element = base + that (base first)
|
||||
* sh zero,2(v1) ; element->half_02 = 0
|
||||
* jal 0x80065794
|
||||
* sh zero,0(v1) ; element->half_00 = 0 (delay slot)
|
||||
* lw ra,16(sp)
|
||||
* addiu sp,sp,24
|
||||
* jr ra
|
||||
* nop
|
||||
*
|
||||
* The callee is passed the routine's OWN arguments: `a0` and `a1` are never
|
||||
* overwritten (only temporaries v0/v1 are used), so the call forwards `(p, index)`
|
||||
* without any explicit argument setup.
|
||||
*
|
||||
* The element displacement must be folded into the POINTER, not into the two
|
||||
* store displacements: the original emits `addiu v0,v0,68` (on the scaled index)
|
||||
* then `addu` into the base, and both stores then use offset 0 and 2. Written as
|
||||
* a flat `base + index * 4 + 68`, cc1 reassociates and emits the stores at
|
||||
* displacements 68 and 70 instead, which is 16 bytes shorter (36 vs 52). Binding
|
||||
* `index * 4 + 68` to its own local forces the original's shape.
|
||||
*
|
||||
* LIMITS: the array-base offset (0x20), the element stride (4), the element
|
||||
* displacement (68) and the two cleared field offsets (0 and 2) are hypotheses
|
||||
* read from the instruction shape; what the array holds is unknown and is not
|
||||
* guessed here. Only the compiled bytes are evidence.
|
||||
*/
|
||||
|
||||
void func_800658FC(char *p, int index) {
|
||||
int offset = index * 4 + 68;
|
||||
char *element = (char *)(*(int *)(p + 32) + offset);
|
||||
|
||||
*(short *)(element + 2) = 0;
|
||||
*(short *)(element + 0) = 0;
|
||||
func_80065794(p, index);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* func_800697C4 — 56 bytes at 0x800697C4..0x800697FC
|
||||
*
|
||||
* Two pointer hops and a guarded call: reads a pointer out of a structure, then a
|
||||
* second pointer out of that, and calls a callee with it only when it is
|
||||
* non-null. The callee receives the pointer that was already in `a0`, so no
|
||||
* argument setup is emitted.
|
||||
*
|
||||
* The observed instructions are:
|
||||
* addiu sp,sp,-24
|
||||
* sw ra,16(sp)
|
||||
* lw v0,12(a0) ; v0 = p->ptr_0c
|
||||
* nop
|
||||
* lw a0,392(v0) ; a0 = v0->ptr_188
|
||||
* nop
|
||||
* beqz a0,0x800697EC ; if (q == 0) skip the call
|
||||
* nop
|
||||
* jal 0x800697A4
|
||||
* nop
|
||||
* EC: lw ra,16(sp)
|
||||
* addiu sp,sp,24
|
||||
* jr ra
|
||||
* nop
|
||||
*
|
||||
* The `nop` after each load is maspsx's load-delay fill.
|
||||
*
|
||||
* LIMITS: the two pointer offsets (0x0c and 0x188) are hypotheses read from the
|
||||
* instruction shape; what the structures are is unknown and is not guessed here.
|
||||
* Only the compiled bytes are evidence.
|
||||
*/
|
||||
|
||||
void func_800697C4(char *p) {
|
||||
char *q = *(char **)(*(char **)(p + 12) + 392);
|
||||
|
||||
if (q != 0)
|
||||
func_800697A4(q);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/* func_8008FF58 — 0x8008FF58..0x8008FF84 (44 bytes).
|
||||
*
|
||||
* Original words:
|
||||
* 27BDFFE0 addiu sp,sp,-32
|
||||
* 8FA20030 lw v0,48(sp) v0 = the incoming FIFTH argument
|
||||
* AFA70010 sw a3,16(sp) outgoing stack argument 1 = the fourth argument
|
||||
* 00003821 move a3,zero fourth register argument = 0
|
||||
* AFBF0018 sw ra,24(sp)
|
||||
* 0C023D83 jal 0x8008F60C
|
||||
* AFA20014 _sw v0,20(sp) (delay slot) outgoing stack argument 2 = the fifth
|
||||
* 8FBF0018 lw ra,24(sp)
|
||||
* 27BD0020 addiu sp,sp,32
|
||||
* 03E00008 jr ra
|
||||
* 00000000 nop
|
||||
*
|
||||
* A six-argument call rebuilt from five: the callee's first three arguments pass
|
||||
* through untouched, its fourth is forced to zero, and its fifth and sixth are the
|
||||
* routine's own fourth and fifth, placed on the stack.
|
||||
*
|
||||
* The `lw v0,48(sp)` is the tell that the routine itself takes five arguments: the
|
||||
* frame is 32 bytes, so the fifth argument sits at 0x30 in the caller's frame and
|
||||
* the load reads it before the outgoing stack slots are written. Both outgoing
|
||||
* stack values land at 0x10 and 0x14(sp), which are the caller's slots 5 and 6.
|
||||
*
|
||||
* The two stack stores are what make this a *shuffling* adapter rather than a
|
||||
* pass-through: every register argument except `a3` is already correct, so only
|
||||
* `a3` is overwritten and the two stack slots are filled.
|
||||
*
|
||||
* LIMITS: the frame size 32 and the incoming displacement 0x30 are read from the
|
||||
* bytes; the arithmetic that places the fifth argument at 0x30 (frame 32 minus the
|
||||
* 16-byte register-save area) is a consequence of the o32 convention rather than a
|
||||
* source fact. The callee is named for its address and nothing establishes what the
|
||||
* forced zero or the forwarded arguments mean.
|
||||
*/
|
||||
|
||||
void func_8008F60C(int a0, int a1, int a2, int zero, int a4, int a5);
|
||||
|
||||
void func_8008FF58(int a0, int a1, int a2, int a3, int a4)
|
||||
{
|
||||
func_8008F60C(a0, a1, a2, 0, a3, a4);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* func_800F79C0 — 0x800F79C0..0x800F79F0 (48 bytes).
|
||||
*
|
||||
* Original words:
|
||||
* 3C028012 lui v0,0x8012
|
||||
* 8C42FB4C lw v0,-1204(v0) v0 = D_8011FB4C (0x80120000 - 1204)
|
||||
* 27BDFFE8 addiu sp,sp,-24
|
||||
* AFBF0010 sw ra,16(sp)
|
||||
* 8C420008 lw v0,8(v0) v0 = *(int *)(v0 + 8)
|
||||
* 0040F809 jalr v0
|
||||
* 00000000 _nop (delay slot)
|
||||
* 8FBF0010 lw ra,16(sp)
|
||||
* 27BD0018 addiu sp,sp,24
|
||||
* 03E00008 jr ra
|
||||
* 00000000 nop
|
||||
*
|
||||
* A virtual dispatch thunk: it reads a global pointer, fetches the function
|
||||
* pointer stored at offset 8 of whatever it points to, and calls it with the
|
||||
* incoming arguments untouched.
|
||||
*
|
||||
* The pointer load is hoisted **above** the frame setup — `lui`/`lw` precede
|
||||
* `addiu sp,sp,-24` — which is the same scheduling seen in `0x8002E4C8`: cc1 moves
|
||||
* a load that does not depend on `sp` ahead of the prologue. The indirect call
|
||||
* carries a `nop` in its delay slot, so the target was not known and no
|
||||
* independent instruction was available to fill it.
|
||||
*
|
||||
* The call site passes nothing: `a0`-`a3` are untouched, so the callee receives
|
||||
* the caller's arguments unchanged and the thunk is transparent apart from the
|
||||
* target it selects.
|
||||
*
|
||||
* `lui`+`lw` into the **same** register is the macro expansion of a symbol load
|
||||
* (cookbook finding 2), so the global at 0x8011FB4C holds a pointer; whether it is
|
||||
* an object whose offset 8 is a function pointer, or a table whose third entry is
|
||||
* one, cannot be told from these bytes. Written here as an indirect call through
|
||||
* the loaded value with the offset folded in, which is what the two `lw`s express.
|
||||
*
|
||||
* LIMITS: the displacement 8 and the frame layout are read from the bytes. The
|
||||
* signature of the called function is entirely unconstrained — the thunk passes
|
||||
* whatever it received, so no argument type can be inferred from this body.
|
||||
*/
|
||||
|
||||
extern int D_8011FB4C;
|
||||
|
||||
void func_800F79C0(void)
|
||||
{
|
||||
(*(void (**)(void))(D_8011FB4C + 8))();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* func_8010AAC0 — 0x8010AAC0..0x8010AAE4 (36 bytes).
|
||||
*
|
||||
* Original words:
|
||||
* 27BDFFE8 addiu sp,sp,-24
|
||||
* AFBF0010 sw ra,16(sp)
|
||||
* 00802821 move a1,a0 second argument = the routine's first
|
||||
* 0C03DE7C jal 0x800F79F0
|
||||
* 24040004 _li a0,4 (delay slot) first argument = 4
|
||||
* 8FBF0010 lw ra,16(sp)
|
||||
* 27BD0018 addiu sp,sp,24
|
||||
* 03E00008 jr ra
|
||||
* 00000000 nop
|
||||
*
|
||||
* A one-call adapter that swaps the argument positions and pins the first: the
|
||||
* routine's single parameter becomes the callee's **second** argument, and the
|
||||
* callee's first argument is the constant 4. Nothing is returned.
|
||||
*
|
||||
* The `move a1,a0` is the whole content of the routine — there is no second
|
||||
* parameter to forward, so this is the minimal case of the pass-through idiom:
|
||||
* the incoming value is moved one register along and the freed register is loaded
|
||||
* with a constant in the call's delay slot.
|
||||
*
|
||||
* LIMITS: that the freed register's constant is 4 rather than a symbol or a
|
||||
* variable is read directly from `li a0,4`. The callee is named for its address
|
||||
* and its signature is inferred from the two registers that are set; in
|
||||
* particular whether it returns a value is invisible, since the result is not
|
||||
* used, so the signature here is `void`.
|
||||
*/
|
||||
|
||||
void func_800F79F0(int four, int arg);
|
||||
|
||||
void func_8010AAC0(int arg)
|
||||
{
|
||||
func_800F79F0(4, arg);
|
||||
}
|
||||
Reference in New Issue
Block a user