checkwps: harden filename extension checking.
[maemo-rb.git] / lib / unwarminder / unwarm_arm.c
blob0c41c05919816a0cefe51de9415fb2438278480c
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 /***************************************************************************
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 /** 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)
68 return FALSE;
70 else if(!S && opcode >= 8 && opcode <= 11)
72 /* TST, TEQ, CMP and CMN all require S to be set */
73 return FALSE;
75 else
77 return TRUE;
81 /***************************************************************************
82 * Global Functions
83 **************************************************************************/
86 UnwResult UnwStartArm(UnwState * const state)
88 Boolean found = FALSE;
89 Int16 t = UNW_MAX_INSTR_COUNT;
93 Int32 instr;
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);
156 else
158 /* Branch to ARM */
160 /* Account for the auto-increment which isn't needed */
161 state->regData[15].v -= 4;
164 /* Branch */
165 else if((instr & 0xff000000) == 0xea000000)
167 SignedInt32 offset = (instr & 0x00ffffff);
169 /* Shift value */
170 offset = offset << 2;
172 /* Sign extend if needed */
173 if(offset & 0x02000000)
175 offset |= 0xfc000000;
178 UnwPrintd2("B %d\n", offset);
180 /* Adjust PC */
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;
189 /* MRS */
190 else if((instr & 0xffbf0fff) == 0xe10f0000)
192 #if defined(UNW_DEBUG)
193 Boolean R = (instr & 0x00400000) ? TRUE : FALSE;
194 #endif
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;
202 /* MSR */
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");
209 #endif
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
216 * performed.
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;
226 #endif
227 Int8 rn = (instr & 0x000f0000) >> 16;
228 Int8 rd = (instr & 0x0000f000) >> 12;
229 Int16 operand2 = (instr & 0x00000fff);
230 Int32 op2val;
231 RegValOrigin op2origin;
233 switch(opcode)
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 */
254 if(I)
256 Int8 shiftDist = (operand2 & 0x0f00) >> 8;
257 Int8 shiftConst = (operand2 & 0x00ff);
259 /* rotate const right by 2 * shiftDist */
260 shiftDist *= 2;
261 op2val = (shiftConst >> shiftDist) |
262 (shiftConst << (32 - shiftDist));
263 op2origin = REG_VAL_FROM_CONST;
265 UnwPrintd2("#0x%x", op2val);
267 else
269 /* Register and shift */
270 Int8 rm = (operand2 & 0x000f);
271 Int8 regShift = (operand2 & 0x0010) ? TRUE : FALSE;
272 Int8 shiftType = (operand2 & 0x0060) >> 5;
273 Int32 shiftDist;
274 #if defined(UNW_DEBUG)
275 const char * const shiftMnu[4] = { "LSL", "LSR", "ASR", "ROR" };
276 #endif
277 UnwPrintd2("r%d ", rm);
279 /* Get the shift distance */
280 if(regShift)
282 Int8 rs = (operand2 & 0x0f00) >> 8;
284 if(operand2 & 0x00800)
286 UnwPrintd1("\nError: Bit should be zero\n");
287 return UNWIND_ILLEGAL_INSTR;
289 else if(rs == 15)
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));
304 else
306 shiftDist = (operand2 & 0x0f80) >> 7;
307 op2origin = REG_VAL_FROM_CONST;
309 if(shiftDist)
311 UnwPrintd3("%s #%d",
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 */
319 switch(shiftType)
321 case 0: /* logical left */
322 op2val = state->regData[rm].v << shiftDist;
323 break;
324 case 1: /* logical right */
326 if(!regShift && shiftDist == 0)
328 shiftDist = 32;
331 op2val = state->regData[rm].v >> shiftDist;
332 break;
333 case 2: /* arithmetic right */
335 if(!regShift && shiftDist == 0)
337 shiftDist = 32;
340 if(state->regData[rm].v & 0x80000000)
342 /* Register shifts maybe greater than 32 */
343 if(shiftDist >= 32)
345 op2val = 0xffffffff;
347 else
349 op2val = state->regData[rm].v >> shiftDist;
350 op2val |= 0xffffffff << (32 - shiftDist);
353 else
355 op2val = state->regData[rm].v >> shiftDist;
357 break;
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
364 * untracked result.
366 op2origin = REG_VAL_INVALID;
367 op2val = 0;
369 else
371 /* Limit shift distance to 0-31 incase of register shift */
372 shiftDist &= 0x1f;
374 op2val = (state->regData[rm].v >> shiftDist) |
375 (state->regData[rm].v << (32 - shiftDist));
377 break;
379 default:
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;
391 else
393 op2origin = REG_VAL_INVALID;
398 /* Propagate register validity */
399 switch(opcode)
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;
413 else
415 state->regData[rd].o = state->regData[rn].o;
416 state->regData[rd].o |= op2origin;
418 break;
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;
424 break;
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 */
430 break;
433 case 13: /* MOV: Rd:= Op2 */
434 case 15: /* MVN: Rd:= NOT Op2 */
435 state->regData[rd].o = op2origin;
436 break;
439 /* Account for pre-fetch by temporarily adjusting PC */
440 if(rn == 15)
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
445 * ahead.
447 if(!I && (operand2 & 0x0010))
448 state->regData[rn].v += 12;
449 else
450 state->regData[rn].v += 8;
453 /* Compute values */
454 switch(opcode)
456 case 0: /* AND: Rd := Op1 AND Op2 */
457 state->regData[rd].v = state->regData[rn].v & op2val;
458 break;
460 case 1: /* EOR: Rd := Op1 EOR Op2 */
461 state->regData[rd].v = state->regData[rn].v ^ op2val;
462 break;
464 case 2: /* SUB: Rd:= Op1 - Op2 */
465 state->regData[rd].v = state->regData[rn].v - op2val;
466 break;
467 case 3: /* RSB: Rd:= Op2 - Op1 */
468 state->regData[rd].v = op2val - state->regData[rn].v;
469 break;
471 case 4: /* ADD: Rd:= Op1 + Op2 */
472 state->regData[rd].v = state->regData[rn].v + op2val;
473 break;
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; ????");
483 break;
485 case 12: /* ORR: Rd:= Op1 OR Op2 */
486 state->regData[rd].v = state->regData[rn].v | op2val;
487 break;
489 case 13: /* MOV: Rd:= Op2 */
490 state->regData[rd].v = op2val;
491 break;
493 case 14: /* BIC: Rd:= Op1 AND NOT Op2 */
494 state->regData[rd].v = state->regData[rn].v & (~op2val);
495 break;
497 case 15: /* MVN: Rd:= NOT Op2 */
498 state->regData[rd].v = ~op2val;
499 break;
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;
507 else
508 state->regData[rn].v -= 8;
512 /* Block Data Transfer
513 * LDM, STM
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);
526 SignedInt8 r;
528 #if defined(UNW_DEBUG)
529 /* Display the instruction */
530 if(L)
532 UnwPrintd6("LDM%c%c r%d%s, {reglist}%s\n",
533 P ? 'E' : 'F',
534 U ? 'D' : 'A',
535 baseReg,
536 W ? "!" : "",
537 S ? "^" : "");
539 else
541 UnwPrintd6("STM%c%c r%d%s, {reglist}%s\n",
542 !P ? 'E' : 'F',
543 !U ? 'D' : 'A',
544 baseReg,
545 W ? "!" : "",
546 S ? "^" : "");
548 #endif
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.
573 r = U ? 0 : 15;
577 /* Check if the register is to be transferred */
578 if(regList & (0x01 << r))
580 if(P) addr += U ? 4 : -4;
582 if(L)
584 if(addrValid)
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",
599 state->regData[r].v,
601 M_Origin2Str(state->regData[r].o));
603 else
605 /* Invalidate the register as the base reg was invalid */
606 state->regData[r].o = REG_VAL_INVALID;
608 UnwPrintd2(" R%d = ???\n", r);
611 else
613 if(addrValid)
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 */
628 r += U ? 1 : -1;
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;
644 else
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);
660 else
662 /* Branch to ARM */
664 /* Account for the auto-increment which isn't needed */
665 state->regData[15].v -= 4;
670 else
672 UnwPrintd1("????");
674 /* Unknown/undecoded. May alter some register, so invalidate file */
675 UnwInvalidateRegisterFile(state->regData);
678 UnwPrintd1("\n");
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) */
687 UnwMemHashGC(state);
689 t--;
690 if(t == 0) return UNWIND_EXHAUSTED;
693 while(!found);
695 return UNWIND_UNSUPPORTED;
698 #endif /* UPGRADE_ARM_STACK_UNWIND */
700 /* END OF FILE */