CHARCELL doesn't have sbs support, so disable it properly.
[maemo-rb.git] / lib / unwarminder / unwarm_thumb.c
blob027d951f332317340c4d79334ee2f079fc2acd6d
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 /***************************************************************************
17 * Include Files
18 **************************************************************************/
20 #include "types.h"
21 #if defined(UPGRADE_ARM_STACK_UNWIND)
22 #include <stdio.h>
23 #include "unwarm.h"
25 /***************************************************************************
26 * Manifest Constants
27 **************************************************************************/
30 /***************************************************************************
31 * Type Definitions
32 **************************************************************************/
35 /***************************************************************************
36 * Variables
37 **************************************************************************/
40 /***************************************************************************
41 * Macros
42 **************************************************************************/
45 /***************************************************************************
46 * Local Functions
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)
57 if(value & 0x400)
59 value |= 0xf800;
62 return value;
66 /***************************************************************************
67 * Global Functions
68 **************************************************************************/
71 UnwResult UnwStartThumb(UnwState * const state)
73 Boolean found = FALSE;
74 Int16 t = UNW_MAX_INSTR_COUNT;
78 Int16 instr;
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)
110 Boolean signExtend;
111 Int8 op = (instr & 0x1800) >> 11;
112 Int8 offset5 = (instr & 0x07c0) >> 6;
113 Int8 rs = (instr & 0x0038) >> 3;
114 Int8 rd = (instr & 0x0007);
116 switch(op)
118 case 0: /* LSL */
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;
123 break;
125 case 1: /* LSR */
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;
130 break;
132 case 2: /* ASR */
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;
137 if(signExtend)
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;
143 break;
146 /* Format 2: add/subtract
147 * ADD Rd, Rs, Rn
148 * ADD Rd, Rs, #Offset3
149 * SUB Rd, Rs, Rn
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);
160 /* Print decoding */
161 UnwPrintd6("%s r%d, r%d, %c%d\t;",
162 op ? "SUB" : "ADD",
163 rd, rs,
164 I ? '#' : 'r',
165 rn);
166 UnwPrintd5("r%d %s, r%d %s",
167 rd, M_Origin2Str(state->regData[rd].o),
168 rs, M_Origin2Str(state->regData[rs].o));
169 if(!I)
171 UnwPrintd3(", r%d %s", rn, M_Origin2Str(state->regData[rn].o));
173 /* Perform calculation */
174 if(op)
176 state->regData[rd].v = state->regData[rs].v - state->regData[rn].v;
178 else
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;
190 else
192 state->regData[rd].o = REG_VAL_INVALID;
195 else
197 /* Perform calculation */
198 if(op)
200 state->regData[rd].v = state->regData[rs].v - rn;
202 else
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
213 * MOV Rd, #Offset8
214 * CMP Rd, #Offset8
215 * ADD Rd, #Offset8
216 * SUB Rd, #Offset8
218 else if((instr & 0xe000) == 0x2000)
220 Int8 op = (instr & 0x1800) >> 11;
221 Int8 rd = (instr & 0x0700) >> 8;
222 Int8 offset8 = (instr & 0x00ff);
224 switch(op)
226 case 0: /* MOV */
227 UnwPrintd3("MOV r%d, #0x%x", rd, offset8);
228 state->regData[rd].v = offset8;
229 state->regData[rd].o = REG_VAL_FROM_CONST;
230 break;
232 case 1: /* CMP */
233 /* Irrelevant to unwinding */
234 UnwPrintd1("CMP ???");
235 break;
237 case 2: /* ADD */
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;
242 break;
244 case 3: /* SUB */
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;
249 break;
252 /* Format 4: ALU operations
253 * AND Rd, Rs
254 * EOR Rd, Rs
255 * LSL Rd, Rs
256 * LSR Rd, Rs
257 * ASR Rd, Rs
258 * ADC Rd, Rs
259 * SBC Rd, Rs
260 * ROR Rd, Rs
261 * TST Rd, Rs
262 * NEG Rd, Rs
263 * CMP Rd, Rs
264 * CMN Rd, Rs
265 * ORR Rd, Rs
266 * MUL Rd, Rs
267 * BIC Rd, Rs
268 * MVN Rd, Rs
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" };
281 #endif
282 /* Print the mnemonic and registers */
283 switch(op)
285 case 0: /* AND */
286 case 1: /* EOR */
287 case 2: /* LSL */
288 case 3: /* LSR */
289 case 4: /* ASR */
290 case 7: /* ROR */
291 case 9: /* NEG */
292 case 12: /* ORR */
293 case 13: /* MUL */
294 case 15: /* MVN */
295 UnwPrintd8("%s r%d ,r%d\t; r%d %s, r%d %s",
296 mnu[op],
297 rd, rs,
298 rd, M_Origin2Str(state->regData[rd].o),
299 rs, M_Origin2Str(state->regData[rs].o));
300 break;
302 case 5: /* ADC */
303 case 6: /* SBC */
304 UnwPrintd4("%s r%d, r%d", mnu[op], rd, rs);
305 break;
307 case 8: /* TST */
308 case 10: /* CMP */
309 case 11: /* CMN */
310 /* Irrelevant to unwinding */
311 UnwPrintd2("%s ???", mnu[op]);
312 break;
314 case 14: /* BIC */
315 UnwPrintd5("r%d ,r%d\t; r%d %s",
316 rd, rs,
317 rs, M_Origin2Str(state->regData[rs].o));
318 state->regData[rd].v &= !state->regData[rs].v;
319 break;
323 /* Perform operation */
324 switch(op)
326 case 0: /* AND */
327 state->regData[rd].v &= state->regData[rs].v;
328 break;
330 case 1: /* EOR */
331 state->regData[rd].v ^= state->regData[rs].v;
332 break;
334 case 2: /* LSL */
335 state->regData[rd].v <<= state->regData[rs].v;
336 break;
338 case 3: /* LSR */
339 state->regData[rd].v >>= state->regData[rs].v;
340 break;
342 case 4: /* ASR */
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);
348 else
350 state->regData[rd].v >>= state->regData[rs].v;
353 break;
355 case 5: /* ADC */
356 case 6: /* SBC */
357 case 8: /* TST */
358 case 10: /* CMP */
359 case 11: /* CMN */
360 break;
361 case 7: /* ROR */
362 state->regData[rd].v = (state->regData[rd].v >> state->regData[rs].v) |
363 (state->regData[rd].v << (32 - state->regData[rs].v));
364 break;
366 case 9: /* NEG */
367 state->regData[rd].v = -state->regData[rs].v;
368 break;
370 case 12: /* ORR */
371 state->regData[rd].v |= state->regData[rs].v;
372 break;
374 case 13: /* MUL */
375 state->regData[rd].v *= state->regData[rs].v;
376 break;
378 case 14: /* BIC */
379 state->regData[rd].v &= !state->regData[rs].v;
380 break;
382 case 15: /* MVN */
383 state->regData[rd].v = !state->regData[rs].v;
384 break;
387 /* Propagate data origins */
388 switch(op)
390 case 0: /* AND */
391 case 1: /* EOR */
392 case 2: /* LSL */
393 case 3: /* LSR */
394 case 4: /* ASR */
395 case 7: /* ROR */
396 case 12: /* ORR */
397 case 13: /* MUL */
398 case 14: /* BIC */
399 if(M_IsOriginValid(state->regData[rd].o) && M_IsOriginValid(state->regData[rs].o))
401 state->regData[rd].o = state->regData[rs].o;
402 state->regData[rd].o |= REG_VAL_ARITHMETIC;
404 else
406 state->regData[rd].o = REG_VAL_INVALID;
408 break;
410 case 5: /* ADC */
411 case 6: /* SBC */
412 /* C-bit not tracked */
413 state->regData[rd].o = REG_VAL_INVALID;
414 break;
416 case 8: /* TST */
417 case 10: /* CMP */
418 case 11: /* CMN */
419 /* Nothing propagated */
420 break;
422 case 9: /* NEG */
423 case 15: /* MVN */
424 state->regData[rd].o = state->regData[rs].o;
425 state->regData[rd].o |= REG_VAL_ARITHMETIC;
426 break;
431 /* Format 5: Hi register operations/branch exchange
432 * ADD Rd, Hs
433 * ADD Hd, Rs
434 * ADD Hd, Hs
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 */
445 if(h2) rhs += 8;
446 if(h1) rhd += 8;
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;
454 switch(op)
456 case 0: /* ADD */
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;
462 break;
464 case 1: /* CMP */
465 /* Irrelevant to unwinding */
466 UnwPrintd1("CMP ???");
467 break;
469 case 2: /* MOV */
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;
474 break;
476 case 3: /* BX */
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;
491 /* Update the PC */
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;
502 else
504 /* Branch to ARM */
505 return UnwStartArm(state);
508 else
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
517 * LDR Rd,[PC, #imm]
519 else if((instr & 0xf800) == 0x4800)
521 Int8 rd = (instr & 0x0700) >> 8;
522 Int8 word8 = (instr & 0x00ff);
523 Int32 address;
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
536 * ADD sp,#+imm
537 * ADD sp,#-imm
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;
549 else
551 UnwPrintd2("ADD sp,#0x%x", value);
552 state->regData[13].v += value;
555 /* Format 14: push/pop registers
556 * PUSH {Rlist}
557 * PUSH {Rlist, LR}
558 * POP {Rlist}
559 * POP {Rlist, PC}
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);
567 if(L)
569 Int8 r;
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))
578 /* Read the word */
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 */
597 if(R)
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;
612 else
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);
636 /* Update the pc */
637 state->regData[13].v += 4;
639 /* Compensate for the auto-increment, which isn't needed here */
640 state->regData[15].v -= 2;
645 else
647 SignedInt8 r;
649 /* Store to memory: PUSH */
650 UnwPrintd2("PUSH {Rlist%s}", R ? ", LR" : "");
652 /* Check if the LR is to be pushed */
653 if(R)
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
685 * B label
687 else if((instr & 0xf800) == 0xe000)
689 SignedInt16 branchValue = signExtend11(instr & 0x07ff);
691 /* Branch distance is twice that specified in the instruction. */
692 branchValue *= 2;
694 UnwPrintd2("B %d \n", branchValue);
696 /* Update PC */
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);
709 else
711 UnwPrintd1("????");
713 /* Unknown/undecoded. May alter some register, so invalidate file */
714 UnwInvalidateRegisterFile(state->regData);
717 UnwPrintd1("\n");
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) */
726 UnwMemHashGC(state);
728 t--;
729 if(t == 0) return UNWIND_EXHAUSTED;
732 while(!found);
734 return UNWIND_SUCCESS;
737 #endif /* UPGRADE_ARM_STACK_UNWIND */
739 /* END OF FILE */