2 * Debugger ARM specific functions
4 * Copyright 2000-2003 Marcus Meissner
6 * 2010, 2011 André Hentschel
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #if defined(__arm__) && !defined(__ARMEB__)
28 * Switch to disassemble Thumb code.
30 static BOOL db_disasm_thumb = FALSE;
33 * Flag to indicate whether we need to display instruction,
34 * or whether we just need to know the address of the next
37 static BOOL db_display = FALSE;
39 #define ARM_INSN_SIZE 4
40 #define THUMB_INSN_SIZE 2
42 #define ROR32(n, r) (((n) >> (r)) | ((n) << (32 - (r))))
44 #define get_cond(ins) tbl_cond[(ins >> 28) & 0x0f]
45 #define get_nibble(ins, num) ((ins >> (num * 4)) & 0x0f)
47 static char const tbl_addrmode[][3] = {
48 "da", "ia", "db", "ib"
51 static char const tbl_cond[][3] = {
52 "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "", ""
55 static char const tbl_dataops[][4] = {
56 "and", "eor", "sub", "rsb", "add", "adc", "sbc", "rsc", "tst", "teq", "cmp", "cmn", "orr",
60 static char const tbl_hiops_t[][4] = {
61 "add", "cmp", "mov", "bx"
64 static char const tbl_immops_t[][4] = {
65 "mov", "cmp", "add", "sub"
68 static UINT db_get_inst(void* addr, int size)
73 if (dbg_read_memory(addr, buffer, size))
78 result = *(UINT*)buffer;
81 result = *(WORD*)buffer;
88 static UINT arm_disasm_branch(UINT inst)
90 short link = (inst >> 24) & 0x01;
91 int offset = (inst << 2) & 0x03ffffff;
93 if (offset & 0x02000000) offset |= 0xfc000000;
96 dbg_printf("\n\tb%s%s\t#%d/0x%08x", link ? "l" : "", get_cond(inst), offset, offset);
100 static UINT arm_disasm_dataprocessing(UINT inst)
102 short condcodes = (inst >> 20) & 0x01;
103 short opcode = (inst >> 21) & 0x0f;
104 short immediate = (inst >> 25) & 0x01;
105 short no_op1 = (opcode & 0x0d) == 0x0d;
108 if (get_nibble(inst, 3) == 15 /* r15 */ && condcodes == 0 &&
109 opcode >= 8 /* tst */ && opcode <= 11 /* cmn */)
111 dbg_printf("\n\tnop");
115 dbg_printf("\n\t%s%s%s", tbl_dataops[opcode], condcodes ? "s" : "", get_cond(inst));
119 dbg_printf("\tr%u, #%u", get_nibble(inst, 3),
120 ROR32(inst & 0xff, 2 * get_nibble(inst, 2)));
122 dbg_printf("\tr%u, r%u", get_nibble(inst, 3), get_nibble(inst, 0));
127 dbg_printf("\tr%u, r%u, #%u", get_nibble(inst, 3), get_nibble(inst, 4),
128 ROR32(inst & 0xff, 2 * get_nibble(inst, 2)));
130 dbg_printf("\tr%u, r%u, r%u", get_nibble(inst, 3), get_nibble(inst, 4),
131 get_nibble(inst, 0));
136 static UINT arm_disasm_singletrans(UINT inst)
138 short load = (inst >> 20) & 0x01;
139 short writeback = (inst >> 21) & 0x01;
140 short byte = (inst >> 22) & 0x01;
141 short direction = (inst >> 23) & 0x01;
142 /* FIXME: what to do with bit 24 (indexing) */
143 short immediate = !((inst >> 25) & 0x01);
144 short offset = inst & 0x0fff;
146 if (!direction) offset *= -1;
148 dbg_printf("\n\t%s%s%s%s", load ? "ldr" : "str", byte ? "b" : "", writeback ? "t" : "",
151 dbg_printf("\tr%u, [r%u, #%d]", get_nibble(inst, 3), get_nibble(inst, 4), offset);
153 dbg_printf("\tr%u, r%u, r%u", get_nibble(inst, 3), get_nibble(inst, 4),
154 get_nibble(inst, 0));
158 static UINT arm_disasm_halfwordtrans(UINT inst)
160 short halfword = (inst >> 5) & 0x01;
161 short sign = (inst >> 6) & 0x01;
162 short load = (inst >> 20) & 0x01;
163 short writeback = (inst >> 21) & 0x01;
164 short immediate = (inst >> 22) & 0x01;
165 short direction = (inst >> 23) & 0x01;
166 /* FIXME: what to do with bit 24 (indexing) */
167 short offset = ((inst >> 4) & 0xf0) + (inst & 0x0f);
169 if (!direction) offset *= -1;
171 dbg_printf("\n\t%s%s%s%s%s", load ? "ldr" : "str", sign ? "s" : "",
172 halfword ? "h" : (sign ? "b" : ""), writeback ? "t" : "", get_cond(inst));
174 dbg_printf("\tr%u, r%u, #%d", get_nibble(inst, 3), get_nibble(inst, 4), offset);
176 dbg_printf("\tr%u, r%u, r%u", get_nibble(inst, 3), get_nibble(inst, 4), get_nibble(inst, 0));
180 static UINT arm_disasm_blocktrans(UINT inst)
182 short load = (inst >> 20) & 0x01;
183 short writeback = (inst >> 21) & 0x01;
184 short psr = (inst >> 22) & 0x01;
185 short addrmode = (inst >> 23) & 0x03;
195 dbg_printf("\n\t%s%s%s\tr%u%s, {", load ? "ldm" : "stm", tbl_addrmode[addrmode], get_cond(inst),
196 get_nibble(inst, 4), writeback ? "!" : "");
200 if (i == last) dbg_printf("r%u", i);
201 else dbg_printf("r%u, ", i);
203 dbg_printf("}%s", psr ? "^" : "");
207 static UINT arm_disasm_swi(UINT inst)
209 UINT comment = inst & 0x00ffffff;
210 dbg_printf("\n\tswi%s\t#%d/0x%08x", get_cond(inst), comment, comment);
214 static UINT arm_disasm_coproctrans(UINT inst)
216 WORD CRm = inst & 0x0f;
217 WORD CP = (inst >> 5) & 0x07;
218 WORD CPnum = (inst >> 8) & 0x0f;
219 WORD CRn = (inst >> 16) & 0x0f;
220 WORD load = (inst >> 20) & 0x01;
221 WORD CP_Opc = (inst >> 21) & 0x07;
223 dbg_printf("\n\t%s%s\t%u, %u, r%u, cr%u, cr%u, {%u}", load ? "mrc" : "mcr", get_cond(inst), CPnum,
224 CP, get_nibble(inst, 3), CRn, CRm, CP_Opc);
228 static UINT arm_disasm_coprocdataop(UINT inst)
230 WORD CRm = inst & 0x0f;
231 WORD CP = (inst >> 5) & 0x07;
232 WORD CPnum = (inst >> 8) & 0x0f;
233 WORD CRd = (inst >> 12) & 0x0f;
234 WORD CRn = (inst >> 16) & 0x0f;
235 WORD CP_Opc = (inst >> 20) & 0x0f;
237 dbg_printf("\n\tcdp%s\t%u, %u, cr%u, cr%u, cr%u, {%u}", get_cond(inst),
238 CPnum, CP, CRd, CRn, CRm, CP_Opc);
242 static UINT arm_disasm_coprocdatatrans(UINT inst)
244 WORD CPnum = (inst >> 8) & 0x0f;
245 WORD CRd = (inst >> 12) & 0x0f;
246 WORD load = (inst >> 20) & 0x01;
247 /* FIXME: what to do with bit 21 (writeback) */
248 WORD translen = (inst >> 22) & 0x01;
249 WORD direction = (inst >> 23) & 0x01;
250 /* FIXME: what to do with bit 24 (indexing) */
251 short offset = (inst & 0xff) << 2;
253 if (!direction) offset *= -1;
255 dbg_printf("\n\t%s%s%s", load ? "ldc" : "stc", translen ? "l" : "", get_cond(inst));
256 dbg_printf("\t%u, cr%u, [r%u, #%d]", CPnum, CRd, get_nibble(inst, 4), offset);
260 static WORD thumb_disasm_hireg(WORD inst)
262 short dst = inst & 0x07;
263 short src = (inst >> 3) & 0x07;
264 short h2 = (inst >> 6) & 0x01;
265 short h1 = (inst >> 7) & 0x01;
266 short op = (inst >> 8) & 0x03;
272 dbg_printf("\n\tb%sx\tr%u", h1?"l":"", src);
274 dbg_printf("\n\t%s\tr%u, r%u", tbl_hiops_t[op], dst, src);
279 static WORD thumb_disasm_blocktrans(WORD inst)
281 short lrpc = (inst >> 8) & 0x01;
282 short load = (inst >> 11) & 0x01;
287 if ((inst>>i) & 1) break;
290 dbg_printf("\n\t%s\t{", load ? "pop" : "push");
295 if (i == last) dbg_printf("r%u", i);
296 else dbg_printf("r%u, ", i);
299 dbg_printf(", %s", load ? "pc" : "lr");
305 static WORD thumb_disasm_swi(WORD inst)
307 WORD comment = inst & 0x00ff;
308 dbg_printf("\n\tswi\t#%d", comment);
312 static WORD thumb_disasm_nop(WORD inst)
314 dbg_printf("\n\tnop");
318 static WORD thumb_disasm_ldrpcrel(WORD inst)
320 WORD offset = (inst & 0xff) << 2;
321 dbg_printf("\n\tldr\tr%u, [pc, #%u]", (inst >> 8) & 0x07, offset);
325 static WORD thumb_disasm_ldrsprel(WORD inst)
327 WORD offset = (inst & 0xff) << 2;
328 dbg_printf("\n\t%s\tr%u, [sp, #%u]", (inst & 0x0800)?"ldr":"str", (inst >> 8) & 0x07, offset);
332 static WORD thumb_disasm_ldrimm(WORD inst)
334 WORD offset = (inst & 0x07c0) >> 6;
335 dbg_printf("\n\t%s%s\tr%u, [r%u, #%u]", (inst & 0x0800)?"ldr":"str", (inst & 0x1000)?"b":"",
336 inst & 0x07, (inst >> 3) & 0x07, (inst & 0x1000)?offset:(offset << 2));
340 static WORD thumb_disasm_immop(WORD inst)
342 WORD op = (inst >> 11) & 0x03;
343 dbg_printf("\n\t%s\tr%u, #%u", tbl_immops_t[op], (inst >> 8) & 0x07, inst & 0xff);
354 static const struct inst_arm tbl_arm[] = {
355 { 0x0e000000, 0x0a000000, arm_disasm_branch },
356 { 0x0c000000, 0x00000000, arm_disasm_dataprocessing },
357 { 0x0c000000, 0x04000000, arm_disasm_singletrans },
358 { 0x0e000090, 0x00000090, arm_disasm_halfwordtrans },
359 { 0x0e000000, 0x08000000, arm_disasm_blocktrans },
360 { 0x0f000000, 0x0f000000, arm_disasm_swi },
361 { 0x0f000010, 0x0e000010, arm_disasm_coproctrans },
362 { 0x0f000010, 0x0e000000, arm_disasm_coprocdataop },
363 { 0x0e000000, 0x0c000000, arm_disasm_coprocdatatrans },
364 { 0x00000000, 0x00000000, NULL }
374 static const struct inst_thumb16 tbl_thumb16[] = {
375 { 0xfc00, 0x4400, thumb_disasm_hireg },
376 { 0xf600, 0xb400, thumb_disasm_blocktrans },
377 { 0xf800, 0x4800, thumb_disasm_ldrpcrel },
378 { 0xf000, 0x9000, thumb_disasm_ldrsprel },
379 { 0xe000, 0x6000, thumb_disasm_ldrimm },
380 { 0xe000, 0x2000, thumb_disasm_immop },
381 { 0xff00, 0xdf00, thumb_disasm_swi },
382 { 0xff00, 0xbf00, thumb_disasm_nop },
383 { 0x0000, 0x0000, NULL }
386 /***********************************************************************
389 * Disassemble instruction at 'addr'. addr is changed to point to the
390 * start of the next instruction.
392 void be_arm_disasm_one_insn(ADDRESS64 *addr, int display)
394 struct inst_arm *a_ptr = (struct inst_arm *)&tbl_arm;
395 struct inst_thumb16 *t_ptr = (struct inst_thumb16 *)&tbl_thumb16;
404 if (!memory_get_register(CV_ARM_CPSR, &pval, tmp, sizeof(tmp)))
405 dbg_printf("\n\tmemory_get_register failed: %s\n",tmp);
407 db_disasm_thumb=(*pval & 0x20)?TRUE:FALSE;
409 if (db_disasm_thumb) size = THUMB_INSN_SIZE;
410 else size = ARM_INSN_SIZE;
412 db_display = display;
413 inst = db_get_inst( memory_to_linear_addr(addr), size );
415 if (!db_disasm_thumb)
417 while (a_ptr->func) {
418 if ((inst & a_ptr->mask) == a_ptr->pattern) {
426 dbg_printf("\n\tUnknown Instruction: %08x\n", inst);
427 addr->Offset += size;
432 if (!a_ptr->func(inst))
435 addr->Offset += size;
443 while (t_ptr->func) {
444 if ((tinst & t_ptr->mask) == t_ptr->pattern) {
452 dbg_printf("\n\tUnknown Instruction: %08x\n", tinst);
453 addr->Offset += size;
458 if (!t_ptr->func(tinst))
461 addr->Offset += size;
468 static unsigned be_arm_get_addr(HANDLE hThread, const CONTEXT* ctx,
469 enum be_cpu_addr bca, ADDRESS64* addr)
474 return be_cpu_build_addr(hThread, ctx, addr, 0, ctx->Pc);
475 case be_cpu_addr_stack:
476 return be_cpu_build_addr(hThread, ctx, addr, 0, ctx->Sp);
477 case be_cpu_addr_frame:
478 return be_cpu_build_addr(hThread, ctx, addr, 0, ctx->Fp);
483 static unsigned be_arm_get_register_info(int regno, enum be_cpu_addr* kind)
487 case CV_ARM_PC: *kind = be_cpu_addr_pc; return TRUE;
488 case CV_ARM_R0 + 11: *kind = be_cpu_addr_frame; return TRUE;
489 case CV_ARM_SP: *kind = be_cpu_addr_stack; return TRUE;
494 static void be_arm_single_step(CONTEXT* ctx, unsigned enable)
496 dbg_printf("be_arm_single_step: not done\n");
499 static void be_arm_print_context(HANDLE hThread, const CONTEXT* ctx, int all_regs)
501 static const char condflags[] = "NZCV";
505 switch (ctx->Cpsr & 0x1F)
507 case 0: strcpy(buf, "User26"); break;
508 case 1: strcpy(buf, "FIQ26"); break;
509 case 2: strcpy(buf, "IRQ26"); break;
510 case 3: strcpy(buf, "SVC26"); break;
511 case 16: strcpy(buf, "User"); break;
512 case 17: strcpy(buf, "FIQ"); break;
513 case 18: strcpy(buf, "IRQ"); break;
514 case 19: strcpy(buf, "SVC"); break;
515 case 23: strcpy(buf, "ABT"); break;
516 case 27: strcpy(buf, "UND"); break;
517 default: strcpy(buf, "UNKNWN"); break;
520 dbg_printf("Register dump:\n");
521 dbg_printf("%s %s Mode\n", (ctx->Cpsr & 0x20) ? "Thumb" : "ARM", buf);
523 strcpy(buf, condflags);
524 for (i = 0; buf[i]; i++)
525 if (!((ctx->Cpsr >> 26) & (1 << (sizeof(condflags) - i))))
528 dbg_printf(" Pc:%04x Sp:%04x Lr:%04x Cpsr:%04x(%s)\n",
529 ctx->Pc, ctx->Sp, ctx->Lr, ctx->Cpsr, buf);
530 dbg_printf(" r0:%04x r1:%04x r2:%04x r3:%04x\n",
531 ctx->R0, ctx->R1, ctx->R2, ctx->R3);
532 dbg_printf(" r4:%04x r5:%04x r6:%04x r7:%04x r8:%04x\n",
533 ctx->R4, ctx->R5, ctx->R6, ctx->R7, ctx->R8 );
534 dbg_printf(" r9:%04x r10:%04x Fp:%04x Ip:%04x\n",
535 ctx->R9, ctx->R10, ctx->Fp, ctx->Ip );
537 if (all_regs) dbg_printf( "Floating point ARM dump not implemented\n" );
540 static void be_arm_print_segment_info(HANDLE hThread, const CONTEXT* ctx)
544 static struct dbg_internal_var be_arm_ctx[] =
546 {CV_ARM_R0 + 0, "r0", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R0), dbg_itype_unsigned_int},
547 {CV_ARM_R0 + 1, "r1", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R1), dbg_itype_unsigned_int},
548 {CV_ARM_R0 + 2, "r2", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R2), dbg_itype_unsigned_int},
549 {CV_ARM_R0 + 3, "r3", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R3), dbg_itype_unsigned_int},
550 {CV_ARM_R0 + 4, "r4", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R4), dbg_itype_unsigned_int},
551 {CV_ARM_R0 + 5, "r5", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R5), dbg_itype_unsigned_int},
552 {CV_ARM_R0 + 6, "r6", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R6), dbg_itype_unsigned_int},
553 {CV_ARM_R0 + 7, "r7", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R7), dbg_itype_unsigned_int},
554 {CV_ARM_R0 + 8, "r8", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R8), dbg_itype_unsigned_int},
555 {CV_ARM_R0 + 9, "r9", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R9), dbg_itype_unsigned_int},
556 {CV_ARM_R0 + 10, "r10", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R10), dbg_itype_unsigned_int},
557 {CV_ARM_R0 + 11, "r11", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Fp), dbg_itype_unsigned_int},
558 {CV_ARM_R0 + 12, "r12", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Ip), dbg_itype_unsigned_int},
559 {CV_ARM_SP, "sp", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Sp), dbg_itype_unsigned_int},
560 {CV_ARM_LR, "lr", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Lr), dbg_itype_unsigned_int},
561 {CV_ARM_PC, "pc", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Pc), dbg_itype_unsigned_int},
562 {CV_ARM_CPSR, "cpsr", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Cpsr), dbg_itype_unsigned_int},
563 {0, NULL, 0, dbg_itype_none}
566 static unsigned be_arm_is_step_over_insn(const void* insn)
568 dbg_printf("be_arm_is_step_over_insn: not done\n");
572 static unsigned be_arm_is_function_return(const void* insn)
574 dbg_printf("be_arm_is_function_return: not done\n");
578 static unsigned be_arm_is_break_insn(const void* insn)
580 dbg_printf("be_arm_is_break_insn: not done\n");
584 static unsigned be_arm_is_func_call(const void* insn, ADDRESS64* callee)
589 static unsigned be_arm_is_jump(const void* insn, ADDRESS64* jumpee)
594 static unsigned be_arm_insert_Xpoint(HANDLE hProcess, const struct be_process_io* pio,
595 CONTEXT* ctx, enum be_xpoint_type type,
596 void* addr, unsigned long* val, unsigned size)
602 case be_xpoint_break:
604 if (!pio->read(hProcess, addr, val, 4, &sz) || sz != 4) return 0;
606 dbg_printf("Unknown/unsupported bp type %c\n", type);
612 static unsigned be_arm_remove_Xpoint(HANDLE hProcess, const struct be_process_io* pio,
613 CONTEXT* ctx, enum be_xpoint_type type,
614 void* addr, unsigned long val, unsigned size)
620 case be_xpoint_break:
622 if (!pio->write(hProcess, addr, &val, 4, &sz) || sz == 4) return 0;
625 dbg_printf("Unknown/unsupported bp type %c\n", type);
631 static unsigned be_arm_is_watchpoint_set(const CONTEXT* ctx, unsigned idx)
633 dbg_printf("be_arm_is_watchpoint_set: not done\n");
637 static void be_arm_clear_watchpoint(CONTEXT* ctx, unsigned idx)
639 dbg_printf("be_arm_clear_watchpoint: not done\n");
642 static int be_arm_adjust_pc_for_break(CONTEXT* ctx, BOOL way)
644 INT step = (ctx->Cpsr & 0x20) ? 2 : 4;
655 static int be_arm_fetch_integer(const struct dbg_lvalue* lvalue, unsigned size,
656 unsigned ext_sign, LONGLONG* ret)
658 if (size != 1 && size != 2 && size != 4 && size != 8) return FALSE;
660 memset(ret, 0, sizeof(*ret)); /* clear unread bytes */
661 /* FIXME: this assumes that debuggee and debugger use the same
662 * integral representation
664 if (!memory_read_value(lvalue, size, ret)) return FALSE;
666 /* propagate sign information */
667 if (ext_sign && size < 8 && (*ret >> (size * 8 - 1)) != 0)
670 *ret |= neg << (size * 8);
675 static int be_arm_fetch_float(const struct dbg_lvalue* lvalue, unsigned size,
678 char tmp[sizeof(long double)];
680 /* FIXME: this assumes that debuggee and debugger use the same
681 * representation for reals
683 if (!memory_read_value(lvalue, size, tmp)) return FALSE;
687 case sizeof(float): *ret = *(float*)tmp; break;
688 case sizeof(double): *ret = *(double*)tmp; break;
689 default: return FALSE;
694 static int be_arm_store_integer(const struct dbg_lvalue* lvalue, unsigned size,
695 unsigned is_signed, LONGLONG val)
697 /* this is simple if we're on a little endian CPU */
698 return memory_write_value(lvalue, size, &val);
701 struct backend_cpu be_arm =
703 IMAGE_FILE_MACHINE_ARMV7,
708 be_arm_get_register_info,
710 be_arm_print_context,
711 be_arm_print_segment_info,
713 be_arm_is_step_over_insn,
714 be_arm_is_function_return,
715 be_arm_is_break_insn,
718 be_arm_disasm_one_insn,
719 be_arm_insert_Xpoint,
720 be_arm_remove_Xpoint,
721 be_arm_is_watchpoint_set,
722 be_arm_clear_watchpoint,
723 be_arm_adjust_pc_for_break,
724 be_arm_fetch_integer,
726 be_arm_store_integer,