David Brownell <david-b@pacbell.net>:
authorzwelch <zwelch@b42882b7-edfa-0310-969c-e2dbd0fdcd60>
Wed, 15 Jul 2009 23:39:37 +0000 (23:39 +0000)
committerzwelch <zwelch@b42882b7-edfa-0310-969c-e2dbd0fdcd60>
Wed, 15 Jul 2009 23:39:37 +0000 (23:39 +0000)
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

doc/openocd.texi
src/target/arm_disassembler.c
src/target/arm_disassembler.h
src/target/cortex_m3.c

index f97037310c07b6d64977d96278ed57af94331d2d..18077ccd6debc5ac15908814b5e71702d5822061 100644 (file)
@@ -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
index 94479261da11de1d0820c5c008cb4e64e97b7669..bd14a6e9c2209cbc873d5ed3ab1876d76d10f5e0 100644 (file)
@@ -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)
index a8b9aba115938f45d59e7f1292d4432960b3c1f9..b841d6cd0c64d6711fc8f7ba599e696399e32dfd 100644 (file)
@@ -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])
index 53ecd0a90d5309e89de466efed373f37a438308a..2e19cd6781c649eac83e30b2dae1282ca49b822d 100644 (file)
@@ -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;
 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)