1 /***************************************************************************
2 * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk
4 * This program is PUBLIC DOMAIN.
5 * This means that there is no copyright and anyone is able to take a copy
6 * for free and use it as they wish, with or without modifications, and in
7 * any context, commercially or otherwise. The only limitation is that I
8 * don't guarantee that the software is fit for any purpose or accept any
9 * liability for it's use or misuse - this software is without warranty.
10 ***************************************************************************
11 * File Description: Abstract interpreter for ARM mode.
12 **************************************************************************/
14 #define MODULE_NAME "UNWARM_ARM"
16 /***************************************************************************
18 **************************************************************************/
21 #if defined(UPGRADE_ARM_STACK_UNWIND)
25 /***************************************************************************
27 **************************************************************************/
30 /***************************************************************************
32 **************************************************************************/
35 /***************************************************************************
37 **************************************************************************/
40 /***************************************************************************
42 **************************************************************************/
45 /***************************************************************************
47 **************************************************************************/
49 /** Check if some instruction is a data-processing instruction.
50 * Decodes the passed instruction, checks if it is a data-processing and
51 * verifies that the parameters and operation really indicate a data-
52 * processing instruction. This is needed because some parts of the
53 * instruction space under this instruction can be extended or represent
54 * other operations such as MRS, MSR.
56 * \param[in] inst The instruction word.
57 * \retval TRUE Further decoding of the instruction indicates that this is
58 * a valid data-processing instruction.
59 * \retval FALSE This is not a data-processing instruction,
61 static Boolean
isDataProc(Int32 instr
)
63 Int8 opcode
= (instr
& 0x01e00000) >> 21;
64 Boolean S
= (instr
& 0x00100000) ? TRUE
: FALSE
;
66 if((instr
& 0xfc000000) != 0xe0000000)
70 else if(!S
&& opcode
>= 8 && opcode
<= 11)
72 /* TST, TEQ, CMP and CMN all require S to be set */
81 /***************************************************************************
83 **************************************************************************/
86 UnwResult
UnwStartArm(UnwState
* const state
)
88 Boolean found
= FALSE
;
89 Int16 t
= UNW_MAX_INSTR_COUNT
;
95 /* Attempt to read the instruction */
96 if(!state
->cb
->readW(state
->regData
[15].v
, &instr
))
98 return UNWIND_IREAD_W_FAIL
;
101 UnwPrintd4("A %x %x %08x:",
102 state
->regData
[13].v
, state
->regData
[15].v
, instr
);
104 /* Check that the PC is still on Arm alignment */
105 if(state
->regData
[15].v
& 0x3)
107 UnwPrintd1("\nError: PC misalignment\n");
108 return UNWIND_INCONSISTENT
;
111 /* Check that the SP and PC have not been invalidated */
112 if(!M_IsOriginValid(state
->regData
[13].o
) || !M_IsOriginValid(state
->regData
[15].o
))
114 UnwPrintd1("\nError: PC or SP invalidated\n");
115 return UNWIND_INCONSISTENT
;
118 /* Branch and Exchange (BX)
119 * This is tested prior to data processing to prevent
120 * mis-interpretation as an invalid TEQ instruction.
122 if((instr
& 0xfffffff0) == 0xe12fff10)
124 Int8 rn
= instr
& 0xf;
126 UnwPrintd4("BX r%d\t ; r%d %s\n", rn
, rn
, M_Origin2Str(state
->regData
[rn
].o
));
128 if(!M_IsOriginValid(state
->regData
[rn
].o
))
130 UnwPrintd1("\nUnwind failure: BX to untracked register\n");
131 return UNWIND_FAILURE
;
134 /* Set the new PC value */
135 state
->regData
[15].v
= state
->regData
[rn
].v
;
137 /* Check if the return value is from the stack */
138 if(state
->regData
[rn
].o
== REG_VAL_FROM_STACK
)
140 /* Now have the return address */
141 UnwPrintd2(" Return PC=%x\n", state
->regData
[15].v
& (~0x1));
143 /* Report the return address */
144 if(!UnwReportRetAddr(state
, state
->regData
[rn
].v
))
146 return UNWIND_TRUNCATED
;
150 /* Determine the return mode */
151 if(state
->regData
[rn
].v
& 0x1)
153 /* Branching to THUMB */
154 return UnwStartThumb(state
);
160 /* Account for the auto-increment which isn't needed */
161 state
->regData
[15].v
-= 4;
165 else if((instr
& 0xff000000) == 0xea000000)
167 SignedInt32 offset
= (instr
& 0x00ffffff);
170 offset
= offset
<< 2;
172 /* Sign extend if needed */
173 if(offset
& 0x02000000)
175 offset
|= 0xfc000000;
178 UnwPrintd2("B %d\n", offset
);
181 state
->regData
[15].v
+= offset
;
183 /* Account for pre-fetch, where normally the PC is 8 bytes
184 * ahead of the instruction just executed.
186 state
->regData
[15].v
+= 4;
190 else if((instr
& 0xffbf0fff) == 0xe10f0000)
192 #if defined(UNW_DEBUG)
193 Boolean R
= (instr
& 0x00400000) ? TRUE
: FALSE
;
195 Int8 rd
= (instr
& 0x0000f000) >> 12;
197 UnwPrintd4("MRS r%d,%s\t; r%d invalidated", rd
, R
? "SPSR" : "CPSR", rd
);
199 /* Status registers untracked */
200 state
->regData
[rd
].o
= REG_VAL_INVALID
;
203 else if((instr
& 0xffb0f000) == 0xe120f000)
205 #if defined(UNW_DEBUG)
206 Boolean R
= (instr
& 0x00400000) ? TRUE
: FALSE
;
208 UnwPrintd2("MSR %s_?, ???", R
? "SPSR" : "CPSR");
210 /* Status registers untracked.
211 * Potentially this could change processor mode and switch
212 * banked registers r8-r14. Most likely is that r13 (sp) will
213 * be banked. However, invalidating r13 will stop unwinding
214 * when potentially this write is being used to disable/enable
215 * interrupts (a common case). Therefore no invalidation is
219 /* Data processing */
220 else if(isDataProc(instr
))
222 Boolean I
= (instr
& 0x02000000) ? TRUE
: FALSE
;
223 Int8 opcode
= (instr
& 0x01e00000) >> 21;
224 #if defined(UNW_DEBUG)
225 Boolean S
= (instr
& 0x00100000) ? TRUE
: FALSE
;
227 Int8 rn
= (instr
& 0x000f0000) >> 16;
228 Int8 rd
= (instr
& 0x0000f000) >> 12;
229 Int16 operand2
= (instr
& 0x00000fff);
231 RegValOrigin op2origin
;
235 case 0: UnwPrintd4("AND%s r%d,r%d,", S
? "S" : "", rd
, rn
); break;
236 case 1: UnwPrintd4("EOR%s r%d,r%d,", S
? "S" : "", rd
, rn
); break;
237 case 2: UnwPrintd4("SUB%s r%d,r%d,", S
? "S" : "", rd
, rn
); break;
238 case 3: UnwPrintd4("RSB%s r%d,r%d,", S
? "S" : "", rd
, rn
); break;
239 case 4: UnwPrintd4("ADD%s r%d,r%d,", S
? "S" : "", rd
, rn
); break;
240 case 5: UnwPrintd4("ADC%s r%d,r%d,", S
? "S" : "", rd
, rn
); break;
241 case 6: UnwPrintd4("SBC%s r%d,r%d,", S
? "S" : "", rd
, rn
); break;
242 case 7: UnwPrintd4("RSC%s r%d,r%d,", S
? "S" : "", rd
, rn
); break;
243 case 8: UnwPrintd3("TST%s r%d,", S
? "S" : "", rn
); break;
244 case 9: UnwPrintd3("TEQ%s r%d,", S
? "S" : "", rn
); break;
245 case 10: UnwPrintd3("CMP%s r%d,", S
? "S" : "", rn
); break;
246 case 11: UnwPrintd3("CMN%s r%d,", S
? "S" : "", rn
); break;
247 case 12: UnwPrintd3("ORR%s r%d,", S
? "S" : "", rn
); break;
248 case 13: UnwPrintd3("MOV%s r%d,", S
? "S" : "", rd
); break;
249 case 14: UnwPrintd4("BIC%s r%d,r%d", S
? "S" : "", rd
, rn
); break;
250 case 15: UnwPrintd3("MVN%s r%d,", S
? "S" : "", rd
); break;
253 /* Decode operand 2 */
256 Int8 shiftDist
= (operand2
& 0x0f00) >> 8;
257 Int8 shiftConst
= (operand2
& 0x00ff);
259 /* rotate const right by 2 * shiftDist */
261 op2val
= (shiftConst
>> shiftDist
) |
262 (shiftConst
<< (32 - shiftDist
));
263 op2origin
= REG_VAL_FROM_CONST
;
265 UnwPrintd2("#0x%x", op2val
);
269 /* Register and shift */
270 Int8 rm
= (operand2
& 0x000f);
271 Int8 regShift
= (operand2
& 0x0010) ? TRUE
: FALSE
;
272 Int8 shiftType
= (operand2
& 0x0060) >> 5;
274 #if defined(UNW_DEBUG)
275 const char * const shiftMnu
[4] = { "LSL", "LSR", "ASR", "ROR" };
277 UnwPrintd2("r%d ", rm
);
279 /* Get the shift distance */
282 Int8 rs
= (operand2
& 0x0f00) >> 8;
284 if(operand2
& 0x00800)
286 UnwPrintd1("\nError: Bit should be zero\n");
287 return UNWIND_ILLEGAL_INSTR
;
291 UnwPrintd1("\nError: Cannot use R15 with register shift\n");
292 return UNWIND_ILLEGAL_INSTR
;
295 /* Get shift distance */
296 shiftDist
= state
->regData
[rs
].v
;
297 op2origin
= state
->regData
[rs
].o
;
299 UnwPrintd7("%s r%d\t; r%d %s r%d %s",
300 shiftMnu
[shiftType
], rs
,
301 rm
, M_Origin2Str(state
->regData
[rm
].o
),
302 rs
, M_Origin2Str(state
->regData
[rs
].o
));
306 shiftDist
= (operand2
& 0x0f80) >> 7;
307 op2origin
= REG_VAL_FROM_CONST
;
312 shiftMnu
[shiftType
], shiftDist
);
314 UnwPrintd3("\t; r%d %s", rm
, M_Origin2Str(state
->regData
[rm
].o
));
318 /* Apply the shift type to the source register */
321 case 0: /* logical left */
322 op2val
= state
->regData
[rm
].v
<< shiftDist
;
324 case 1: /* logical right */
326 if(!regShift
&& shiftDist
== 0)
331 op2val
= state
->regData
[rm
].v
>> shiftDist
;
333 case 2: /* arithmetic right */
335 if(!regShift
&& shiftDist
== 0)
340 if(state
->regData
[rm
].v
& 0x80000000)
342 /* Register shifts maybe greater than 32 */
349 op2val
= state
->regData
[rm
].v
>> shiftDist
;
350 op2val
|= 0xffffffff << (32 - shiftDist
);
355 op2val
= state
->regData
[rm
].v
>> shiftDist
;
358 case 3: /* rotate right */
360 if(!regShift
&& shiftDist
== 0)
362 /* Rotate right with extend.
363 * This uses the carry bit and so always has an
366 op2origin
= REG_VAL_INVALID
;
371 /* Limit shift distance to 0-31 incase of register shift */
374 op2val
= (state
->regData
[rm
].v
>> shiftDist
) |
375 (state
->regData
[rm
].v
<< (32 - shiftDist
));
380 UnwPrintd2("\nError: Invalid shift type: %d\n", shiftType
);
381 return UNWIND_FAILURE
;
384 /* Decide the data origin */
385 if(M_IsOriginValid(op2origin
) &&
386 M_IsOriginValid(state
->regData
[rm
].o
))
388 op2origin
= state
->regData
[rm
].o
;
389 op2origin
|= REG_VAL_ARITHMETIC
;
393 op2origin
= REG_VAL_INVALID
;
398 /* Propagate register validity */
401 case 0: /* AND: Rd := Op1 AND Op2 */
402 case 1: /* EOR: Rd := Op1 EOR Op2 */
403 case 2: /* SUB: Rd:= Op1 - Op2 */
404 case 3: /* RSB: Rd:= Op2 - Op1 */
405 case 4: /* ADD: Rd:= Op1 + Op2 */
406 case 12: /* ORR: Rd:= Op1 OR Op2 */
407 case 14: /* BIC: Rd:= Op1 AND NOT Op2 */
408 if(!M_IsOriginValid(state
->regData
[rn
].o
) ||
409 !M_IsOriginValid(op2origin
))
411 state
->regData
[rd
].o
= REG_VAL_INVALID
;
415 state
->regData
[rd
].o
= state
->regData
[rn
].o
;
416 state
->regData
[rd
].o
|= op2origin
;
419 case 5: /* ADC: Rd:= Op1 + Op2 + C */
420 case 6: /* SBC: Rd:= Op1 - Op2 + C */
421 case 7: /* RSC: Rd:= Op2 - Op1 + C */
422 /* CPSR is not tracked */
423 state
->regData
[rd
].o
= REG_VAL_INVALID
;
426 case 8: /* TST: set condition codes on Op1 AND Op2 */
427 case 9: /* TEQ: set condition codes on Op1 EOR Op2 */
428 case 10: /* CMP: set condition codes on Op1 - Op2 */
429 case 11: /* CMN: set condition codes on Op1 + Op2 */
433 case 13: /* MOV: Rd:= Op2 */
434 case 15: /* MVN: Rd:= NOT Op2 */
435 state
->regData
[rd
].o
= op2origin
;
439 /* Account for pre-fetch by temporarily adjusting PC */
442 /* If the shift amount is specified in the instruction,
443 * the PC will be 8 bytes ahead. If a register is used
444 * to specify the shift amount the PC will be 12 bytes
447 if(!I
&& (operand2
& 0x0010))
448 state
->regData
[rn
].v
+= 12;
450 state
->regData
[rn
].v
+= 8;
456 case 0: /* AND: Rd := Op1 AND Op2 */
457 state
->regData
[rd
].v
= state
->regData
[rn
].v
& op2val
;
460 case 1: /* EOR: Rd := Op1 EOR Op2 */
461 state
->regData
[rd
].v
= state
->regData
[rn
].v
^ op2val
;
464 case 2: /* SUB: Rd:= Op1 - Op2 */
465 state
->regData
[rd
].v
= state
->regData
[rn
].v
- op2val
;
467 case 3: /* RSB: Rd:= Op2 - Op1 */
468 state
->regData
[rd
].v
= op2val
- state
->regData
[rn
].v
;
471 case 4: /* ADD: Rd:= Op1 + Op2 */
472 state
->regData
[rd
].v
= state
->regData
[rn
].v
+ op2val
;
475 case 5: /* ADC: Rd:= Op1 + Op2 + C */
476 case 6: /* SBC: Rd:= Op1 - Op2 + C */
477 case 7: /* RSC: Rd:= Op2 - Op1 + C */
478 case 8: /* TST: set condition codes on Op1 AND Op2 */
479 case 9: /* TEQ: set condition codes on Op1 EOR Op2 */
480 case 10: /* CMP: set condition codes on Op1 - Op2 */
481 case 11: /* CMN: set condition codes on Op1 + Op2 */
482 UnwPrintd1("\t; ????");
485 case 12: /* ORR: Rd:= Op1 OR Op2 */
486 state
->regData
[rd
].v
= state
->regData
[rn
].v
| op2val
;
489 case 13: /* MOV: Rd:= Op2 */
490 state
->regData
[rd
].v
= op2val
;
493 case 14: /* BIC: Rd:= Op1 AND NOT Op2 */
494 state
->regData
[rd
].v
= state
->regData
[rn
].v
& (~op2val
);
497 case 15: /* MVN: Rd:= NOT Op2 */
498 state
->regData
[rd
].v
= ~op2val
;
502 /* Remove the prefetch offset from the PC */
503 if(rd
!= 15 && rn
== 15)
505 if(!I
&& (operand2
& 0x0010))
506 state
->regData
[rn
].v
-= 12;
508 state
->regData
[rn
].v
-= 8;
512 /* Block Data Transfer
515 else if((instr
& 0xfe000000) == 0xe8000000)
517 Boolean P
= (instr
& 0x01000000) ? TRUE
: FALSE
;
518 Boolean U
= (instr
& 0x00800000) ? TRUE
: FALSE
;
519 Boolean S
= (instr
& 0x00400000) ? TRUE
: FALSE
;
520 Boolean W
= (instr
& 0x00200000) ? TRUE
: FALSE
;
521 Boolean L
= (instr
& 0x00100000) ? TRUE
: FALSE
;
522 Int16 baseReg
= (instr
& 0x000f0000) >> 16;
523 Int16 regList
= (instr
& 0x0000ffff);
524 Int32 addr
= state
->regData
[baseReg
].v
;
525 Boolean addrValid
= M_IsOriginValid(state
->regData
[baseReg
].o
);
528 #if defined(UNW_DEBUG)
529 /* Display the instruction */
532 UnwPrintd6("LDM%c%c r%d%s, {reglist}%s\n",
541 UnwPrintd6("STM%c%c r%d%s, {reglist}%s\n",
549 /* S indicates that banked registers (untracked) are used, unless
550 * this is a load including the PC when the S-bit indicates that
551 * that CPSR is loaded from SPSR (also untracked, but ignored).
553 if(S
&& (!L
|| (regList
& (0x01 << 15)) == 0))
555 UnwPrintd1("\nError:S-bit set requiring banked registers\n");
556 return UNWIND_FAILURE
;
558 else if(baseReg
== 15)
560 UnwPrintd1("\nError: r15 used as base register\n");
561 return UNWIND_FAILURE
;
563 else if(regList
== 0)
565 UnwPrintd1("\nError: Register list empty\n");
566 return UNWIND_FAILURE
;
569 /* Check if ascending or descending.
570 * Registers are loaded/stored in order of address.
571 * i.e. r0 is at the lowest address, r15 at the highest.
577 /* Check if the register is to be transferred */
578 if(regList
& (0x01 << r
))
580 if(P
) addr
+= U
? 4 : -4;
586 if(!UnwMemReadRegister(state
, addr
, &state
->regData
[r
]))
588 return UNWIND_DREAD_W_FAIL
;
591 /* Update the origin if read via the stack pointer */
592 if(M_IsOriginValid(state
->regData
[r
].o
) && baseReg
== 13)
594 state
->regData
[r
].o
= REG_VAL_FROM_STACK
;
597 UnwPrintd5(" R%d = 0x%08x\t; r%d %s\n",
601 M_Origin2Str(state
->regData
[r
].o
));
605 /* Invalidate the register as the base reg was invalid */
606 state
->regData
[r
].o
= REG_VAL_INVALID
;
608 UnwPrintd2(" R%d = ???\n", r
);
615 if(!UnwMemWriteRegister(state
, state
->regData
[13].v
, &state
->regData
[r
]))
617 return UNWIND_DWRITE_W_FAIL
;
621 UnwPrintd2(" R%d = 0x%08x\n", r
);
624 if(!P
) addr
+= U
? 4 : -4;
627 /* Check the next register */
630 while(r
>= 0 && r
<= 15);
632 /* Check the writeback bit */
633 if(W
) state
->regData
[baseReg
].v
= addr
;
635 /* Check if the PC was loaded */
636 if(L
&& (regList
& (0x01 << 15)))
638 if(!M_IsOriginValid(state
->regData
[15].o
))
640 /* Return address is not valid */
641 UnwPrintd1("PC popped with invalid address\n");
642 return UNWIND_FAILURE
;
646 /* Store the return address */
647 if(!UnwReportRetAddr(state
, state
->regData
[15].v
))
649 return UNWIND_TRUNCATED
;
652 UnwPrintd2(" Return PC=0x%x", state
->regData
[15].v
);
654 /* Determine the return mode */
655 if(state
->regData
[15].v
& 0x1)
657 /* Branching to THUMB */
658 return UnwStartThumb(state
);
664 /* Account for the auto-increment which isn't needed */
665 state
->regData
[15].v
-= 4;
674 /* Unknown/undecoded. May alter some register, so invalidate file */
675 UnwInvalidateRegisterFile(state
->regData
);
680 /* Should never hit the reset vector */
681 if(state
->regData
[15].v
== 0) return UNWIND_RESET
;
683 /* Check next address */
684 state
->regData
[15].v
+= 4;
686 /* Garbage collect the memory hash (used only for the stack) */
690 if(t
== 0) return UNWIND_EXHAUSTED
;
695 return UNWIND_UNSUPPORTED
;
698 #endif /* UPGRADE_ARM_STACK_UNWIND */