David Brownell <david-b@pacbell.net>:
Initial support for disassembling Thumb2 code. This works only for Cortex-M3 cores so far. Eventually other cores will also need Thumb2 support ... but they don't yet support any kind of disassembly. - Update the 16-bit Thumb decoder: * Understand CPS, REV*, SETEND, {U,S}XT{B,H} opcodes added by ARMv6. (It already seems to treat CPY as MOV.) * Understand CB, CBNZ, WFI, IT, and other opcodes added by in Thumb2. - A new Thumb2 instruction decode routine is provided. * This has a different signature: pass the target, not the instruction, so it can fetch a second halfword when needed. The instruction size is likewise returned to the caller. * 32-bit instructions are recognized but not yet decoded. - Start using the current "UAL" syntax in some cases. "SWI" is renamed as "SVC"; "LDMIA" as "LDM"; "STMIA" as "STM". - Define a new "cortex_m3 disassemble addr count" command to give access to this disassembly. Sanity checked against "objdump -d" output; a bunch of the new instructions checked out fine. git-svn-id: svn://svn.berlios.de/openocd/trunk@2530 b42882b7-edfa-0310-969c-e2dbd0fdcd60
This commit is contained in:
parent
2ff59c9aaf
commit
309870e414
|
@ -5003,6 +5003,11 @@ If @var{value} is defined, first assigns that.
|
|||
@subsection Cortex-M3 specific commands
|
||||
@cindex Cortex-M3
|
||||
|
||||
@deffn Command {cortex_m3 disassemble} address count
|
||||
@cindex disassemble
|
||||
Disassembles @var{count} Thumb2 instructions starting at @var{address}.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {cortex_m3 maskisr} (@option{on}|@option{off})
|
||||
Control masking (disabling) interrupts during target step/resume.
|
||||
@end deffn
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "target.h"
|
||||
#include "arm_disassembler.h"
|
||||
#include "log.h"
|
||||
|
||||
|
@ -63,7 +64,9 @@ int evaluate_swi(uint32_t opcode, uint32_t address, arm_instruction_t *instructi
|
|||
{
|
||||
instruction->type = ARM_SWI;
|
||||
|
||||
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSWI 0x%6.6" PRIx32 "", address, opcode, (opcode & 0xffffff));
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSVC %#6.6" PRIx32,
|
||||
address, opcode, (opcode & 0xffffff));
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
@ -614,7 +617,8 @@ int evaluate_ldm_stm(uint32_t opcode, uint32_t address, arm_instruction_t *instr
|
|||
if (U)
|
||||
{
|
||||
instruction->info.load_store_multiple.addressing_mode = 0;
|
||||
addressing_mode = "IA";
|
||||
/* "IA" is the default in UAL syntax */
|
||||
addressing_mode = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1180,6 +1184,7 @@ int arm_evaluate_opcode(uint32_t opcode, uint32_t address, arm_instruction_t *in
|
|||
/* clear fields, to avoid confusion */
|
||||
memset(instruction, 0, sizeof(arm_instruction_t));
|
||||
instruction->opcode = opcode;
|
||||
instruction->instruction_size = 4;
|
||||
|
||||
/* catch opcodes with condition field [31:28] = b1111 */
|
||||
if ((opcode & 0xf0000000) == 0xf0000000)
|
||||
|
@ -1356,7 +1361,11 @@ int evaluate_b_bl_blx_thumb(uint16_t opcode, uint32_t address, arm_instruction_t
|
|||
mnemonic = "BL";
|
||||
break;
|
||||
}
|
||||
/* TODO: deals correctly with dual opcodes BL/BLX ... */
|
||||
|
||||
/* TODO: deal correctly with dual opcode (prefixed) BL/BLX;
|
||||
* these are effectively 32-bit instructions even in Thumb1.
|
||||
* Might be simplest to always use the Thumb2 decoder.
|
||||
*/
|
||||
|
||||
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\t%s 0x%8.8" PRIx32 , address, opcode,mnemonic, target_address);
|
||||
|
||||
|
@ -1887,12 +1896,12 @@ int evaluate_load_store_multiple_thumb(uint16_t opcode, uint32_t address, arm_in
|
|||
if (L)
|
||||
{
|
||||
instruction->type = ARM_LDM;
|
||||
mnemonic = "LDMIA";
|
||||
mnemonic = "LDM";
|
||||
}
|
||||
else
|
||||
{
|
||||
instruction->type = ARM_STM;
|
||||
mnemonic = "STMIA";
|
||||
mnemonic = "STM";
|
||||
}
|
||||
snprintf(ptr_name,7,"r%i!, ",Rn);
|
||||
}
|
||||
|
@ -1945,7 +1954,9 @@ int evaluate_cond_branch_thumb(uint16_t opcode, uint32_t address, arm_instructio
|
|||
if (cond == 0xf)
|
||||
{
|
||||
instruction->type = ARM_SWI;
|
||||
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\tSWI 0x%02" PRIx32 , address, opcode, offset);
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%4.4x\tSVC 0x%02" PRIx32,
|
||||
address, opcode, offset);
|
||||
return ERROR_OK;
|
||||
}
|
||||
else if (cond == 0xe)
|
||||
|
@ -1971,11 +1982,148 @@ int evaluate_cond_branch_thumb(uint16_t opcode, uint32_t address, arm_instructio
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int evaluate_cb_thumb(uint16_t opcode, uint32_t address,
|
||||
arm_instruction_t *instruction)
|
||||
{
|
||||
unsigned offset;
|
||||
|
||||
/* added in Thumb2 */
|
||||
offset = (opcode >> 3) & 0x1f;
|
||||
offset |= (opcode & 0x0200) >> 4;
|
||||
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%4.4x\tCB%sZ r%d, %#8.8" PRIx32,
|
||||
address, opcode,
|
||||
(opcode & 0x0800) ? "N" : "",
|
||||
opcode & 0x7, address + 4 + (offset << 1));
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int evaluate_extend_thumb(uint16_t opcode, uint32_t address,
|
||||
arm_instruction_t *instruction)
|
||||
{
|
||||
/* added in ARMv6 */
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%4.4x\t%cXT%c r%d, r%d",
|
||||
address, opcode,
|
||||
(opcode & 0x0080) ? 'U' : 'S',
|
||||
(opcode & 0x0040) ? 'B' : 'H',
|
||||
opcode & 0x7, (opcode >> 3) & 0x7);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int evaluate_cps_thumb(uint16_t opcode, uint32_t address,
|
||||
arm_instruction_t *instruction)
|
||||
{
|
||||
/* added in ARMv6 */
|
||||
if ((opcode & 0x0ff0) == 0x0650)
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%4.4x\tSETEND %s",
|
||||
address, opcode,
|
||||
(opcode & 0x80) ? "BE" : "LE");
|
||||
else /* ASSUME (opcode & 0x0fe0) == 0x0660 */
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%4.4x\tCPSI%c %s%s%s",
|
||||
address, opcode,
|
||||
(opcode & 0x0010) ? 'D' : 'E',
|
||||
(opcode & 0x0004) ? "A" : "",
|
||||
(opcode & 0x0002) ? "I" : "",
|
||||
(opcode & 0x0001) ? "F" : "");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int evaluate_byterev_thumb(uint16_t opcode, uint32_t address,
|
||||
arm_instruction_t *instruction)
|
||||
{
|
||||
char *suffix;
|
||||
|
||||
/* added in ARMv6 */
|
||||
switch (opcode & 0x00c0) {
|
||||
case 0:
|
||||
suffix = "";
|
||||
break;
|
||||
case 1:
|
||||
suffix = "16";
|
||||
break;
|
||||
default:
|
||||
suffix = "SH";
|
||||
break;
|
||||
}
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%4.4x\tREV%s r%d, r%d",
|
||||
address, opcode, suffix,
|
||||
opcode & 0x7, (opcode >> 3) & 0x7);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int evaluate_hint_thumb(uint16_t opcode, uint32_t address,
|
||||
arm_instruction_t *instruction)
|
||||
{
|
||||
char *hint;
|
||||
|
||||
switch ((opcode >> 4) & 0x0f) {
|
||||
case 0:
|
||||
hint = "NOP";
|
||||
break;
|
||||
case 1:
|
||||
hint = "YIELD";
|
||||
break;
|
||||
case 2:
|
||||
hint = "WFE";
|
||||
break;
|
||||
case 3:
|
||||
hint = "WFI";
|
||||
break;
|
||||
case 4:
|
||||
hint = "SEV";
|
||||
break;
|
||||
default:
|
||||
hint = "HINT (UNRECOGNIZED)";
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%4.4x\t%s",
|
||||
address, opcode, hint);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int evaluate_ifthen_thumb(uint16_t opcode, uint32_t address,
|
||||
arm_instruction_t *instruction)
|
||||
{
|
||||
unsigned cond = (opcode >> 4) & 0x0f;
|
||||
char *x = "", *y = "", *z = "";
|
||||
|
||||
if (opcode & 0x01)
|
||||
z = (opcode & 0x02) ? "T" : "E";
|
||||
if (opcode & 0x03)
|
||||
y = (opcode & 0x04) ? "T" : "E";
|
||||
if (opcode & 0x07)
|
||||
x = (opcode & 0x08) ? "T" : "E";
|
||||
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%4.4x\tIT%s%s%s %s",
|
||||
address, opcode,
|
||||
x, y, z, arm_condition_strings[cond]);
|
||||
|
||||
/* NOTE: strictly speaking, the next 1-4 instructions should
|
||||
* now be displayed with the relevant conditional suffix...
|
||||
*/
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
|
||||
{
|
||||
/* clear fields, to avoid confusion */
|
||||
memset(instruction, 0, sizeof(arm_instruction_t));
|
||||
instruction->opcode = opcode;
|
||||
instruction->instruction_size = 2;
|
||||
|
||||
if ((opcode & 0xe000) == 0x0000)
|
||||
{
|
||||
|
@ -2033,18 +2181,44 @@ int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, arm_instruction_t *
|
|||
/* Misc */
|
||||
if ((opcode & 0xf000) == 0xb000)
|
||||
{
|
||||
if ((opcode & 0x0f00) == 0x0000)
|
||||
switch ((opcode >> 8) & 0x0f) {
|
||||
case 0x0:
|
||||
return evaluate_adjust_stack_thumb(opcode, address, instruction);
|
||||
else if ((opcode & 0x0f00) == 0x0e00)
|
||||
case 0x1:
|
||||
case 0x3:
|
||||
case 0x9:
|
||||
case 0xb:
|
||||
return evaluate_cb_thumb(opcode, address, instruction);
|
||||
case 0x2:
|
||||
return evaluate_extend_thumb(opcode, address, instruction);
|
||||
case 0x4:
|
||||
case 0x5:
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
return evaluate_load_store_multiple_thumb(opcode, address,
|
||||
instruction);
|
||||
case 0x6:
|
||||
return evaluate_cps_thumb(opcode, address, instruction);
|
||||
case 0xa:
|
||||
if ((opcode & 0x00c0) == 0x0080)
|
||||
break;
|
||||
return evaluate_byterev_thumb(opcode, address, instruction);
|
||||
case 0xe:
|
||||
return evaluate_breakpoint_thumb(opcode, address, instruction);
|
||||
else if ((opcode & 0x0600) == 0x0400) /* push pop */
|
||||
return evaluate_load_store_multiple_thumb(opcode, address, instruction);
|
||||
else
|
||||
{
|
||||
instruction->type = ARM_UNDEFINED_INSTRUCTION;
|
||||
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\tUNDEFINED INSTRUCTION", address, opcode);
|
||||
return ERROR_OK;
|
||||
case 0xf:
|
||||
if (opcode & 0x000f)
|
||||
return evaluate_ifthen_thumb(opcode, address,
|
||||
instruction);
|
||||
else
|
||||
return evaluate_hint_thumb(opcode, address,
|
||||
instruction);
|
||||
}
|
||||
|
||||
instruction->type = ARM_UNDEFINED_INSTRUCTION;
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%4.4x\tUNDEFINED INSTRUCTION",
|
||||
address, opcode);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/* Load/Store multiple */
|
||||
|
@ -2078,6 +2252,56 @@ int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, arm_instruction_t *
|
|||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* REVISIT for Thumb2 instructions, instruction->type and friends aren't
|
||||
* always set. That means eventual arm_simulate_step() support for Thumb2
|
||||
* will need work in this area.
|
||||
*/
|
||||
int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruction)
|
||||
{
|
||||
int retval;
|
||||
uint16_t op;
|
||||
uint32_t opcode;
|
||||
|
||||
/* clear low bit ... it's set on function pointers */
|
||||
address &= ~1;
|
||||
|
||||
/* clear fields, to avoid confusion */
|
||||
memset(instruction, 0, sizeof(arm_instruction_t));
|
||||
|
||||
/* read first halfword, see if this is the only one */
|
||||
retval = target_read_u16(target, address, &op);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
switch (op & 0xf800) {
|
||||
case 0xf800:
|
||||
case 0xf000:
|
||||
case 0xe800:
|
||||
/* 32-bit instructions */
|
||||
instruction->instruction_size = 4;
|
||||
opcode = op << 16;
|
||||
retval = target_read_u16(target, address + 2, &op);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
opcode |= op;
|
||||
instruction->opcode = opcode;
|
||||
break;
|
||||
default:
|
||||
/* 16-bit: Thumb1 + IT + CBZ/CBNZ + ... */
|
||||
return thumb_evaluate_opcode(op, address, instruction);
|
||||
}
|
||||
|
||||
/* FIXME decode the 32-bit instructions */
|
||||
|
||||
LOG_DEBUG("Can't decode 32-bit Thumb2 yet (opcode=%08x)", opcode);
|
||||
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%8.8x\t... 32-bit Thumb2 ...",
|
||||
address, opcode);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int arm_access_size(arm_instruction_t *instruction)
|
||||
{
|
||||
if ((instruction->type == ARM_LDRB)
|
||||
|
|
|
@ -185,6 +185,9 @@ typedef struct arm_instruction_s
|
|||
char text[128];
|
||||
uint32_t opcode;
|
||||
|
||||
/* return value ... Thumb-2 sizes vary */
|
||||
unsigned instruction_size;
|
||||
|
||||
union {
|
||||
arm_b_bl_bx_blx_instr_t b_bl_bx_blx;
|
||||
arm_data_proc_instr_t data_proc;
|
||||
|
@ -196,6 +199,8 @@ typedef struct arm_instruction_s
|
|||
|
||||
extern int arm_evaluate_opcode(uint32_t opcode, uint32_t address, arm_instruction_t *instruction);
|
||||
extern int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, arm_instruction_t *instruction);
|
||||
extern int thumb2_opcode(target_t *target, uint32_t address,
|
||||
arm_instruction_t *instruction);
|
||||
extern int arm_access_size(arm_instruction_t *instruction);
|
||||
|
||||
#define COND(opcode) (arm_condition_strings[(opcode & 0xf0000000) >> 28])
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "cortex_m3.h"
|
||||
#include "target_request.h"
|
||||
#include "target_type.h"
|
||||
#include "arm_disassembler.h"
|
||||
|
||||
|
||||
/* cli handling */
|
||||
|
@ -1646,6 +1647,47 @@ int cortex_m3_target_create(struct target_s *target, Jim_Interp *interp)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* REVISIT Thumb2 disassembly should work for all ARMv7 cores, as well
|
||||
* as at least ARM-1156T2. The interesting thing about Cortex-M is
|
||||
* that *only* Thumb2 disassembly matters. There are also some small
|
||||
* additions to Thumb2 that are specific to ARMv7-M.
|
||||
*/
|
||||
static int
|
||||
handle_cortex_m3_disassemble_command(struct command_context_s *cmd_ctx,
|
||||
char *cmd, char **args, int argc)
|
||||
{
|
||||
int retval = ERROR_OK;
|
||||
target_t *target = get_current_target(cmd_ctx);
|
||||
uint32_t address;
|
||||
unsigned long count;
|
||||
arm_instruction_t cur_instruction;
|
||||
|
||||
if (argc != 2) {
|
||||
command_print(cmd_ctx,
|
||||
"usage: cortex_m3 disassemble <address> <count>");
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
address = strtoul(args[0], NULL, 0);
|
||||
if (errno)
|
||||
return ERROR_FAIL;
|
||||
count = strtoul(args[1], NULL, 0);
|
||||
if (errno)
|
||||
return ERROR_FAIL;
|
||||
|
||||
while (count--) {
|
||||
retval = thumb2_opcode(target, address, &cur_instruction);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
command_print(cmd_ctx, "%s", cur_instruction.text);
|
||||
address += cur_instruction.instruction_size;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int cortex_m3_register_commands(struct command_context_s *cmd_ctx)
|
||||
{
|
||||
int retval;
|
||||
|
@ -1653,8 +1695,15 @@ int cortex_m3_register_commands(struct command_context_s *cmd_ctx)
|
|||
|
||||
retval = armv7m_register_commands(cmd_ctx);
|
||||
|
||||
cortex_m3_cmd = register_command(cmd_ctx, NULL, "cortex_m3", NULL, COMMAND_ANY, "cortex_m3 specific commands");
|
||||
register_command(cmd_ctx, cortex_m3_cmd, "maskisr", handle_cortex_m3_mask_interrupts_command, COMMAND_EXEC, "mask cortex_m3 interrupts ['on'|'off']");
|
||||
cortex_m3_cmd = register_command(cmd_ctx, NULL, "cortex_m3",
|
||||
NULL, COMMAND_ANY, "cortex_m3 specific commands");
|
||||
|
||||
register_command(cmd_ctx, cortex_m3_cmd, "disassemble",
|
||||
handle_cortex_m3_disassemble_command, COMMAND_EXEC,
|
||||
"disassemble Thumb2 instructions <address> <count>");
|
||||
register_command(cmd_ctx, cortex_m3_cmd, "maskisr",
|
||||
handle_cortex_m3_mask_interrupts_command, COMMAND_EXEC,
|
||||
"mask cortex_m3 interrupts ['on'|'off']");
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue