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 interpretation for Thumb mode.
12 **************************************************************************/
14 #define MODULE_NAME "UNWARM_THUMB"
16 /***************************************************************************
18 **************************************************************************/
21 #if defined(UPGRADE_ARM_STACK_UNWIND)
25 /***************************************************************************
27 **************************************************************************/
30 /***************************************************************************
32 **************************************************************************/
35 /***************************************************************************
37 **************************************************************************/
40 /***************************************************************************
42 **************************************************************************/
45 /***************************************************************************
47 **************************************************************************/
49 /** Sign extend an 11 bit value.
50 * This function simply inspects bit 11 of the input \a value, and if
51 * set, the top 5 bits are set to give a 2's compliment signed value.
52 * \param value The value to sign extend.
53 * \return The signed-11 bit value stored in a 16bit data type.
55 static SignedInt16
signExtend11(Int16 value
)
66 /***************************************************************************
68 **************************************************************************/
71 UnwResult
UnwStartThumb(UnwState
* const state
)
73 Boolean found
= FALSE
;
74 Int16 t
= UNW_MAX_INSTR_COUNT
;
80 /* Attempt to read the instruction */
81 if(!state
->cb
->readH(state
->regData
[15].v
& (~0x1), &instr
))
83 return UNWIND_IREAD_H_FAIL
;
86 UnwPrintd4("T %x %x %04x:",
87 state
->regData
[13].v
, state
->regData
[15].v
, instr
);
89 /* Check that the PC is still on Thumb alignment */
90 if(!(state
->regData
[15].v
& 0x1))
92 UnwPrintd1("\nError: PC misalignment\n");
93 return UNWIND_INCONSISTENT
;
96 /* Check that the SP and PC have not been invalidated */
97 if(!M_IsOriginValid(state
->regData
[13].o
) || !M_IsOriginValid(state
->regData
[15].o
))
99 UnwPrintd1("\nError: PC or SP invalidated\n");
100 return UNWIND_INCONSISTENT
;
103 /* Format 1: Move shifted register
104 * LSL Rd, Rs, #Offset5
105 * LSR Rd, Rs, #Offset5
106 * ASR Rd, Rs, #Offset5
108 if((instr
& 0xe000) == 0x0000 && (instr
& 0x1800) != 0x1800)
111 Int8 op
= (instr
& 0x1800) >> 11;
112 Int8 offset5
= (instr
& 0x07c0) >> 6;
113 Int8 rs
= (instr
& 0x0038) >> 3;
114 Int8 rd
= (instr
& 0x0007);
119 UnwPrintd6("LSL r%d, r%d, #%d\t; r%d %s", rd
, rs
, offset5
, rs
, M_Origin2Str(state
->regData
[rs
].o
));
120 state
->regData
[rd
].v
= state
->regData
[rs
].v
<< offset5
;
121 state
->regData
[rd
].o
= state
->regData
[rs
].o
;
122 state
->regData
[rd
].o
|= REG_VAL_ARITHMETIC
;
126 UnwPrintd6("LSR r%d, r%d, #%d\t; r%d %s", rd
, rs
, offset5
, rs
, M_Origin2Str(state
->regData
[rs
].o
));
127 state
->regData
[rd
].v
= state
->regData
[rs
].v
>> offset5
;
128 state
->regData
[rd
].o
= state
->regData
[rs
].o
;
129 state
->regData
[rd
].o
|= REG_VAL_ARITHMETIC
;
133 UnwPrintd6("ASL r%d, r%d, #%d\t; r%d %s", rd
, rs
, offset5
, rs
, M_Origin2Str(state
->regData
[rs
].o
));
135 signExtend
= (state
->regData
[rs
].v
& 0x8000) ? TRUE
: FALSE
;
136 state
->regData
[rd
].v
= state
->regData
[rs
].v
>> offset5
;
139 state
->regData
[rd
].v
|= 0xffffffff << (32 - offset5
);
141 state
->regData
[rd
].o
= state
->regData
[rs
].o
;
142 state
->regData
[rd
].o
|= REG_VAL_ARITHMETIC
;
146 /* Format 2: add/subtract
148 * ADD Rd, Rs, #Offset3
150 * SUB Rd, Rs, #Offset3
152 else if((instr
& 0xf800) == 0x1800)
154 Boolean I
= (instr
& 0x0400) ? TRUE
: FALSE
;
155 Boolean op
= (instr
& 0x0200) ? TRUE
: FALSE
;
156 Int8 rn
= (instr
& 0x01c0) >> 6;
157 Int8 rs
= (instr
& 0x0038) >> 3;
158 Int8 rd
= (instr
& 0x0007);
161 UnwPrintd6("%s r%d, r%d, %c%d\t;",
166 UnwPrintd5("r%d %s, r%d %s",
167 rd
, M_Origin2Str(state
->regData
[rd
].o
),
168 rs
, M_Origin2Str(state
->regData
[rs
].o
));
171 UnwPrintd3(", r%d %s", rn
, M_Origin2Str(state
->regData
[rn
].o
));
173 /* Perform calculation */
176 state
->regData
[rd
].v
= state
->regData
[rs
].v
- state
->regData
[rn
].v
;
180 state
->regData
[rd
].v
= state
->regData
[rs
].v
+ state
->regData
[rn
].v
;
183 /* Propagate the origin */
184 if(M_IsOriginValid(state
->regData
[rs
].v
) &&
185 M_IsOriginValid(state
->regData
[rn
].v
))
187 state
->regData
[rd
].o
= state
->regData
[rs
].o
;
188 state
->regData
[rd
].o
|= REG_VAL_ARITHMETIC
;
192 state
->regData
[rd
].o
= REG_VAL_INVALID
;
197 /* Perform calculation */
200 state
->regData
[rd
].v
= state
->regData
[rs
].v
- rn
;
204 state
->regData
[rd
].v
= state
->regData
[rs
].v
+ rn
;
207 /* Propagate the origin */
208 state
->regData
[rd
].o
= state
->regData
[rs
].o
;
209 state
->regData
[rd
].o
|= REG_VAL_ARITHMETIC
;
212 /* Format 3: move/compare/add/subtract immediate
218 else if((instr
& 0xe000) == 0x2000)
220 Int8 op
= (instr
& 0x1800) >> 11;
221 Int8 rd
= (instr
& 0x0700) >> 8;
222 Int8 offset8
= (instr
& 0x00ff);
227 UnwPrintd3("MOV r%d, #0x%x", rd
, offset8
);
228 state
->regData
[rd
].v
= offset8
;
229 state
->regData
[rd
].o
= REG_VAL_FROM_CONST
;
233 /* Irrelevant to unwinding */
234 UnwPrintd1("CMP ???");
238 UnwPrintd5("ADD r%d, #0x%x\t; r%d %s",
239 rd
, offset8
, rd
, M_Origin2Str(state
->regData
[rd
].o
));
240 state
->regData
[rd
].v
+= offset8
;
241 state
->regData
[rd
].o
|= REG_VAL_ARITHMETIC
;
245 UnwPrintd5("SUB r%d, #0x%d\t; r%d %s",
246 rd
, offset8
, rd
, M_Origin2Str(state
->regData
[rd
].o
));
247 state
->regData
[rd
].v
+= offset8
;
248 state
->regData
[rd
].o
|= REG_VAL_ARITHMETIC
;
252 /* Format 4: ALU operations
270 else if((instr
& 0xfc00) == 0x4000)
272 Int8 op
= (instr
& 0x03c0) >> 6;
273 Int8 rs
= (instr
& 0x0038) >> 3;
274 Int8 rd
= (instr
& 0x0007);
275 #if defined(UNW_DEBUG)
276 static const char * const mnu
[16] =
277 { "AND", "EOR", "LSL", "LSR",
278 "ASR", "ADC", "SBC", "ROR",
279 "TST", "NEG", "CMP", "CMN",
280 "ORR", "MUL", "BIC", "MVN" };
282 /* Print the mnemonic and registers */
295 UnwPrintd8("%s r%d ,r%d\t; r%d %s, r%d %s",
298 rd
, M_Origin2Str(state
->regData
[rd
].o
),
299 rs
, M_Origin2Str(state
->regData
[rs
].o
));
304 UnwPrintd4("%s r%d, r%d", mnu
[op
], rd
, rs
);
310 /* Irrelevant to unwinding */
311 UnwPrintd2("%s ???", mnu
[op
]);
315 UnwPrintd5("r%d ,r%d\t; r%d %s",
317 rs
, M_Origin2Str(state
->regData
[rs
].o
));
318 state
->regData
[rd
].v
&= !state
->regData
[rs
].v
;
323 /* Perform operation */
327 state
->regData
[rd
].v
&= state
->regData
[rs
].v
;
331 state
->regData
[rd
].v
^= state
->regData
[rs
].v
;
335 state
->regData
[rd
].v
<<= state
->regData
[rs
].v
;
339 state
->regData
[rd
].v
>>= state
->regData
[rs
].v
;
343 if(state
->regData
[rd
].v
& 0x80000000)
345 state
->regData
[rd
].v
>>= state
->regData
[rs
].v
;
346 state
->regData
[rd
].v
|= 0xffffffff << (32 - state
->regData
[rs
].v
);
350 state
->regData
[rd
].v
>>= state
->regData
[rs
].v
;
362 state
->regData
[rd
].v
= (state
->regData
[rd
].v
>> state
->regData
[rs
].v
) |
363 (state
->regData
[rd
].v
<< (32 - state
->regData
[rs
].v
));
367 state
->regData
[rd
].v
= -state
->regData
[rs
].v
;
371 state
->regData
[rd
].v
|= state
->regData
[rs
].v
;
375 state
->regData
[rd
].v
*= state
->regData
[rs
].v
;
379 state
->regData
[rd
].v
&= !state
->regData
[rs
].v
;
383 state
->regData
[rd
].v
= !state
->regData
[rs
].v
;
387 /* Propagate data origins */
399 if(M_IsOriginValid(state
->regData
[rs
].o
) && M_IsOriginValid(state
->regData
[rs
].o
))
401 state
->regData
[rd
].o
= state
->regData
[rs
].o
;
402 state
->regData
[rd
].o
|= REG_VAL_ARITHMETIC
;
406 state
->regData
[rd
].o
= REG_VAL_INVALID
;
412 /* C-bit not tracked */
413 state
->regData
[rd
].o
= REG_VAL_INVALID
;
419 /* Nothing propagated */
424 state
->regData
[rd
].o
= state
->regData
[rs
].o
;
425 state
->regData
[rd
].o
|= REG_VAL_ARITHMETIC
;
431 /* Format 5: Hi register operations/branch exchange
436 else if((instr
& 0xfc00) == 0x4400)
438 Int8 op
= (instr
& 0x0300) >> 8;
439 Boolean h1
= (instr
& 0x0080) ? TRUE
: FALSE
;
440 Boolean h2
= (instr
& 0x0040) ? TRUE
: FALSE
;
441 Int8 rhs
= (instr
& 0x0038) >> 3;
442 Int8 rhd
= (instr
& 0x0007);
444 /* Adjust the register numbers */
448 if(op
!= 3 && !h1
&& !h2
)
450 UnwPrintd1("\nError: h1 or h2 must be set for ADD, CMP or MOV\n");
451 return UNWIND_ILLEGAL_INSTR
;
457 UnwPrintd5("ADD r%d, r%d\t; r%d %s",
458 rhd
, rhs
, rhs
, M_Origin2Str(state
->regData
[rhs
].o
));
459 state
->regData
[rhd
].v
+= state
->regData
[rhs
].v
;
460 state
->regData
[rhd
].o
= state
->regData
[rhs
].o
;
461 state
->regData
[rhd
].o
|= REG_VAL_ARITHMETIC
;
465 /* Irrelevant to unwinding */
466 UnwPrintd1("CMP ???");
470 UnwPrintd5("MOV r%d, r%d\t; r%d %s",
471 rhd
, rhs
, rhd
, M_Origin2Str(state
->regData
[rhs
].o
));
472 state
->regData
[rhd
].v
+= state
->regData
[rhs
].v
;
473 state
->regData
[rhd
].o
= state
->regData
[rhd
].o
;
477 UnwPrintd4("BX r%d\t; r%d %s\n",
478 rhs
, rhs
, M_Origin2Str(state
->regData
[rhs
].o
));
480 /* Only follow BX if the data was from the stack */
481 if(state
->regData
[rhs
].o
== REG_VAL_FROM_STACK
)
483 UnwPrintd2(" Return PC=0x%x\n", state
->regData
[rhs
].v
& (~0x1));
485 /* Report the return address, including mode bit */
486 if(!UnwReportRetAddr(state
, state
->regData
[rhs
].v
))
488 return UNWIND_TRUNCATED
;
492 state
->regData
[15].v
= state
->regData
[rhs
].v
;
494 /* Determine the new mode */
495 if(state
->regData
[rhs
].v
& 0x1)
497 /* Branching to THUMB */
499 /* Account for the auto-increment which isn't needed */
500 state
->regData
[15].v
-= 2;
505 return UnwStartArm(state
);
510 UnwPrintd4("\nError: BX to invalid register: r%d = 0x%x (%s)\n",
511 rhs
, state
->regData
[rhs
].o
, M_Origin2Str(state
->regData
[rhs
].o
));
512 return UNWIND_FAILURE
;
516 /* Format 9: PC-relative load
519 else if((instr
& 0xf800) == 0x4800)
521 Int8 rd
= (instr
& 0x0700) >> 8;
522 Int8 word8
= (instr
& 0x00ff);
525 /* Compute load address, adding a word to account for prefetch */
526 address
= (state
->regData
[15].v
& (~0x3)) + 4 + (word8
<< 2);
528 UnwPrintd3("LDR r%d, 0x%08x", rd
, address
);
530 if(!UnwMemReadRegister(state
, address
, &state
->regData
[rd
]))
532 return UNWIND_DREAD_W_FAIL
;
535 /* Format 13: add offset to Stack Pointer
539 else if((instr
& 0xff00) == 0xB000)
541 Int8 value
= (instr
& 0x7f) * 4;
543 /* Check the negative bit */
544 if((instr
& 0x80) != 0)
546 UnwPrintd2("SUB sp,#0x%x", value
);
547 state
->regData
[13].v
-= value
;
551 UnwPrintd2("ADD sp,#0x%x", value
);
552 state
->regData
[13].v
+= value
;
555 /* Format 14: push/pop registers
561 else if((instr
& 0xf600) == 0xb400)
563 Boolean L
= (instr
& 0x0800) ? TRUE
: FALSE
;
564 Boolean R
= (instr
& 0x0100) ? TRUE
: FALSE
;
565 Int8 rList
= (instr
& 0x00ff);
571 /* Load from memory: POP */
572 UnwPrintd2("POP {Rlist%s}\n", R
? ", PC" : "");
574 for(r
= 0; r
< 8; r
++)
576 if(rList
& (0x1 << r
))
579 if(!UnwMemReadRegister(state
, state
->regData
[13].v
, &state
->regData
[r
]))
581 return UNWIND_DREAD_W_FAIL
;
584 /* Alter the origin to be from the stack if it was valid */
585 if(M_IsOriginValid(state
->regData
[r
].o
))
587 state
->regData
[r
].o
= REG_VAL_FROM_STACK
;
590 state
->regData
[13].v
+= 4;
592 UnwPrintd3(" r%d = 0x%08x\n", r
, state
->regData
[r
].v
);
596 /* Check if the PC is to be popped */
599 /* Get the return address */
600 if(!UnwMemReadRegister(state
, state
->regData
[13].v
, &state
->regData
[15]))
602 return UNWIND_DREAD_W_FAIL
;
605 /* Alter the origin to be from the stack if it was valid */
606 if(!M_IsOriginValid(state
->regData
[15].o
))
608 /* Return address is not valid */
609 UnwPrintd1("PC popped with invalid address\n");
610 return UNWIND_FAILURE
;
614 /* The bottom bit should have been set to indicate that
615 * the caller was from Thumb. This would allow return
616 * by BX for interworking APCS.
618 if((state
->regData
[15].v
& 0x1) == 0)
620 UnwPrintd2("Warning: Return address not to Thumb: 0x%08x\n",
621 state
->regData
[15].v
);
623 /* Pop into the PC will not switch mode */
624 return UNWIND_INCONSISTENT
;
627 /* Store the return address */
628 if(!UnwReportRetAddr(state
, state
->regData
[15].v
))
630 return UNWIND_TRUNCATED
;
633 /* Now have the return address */
634 UnwPrintd2(" Return PC=%x\n", state
->regData
[15].v
);
637 state
->regData
[13].v
+= 4;
639 /* Compensate for the auto-increment, which isn't needed here */
640 state
->regData
[15].v
-= 2;
649 /* Store to memory: PUSH */
650 UnwPrintd2("PUSH {Rlist%s}", R
? ", LR" : "");
652 /* Check if the LR is to be pushed */
655 UnwPrintd3("\n lr = 0x%08x\t; %s",
656 state
->regData
[14].v
, M_Origin2Str(state
->regData
[14].o
));
658 state
->regData
[13].v
-= 4;
660 /* Write the register value to memory */
661 if(!UnwMemWriteRegister(state
, state
->regData
[13].v
, &state
->regData
[14]))
663 return UNWIND_DWRITE_W_FAIL
;
667 for(r
= 7; r
>= 0; r
--)
669 if(rList
& (0x1 << r
))
671 UnwPrintd4("\n r%d = 0x%08x\t; %s",
672 r
, state
->regData
[r
].v
, M_Origin2Str(state
->regData
[r
].o
));
674 state
->regData
[13].v
-= 4;
676 if(!UnwMemWriteRegister(state
, state
->regData
[13].v
, &state
->regData
[r
]))
678 return UNWIND_DWRITE_W_FAIL
;
684 /* Format 18: unconditional branch
687 else if((instr
& 0xf800) == 0xe000)
689 SignedInt16 branchValue
= signExtend11(instr
& 0x07ff);
691 /* Branch distance is twice that specified in the instruction. */
694 UnwPrintd2("B %d \n", branchValue
);
697 state
->regData
[15].v
+= branchValue
;
699 /* Need to advance by a word to account for pre-fetch.
700 * Advance by a half word here, allowing the normal address
701 * advance to account for the other half word.
703 state
->regData
[15].v
+= 2;
705 /* Display PC of next instruction */
706 UnwPrintd2(" New PC=%x", state
->regData
[15].v
+ 2);
713 /* Unknown/undecoded. May alter some register, so invalidate file */
714 UnwInvalidateRegisterFile(state
->regData
);
719 /* Should never hit the reset vector */
720 if(state
->regData
[15].v
== 0) return UNWIND_RESET
;
722 /* Check next address */
723 state
->regData
[15].v
+= 2;
725 /* Garbage collect the memory hash (used only for the stack) */
729 if(t
== 0) return UNWIND_EXHAUSTED
;
734 return UNWIND_SUCCESS
;
737 #endif /* UPGRADE_ARM_STACK_UNWIND */