This is an automated email from Gerrit.

"Antonio Borneo <[email protected]>" just uploaded a new patch set to 
Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9507

-- gerrit

commit ee7112e73c7ca8e14bccaaf205bc622b8a0be11b
Author: Antonio Borneo <[email protected]>
Date:   Sun Mar 15 01:01:13 2026 +0100

    target: extend use of capstone disassembler
    
    OpenOCD uses capstone library for the disassemble commands, but
    only limited to aarch64 and arm, and using dedicated commands.
    The code also has some issue in handling capstone return errors,
    does not provide the correct settings for Cortex-M, does not
    support endianness and does not handle the different modes of
    aarch64.
    
    While support for MIPS is already present in Capstone v4.0 but
    not in OpenOCD, the latest v5.0 have also support for RISC-V, and
    next v6.0 already includes support for esp32 and arc.
    
    Create a new generic command '$target_name disassemble'. It can
    use the instruction-set of the current target, and is also allows
    the disassemble of a specific instruction-set while using the
    current target only for reading the instructions from memory; this
    is useful with a mem-ap target.
    Introduce a callback target_type::insn_set() to let a target to
    feedback the current instruction-set and endianness to the new
    command.
    
    Change-Id: Ifd832e97794e5d9988629898f482829400bc2ae0
    Signed-off-by: Antonio Borneo <[email protected]>

diff --git a/doc/openocd.texi b/doc/openocd.texi
index a21a3e4bda..e392f88db3 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -5646,6 +5646,20 @@ Otherwise, or if the optional @var{phys} flag is 
specified,
 If @var{count} is specified, fills that many units of consecutive address.
 @end deffn
 
+@deffn {Command} {$target_name disassemble} address [count [instruction-set]]
+@deffnx {Command} {$target_name disassemble} list
+This command requires Capstone library installed.
+
+Disassembles @var{count} instructions starting at @var{address}.
+If @var{count} is not specified, a single instruction is disassembled.
+
+It uses the target @var{$target_name} to read the instructions from memory,
+then disassemble them using either the instruction-set detected on the same
+target or the one provided with @var{instruction-set}.
+
+With parameter @var{list} it dumps the list of supported 'instruction-set'.
+@end deffn
+
 @anchor{targetevents}
 @section Target Events
 @cindex target events
diff --git a/src/target/Makefile.am b/src/target/Makefile.am
index 0b5a85704c..65331f58cf 100644
--- a/src/target/Makefile.am
+++ b/src/target/Makefile.am
@@ -34,6 +34,7 @@ noinst_LTLIBRARIES += %D%/libtarget.la
 if HAVE_CAPSTONE
 %C%_libtarget_la_CPPFLAGS += $(CAPSTONE_CFLAGS)
 %C%_libtarget_la_LIBADD += $(CAPSTONE_LIBS)
+%C%_libtarget_la_SOURCES += %D%/oocd_capstone.c
 endif
 
 TARGET_CORE_SRC = \
@@ -237,7 +238,8 @@ ARC_SRC = \
        %D%/arc_cmd.h \
        %D%/arc_jtag.h \
        %D%/arc_mem.h \
-       %D%/rtt.h
+       %D%/rtt.h \
+       %D%/oocd_capstone.h
 
 include %D%/openrisc/Makefile.am
 include %D%/riscv/Makefile.am
diff --git a/src/target/oocd_capstone.c b/src/target/oocd_capstone.c
new file mode 100644
index 0000000000..10507ed304
--- /dev/null
+++ b/src/target/oocd_capstone.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * This file wraps the functions in capstone library.
+ * It also takes care of API changes across versions.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <capstone.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <helper/command.h>
+#include <helper/log.h>
+#include <target/oocd_capstone.h>
+#include <target/target.h>
+
+/*
+ * Extracted from Capstone tool 'cstool', file 'cstool/cstool.c'.
+ * Big rework expected for next Capstone v6.
+ */
+static struct {
+       const char *name;
+       cs_arch arch;
+       cs_mode mode;
+} all_archs[] = {
+       { "arm", CS_ARCH_ARM, CS_MODE_ARM },
+       { "arm64", CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN },
+       { "thumb", CS_ARCH_ARM, CS_MODE_ARM | CS_MODE_THUMB },
+};
+
+int oocd_cs_list_insn_types(struct command_invocation *cmd)
+{
+       for (size_t i = 0; i < ARRAY_SIZE(all_archs); i++)
+               command_print_sameline(cmd, (i == 0) ? "%s" : " %s",
+                                                          all_archs[i].name);
+
+       return ERROR_OK;
+}
+
+static void print_opcode(struct command_invocation *cmd, const cs_insn *insn)
+{
+       char opcode[3 * ARRAY_SIZE(insn->bytes) + 1];
+
+       for (uint16_t i = 0; i < insn->size; i++)
+               sprintf(&opcode[3 * i], " %02" PRIx8, insn->bytes[i]);
+
+       command_print(cmd, "0x%08" PRIx64 "  %s\t%s%s%s",
+                                 insn->address, opcode, insn->mnemonic,
+                                 insn->op_str[0] ? "\t" : "", insn->op_str);
+}
+
+int oocd_cs_disassemble(struct command_invocation *cmd, struct target *target,
+       uint64_t address, unsigned int count, const char *insn_set)
+{
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(all_archs); i++)
+               if (!strcmp(insn_set, all_archs[i].name))
+                       break;
+
+       if (i == ARRAY_SIZE(all_archs)) {
+               command_print(cmd, "Unknown instruction-set \"%s\"", insn_set);
+               return ERROR_FAIL;
+       }
+
+       csh handle;
+       cs_err csret = cs_open(all_archs[i].arch, all_archs[i].mode, &handle);
+       if (csret != CS_ERR_OK) {
+               command_print(cmd, "Capstone cs_open() failed: %s", 
cs_strerror(csret));
+               return ERROR_FAIL;
+       }
+
+       csret = cs_option(handle, CS_OPT_SKIPDATA, CS_OPT_ON);
+       if (csret != CS_ERR_OK) {
+               command_print(cmd, "Capstone cs_option() failed: %s", 
cs_strerror(csret));
+               cs_close(&handle);
+               return ERROR_FAIL;
+       }
+
+       cs_insn *insn = cs_malloc(handle);
+       if (!insn) {
+               command_print(cmd, "Capstone cs_malloc() failed: %s", 
cs_strerror(csret));
+               cs_close(&handle);
+               return ERROR_FAIL;
+       }
+
+       while (count > 0) {
+               uint8_t buf[4];
+
+               int retval = target_read_buffer(target, address, sizeof(buf), 
buf);
+               if (retval != ERROR_OK) {
+                       cs_free(insn, 1);
+                       cs_close(&handle);
+                       return retval;
+               }
+
+               size_t size = sizeof(buf);
+               const uint8_t *tmp = buf;
+               bool csbool = cs_disasm_iter(handle, &tmp, &size, &address, 
insn);
+               if (!csbool) {
+                       command_print(cmd, "Capstone cs_disasm_iter() failed: 
%s", cs_strerror(csret));
+                       cs_free(insn, 1);
+                       cs_close(&handle);
+                       return ERROR_FAIL;
+               }
+
+               print_opcode(cmd, insn);
+               count--;
+       }
+
+       cs_free(insn, 1);
+       cs_close(&handle);
+
+       return ERROR_OK;
+}
diff --git a/src/target/oocd_capstone.h b/src/target/oocd_capstone.h
new file mode 100644
index 0000000000..28cb6380d8
--- /dev/null
+++ b/src/target/oocd_capstone.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef OPENOCD_TARGET_WRAP_CAPSTONE_H
+#define OPENOCD_TARGET_WRAP_CAPSTONE_H
+
+#include <stdint.h>
+
+struct command_invocation;
+struct target;
+
+#if HAVE_CAPSTONE
+
+int oocd_cs_list_insn_types(struct command_invocation *cmd);
+int oocd_cs_disassemble(struct command_invocation *cmd, struct target *target,
+       uint64_t address, unsigned int count, const char *insn_set);
+
+#else /* HAVE_CAPSTONE */
+
+#include <helper/command.h>
+#include <helper/log.h>
+
+static inline
+int oocd_cs_list_insn_types(struct command_invocation *cmd)
+{
+       command_print(cmd, "Capstone library not present");
+       return ERROR_NOT_IMPLEMENTED;
+}
+
+static inline
+int oocd_cs_disassemble(struct command_invocation *cmd, struct target *target,
+       uint64_t address, unsigned int count, const char *insn_set)
+{
+       command_print(cmd, "Capstone library not present");
+       return ERROR_NOT_IMPLEMENTED;
+}
+
+#endif /* HAVE_CAPSTONE */
+
+#endif /* OPENOCD_TARGET_WRAP_CAPSTONE_H */
diff --git a/src/target/target.c b/src/target/target.c
index f766d1da43..496fc4ea0d 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -36,6 +36,7 @@
 #include <helper/time_support.h>
 #include <jtag/jtag.h>
 #include <flash/nor/core.h>
+#include <target/oocd_capstone.h>
 
 #include "target.h"
 #include "target_type.h"
@@ -2352,6 +2353,20 @@ int target_profiling_default(struct target *target, 
uint32_t *samples,
        return retval;
 }
 
+static int target_insn_set(struct command_invocation *cmd, struct target 
*target,
+                                                  const char **insn_set)
+{
+       if (target->type->insn_set)
+               return target->type->insn_set(cmd, target, insn_set);
+
+       command_print(cmd, "Instruction-set detection not implemented on target 
%s",
+                                 target_name(target));
+       command_print(cmd, "Change target or specify one of the 
instruction-set:");
+       oocd_cs_list_insn_types(cmd);
+
+       return ERROR_NOT_IMPLEMENTED;
+}
+
 /* Single aligned words are guaranteed to use 16 or 32 bit access
  * mode respectively, otherwise data is handled as quickly as
  * possible
@@ -5474,6 +5489,36 @@ COMMAND_HANDLER(handle_target_invoke_event)
        return ERROR_OK;
 }
 
+COMMAND_HANDLER(handle_target_disassemble)
+{
+       struct target *target = get_current_target(CMD_CTX);
+
+       if (CMD_ARGC < 1 || CMD_ARGC > 3)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (CMD_ARGC == 1 && !strcmp("list", CMD_ARGV[0]))
+               return oocd_cs_list_insn_types(CMD);
+
+       target_addr_t address;
+       COMMAND_PARSE_ADDRESS(CMD_ARGV[0], address);
+
+       unsigned int count = 1;
+       if (CMD_ARGC > 1)
+               COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], count);
+
+       const char *insn_set;
+       if (CMD_ARGC > 2) {
+               insn_set = CMD_ARGV[2];
+       } else {
+               int retval = target_insn_set(CMD, target, &insn_set);
+               if (retval != ERROR_OK)
+                       return retval;
+               command_print(CMD, "instruction-set \"%s\"", insn_set);
+       }
+
+       return oocd_cs_disassemble(CMD, target, address, count, insn_set);
+}
+
 static const struct command_registration target_instance_command_handlers[] = {
        {
                .name = "configure",
@@ -5657,6 +5702,13 @@ static const struct command_registration 
target_instance_command_handlers[] = {
                .help = "invoke handler for specified event",
                .usage = "event_name",
        },
+       {
+               .name = "disassemble",
+               .mode = COMMAND_EXEC,
+               .handler = handle_target_disassemble,
+               .help = "disassemble instructions",
+               .usage = "list | address [count [instruction-set]]",
+       },
        COMMAND_REGISTRATION_DONE
 };
 
@@ -6711,6 +6763,13 @@ static const struct command_registration 
target_exec_command_handlers[] = {
                .help = "Test the target's memory access functions",
                .usage = "size",
        },
+       {
+               .name = "disassemble",
+               .mode = COMMAND_EXEC,
+               .handler = handle_target_disassemble,
+               .help = "disassemble instructions",
+               .usage = "list | address [count [instruction-set]]",
+       },
 
        COMMAND_REGISTRATION_DONE
 };
diff --git a/src/target/target_type.h b/src/target/target_type.h
index 68ff2b6067..3310ba868a 100644
--- a/src/target/target_type.h
+++ b/src/target/target_type.h
@@ -16,6 +16,7 @@
 
 #include <helper/jim-nvp.h>
 
+struct command_invocation;
 struct target;
 
 /**
@@ -311,6 +312,17 @@ struct target_type {
         * will typically be 32 for 32-bit targets, and 64 for 64-bit targets. 
If
         * not implemented, it's assumed to be 32. */
        unsigned int (*data_bits)(struct target *target);
+
+       /*
+        * Reports the instruction-set in execution on the CPU.
+        * The returned string should be statically allocated, no free() 
required.
+        * The strings should be the same used by Capstone 'cstool' command line
+        * parameter <arch+mode>.
+        * Returns ERROR_OK or an error code if the current instruction-set 
cannot
+        * be determined.
+        */
+       int (*insn_set)(struct command_invocation *cmd, struct target *target,
+                                       const char **insn_set);
 };
 
 // Keep in alphabetic order this list of targets

-- 

Reply via email to