$gp-relative load resolution - Can now resolve function pointers loaded from the Global Offset Table (GOT)
lw $t9, 0x18($gp) # Load function pointer from GOT
jalr $t9 # Call the functionTotal indirect calls: 7,588
| Pattern | Count | % | Status |
|---|---|---|---|
| No lw found | 5,818 | 76.7% | ⏳ Next: Function parameter detection |
| Register-relative | 3,540 | 46.7% | 🔜 Future (stack, struct tracking) |
| $gp-relative | 28 | 0.4% | ✅ IMPLEMENTED! |
Evidence:
71 cases - searched 5 instructions
66 cases - searched 13 instructions
55 cases - searched 19 instructions
51 cases - searched 22 instructions
What this means:
- The register is NOT loaded by
lwin the function - It's likely a function parameter passed in by the caller
- Registers
$a0-$a3are used for first 4 arguments in MIPS O32 ABI
Example:
// Function signature
int callback(void *device, int cmd, void *data);
// Assembly
callback:
# $a0 = device (parameter 1)
# $a1 = cmd (parameter 2)
# $a2 = data (parameter 3)
lw $t9, 0x10($a0) # Load from device->callback
jalr $t9 # Call device->callback(...)The jalr $t9 uses a function pointer, but $t9 was loaded from $a0, which is a parameter!
Only 28 cases (0.4%) but these should be 100% resolvable!
Pattern:
lw $t9, 0x18($gp) # Load from GOT
jalr $t9 # CallWhat we do:
- Get $gp value for the section (e.g., 0xe3ff0 for .text)
- Calculate GOT address = $gp + offset (e.g., 0xe3ff0 + 0x18 = 0xe4008)
- Read function pointer from GOT address
- Create reference
From the logs, we saw:
_mips_gp0_value=0x7ff0
%got.text block (gp=0xe3ff0)
%got.init.text block (gp=0xf3ff0)
%got.exit.text block (gp=0x103ff0)
This is critical! Different sections use different $gp values. Our implementation handles this by:
- Detecting which section the instruction is in
- Finding the corresponding GOT block
- Using the correct $gp value
private Address tryResolveGpRelative(Program program, Instruction lwInstr, Function function) {
// 1. Check if this is a $gp-relative load
String op1 = lwInstr.getDefaultOperandRepresentation(1);
if (!op1.contains("(gp)")) {
return null;
}
// 2. Extract offset (e.g., "0x18(gp)" -> 0x18)
long offset = parseOffset(op1);
// 3. Get $gp value for this section
Long gpValue = getGlobalPointerValue(program, lwInstr.getAddress());
// 4. Calculate GOT address
long gotAddress = gpValue + offset;
// 5. Read function pointer from GOT
long funcPtr = memory.getInt(gotAddr) & 0xFFFFFFFFL;
// 6. Validate and return
return funcAddr;
}private Long getGlobalPointerValue(Program program, Address addr) {
// 1. Get the memory block for this address
MemoryBlock block = program.getMemory().getBlock(addr);
// 2. Find the corresponding GOT block
if (block.getName().contains(".text")) {
MemoryBlock gotBlock = program.getMemory().getBlock("%got.text");
if (gotBlock != null) {
// $gp points to GOT + 0x7ff0 (MIPS O32 ABI)
return gotBlock.getStart().getOffset() + 0x7ff0;
}
}
// 3. Fallback to program properties
return props.getLong("_mips_gp0_value", 0L);
}28 $gp-relative failures
0 resolutions
0-5 $gp-relative failures (if any edge cases)
23-28 resolutions (82-100% success rate!)
First actual resolutions! 🎉
grep "Resolved via \$gp-relative" logs.txt | wc -lExpected: 20-28 (most or all $gp-relative loads resolved)
grep "Register-relative load.*gp" logs.txt | wc -lExpected: 0-8 (edge cases or errors)
grep "Resolved jalr\|Resolved jr\|Created.*COMPUTED" logs.txt | wc -lExpected: 20-28 (first successful resolutions!)
grep -A5 "Resolved via \$gp-relative" logs.txt | head -30Should show: $gp value, offset, GOT address, target function
Impact: 5,818 cases (76.7% of all failures!)
Pattern:
No lw instruction found within function (searched 5-55 instructions)
What this means:
- Register is a function parameter (
$a0-$a3or stack parameter) - Not loaded by
lwin this function - Passed in by caller
Solution:
- Detect if target register is
$a0,$a1,$a2, or$a3 - Check if it's used early in the function (within first 10-20 instructions)
- Mark as "function parameter - cannot resolve locally"
- Optionally: Analyze callers to see what they pass
Implementation complexity: Low-Medium
Expected impact: Reduces noise by 76.7%!
- Lines 456-475: Added call to
tryResolveGpRelative() - Lines 600-700: Implemented
tryResolveGpRelative()method - Lines 701-721: Implemented
getGlobalPointerValue()method - Status: ✅ Compiles successfully
$ ./gradlew :MIPS:compileJava
BUILD SUCCESSFUL in 2scd /home/matteius/ghidra
./gradlew buildGhidra1. Open tx-isp-t31.ko
2. Analysis → One Shot → Clear Code Bytes
3. Analysis → Auto Analyze
4. Wait for completion
$gp resolutions:
grep "Resolved via \$gp-relative" logs.txtTotal resolutions:
grep "Created.*reference.*COMPUTED" logs.txt | wc -lRemaining $gp failures:
grep "Register-relative load.*gp" logs.txtThis should be the first iteration with actual successful resolutions!
Even though it's only 28 cases (0.4%), it proves:
- ✅ The infrastructure works
- ✅ We can resolve indirect calls
- ✅ The approach is sound
Next: Tackle the 76.7% that are function parameters!
Iteration 1: Added jr support, basic tracking
Iteration 2: Added diagnostics, fixed switch table analyzer
Iteration 3: Fixed critical bugs, removed search limits
Iteration 4: Implemented $gp resolution ← YOU ARE HERE
Iteration 5: Function parameter detection (76.7% impact!)
Iteration 6: Stack tracking (12.5% impact)
Iteration 7: Multi-level tracking (remaining cases)
Status: ✅ CODE COMPLETE
Build: ✅ SUCCESSFUL
Impact: 🎉 FIRST RESOLUTIONS! (Expected: 20-28 cases)
Next: Rebuild and test! 🚀