P1-B/C: WM survey — 555K instr/4,422 funcs; negative dispatch-table result recorded (U4); GP/tooling scripts; findings F8
Some checks are pending
ci / test (push) Waiting to run
ci / track (push) Waiting to run

This commit is contained in:
Zachery Aaron Shores-Chmielewski 2026-08-21 18:53:30 +04:00
parent f215c88155
commit 1a1c75ae1d
3 changed files with 81 additions and 0 deletions

View file

@ -149,6 +149,22 @@ decompiler shows `unaff_gp + imm`) — dispatch found structurally instead:
Next: same scan on WM; handler-by-handler decompile + naming pass.
## F8 — mt7981_wm survey (2026-08-20)
Ghidra project `wm`: forced disassembly to **555,385 instructions / 4,422
functions** (import-time analysis alone: 226K/4,355).
- Code model differs from patch/WA: only **27 GP-relative references** in
the whole image → WM uses (near-)absolute global addressing, so GP-base
inference is not the unlock it would otherwise be.
- Structural dispatch scan (scan_tables.py) across every region pairing
found **no absolute function-pointer table** (unlike WA's 65-entry
table, F7). WM command dispatch must use another mechanism —
FP/register-relative tables, switch jump tables, or runtime handler
registration (unknown U4).
- Regions: code 0xe003b000 (398KB) + 0xe009c400 (473KB mixed);
data 0x0231dc00 (205KB), 0x0041xxxx pair; 0xf0xxxxxx block (feat 0x80)
still uncharacterized (U1).
## Unknowns registry
- **U1 — `feature_set` bit 7 (0x80):** observed only on WM regions at

View file

@ -0,0 +1,28 @@
# Headless post-script: find instructions that write the GP register and
# report candidate GP base values. Once GP is known, set it as register
# context and re-run analysis so GP-relative data references resolve.
# Run via pyghidra ghidra_launch ... -postScript FindGPInit.py
#@category Analysis
listing = currentProgram.getListing()
reg = currentProgram.getLanguage().getRegisters()
gp_regs = [r for r in reg if r.getName().lower() in ('gp', 'r26', 'r11')]
print('GP candidates: %s' % [r.getName() for r in gp_regs])
it = listing.getInstructions(True)
hits = 0
while it.hasNext() and hits < 80:
ins = it.next()
n = ins.getNumOperands()
for i in range(n):
try:
ops = ins.getOpObjects(i)
except Exception: # noqa: BLE001
continue
for o in ops:
if hasattr(o, 'getName') and o in gp_regs:
# operand 0 = destination on NDS32 ALU forms
if i == 0:
print('%s %s' % (ins.getAddress(), ins))
hits += 1
print('total gp-writes shown: %d' % hits)

View file

@ -0,0 +1,37 @@
# Headless post-script: collect all GP-relative references (addr, imm).
# Dump as CSV lines "GPREF,<insn_addr>,<imm>" for offline base inference.
#@category Analysis
listing = currentProgram.getListing()
lang = currentProgram.getLanguage()
gp = None
for r in lang.getRegisters():
if r.getName().lower() == 'gp':
gp = r
break
print('gp register: %s' % (gp.getName() if gp else None))
from ghidra.program.model.scalar import Scalar # noqa: E402
it = listing.getInstructions(True)
n = 0
while it.hasNext():
ins = it.next()
uses_gp = False
for i in range(ins.getNumOperands()):
for o in ins.getOpObjects(i):
if o == gp:
uses_gp = True
if not uses_gp:
continue
imm = None
for i in range(ins.getNumOperands()):
for o in ins.getOpObjects(i):
if isinstance(o, Scalar):
imm = o.getSignedValue()
break
if imm is not None:
break
print('GPREF,%s,%s' % (ins.getAddress(), imm if imm is not None else ''))
n += 1
print('total gp-refs: %d' % n)