|
@@ -38,6 +38,8 @@ UnwResult UnwStartThumb(UnwState * const state) {
|
38
|
38
|
|
39
|
39
|
bool found = false;
|
40
|
40
|
uint16_t t = UNW_MAX_INSTR_COUNT;
|
|
41
|
+ uint32_t lastJumpAddr = 0; // Last JUMP address, to try to detect infinite loops
|
|
42
|
+ bool loopDetected = false; // If a loop was detected
|
41
|
43
|
|
42
|
44
|
do {
|
43
|
45
|
uint16_t instr;
|
|
@@ -61,12 +63,332 @@ UnwResult UnwStartThumb(UnwState * const state) {
|
61
|
63
|
return UNWIND_INCONSISTENT;
|
62
|
64
|
}
|
63
|
65
|
|
|
66
|
+ /*
|
|
67
|
+ * Detect 32bit thumb instructions
|
|
68
|
+ */
|
|
69
|
+ if ((instr & 0xe000) == 0xe000 && (instr & 0x1800) != 0) {
|
|
70
|
+ uint16_t instr2;
|
|
71
|
+
|
|
72
|
+ /* Check next address */
|
|
73
|
+ state->regData[15].v += 2;
|
|
74
|
+
|
|
75
|
+ /* Attempt to read the 2nd part of the instruction */
|
|
76
|
+ if(!state->cb->readH(state->regData[15].v & (~0x1), &instr2)) {
|
|
77
|
+ return UNWIND_IREAD_H_FAIL;
|
|
78
|
+ }
|
|
79
|
+
|
|
80
|
+ UnwPrintd3(" %x %04x:", state->regData[15].v, instr2);
|
|
81
|
+
|
|
82
|
+ /*
|
|
83
|
+ * Load/Store multiple: Only interpret
|
|
84
|
+ * PUSH and POP
|
|
85
|
+ */
|
|
86
|
+ if ((instr & 0xfe6f) == 0xe82d) {
|
|
87
|
+ bool L = (instr & 0x10) ? true : false;
|
|
88
|
+ uint16_t rList = instr2;
|
|
89
|
+
|
|
90
|
+ if(L) {
|
|
91
|
+ uint8_t r;
|
|
92
|
+
|
|
93
|
+ /* Load from memory: POP */
|
|
94
|
+ UnwPrintd1("POP {Rlist}\n");
|
|
95
|
+
|
|
96
|
+ /* Load registers from stack */
|
|
97
|
+ for(r = 0; r < 16; r++) {
|
|
98
|
+ if(rList & (0x1 << r)) {
|
|
99
|
+
|
|
100
|
+ /* Read the word */
|
|
101
|
+ if(!UnwMemReadRegister(state, state->regData[13].v, &state->regData[r])) {
|
|
102
|
+ return UNWIND_DREAD_W_FAIL;
|
|
103
|
+ }
|
|
104
|
+
|
|
105
|
+ /* Alter the origin to be from the stack if it was valid */
|
|
106
|
+ if(M_IsOriginValid(state->regData[r].o)) {
|
|
107
|
+
|
|
108
|
+ state->regData[r].o = REG_VAL_FROM_STACK;
|
|
109
|
+
|
|
110
|
+ /* If restoring the PC */
|
|
111
|
+ if (r == 15) {
|
|
112
|
+
|
|
113
|
+ /* The bottom bit should have been set to indicate that
|
|
114
|
+ * the caller was from Thumb. This would allow return
|
|
115
|
+ * by BX for interworking APCS.
|
|
116
|
+ */
|
|
117
|
+ if((state->regData[15].v & 0x1) == 0) {
|
|
118
|
+ UnwPrintd2("Warning: Return address not to Thumb: 0x%08x\n", state->regData[15].v);
|
|
119
|
+
|
|
120
|
+ /* Pop into the PC will not switch mode */
|
|
121
|
+ return UNWIND_INCONSISTENT;
|
|
122
|
+ }
|
|
123
|
+
|
|
124
|
+ /* Store the return address */
|
|
125
|
+ if(!UnwReportRetAddr(state, state->regData[15].v)) {
|
|
126
|
+ return UNWIND_TRUNCATED;
|
|
127
|
+ }
|
|
128
|
+
|
|
129
|
+ /* Now have the return address */
|
|
130
|
+ UnwPrintd2(" Return PC=%x\n", state->regData[15].v);
|
|
131
|
+
|
|
132
|
+ /* Compensate for the auto-increment, which isn't needed here */
|
|
133
|
+ state->regData[15].v -= 2;
|
|
134
|
+
|
|
135
|
+ }
|
|
136
|
+
|
|
137
|
+ } else {
|
|
138
|
+
|
|
139
|
+ if (r == 15) {
|
|
140
|
+ /* Return address is not valid */
|
|
141
|
+ UnwPrintd1("PC popped with invalid address\n");
|
|
142
|
+ return UNWIND_FAILURE;
|
|
143
|
+ }
|
|
144
|
+ }
|
|
145
|
+
|
|
146
|
+ state->regData[13].v += 4;
|
|
147
|
+
|
|
148
|
+ UnwPrintd3(" r%d = 0x%08x\n", r, state->regData[r].v);
|
|
149
|
+ }
|
|
150
|
+ }
|
|
151
|
+ }
|
|
152
|
+ else {
|
|
153
|
+ int8_t r;
|
|
154
|
+
|
|
155
|
+ /* Store to memory: PUSH */
|
|
156
|
+ UnwPrintd1("PUSH {Rlist}");
|
|
157
|
+
|
|
158
|
+ for(r = 15; r >= 0; r--) {
|
|
159
|
+ if(rList & (0x1 << r)) {
|
|
160
|
+ UnwPrintd4("\n r%d = 0x%08x\t; %s", r, state->regData[r].v, M_Origin2Str(state->regData[r].o));
|
|
161
|
+
|
|
162
|
+ state->regData[13].v -= 4;
|
|
163
|
+
|
|
164
|
+ if(!UnwMemWriteRegister(state, state->regData[13].v, &state->regData[r])) {
|
|
165
|
+ return UNWIND_DWRITE_W_FAIL;
|
|
166
|
+ }
|
|
167
|
+ }
|
|
168
|
+ }
|
|
169
|
+ }
|
|
170
|
+ }
|
|
171
|
+ /*
|
|
172
|
+ * PUSH register
|
|
173
|
+ */
|
|
174
|
+ else if (instr == 0xf84d && (instr2 & 0x0fff) == 0x0d04) {
|
|
175
|
+ uint8_t r = instr2 >> 12;
|
|
176
|
+
|
|
177
|
+ /* Store to memory: PUSH */
|
|
178
|
+ UnwPrintd2("PUSH {R%d}\n", r);
|
|
179
|
+ UnwPrintd4("\n r%d = 0x%08x\t; %s", r, state->regData[r].v, M_Origin2Str(state->regData[r].o));
|
|
180
|
+
|
|
181
|
+ state->regData[13].v -= 4;
|
|
182
|
+
|
|
183
|
+ if(!UnwMemWriteRegister(state, state->regData[13].v, &state->regData[r])) {
|
|
184
|
+ return UNWIND_DWRITE_W_FAIL;
|
|
185
|
+ }
|
|
186
|
+ }
|
|
187
|
+ /*
|
|
188
|
+ * POP register
|
|
189
|
+ */
|
|
190
|
+ else if (instr == 0xf85d && (instr2 & 0x0fff) == 0x0b04) {
|
|
191
|
+ uint8_t r = instr2 >> 12;
|
|
192
|
+
|
|
193
|
+ /* Load from memory: POP */
|
|
194
|
+ UnwPrintd2("POP {R%d}\n", r);
|
|
195
|
+
|
|
196
|
+ /* Read the word */
|
|
197
|
+ if(!UnwMemReadRegister(state, state->regData[13].v, &state->regData[r])) {
|
|
198
|
+ return UNWIND_DREAD_W_FAIL;
|
|
199
|
+ }
|
|
200
|
+
|
|
201
|
+ /* Alter the origin to be from the stack if it was valid */
|
|
202
|
+ if(M_IsOriginValid(state->regData[r].o)) {
|
|
203
|
+
|
|
204
|
+ state->regData[r].o = REG_VAL_FROM_STACK;
|
|
205
|
+
|
|
206
|
+ /* If restoring the PC */
|
|
207
|
+ if (r == 15) {
|
|
208
|
+
|
|
209
|
+ /* The bottom bit should have been set to indicate that
|
|
210
|
+ * the caller was from Thumb. This would allow return
|
|
211
|
+ * by BX for interworking APCS.
|
|
212
|
+ */
|
|
213
|
+ if((state->regData[15].v & 0x1) == 0) {
|
|
214
|
+ UnwPrintd2("Warning: Return address not to Thumb: 0x%08x\n", state->regData[15].v);
|
|
215
|
+
|
|
216
|
+ /* Pop into the PC will not switch mode */
|
|
217
|
+ return UNWIND_INCONSISTENT;
|
|
218
|
+ }
|
|
219
|
+
|
|
220
|
+ /* Store the return address */
|
|
221
|
+ if(!UnwReportRetAddr(state, state->regData[15].v)) {
|
|
222
|
+ return UNWIND_TRUNCATED;
|
|
223
|
+ }
|
|
224
|
+
|
|
225
|
+ /* Now have the return address */
|
|
226
|
+ UnwPrintd2(" Return PC=%x\n", state->regData[15].v);
|
|
227
|
+
|
|
228
|
+ /* Compensate for the auto-increment, which isn't needed here */
|
|
229
|
+ state->regData[15].v -= 2;
|
|
230
|
+
|
|
231
|
+ }
|
|
232
|
+
|
|
233
|
+ } else {
|
|
234
|
+
|
|
235
|
+ if (r == 15) {
|
|
236
|
+ /* Return address is not valid */
|
|
237
|
+ UnwPrintd1("PC popped with invalid address\n");
|
|
238
|
+ return UNWIND_FAILURE;
|
|
239
|
+ }
|
|
240
|
+ }
|
|
241
|
+
|
|
242
|
+ state->regData[13].v += 4;
|
|
243
|
+
|
|
244
|
+ UnwPrintd3(" r%d = 0x%08x\n", r, state->regData[r].v);
|
|
245
|
+ }
|
|
246
|
+ /*
|
|
247
|
+ * Unconditional branch
|
|
248
|
+ */
|
|
249
|
+ else if ((instr & 0xf800) == 0xf000 && (instr2 & 0xd000) == 0x9000) {
|
|
250
|
+ uint32_t v;
|
|
251
|
+
|
|
252
|
+ uint8_t S = (instr & 0x400) >> 10;
|
|
253
|
+ uint16_t imm10 = (instr & 0x3ff);
|
|
254
|
+ uint8_t J1 = (instr2 & 0x2000) >> 13;
|
|
255
|
+ uint8_t J2 = (instr2 & 0x0800) >> 11;
|
|
256
|
+ uint16_t imm11 = (instr2 & 0x7ff);
|
|
257
|
+
|
|
258
|
+ uint8_t I1 = J1 ^ S ^ 1;
|
|
259
|
+ uint8_t I2 = J2 ^ S ^ 1;
|
|
260
|
+ uint32_t imm32 = (S << 24) | (I1 << 23) | (I2 << 22) |(imm10 << 12) | (imm11 << 1);
|
|
261
|
+ if (S) imm32 |= 0xfe000000;
|
|
262
|
+
|
|
263
|
+ UnwPrintd2("B %d \n", imm32);
|
|
264
|
+
|
|
265
|
+ /* Update PC */
|
|
266
|
+ state->regData[15].v += imm32;
|
|
267
|
+
|
|
268
|
+ /* Need to advance by a word to account for pre-fetch.
|
|
269
|
+ * Advance by a half word here, allowing the normal address
|
|
270
|
+ * advance to account for the other half word.
|
|
271
|
+ */
|
|
272
|
+ state->regData[15].v += 2;
|
|
273
|
+
|
|
274
|
+ /* Compute the jump address */
|
|
275
|
+ v = state->regData[15].v + 2;
|
|
276
|
+
|
|
277
|
+ /* Display PC of next instruction */
|
|
278
|
+ UnwPrintd2(" New PC=%x", v);
|
|
279
|
+
|
|
280
|
+ /* Did we detect an infinite loop ? */
|
|
281
|
+ loopDetected = lastJumpAddr == v;
|
|
282
|
+
|
|
283
|
+ /* Remember the last address we jumped to */
|
|
284
|
+ lastJumpAddr = v;
|
|
285
|
+ }
|
|
286
|
+
|
|
287
|
+ /*
|
|
288
|
+ * Branch with link
|
|
289
|
+ */
|
|
290
|
+ else if ((instr & 0xf800) == 0xf000 && (instr2 & 0xd000) == 0xd000) {
|
|
291
|
+
|
|
292
|
+ uint8_t S = (instr & 0x400) >> 10;
|
|
293
|
+ uint16_t imm10 = (instr & 0x3ff);
|
|
294
|
+ uint8_t J1 = (instr2 & 0x2000) >> 13;
|
|
295
|
+ uint8_t J2 = (instr2 & 0x0800) >> 11;
|
|
296
|
+ uint16_t imm11 = (instr2 & 0x7ff);
|
|
297
|
+
|
|
298
|
+ uint8_t I1 = J1 ^ S ^ 1;
|
|
299
|
+ uint8_t I2 = J2 ^ S ^ 1;
|
|
300
|
+ uint32_t imm32 = (S << 24) | (I1 << 23) | (I2 << 22) |(imm10 << 12) | (imm11 << 1);
|
|
301
|
+ if (S) imm32 |= 0xfe000000;
|
|
302
|
+
|
|
303
|
+ UnwPrintd2("BL %d \n", imm32);
|
|
304
|
+
|
|
305
|
+ /* Never taken, as we are unwinding the stack */
|
|
306
|
+ if (0) {
|
|
307
|
+
|
|
308
|
+ /* Store return address in LR register */
|
|
309
|
+ state->regData[14].v = state->regData[15].v + 2;
|
|
310
|
+ state->regData[14].o = REG_VAL_FROM_CONST;
|
|
311
|
+
|
|
312
|
+ /* Update PC */
|
|
313
|
+ state->regData[15].v += imm32;
|
|
314
|
+
|
|
315
|
+ /* Need to advance by a word to account for pre-fetch.
|
|
316
|
+ * Advance by a half word here, allowing the normal address
|
|
317
|
+ * advance to account for the other half word.
|
|
318
|
+ */
|
|
319
|
+ state->regData[15].v += 2;
|
|
320
|
+
|
|
321
|
+ /* Display PC of next instruction */
|
|
322
|
+ UnwPrintd2(" Return PC=%x", state->regData[15].v);
|
|
323
|
+
|
|
324
|
+ /* Report the return address, including mode bit */
|
|
325
|
+ if(!UnwReportRetAddr(state, state->regData[15].v)) {
|
|
326
|
+ return UNWIND_TRUNCATED;
|
|
327
|
+ }
|
|
328
|
+
|
|
329
|
+ /* Determine the new mode */
|
|
330
|
+ if(state->regData[15].v & 0x1) {
|
|
331
|
+ /* Branching to THUMB */
|
|
332
|
+
|
|
333
|
+ /* Account for the auto-increment which isn't needed */
|
|
334
|
+ state->regData[15].v -= 2;
|
|
335
|
+ }
|
|
336
|
+ else {
|
|
337
|
+ /* Branch to ARM */
|
|
338
|
+ return UnwStartArm(state);
|
|
339
|
+ }
|
|
340
|
+ }
|
|
341
|
+ }
|
|
342
|
+
|
|
343
|
+ /*
|
|
344
|
+ * Conditional branches. Usually not taken, unless infinite loop is detected
|
|
345
|
+ */
|
|
346
|
+ else if ((instr & 0xf800) == 0xf000 && (instr2 & 0xd000) == 0x8000) {
|
|
347
|
+
|
|
348
|
+ uint8_t S = (instr & 0x400) >> 10;
|
|
349
|
+ uint16_t imm6 = (instr & 0x3f);
|
|
350
|
+ uint8_t J1 = (instr2 & 0x2000) >> 13;
|
|
351
|
+ uint8_t J2 = (instr2 & 0x0800) >> 11;
|
|
352
|
+ uint16_t imm11 = (instr2 & 0x7ff);
|
|
353
|
+
|
|
354
|
+ uint8_t I1 = J1 ^ S ^ 1;
|
|
355
|
+ uint8_t I2 = J2 ^ S ^ 1;
|
|
356
|
+ uint32_t imm32 = (S << 20) | (I1 << 19) | (I2 << 18) |(imm6 << 12) | (imm11 << 1);
|
|
357
|
+ if (S) imm32 |= 0xffe00000;
|
|
358
|
+
|
|
359
|
+ UnwPrintd2("Bcond %d\n", imm32);
|
|
360
|
+
|
|
361
|
+ /* Take the jump only if a loop is detected */
|
|
362
|
+ if (loopDetected) {
|
|
363
|
+
|
|
364
|
+ /* Update PC */
|
|
365
|
+ state->regData[15].v += imm32;
|
|
366
|
+
|
|
367
|
+ /* Need to advance by a word to account for pre-fetch.
|
|
368
|
+ * Advance by a half word here, allowing the normal address
|
|
369
|
+ * advance to account for the other half word.
|
|
370
|
+ */
|
|
371
|
+ state->regData[15].v += 2;
|
|
372
|
+
|
|
373
|
+ /* Display PC of next instruction */
|
|
374
|
+ UnwPrintd2(" New PC=%x", state->regData[15].v + 2);
|
|
375
|
+ }
|
|
376
|
+ }
|
|
377
|
+ else {
|
|
378
|
+ UnwPrintd1("???? (32)");
|
|
379
|
+
|
|
380
|
+ /* Unknown/undecoded. May alter some register, so invalidate file */
|
|
381
|
+ UnwInvalidateRegisterFile(state->regData);
|
|
382
|
+ }
|
|
383
|
+ /* End of thumb 32bit code */
|
|
384
|
+
|
|
385
|
+ }
|
64
|
386
|
/* Format 1: Move shifted register
|
65
|
387
|
* LSL Rd, Rs, #Offset5
|
66
|
388
|
* LSR Rd, Rs, #Offset5
|
67
|
389
|
* ASR Rd, Rs, #Offset5
|
68
|
390
|
*/
|
69
|
|
- if((instr & 0xe000) == 0x0000 && (instr & 0x1800) != 0x1800) {
|
|
391
|
+ else if((instr & 0xe000) == 0x0000 && (instr & 0x1800) != 0x1800) {
|
70
|
392
|
bool signExtend;
|
71
|
393
|
uint8_t op = (instr & 0x1800) >> 11;
|
72
|
394
|
uint8_t offset5 = (instr & 0x07c0) >> 6;
|
|
@@ -355,8 +677,8 @@ UnwResult UnwStartThumb(UnwState * const state) {
|
355
|
677
|
}
|
356
|
678
|
/* Format 5: Hi register operations/branch exchange
|
357
|
679
|
* ADD Rd, Hs
|
358
|
|
- * ADD Hd, Rs
|
359
|
|
- * ADD Hd, Hs
|
|
680
|
+ * CMP Hd, Rs
|
|
681
|
+ * MOV Hd, Hs
|
360
|
682
|
*/
|
361
|
683
|
else if((instr & 0xfc00) == 0x4400) {
|
362
|
684
|
uint8_t op = (instr & 0x0300) >> 8;
|
|
@@ -371,11 +693,6 @@ UnwResult UnwStartThumb(UnwState * const state) {
|
371
|
693
|
if(h1)
|
372
|
694
|
rhd += 8;
|
373
|
695
|
|
374
|
|
- if(op != 3 && !h1 && !h2) {
|
375
|
|
- UnwPrintd1("\nError: h1 or h2 must be set for ADD, CMP or MOV\n");
|
376
|
|
- return UNWIND_ILLEGAL_INSTR;
|
377
|
|
- }
|
378
|
|
-
|
379
|
696
|
switch(op) {
|
380
|
697
|
case 0: /* ADD */
|
381
|
698
|
UnwPrintd5("ADD r%d, r%d\t; r%d %s", rhd, rhs, rhs, M_Origin2Str(state->regData[rhs].o));
|
|
@@ -407,6 +724,10 @@ UnwResult UnwStartThumb(UnwState * const state) {
|
407
|
724
|
return UNWIND_TRUNCATED;
|
408
|
725
|
}
|
409
|
726
|
|
|
727
|
+ /* Store return address in LR register */
|
|
728
|
+ state->regData[14].v = state->regData[15].v + 2;
|
|
729
|
+ state->regData[14].o = REG_VAL_FROM_CONST;
|
|
730
|
+
|
410
|
731
|
/* Update the PC */
|
411
|
732
|
state->regData[15].v = state->regData[rhs].v;
|
412
|
733
|
|
|
@@ -570,10 +891,42 @@ UnwResult UnwStartThumb(UnwState * const state) {
|
570
|
891
|
}
|
571
|
892
|
}
|
572
|
893
|
}
|
|
894
|
+
|
|
895
|
+ /*
|
|
896
|
+ * Conditional branches
|
|
897
|
+ * Bcond
|
|
898
|
+ */
|
|
899
|
+ else if((instr & 0xf000) == 0xd000) {
|
|
900
|
+ int32_t branchValue = (instr & 0xff);
|
|
901
|
+ if (branchValue & 0x80) branchValue |= 0xffffff00;
|
|
902
|
+
|
|
903
|
+ /* Branch distance is twice that specified in the instruction. */
|
|
904
|
+ branchValue *= 2;
|
|
905
|
+
|
|
906
|
+ UnwPrintd2("Bcond %d \n", branchValue);
|
|
907
|
+
|
|
908
|
+ /* Only take the branch if a loop was detected */
|
|
909
|
+ if (loopDetected) {
|
|
910
|
+
|
|
911
|
+ /* Update PC */
|
|
912
|
+ state->regData[15].v += branchValue;
|
|
913
|
+
|
|
914
|
+ /* Need to advance by a word to account for pre-fetch.
|
|
915
|
+ * Advance by a half word here, allowing the normal address
|
|
916
|
+ * advance to account for the other half word.
|
|
917
|
+ */
|
|
918
|
+ state->regData[15].v += 2;
|
|
919
|
+
|
|
920
|
+ /* Display PC of next instruction */
|
|
921
|
+ UnwPrintd2(" New PC=%x", state->regData[15].v + 2);
|
|
922
|
+ }
|
|
923
|
+ }
|
|
924
|
+
|
573
|
925
|
/* Format 18: unconditional branch
|
574
|
926
|
* B label
|
575
|
927
|
*/
|
576
|
928
|
else if((instr & 0xf800) == 0xe000) {
|
|
929
|
+ uint32_t v;
|
577
|
930
|
int16_t branchValue = signExtend11(instr & 0x07ff);
|
578
|
931
|
|
579
|
932
|
/* Branch distance is twice that specified in the instruction. */
|
|
@@ -590,9 +943,17 @@ UnwResult UnwStartThumb(UnwState * const state) {
|
590
|
943
|
*/
|
591
|
944
|
state->regData[15].v += 2;
|
592
|
945
|
|
|
946
|
+ /* Compute the jump address */
|
|
947
|
+ v = state->regData[15].v + 2;
|
|
948
|
+
|
593
|
949
|
/* Display PC of next instruction */
|
594
|
|
- UnwPrintd2(" New PC=%x", state->regData[15].v + 2);
|
|
950
|
+ UnwPrintd2(" New PC=%x", v);
|
|
951
|
+
|
|
952
|
+ /* Did we detect an infinite loop ? */
|
|
953
|
+ loopDetected = lastJumpAddr == v;
|
595
|
954
|
|
|
955
|
+ /* Remember the last address we jumped to */
|
|
956
|
+ lastJumpAddr = v;
|
596
|
957
|
}
|
597
|
958
|
else {
|
598
|
959
|
UnwPrintd1("????");
|