175 lines
4.1 KiB
Markdown
175 lines
4.1 KiB
Markdown
|
---
|
||
|
date: 2022-04-10
|
||
|
status: draft
|
||
|
---
|
||
|
|
||
|
# Control-flow analysis
|
||
|
|
||
|
CFA analyses the entire `.text` section with linear complexity.
|
||
|
|
||
|
It has the following goals:
|
||
|
- Detect function boundaries
|
||
|
- Create the control-flow graph of each function
|
||
|
|
||
|
## Pass 1: Find control-flow intrinsics
|
||
|
|
||
|
Input:
|
||
|
- `.text` section
|
||
|
|
||
|
Output:
|
||
|
- Locations of compiler intrinsics
|
||
|
|
||
|
Rules
|
||
|
|
||
|
*savegpr/restgpr detection* (CodeWarrior only)
|
||
|
|
||
|
Find the following instructions in `.text`
|
||
|
|
||
|
```asm
|
||
|
_save_gpr:
|
||
|
stw r14, -0x48(r11)
|
||
|
stw r15, -0x44(r11)
|
||
|
stw r16, -0x40(r11)
|
||
|
stw r17, -0x3c(r11)
|
||
|
stw r18, -0x38(r11)
|
||
|
stw r19, -0x34(r11)
|
||
|
stw r20, -0x30(r11)
|
||
|
stw r21, -0x2c(r11)
|
||
|
stw r22, -0x28(r11)
|
||
|
stw r23, -0x24(r11)
|
||
|
stw r24, -0x20(r11)
|
||
|
stw r25, -0x1c(r11)
|
||
|
stw r26, -0x18(r11)
|
||
|
stw r27, -0x14(r11)
|
||
|
stw r28, -0x10(r11)
|
||
|
stw r29, -0xc(r11)
|
||
|
stw r30, -0x8(r11)
|
||
|
stw r31, -0x4(r11)
|
||
|
blr
|
||
|
|
||
|
_load_gpr:
|
||
|
lwz r14, -0x48(r11)
|
||
|
lwz r15, -0x44(r11)
|
||
|
lwz r16, -0x40(r11)
|
||
|
lwz r17, -0x3c(r11)
|
||
|
lwz r18, -0x38(r11)
|
||
|
lwz r19, -0x34(r11)
|
||
|
lwz r20, -0x30(r11)
|
||
|
lwz r21, -0x2c(r11)
|
||
|
lwz r22, -0x28(r11)
|
||
|
lwz r23, -0x24(r11)
|
||
|
lwz r24, -0x20(r11)
|
||
|
lwz r25, -0x1c(r11)
|
||
|
lwz r26, -0x18(r11)
|
||
|
lwz r27, -0x14(r11)
|
||
|
lwz r28, -0x10(r11)
|
||
|
lwz r29, -0xc(r11)
|
||
|
lwz r30, -0x8(r11)
|
||
|
lwz r31, -0x4(r11)
|
||
|
blr
|
||
|
```
|
||
|
|
||
|
## Pass 2: Branch analysis
|
||
|
|
||
|
Input:
|
||
|
- `.text` section
|
||
|
|
||
|
Output:
|
||
|
- Slices (cuts) from which basic blocks can be derived
|
||
|
- Forward edges between basic blocks
|
||
|
- Initial set of function boundaries
|
||
|
|
||
|
#### Branch instruction hints
|
||
|
|
||
|
- Iterate over all branch opcodes (b, bc, bcctr, bclr)
|
||
|
- Assume that branches with link register save …
|
||
|
- point to the start of another function;
|
||
|
- eventually return back to the call site.
|
||
|
- Assume that branches without link register save are …
|
||
|
- tail calls if their target precedes the function start of the call site;
|
||
|
- probably jumps to basic blocks within the function otherwise (might be wrong)
|
||
|
- Skip indirect local branches (bcctr, bclr) for now.
|
||
|
|
||
|
#### Stack frame detection (CodeWarrior only)
|
||
|
|
||
|
Detect patterns matching stack frames. They might be reordered due to instruction scheduling.
|
||
|
|
||
|
Since CodeWarrior will only emit one function epilogue and prologue per function,
|
||
|
we can use this hint (if present) to reliably detect function bounds.
|
||
|
|
||
|
- On function entry point:
|
||
|
- Execute instructions up to next branch
|
||
|
- Derive stack frame from changes in machine state
|
||
|
- If the stack changed, we found an epilog
|
||
|
- When a `blr` (function return) is encountered:
|
||
|
- Execute instructions in basic block of `blr`
|
||
|
- If stack-related machine state got reverted, assume this BB to contain the prolog
|
||
|
|
||
|
Example 0x80045de0 from RMCP01 (savegpr/restgpr sled):
|
||
|
|
||
|
```asm
|
||
|
# Stack
|
||
|
# +0x04..+0x08: ret addr to caller
|
||
|
# 0x00..+0x04: caller backchain <- caller r1
|
||
|
# -0x30.. 0x00: callee saved gprs <- callee r11
|
||
|
# -0x68..-0x30: callee stack
|
||
|
# -0x6c..-0x68: ret addr to callee
|
||
|
# -0x70..-0x6c: callee backchain <- callee r1
|
||
|
|
||
|
_alloc_stack_frame:
|
||
|
stwu r1, -0x70(r1) # store callee backchain & alloc stack frame
|
||
|
mflr r0
|
||
|
…
|
||
|
stw r0, 0x74(r1) # store ret addr to caller
|
||
|
…
|
||
|
la r11, 0x40(r1) # load ptr to callee saved gprs
|
||
|
bl _savegpr_22 # save gprs
|
||
|
mr r31, r1
|
||
|
|
||
|
…
|
||
|
|
||
|
_free_stack_frame:
|
||
|
mr r10, r31
|
||
|
la r11, 0x40(r10) # load ptr to callee saved gprs
|
||
|
bl _restgpr_22 # restore gprs
|
||
|
lwz r10, 0x0(r1) # load ptr to caller stack frame
|
||
|
lwz r0, 0x4(r10) # load ret addr to caller
|
||
|
mr r1, r10 # free stack frame
|
||
|
mtlr r0
|
||
|
blr # return to caller
|
||
|
```
|
||
|
|
||
|
Example 0x80228490 from RMCP01 (stmw/lmw):
|
||
|
|
||
|
```asm
|
||
|
# Stack
|
||
|
# +0x04..+0x08: ret addr to caller
|
||
|
# 0x00..+0x04: caller backchain <- caller r1
|
||
|
# -0x30.. 0x00: callee saved gprs <- callee r11
|
||
|
# -0x38..-0x30: callee stack
|
||
|
# -0x3c..-0x38: ret addr to callee
|
||
|
# -0x40..-0x3c: callee backchain <- callee r1
|
||
|
|
||
|
_alloc_stack_frame:
|
||
|
stwu r1, -0x40(r1) # store callee backchain & alloc stack frame
|
||
|
mflr r0
|
||
|
stw r0, 0x44(r1) # store ret addr to caller
|
||
|
stmw r20, -0x30(r1) # save gprs
|
||
|
|
||
|
…
|
||
|
|
||
|
_free_stack_frame:
|
||
|
lmw r20, -0x30(r1) # restore gprs
|
||
|
lwz r0, 0x44(r1) # load ret addr to caller
|
||
|
mtlr r0
|
||
|
la r1, 0x40(r1) # free stack frame
|
||
|
blr # return to caller
|
||
|
```
|
||
|
|
||
|
## TODO
|
||
|
|
||
|
Add the following rules:
|
||
|
- follow indirect local branches
|
||
|
- destructor detection
|
||
|
- vtable detection
|