ghidra
ghidra copied to clipboard
Indirect switch statement not always decompiled.
I have an ARM binary with a someone unique switch statement/jump table. The jump table consists of a length byte and then offset bytes. The PC advances by the offset byte * 2. The code performs a branch with link (bl) to the jump table function with uses the link register to locate the jump table and branch to the appropriate target. The jump table function looks like this:
0x0001baad: push {r4, r5}
0x0001baaf: mov r4, lr
0x0001bab1: subs r4, r4, #1
0x0001bab3: ldrb r5, [r4, #0]
0x0001bab5: adds r4, r4, #1
0x0001bab7: cmp r3, r5
0x0001bab9: bcs.n 0x1babc
0x0001babb: mov r5, r3
0x0001babd: ldrb r3, [r4, r5]
0x0001babf: lsls r3, r3, #1
0x0001bac1: adds r3, r4, r3
0x0001bac3: pop {r4, r5}
0x0001bac5: bx r3
Without any intervention, none of these are handled. It either just falls through to the first case, or stops decompilation upon encountering the illegal instruction bytes that make up the jump table. However, if I modify the instruction flow of the bl instruction to branch, in many cases the switch statement miraculously appears.
In some cases however, the decompiled code shows the jump table code inlined into the function, but fails to handle the switch with:
/* WARNING: Could not execute MULTIEQUAL */
/* WARNING: Treating indirect jump as call */
(*(code *)(BYTE_ARRAY_0001d3d5 + (uint)BYTE_ARRAY_0001d3d5[i] * 2))();
This is currently tested with Ghidra 9.1.
I had a similar problem and spent quite some time trying to figure out how I could coerce the decompiler into recognizing the switch statement via annotations and cross references. Turns out that none of that is necessary; there's a predefined function fixup called switch8_r3
for this type of switch; the "ARM Pre-Pattern Analyzer" will actually try to identify these jump table functions and apply the fixup - it just doesn't recognize the specific code you (and I) ran across.
The fix is simple: Edit the jump table function and there's a small "Call Fixup" dropdown at the bottom left. Change that to switch8_r3
and all your switch statements will magically appear in the decompiler :)
Since I recently dealt with a similar problem, I know what to look for: In recent versions of Ghidra, since 10.2, the switch helper functions are recognized through a file called ARM_switch_patterns.xml, which also contains the pattern mentioned above. The semantics of the call fixup are defined in ARM.cspec, in the <callfixup name="switch8_r3">
element.
@russdill if you update to a more recent Ghidra version and re-run auto-analysis from scratch, does it decompile the switches in your ARM binary?