Porting McTernan's unwinder to armv7l Tizen 92/62792/3
authorAdrian Szyndela <adrian.s@samsung.com>
Fri, 18 Mar 2016 09:03:17 +0000 (10:03 +0100)
committerAdrian Szyndela <adrian.s@samsung.com>
Wed, 23 Mar 2016 11:49:09 +0000 (12:49 +0100)
- includes
- registers
- recognizing "Thumbness"
- support for 32-bit instructions
- support for newer ARM instructions

Change-Id: I2096e3bf2abcd9ceccd4b6614cd488947ba79e6d

src/crash-stack/wind/unwarm.c
src/crash-stack/wind/unwarm.h
src/crash-stack/wind/unwarm_arm.c
src/crash-stack/wind/unwarm_thumb.c
src/crash-stack/wind/unwarminder.c
src/crash-stack/wind/unwarminder.h
src/crash-stack/wind/unwarmmem.c
src/crash-stack/wind/unwarmmem.h

index 2be3facaafb6a6e99777128bff11378f555734b5..cdfc882047554713926a33b36c2425ae2d12f556 100644 (file)
@@ -17,7 +17,7 @@
  * Include Files
  **************************************************************************/
 
-#include <system.h>
+#include "system.h"
 #if defined(UPGRADE_ARM_STACK_UNWIND)
 #include <stdio.h>
 #include <stdarg.h>
@@ -68,6 +68,11 @@ void UnwPrintf(const char *format, ...)
 }
 #endif
 
+Boolean UnwIsAddrThumb (Int32 pc, Int32 spsr)
+{
+  return (pc & 0x1) != 0 || (spsr & 0x20) != 0;
+}
+
 /** Invalidate all general purpose registers.
  */
 void UnwInvalidateRegisterFile(RegData *regFile)
@@ -108,6 +113,9 @@ void UnwInitState(UnwState * const       state,   /**< Pointer to structure to f
 
     /* Invalidate all memory addresses */
     memset(state->memData.used, 0, sizeof(state->memData.used));
+    memset(state->branchData.used, 0, sizeof(state->branchData.used));
+
+    state->lastReported = 0;
 }
 
 
@@ -117,6 +125,7 @@ void UnwInitState(UnwState * const       state,   /**< Pointer to structure to f
  */
 Boolean UnwReportRetAddr(UnwState * const state, Int32 addr)
 {
+    state->lastReported = addr;
     /* Cast away const from reportData.
      *  The const is only to prevent the unw module modifying the data.
      */
index 9be12f2d6a3dcf779a7b2162439233d57ffc6081..b75b6cb370d597f29b1b4df4ef7a94760435e564 100644 (file)
@@ -18,7 +18,7 @@
  * Nested Include Files
  **************************************************************************/
 
-#include <system.h>
+#include "system.h"
 #if defined(UPGRADE_ARM_STACK_UNWIND)
 #include "unwarminder.h"
 
@@ -32,7 +32,7 @@
  * function without unwinding a stack frame.  This prevents infinite loops
  * or corrupted program memory from preventing unwinding from progressing.
  */
-#define UNW_MAX_INSTR_COUNT 100
+#define UNW_MAX_INSTR_COUNT 200
 
 /** The size of the hash used to track reads and writes to memory.
  * This should be a prime value for efficiency.
@@ -97,6 +97,11 @@ typedef struct
 }
 MemData;
 
+#define REGS_REGULAR_NUM 13
+#define REG_SP 13
+#define REG_LR 14
+#define REG_PC 15
+#define REG_SPSR 16
 
 /** Structure that is used to keep track of unwinding meta-data.
  * This data is passed between all the unwinding functions.
@@ -104,16 +109,22 @@ MemData;
 typedef struct
 {
     /** The register values and meta-data. */
-    RegData regData[16];
+    RegData regData[17];
 
     /** Memory tracking data. */
     MemData memData;
 
+    /** Branches tracking data. */
+    MemData branchData;
+
     /** Pointer to the callback functions */
     const UnwindCallbacks *cb;
 
     /** Pointer to pass to the report function. */
     const void *reportData;
+
+    /** Pointer to last reported function. */
+    Int32 lastReported;
 }
 UnwState;
 
@@ -148,6 +159,8 @@ UnwState;
  *  Function Prototypes
  **************************************************************************/
 
+Boolean UnwIsAddrThumb (Int32 pc, Int32 spsr);
+
 UnwResult UnwStartArm       (UnwState * const state);
 
 UnwResult UnwStartThumb     (UnwState * const state);
index a7fb35ef559ee55bc75a6c6e449acb430c47a814..f1eab59a2e9442986703edcb7d5b1d53f999b661 100644 (file)
@@ -17,7 +17,7 @@
  * Include Files
  **************************************************************************/
 
-#include <system.h>
+#include "system.h"
 #if defined(UPGRADE_ARM_STACK_UNWIND)
 #include <stdio.h>
 #include "unwarm.h"
@@ -102,7 +102,7 @@ UnwResult UnwStartArm(UnwState * const state)
                    state->regData[13].v, state->regData[15].v, instr);
 
         /* Check that the PC is still on Arm alignment */
-        if(state->regData[15].v & 0x3)
+        if(UnwIsAddrThumb(state->regData[REG_PC].v, state->regData[REG_SPSR].v))
         {
             UnwPrintd1("\nError: PC misalignment\n");
             return UNWIND_INCONSISTENT;
@@ -148,7 +148,7 @@ UnwResult UnwStartArm(UnwState * const state)
             }
 
             /* Determine the return mode */
-            if(state->regData[rn].v & 0x1)
+            if(UnwIsAddrThumb(state->regData[rn].v, state->regData[REG_SPSR].v))
             {
                 /* Branching to THUMB */
                 return UnwStartThumb(state);
@@ -652,7 +652,7 @@ UnwResult UnwStartArm(UnwState * const state)
                     UnwPrintd2("  Return PC=0x%x", state->regData[15].v);
 
                     /* Determine the return mode */
-                    if(state->regData[15].v & 0x1)
+                    if(UnwIsAddrThumb(state->regData[REG_PC].v, state->regData[REG_SPSR].v))
                     {
                         /* Branching to THUMB */
                         return UnwStartThumb(state);
index a1ddc6438a39c1b990b755e72d9b219bcfc8e13c..6a26349e47e676f9a370501d1445d58a4236abc3 100644 (file)
  * 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
@@ -62,21 +63,496 @@ static SignedInt16 signExtend11(Int16 value)
     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))
         {
@@ -87,7 +563,7 @@ UnwResult UnwStartThumb(UnwState * const state)
                    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;
@@ -432,6 +908,11 @@ UnwResult UnwStartThumb(UnwState * const state)
          *  ADD Rd, Hs
          *  ADD Hd, Rs
          *  ADD Hd, Hs
+         *  CMP Hd
+         *  MOV Rd
+         *  MOV Hd
+         *  BX
+         *  BLX
          */
         else if((instr & 0xfc00) == 0x4400)
         {
@@ -445,7 +926,7 @@ UnwResult UnwStartThumb(UnwState * const state)
             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;
@@ -469,7 +950,7 @@ UnwResult UnwStartThumb(UnwState * const state)
                 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;
 
@@ -487,12 +968,13 @@ UnwResult UnwStartThumb(UnwState * const state)
                         {
                             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 */
 
@@ -509,10 +991,38 @@ UnwResult UnwStartThumb(UnwState * const state)
                     {
                         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]
          */
@@ -529,9 +1039,14 @@ UnwResult UnwStartThumb(UnwState * const state)
 
             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
@@ -615,7 +1130,7 @@ UnwResult UnwStartThumb(UnwState * const state)
                          *  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);
@@ -629,6 +1144,7 @@ UnwResult UnwStartThumb(UnwState * const state)
                         {
                             return UNWIND_TRUNCATED;
                         }
+                        t = UNW_MAX_INSTR_COUNT;
 
                         /* Now have the return address */
                         UnwPrintd2(" Return PC=%x\n", state->regData[15].v);
@@ -693,18 +1209,198 @@ UnwResult UnwStartThumb(UnwState * const state)
 
             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
         {
@@ -726,12 +1422,118 @@ UnwResult UnwStartThumb(UnwState * const state)
         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 */
index fc700b3d71e56fa9ad1add181fbb0991e0a068f2..750ee3f5436c4e6e42863d09c7bd204a7d8eddea 100644 (file)
@@ -17,7 +17,7 @@
  * Include Files
  **************************************************************************/
 
-#include <system.h>
+#include "system.h"
 #if defined(UPGRADE_ARM_STACK_UNWIND)
 #include <stdio.h>
 #include <string.h>
index 755623663a8c61702bb7cefc63858e8353b3e8bd..1d078bb5cce3daef55bb47ce421fcab1ffa94ad4 100644 (file)
@@ -19,7 +19,7 @@
  * Nested Include Files
  **************************************************************************/
 
-#include <system.h>
+#include "system.h"
 #if defined(UPGRADE_ARM_STACK_UNWIND)
 
 /***************************************************************************
@@ -131,6 +131,8 @@ typedef struct UnwindCallbacksTag
      */
     Boolean (*readB)(const Int32 address, Int8  *val);
 
+    Int32 (*getProloguePC)(const Int32 current_pc);
+
 #if defined(UNW_DEBUG)
     /** Print a formatted line for debug. */
     int (*printf)(const char *format, ...);
index bb3cd9bd83bcfd5bb77d67a46609fe477e7b55a6..31796ee15921bbdf28d92720e0bac6a0e51cb40f 100644 (file)
@@ -17,7 +17,7 @@
  * Include Files
  **************************************************************************/
 
-#include <system.h>
+#include "system.h"
 #if defined(UPGRADE_ARM_STACK_UNWIND)
 #include <stdio.h>
 #include "unwarmmem.h"
index fe801a516881fb23f630a22ed4435db4d9fb89fc..4ba505e54929a0c6f9821c59eb8b170c23ad74f2 100644 (file)
@@ -18,7 +18,7 @@
  * Nested Include Files
  **************************************************************************/
 
-#include <system.h>
+#include "system.h"
 #if defined(UPGRADE_ARM_STACK_UNWIND)
 #include "unwarm.h"