* Include Files
**************************************************************************/
-#include <system.h>
+#include "system.h"
#if defined(UPGRADE_ARM_STACK_UNWIND)
#include <stdio.h>
#include "unwarm.h"
+#include "unwarmmem.h"
/***************************************************************************
* Manifest Constants
return value;
}
+static SignedInt16 signExtend8(Int16 value)
+{
+ if(value & 0x80)
+ {
+ value |= 0xff00;
+ }
+
+ return value;
+}
/***************************************************************************
* Global Functions
**************************************************************************/
+static Int32 ThumbExpandImm (Int32 imm32)
+{
+ Int8 ab = (imm32 & (3 << 10)) >> 10;
+ Int8 cd = (imm32 & (3 << 8)) >> 8;
+ Int8 ror = (imm32 & (0x1F << 7)) >> 7;
+ Int32 nn = imm32 & 0xFF;
+ if (0 == ab)
+ {
+ switch (cd)
+ {
+ case 0: return nn;
+ case 1: return (nn << 16) | nn;
+ case 2: return (nn << 24) | (nn << 8);
+ case 3: return (nn << 24) | (nn << 16) | (nn << 8) | nn;
+ }
+ }
+ else
+ {
+ nn = nn | 0x80;
+ ror = 32 - ror;
+ return nn << ror;
+ }
+ return 0;
+}
+
+static UnwResult Unw32DataProcessingModifiedImmediate (UnwState * const state, Int16 instr, Int16 instr2)
+{
+ Int8 op = (instr & (0xF << 5)) >> 5;
+ Int8 Rn = instr & 0xF;
+ Int8 S = (instr & (1 << 4)) >> 4;
+ Int8 Rd = (instr2 & (0xF << 8)) >> 8;
+
+ switch (op)
+ {
+ case 0:
+ if (0xF==Rd)
+ {
+ if (0==S) return UNWIND_ILLEGAL_INSTR;
+
+ UnwPrintd1("TST ...\n");
+ }
+ else
+ {
+ state->regData[Rd].o = REG_VAL_INVALID;
+ UnwPrintd1("AND ...\n");
+ }
+ break;
+ case 1:
+ state->regData[Rd].o = REG_VAL_INVALID;
+ UnwPrintd1("BIC ...\n");
+ break;
+ case 2:
+ state->regData[Rd].o = REG_VAL_INVALID;
+ if (0xF != Rn)
+ UnwPrintd1("ORR ...\n");
+ else
+ UnwPrintd1("MOV ...\n");
+ break;
+ case 3:
+ if (0xF != Rn)
+ {
+ state->regData[Rd].o = REG_VAL_INVALID;
+ UnwPrintd1("ORN ...\n");
+ }
+ else
+ UnwPrintd1("MVN ...\n");
+ break;
+ case 4:
+ if (0xF != Rd)
+ {
+ UnwPrintd1("EOR ...\n");
+ state->regData[Rd].o = REG_VAL_INVALID;
+ }
+ else
+ {
+ if (0==S) return UNWIND_ILLEGAL_INSTR;
+
+ UnwPrintd1("TEQ ...\n");
+ }
+ break;
+ case 8:
+ if (0xF != Rd)
+ {
+ Int16 i = (instr & (1 << 10)) >> 10;
+ Int16 imm3 = (instr2 & (0x7 << 12)) >> 12;
+ Int16 imm8 = instr2 & 0xFF;
+ Int32 imm32 = imm8 | (imm3 << 8) | (i << 11);
+
+ imm32 = ThumbExpandImm (imm32);
+
+ UnwPrintd4("ADD r%d, r%d, #0x%x", Rd, Rn, imm32);
+ state->regData[Rd].v = state->regData[Rn].v + imm32;
+ state->regData[Rd].o = state->regData[Rn].o;
+ }
+ else
+ {
+ if (0==S) return UNWIND_ILLEGAL_INSTR;
+
+ UnwPrintd1("CMN ...\n");
+ }
+ break;
+ case 10:
+ state->regData[Rd].o = REG_VAL_INVALID;
+ UnwPrintd1("ADC ...\n");
+ break;
+ case 11:
+ state->regData[Rd].o = REG_VAL_INVALID;
+ UnwPrintd1("SBC ...\n");
+ break;
+ case 13:
+ if (0xF != Rd)
+ {
+ state->regData[Rd].o = REG_VAL_INVALID;
+ UnwPrintd1("SUB ...\n");
+ }
+ else
+ {
+ if (0==S) return UNWIND_ILLEGAL_INSTR;
+
+ UnwPrintd1("CMP ...\n");
+ }
+ break;
+ case 14:
+ state->regData[Rd].o = REG_VAL_INVALID;
+ UnwPrintd1("RSB ...\n");
+ break;
+ default:
+ return UNWIND_ILLEGAL_INSTR;
+ }
+ return UNWIND_SUCCESS;
+}
+
+static UnwResult Unw32LoadWord (UnwState * const state, Int16 instr, Int16 instr2)
+{
+ Int8 op1 = (instr & (0x3 << 7)) >> 7;
+ Int8 Rn = instr & 0xF;
+// Int8 op2 = (instr2 & (0x3F << 6)) >> 6;
+
+ if (1 == op1 && 0xF != Rn)
+ {
+ /* LDR imm */
+ Int8 Rt = (instr2 & (0xF << 12)) >> 12;
+ Int32 imm12 = instr2 & 0xFFF;
+
+ UnwPrintd4("LDR r%d, [r%d, #0x%08x]", Rt, Rn, imm12);
+
+ imm12 += state->regData[Rn].v;
+
+ if (state->regData[Rn].o == REG_VAL_INVALID)
+ {
+ state->regData[Rt].o = REG_VAL_INVALID;
+ }
+ else
+ {
+ if(!UnwMemReadRegister(state, imm12, &state->regData[Rt]))
+ {
+ return UNWIND_DREAD_W_FAIL;
+ }
+ }
+ }
+ else if (0 == op1 && 0xF != Rn)
+ {
+ Int8 Rt = (instr2 & (0xF << 12)) >> 12;
+ Int32 imm8 = instr2 & 0xFF;
+ Int8 U = (instr2 & (1 << 9)) >> 9;
+ Int8 P = (instr2 & (1 << 10)) >> 10;
+ Int8 W = (instr2 & (1 << 8)) >> 8;
+ Int32 offset_addr;
+ Int32 addr;
+
+ UnwPrintd8("LDR r%d, [r%d%c,#%c0x%08x%c%c\n",
+ Rt, Rn,
+ P ? ' ' : ']',
+ U ? '+' : '-',
+ imm8,
+ P ? ']' : ' ',
+ W ? '!' : ' ');
+
+ if (state->regData[Rn].o == REG_VAL_INVALID)
+ {
+ state->regData[Rt].o = REG_VAL_INVALID;
+ }
+ else
+ {
+ offset_addr = state->regData[Rn].v + (U ? 1 : -1) * imm8;
+ addr = P ? offset_addr : state->regData[Rn].v;
+
+ if(!UnwMemReadRegister(state, addr, &state->regData[Rt]))
+ {
+ return UNWIND_DREAD_W_FAIL;
+ }
+
+ if (REG_SP == Rn)
+ state->regData[Rt].o = REG_VAL_FROM_STACK;
+
+ if (W)
+ state->regData[Rn].v = offset_addr;
+ }
+ }
+// else if (op1 < 2 && 0xF == Rn)
+// {
+ /* LDR literal */
+// }
+ else
+ {
+ /* UNDEFINED */
+ UnwPrintd1("????");
+ UnwInvalidateRegisterFile(state->regData);
+ }
+ return UNWIND_SUCCESS;
+}
+static UnwResult Unw32LoadStoreMultiple (UnwState * const state, Int16 instr, Int16 instr2)
+{
+ Int8 op = (instr & (0x3 << 7)) >> 7;
+ Int8 L = (instr & (0x1 << 4)) >> 4;
+ Int8 Rn = instr & 0xF;
+
+ UnwResult res = UNWIND_SUCCESS;
+
+ switch (op)
+ {
+ case 0:
+ if (0 == L) UnwPrintd1("SRS ...");
+ else
+ {
+ state->regData[REG_PC].o = REG_VAL_INVALID;
+ UnwPrintd1("LRE ...");
+ }
+ break;
+ case 1:
+ {
+ Int8 bitCount = 0;
+ Int16 register_list = (instr2 & 0x7FFF);
+ int i;
+
+ for (i = 0; i < 15; i++)
+ {
+ if ((register_list & (0x1 << i)) != 0) bitCount++;
+ }
+
+ if (0 == L)
+ {
+ Int8 W = (instr & (0x1 << 5)) >> 5;
+ if (W) state->regData[Rn].v += 4*bitCount;
+ UnwPrintd1("STM ...");
+ }
+ else
+ {
+ if (13 != Rn)
+ {
+ Int8 W = (instr & (0x1 << 5)) >> 5;
+ for (i = 0; i < 15; i++)
+ {
+ if ((register_list & (0x1 << i)) != 0)
+ state->regData[i].o = REG_VAL_INVALID;
+ }
+ if (W)
+ {
+ if ((register_list & (1 << Rn)) == 0)
+ state->regData[Rn].v += 4*bitCount;
+ else
+ state->regData[Rn].o = REG_VAL_INVALID;
+ }
+ UnwPrintd1("LDM ...");
+ }
+ else
+ {
+ Boolean new_pc = FALSE;
+ Boolean got_lr = FALSE;
+
+ register_list = register_list | (instr2 & 0x8000);
+ UnwPrintd1("POP {");
+ for (i = 0; i < 16; i++)
+ {
+ if ((register_list & (0x1 << i)) != 0)
+ {
+ if (!UnwMemReadRegister(state,
+ state->regData[REG_SP].v,
+ &state->regData[i]))
+ {
+ return UNWIND_DREAD_W_FAIL;
+ }
+ state->regData[i].o = REG_VAL_FROM_STACK;
+ state->regData[REG_SP].v += 4;
+ if (i < 13)
+ UnwPrintd2 ("r%d,", i);
+ else if (REG_LR==i)
+ {
+ UnwPrintd1 ("lr,");
+ got_lr = TRUE;
+ }
+ else if (REG_PC==i)
+ {
+ UnwPrintd1 ("pc");
+ new_pc = TRUE;
+ }
+ }
+ }
+ UnwPrintd1("}");
+ if (new_pc)
+ {
+ UnwPrintd2("\n New PC=%x\n", state->regData[REG_PC].v);
+
+ /* Report the return address, including mode bit */
+ if(!UnwReportRetAddr(state, state->regData[REG_PC].v))
+ {
+ return UNWIND_TRUNCATED;
+ }
+ }
+ else if (got_lr)
+ {
+ UnwPrintd2("\n New PC=%x\n", state->regData[REG_LR].v);
+
+ /* Report the return address, including mode bit */
+ if(!UnwReportRetAddr(state, state->regData[REG_LR].v))
+ {
+ return UNWIND_TRUNCATED;
+ }
+
+ state->regData[REG_PC].v = state->regData[REG_LR].v;
+
+ if(UnwIsAddrThumb(state->regData[REG_PC].v, state->regData[REG_SPSR].v))
+ {
+ /* Branching to THUMB */
+
+ /* Account for the auto-increment which isn't needed */
+ state->regData[REG_PC].v -= 2;
+ }
+ else
+ {
+ return UnwStartArm(state);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 2:
+ if (0 == L)
+ {
+ /* STMDB / PUSH if Rn == 13 */
+ if (13 != Rn)
+ {
+ UnwPrintd1("STMDB ...");
+ }
+ else
+ {
+ Int16 register_list = (instr2 & 0x7FFF);
+ Int8 bitCount = 0;
+ Int32 address;
+ Int8 i;
+
+ for (i = 0; i < 15; i++)
+ {
+ if ((register_list & (0x1 << i)) != 0) bitCount++;
+ }
+ address = state->regData[REG_SP].v - 4*bitCount;
+ UnwPrintd1("PUSH {");
+ for (i = 0; i < 15; i++)
+ {
+ if ((register_list & (0x1 << i)) != 0)
+ {
+ if (!UnwMemWriteRegister (state,
+ address,
+ &state->regData[i]))
+ {
+ return UNWIND_DREAD_W_FAIL;
+ }
+ address += 4;
+ UnwPrintd2("r%d,", i);
+ }
+ }
+ state->regData[REG_SP].v = address;
+ UnwPrintd1("}");
+ }
+ }
+ else
+ {
+ /* LDMDB */
+ UnwPrintd1("LDMDB ...");
+ }
+ break;
+ case 3:
+ if (0 == L)
+ {
+ /* SRS */
+ UnwPrintd1("SRS ...");
+ }
+ else
+ {
+ /* RFE */
+ UnwPrintd1("RFE ...");
+ }
+ break;
+ }
+
+ return res;
+}
+
+static UnwResult UnwLoadStoreSingleDataItemOpA0101(UnwState * const state, Int32 instr)
+{
+ Int8 opB = (instr & (0x7 << 9)) >> 9;
+ Int8 Rt;
+ switch (opB)
+ {
+ case 0:
+ case 1:
+ case 2:
+ /* stores - ignore */
+ UnwPrintd1("STR(H/B) ...");
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ Rt = instr & 0x7;
+ state->regData[Rt].o = REG_VAL_INVALID;
+ UnwPrintd2("LDR(SB/H/B/SH) r%d", Rt);
+ break;
+ }
+ return UNWIND_SUCCESS;
+}
+
+static void doBranchOffset(UnwState * const state, SignedInt16 branchValue)
+{
+ /* Update PC */
+ state->regData[REG_PC].v += branchValue;
+
+ /* Need to advance by a word to account for pre-fetch.
+ * Advance by a half word here, allowing the normal address
+ * advance to account for the other half word.
+ */
+ state->regData[REG_PC].v += 2;
+
+ /* Display PC of next instruction */
+ UnwPrintd2("\n New PC=%x", state->regData[REG_PC].v + 2);
+}
+
+static UnwResult doBranch (UnwState * const state, SignedInt16 branchValue,
+ const char *opname)
+{
+ Int32 data;
+ Boolean tracked;
+
+ Boolean used = UnwMemHashRead (&state->branchData,
+ state->regData[REG_PC].v, &data, &tracked);
+
+ UnwPrintd4 ("%s %d ; %s", opname, branchValue, used ? "FOLLOW" : "SKIPPING");
+
+ if (!used)
+ {
+ UnwMemHashWrite (&state->branchData,
+ state->regData[REG_PC].v, 0, TRUE);
+ }
+ else
+ {
+ UnwMemHashWrite (&state->branchData,
+ state->regData[REG_PC].v, 0, FALSE);
+ doBranchOffset (state, branchValue);
+ }
+ return UNWIND_SUCCESS;
+}
UnwResult UnwStartThumb(UnwState * const state)
{
Boolean found = FALSE;
Int16 t = UNW_MAX_INSTR_COUNT;
+ UnwResult result = UNWIND_SUCCESS;
do
{
Int16 instr;
+ result = UNWIND_SUCCESS;
+
/* Attempt to read the instruction */
if(!state->cb->readH(state->regData[15].v & (~0x1), &instr))
{
state->regData[13].v, state->regData[15].v, instr);
/* Check that the PC is still on Thumb alignment */
- if(!(state->regData[15].v & 0x1))
+ if(!UnwIsAddrThumb(state->regData[REG_PC].v, state->regData[REG_SPSR].v))
{
UnwPrintd1("\nError: PC misalignment\n");
return UNWIND_INCONSISTENT;
* ADD Rd, Hs
* ADD Hd, Rs
* ADD Hd, Hs
+ * CMP Hd
+ * MOV Rd
+ * MOV Hd
+ * BX
+ * BLX
*/
else if((instr & 0xfc00) == 0x4400)
{
if(h2) rhs += 8;
if(h1) rhd += 8;
- if(op != 3 && !h1 && !h2)
+ if(op == 1 && !h1 && !h2)
{
UnwPrintd1("\nError: h1 or h2 must be set for ADD, CMP or MOV\n");
return UNWIND_ILLEGAL_INSTR;
case 2: /* MOV */
UnwPrintd5("MOV r%d, r%d\t; r%d %s",
rhd, rhs, rhd, M_Origin2Str(state->regData[rhs].o));
- state->regData[rhd].v += state->regData[rhs].v;
+ state->regData[rhd].v = state->regData[rhs].v;
state->regData[rhd].o = state->regData[rhd].o;
break;
{
return UNWIND_TRUNCATED;
}
+ t = UNW_MAX_INSTR_COUNT;
/* Update the PC */
state->regData[15].v = state->regData[rhs].v;
/* Determine the new mode */
- if(state->regData[rhs].v & 0x1)
+ if(UnwIsAddrThumb(state->regData[rhs].v, state->regData[REG_SPSR].v))
{
/* Branching to THUMB */
{
UnwPrintd4("\nError: BX to invalid register: r%d = 0x%x (%s)\n",
rhs, state->regData[rhs].o, M_Origin2Str(state->regData[rhs].o));
- return UNWIND_FAILURE;
+ result = UNWIND_FAILURE;
}
}
}
+ /* Format 9: load/store with immediate offset
+ * LDR/STR Rd, [Rb, #imm]
+ */
+ else if ((instr & 0xe000) == 0x6000)
+ {
+ Int8 rd = instr & 0x7;
+ Int8 rb = (instr & (0x7 << 3)) >> 3;
+ Int32 offset5 = (instr & (0x1f << 6)) >> 6;
+
+ offset5 += state->regData[rb].v;
+
+ if ((instr & 0x0400) != 0)
+ {
+ /* This is LDR */
+
+ UnwPrintd3("LDR r%d, 0x%08x", rd, offset5);
+
+ if (!UnwMemReadRegister (state, offset5, &state->regData[rd]))
+ {
+ state->regData[rd].o = REG_VAL_INVALID;
+ }
+ }
+ else
+ {
+ /* in STR case, ignore it (for now) */
+ UnwPrintd3("STR r%d, 0x%08x", rd, offset5);
+ }
+ }
/* Format 9: PC-relative load
* LDR Rd,[PC, #imm]
*/
if(!UnwMemReadRegister(state, address, &state->regData[rd]))
{
- return UNWIND_DREAD_W_FAIL;
+ state->regData[rd].o = REG_VAL_INVALID;
}
}
+ else if((instr & 0xf800) == 0x4000)
+ {
+ /* in STR case, ignore it (for now) */
+ UnwPrintd1("STR ???");
+ }
/* Format 13: add offset to Stack Pointer
* ADD sp,#+imm
* ADD sp,#-imm
* the caller was from Thumb. This would allow return
* by BX for interworking APCS.
*/
- if((state->regData[15].v & 0x1) == 0)
+ if(!UnwIsAddrThumb(state->regData[REG_PC].v, state->regData[REG_SPSR].v))
{
UnwPrintd2("Warning: Return address not to Thumb: 0x%08x\n",
state->regData[15].v);
{
return UNWIND_TRUNCATED;
}
+ t = UNW_MAX_INSTR_COUNT;
/* Now have the return address */
UnwPrintd2(" Return PC=%x\n", state->regData[15].v);
UnwPrintd2("B %d \n", branchValue);
- /* Update PC */
- state->regData[15].v += branchValue;
+ doBranchOffset (state, branchValue);
+ }
+ /* Load/store single data item
+ * STR/STRH/STRB/LDRSB/LDR/LDRH/LDRB/LDRSH (register)
+ */
+ else if ((instr & 0xf000) == 0x5000)
+ {
+ result = UnwLoadStoreSingleDataItemOpA0101(state, instr);
+ }
+ /* Load/store single data item
+ * Store/Load Register SP relative
+ */
+ else if ((instr & 0xf000) == 0x9000)
+ {
+ Int8 opB = (instr & (0x1 << 11)) >> 11;
+ Int16 Rt = (instr & (0x7 << 8)) >> 8;
+ Int16 imm8 = instr &0xFF;
- /* Need to advance by a word to account for pre-fetch.
- * Advance by a half word here, allowing the normal address
- * advance to account for the other half word.
- */
- state->regData[15].v += 2;
+ imm8 <<= 2;
- /* Display PC of next instruction */
- UnwPrintd2(" New PC=%x", state->regData[15].v + 2);
+ if (0 == opB)
+ {
+ UnwPrintd3("STR r%d, [sp, #0x%08x]", Rt, imm8);
+ if (!UnwMemWriteRegister(state, state->regData[REG_SP].v+imm8, &state->regData[Rt]))
+ {
+ return UNWIND_DREAD_W_FAIL;
+ }
+ }
+ else
+ {
+ UnwPrintd3("LDR r%d, [sp, #0x%08x]", Rt, imm8);
+ if (!UnwMemReadRegister(state, state->regData[REG_SP].v+imm8, &state->regData[Rt]))
+ {
+ return UNWIND_DREAD_W_FAIL;
+ }
+ state->regData[Rt].v = REG_VAL_FROM_STACK;
+ }
+ }
+ /* Conditional branch, and Supervisor Call */
+ else if ((instr & 0xf000) == 0xd000)
+ {
+ Int8 cond = (instr & (0xF << 8)) >> 8;
+ switch (cond)
+ {
+ case 0xF:
+ UnwPrintd2("SVC ... (lr=%x)", state->regData[REG_LR].v);
+ break;
+ case 0xE:
+ result = UNWIND_FAILURE;
+ UnwPrintd1("ILLEGAL ; breaking");
+ break;
+ default:
+ {
+ SignedInt16 branchValue = (signExtend8 (instr & 0xFF)) * 2;
+ result = doBranch(state, branchValue, "B(cond)");
+ }
+ break;
+ }
+ }
+ /* Compare and Branch on Zero */
+ else if ((instr & 0xff00) == 0xb100)
+ {
+ SignedInt16 imm32 = (instr & (0x1F << 3)) >> 3;
+ SignedInt16 i = (instr & (1 << 9)) >> 9;
+ imm32 = (imm32 | (i << 5)) << 1;
+
+ result = doBranch(state, imm32, "CB{N}Z");
+ }
+ else if ((instr & 0xff00) == 0xbf00)
+ {
+ UnwPrintd1("IT/NOP/YIELD/WFE/WFI/SEV ...");
+ }
+ /* 32-bit instructions */
+ else if (((instr & 0xe000) == 0xe000) && ((instr & 0xf800) != 0xe00))
+ {
+ Int8 op1 = (instr & (0x3 << 11)) >> 11;
+ Int8 op2 = (instr & (0x7F << 4)) >> 4;
+ Int8 op;
+ UnwResult res = UNWIND_SUCCESS;
+
+ Int16 instr2;
+ /* read second part of this 32-bit instruction */
+ if(!state->cb->readH((state->regData[15].v + 2) & (~0x1), &instr2))
+ {
+ return UNWIND_IREAD_H_FAIL;
+ }
+
+ op = (instr2 & (1 << 15)) >> 15;
+
+ switch (op1)
+ {
+ case 1:
+ if ((op2 & 0x64) == 0)
+ {
+ /* Load/store multiple */
+ res = Unw32LoadStoreMultiple(state, instr, instr2);
+ }
+ else if ((op2 & 0x64) == 4)
+ {
+ /* Load/store dual, load/store exclusive, table branch */
+ UnwPrintd1("32-bit load/store dual, load/store exclusive, table branch...");
+ }
+ else if ((op2 & 0x60) == 0x20)
+ {
+ /* Data-processing (shifted register) */
+ UnwPrintd1("32-bit data processing (shifted register)...");
+ }
+ else /* if (op2 & 0x40 == 0x40) */
+ {
+ /* Coprocessor instructions */
+ UnwPrintd1("32-bit coprocessor...");
+ }
+ break;
+ case 2:
+ if (0 == op)
+ {
+ if ((op2 & 0x20) == 0)
+ {
+ /* Data processing (modified immediate) */
+ res = Unw32DataProcessingModifiedImmediate(state, instr, instr2);
+ }
+ else
+ {
+ /* Data-processing (plain binary immediate) */
+ UnwPrintd1("32-bit data processing (plain binary immediate)...");
+ }
+ }
+ else
+ {
+ /* Branches and miscellaneous control */
+ UnwPrintd1("32-bit branches and miscellaneous control...");
+ }
+ break;
+ case 3:
+ if ((op2 & 0x71) == 0)
+ {
+ /* Store single data item */
+ UnwPrintd1("32-bit store single data item...");
+ }
+ else if ((op2 & 0x71) == 0x10)
+ {
+ /* Advanced SIMD element or structure load/store instructions */
+ UnwPrintd1("32-bit advanced SIMD element or structure load/store...");
+ }
+ else if ((op2 & 0x67) == 1)
+ {
+ /* Load byte, memory hints */
+ UnwPrintd1("32-bit load byte, memory hints...");
+ }
+ else if ((op2 & 0x67) == 3)
+ {
+ /* Load halfword, memory hints */
+ UnwPrintd1("32-bit load halfword, memory hints...");
+ }
+ else if ((op2 & 0x67) == 5)
+ {
+ /* Load word */
+ res = Unw32LoadWord (state, instr, instr2);
+ }
+ else if ((op2 & 0x67) == 7)
+ {
+ res = UNWIND_ILLEGAL_INSTR;
+ }
+ else if ((op2 & 0x70) == 0x20)
+ {
+ /* Data-processing (register) */
+ UnwPrintd1("32-bit data processing...");
+ }
+ else if ((op2 & 0x78) == 0x30)
+ {
+ /* Multiply, multiply accumulate, and absolute difference */
+ UnwPrintd1("32-bit multiply...");
+ }
+ else if ((op2 & 0x78) == 0x38)
+ {
+ /* Long multiply, long multiply accumulate, and divide */
+ UnwPrintd1("32-bit long multiply...");
+ }
+ else if ((op2 & 0x80) == 0x80)
+ {
+ /* Coprocessor instructions */
+ UnwPrintd1("32-bit coprocessor (2) ...");
+ }
+ else
+ res = UNWIND_ILLEGAL_INSTR;
+ }
+
+ state->regData[REG_PC].v += 2;
+
+ if (UNWIND_SUCCESS != res)
+ return res;
}
else
{
UnwMemHashGC(state);
t--;
- if(t == 0) return UNWIND_EXHAUSTED;
+ if(t == 0 || result == UNWIND_FAILURE)
+ {
+ /* TODO: try scanning prologue - here */
+ /* call callback to get address of current function */
+ Int32 prologue_pc = state->cb->getProloguePC (state->lastReported);
+ /* Prologue fits within 32 16-bit instructions */
+ Int32 prologue_pc_end = prologue_pc + 31*2;
+ Boolean changed = FALSE;
+
+ if (0 == t) result = UNWIND_EXHAUSTED;
+
+ UnwPrintd3("Unwind exhausted (result=%d), prologue at: %x\n", result, prologue_pc);
+
+ if (0 == prologue_pc) return UNWIND_FAILURE;
+
+ UnwInvalidateRegisterFile (state->regData);
+
+ while (prologue_pc_end > prologue_pc)
+ {
+ Int16 instr;
+ Int16 prev_instr = 0;
+
+ if(!state->cb->readH(prologue_pc_end & (~0x1), &instr))
+ {
+ return UNWIND_IREAD_H_FAIL;
+ }
+ if (prologue_pc_end - 2 >= prologue_pc)
+ {
+ if(!state->cb->readH((prologue_pc_end-2) & (~0x1), &prev_instr))
+ {
+ return UNWIND_IREAD_H_FAIL;
+ }
+ }
+ /* Check if this is not a part of previous 32-bit instruction */
+ if ((prev_instr & 0xf800) != 0xe000)
+ {
+ /* Now, let's look for all the interesting stuff we want:
+ * push {rlist, lr}
+ * sub sp, #imm
+ */
+ if ((instr & 0xff80) == 0xb080)
+ {
+ /* sub sp, #imm */
+ Int16 imm7 = instr & 0x7F;
+ imm7 = imm7 << 2;
+ state->regData[REG_SP].v += imm7;
+ changed = TRUE;
+ }
+ else if ((instr & 0xfe00) == 0xb400)
+ {
+ /* push {rlist, lr} */
+ Int16 M = (instr & (0x1 << 8)) >> 8;
+ Int16 register_list = (instr & 0xFF) | (M << 14);
+ int i;
+
+ for (i = 0; i < 15; i++)
+ {
+ if ((register_list & (0x1 << i)) != 0)
+ {
+ if (!UnwMemReadRegister(state, state->regData[REG_SP].v,
+ &state->regData[i]))
+ {
+ return UNWIND_DREAD_W_FAIL;
+ }
+ state->regData[i].o = REG_VAL_FROM_STACK;
+ state->regData[REG_SP].v += 4;
+ changed = TRUE;
+ }
+ }
+ }
+ }
+ prologue_pc_end -= 2;
+ }
+
+ if (changed)
+ {
+ /* Report the return address, including mode bit */
+ if(!UnwReportRetAddr(state, state->regData[REG_LR].v))
+ {
+ return UNWIND_TRUNCATED;
+ }
+
+ if (state->regData[REG_PC].v-2 == state->regData[REG_LR].v)
+ {
+ return UNWIND_FAILURE;
+ }
+
+ /* Update the PC */
+ state->regData[REG_PC].v = state->regData[REG_LR].v;
+
+ UnwPrintd2(" New PC=%x\n", state->regData[REG_PC].v);
+
+ /* Determine the new mode */
+ if(UnwIsAddrThumb(state->regData[REG_PC].v, state->regData[REG_SPSR].v))
+ {
+ /* Branching to THUMB - PC already set - restore count */
+ t = UNW_MAX_INSTR_COUNT;
+ }
+ else
+ {
+ /* Branch to ARM */
+ return UnwStartArm(state);
+ }
+ }
+ else
+ return result;
+ }
}
while(!found);
- return UNWIND_SUCCESS;
+ return result;
}
#endif /* UPGRADE_ARM_STACK_UNWIND */