[PATCH] RISC-V: Add support for 'set disassembler-options'
Simon Cook
simon.cook@embecosm.com
Thu Jan 14 10:52:01 GMT 2021
Implement support for passing options to the disassembler from GDB. This
supports all options supported by the -M option in objdump, and updates
the RISC-V disassembler to support resetting options back to the default
should the user later request no options.
The disassembler now holds a table of supported options and arguments.
Option printing (for objdump --help) has been updated to use this table;
this implementation is based on the MIPS disassembler options.
gdb/ChangeLog:
* riscv-tdep.c (riscv_gdbarch_init): Add disassembler options.
gdb/testsuite/ChangeLog:
* gdb.arch/riscv-disassembler-options.exp: New test.
* gdb.arch/riscv-disassembler-options.s: New test source.
include/Changelog:
* dis-asm.h (disassembler_options_riscv): New prototype.
opcodes/ChangeLog:
* riscv-dis.c (init_csr): New variable.
(set_default_riscv_dis_options): Reset default_priv_spec and
init_csr.
(print_insn_args): Remove init_csr from function.
(previous_disassembler_options): New variable.
(print_insn_riscv): Add parsing of updated disassembler
options.
(riscv_options): New variable.
(riscv_options_args_privspec): New variable.
(riscv_option_args): New variable.
(disassembler_options_riscv): New function.
(print_riscv_disassembler_options): Reimplement using
disassembler_options_riscv.
---
gdb/riscv-tdep.c | 8 +
.../gdb.arch/riscv-disassembler-options.exp | 57 ++++++
.../gdb.arch/riscv-disassembler-options.s | 29 +++
include/dis-asm.h | 1 +
opcodes/riscv-dis.c | 169 ++++++++++++++++--
5 files changed, 251 insertions(+), 13 deletions(-)
create mode 100644 gdb/testsuite/gdb.arch/riscv-disassembler-options.exp
create mode 100644 gdb/testsuite/gdb.arch/riscv-disassembler-options.s
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index c0e84e585d..74aeaca518 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -94,6 +94,9 @@ static unsigned int riscv_debug_unwinder = 0;
static unsigned int riscv_debug_gdbarch = 0;
+/* Options to pass to disassembler. */
+static char *riscv_disassembler_options = NULL;
+
/* Cached information about a frame. */
struct riscv_unwind_cache
@@ -3636,6 +3639,11 @@ riscv_gdbarch_init (struct gdbarch_info info,
register_riscv_ravenscar_ops (gdbarch);
+ /* Disassembler options. */
+ set_gdbarch_disassembler_options (gdbarch, &riscv_disassembler_options);
+ set_gdbarch_valid_disassembler_options (gdbarch,
+ disassembler_options_riscv ());
+
return gdbarch;
}
diff --git a/gdb/testsuite/gdb.arch/riscv-disassembler-options.exp b/gdb/testsuite/gdb.arch/riscv-disassembler-options.exp
new file mode 100644
index 0000000000..c118d96113
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-disassembler-options.exp
@@ -0,0 +1,57 @@
+# Copyright 2021 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test RISC-V disassembler options.
+
+if { ![istarget "riscv*-*-*"] } then {
+ verbose "Skipping RISC-V disassembler option tests."
+ return
+}
+
+standard_testfile .s
+set objfile [standard_output_file ${testfile}.o]
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${objfile}" object {}] \
+ != "" } {
+ return
+}
+
+clean_restart ${objfile}
+
+proc riscv_disassemble_test { func insn mesg } {
+ gdb_test "disassemble $func" \
+ "Dump of assembler code for function\
+ $func:\r\n\[^:\]+:\t$insn\r\nEnd of assembler dump\." \
+ $mesg
+}
+
+# Verify defaults.
+riscv_disassemble_test foo "nop" "disassemble implicit default"
+
+# Verify option overrides.
+gdb_test "set disassembler-options no-aliases"
+riscv_disassemble_test foo "c.addi\tzero,0" "disassemble no-aliases"
+gdb_test "set disassembler-options no-aliases,numeric"
+riscv_disassemble_test foo "c.addi\tx0,0" "disassemble no-aliases,numeric"
+
+# Verify switching of priviledge spec.
+gdb_test "set disassembler-options priv-spec=1.9.1"
+riscv_disassemble_test bar "csrr\tt6,mucounteren" "disassemble priv-spec=1.9.1"
+gdb_test "set disassembler-options priv-spec=1.11"
+riscv_disassemble_test bar "csrr\tt6,mcountinhibit" "disassemble priv-spec=1.11"
+
+# Verify can roll back to defaults.
+gdb_test "set disassembler-options"
+riscv_disassemble_test foo "nop" "disassemble explicit default"
diff --git a/gdb/testsuite/gdb.arch/riscv-disassembler-options.s b/gdb/testsuite/gdb.arch/riscv-disassembler-options.s
new file mode 100644
index 0000000000..bc337556bd
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-disassembler-options.s
@@ -0,0 +1,29 @@
+# This test is part of GDB, the GNU debugger.
+#
+# Copyright 2021 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+ .globl foo
+ .option rvc
+foo:
+ c.nop
+ .size foo, .-foo
+
+# 0x320 is MUCOUNTEREN in 1.9.1 MCOUNTINHIBIT in 1.11
+ .globl bar
+bar:
+ csrr x31,0x320
+ .size bar, .-bar
diff --git a/include/dis-asm.h b/include/dis-asm.h
index 368eb2734f..4b06e0856e 100644
--- a/include/dis-asm.h
+++ b/include/dis-asm.h
@@ -315,6 +315,7 @@ extern const disasm_options_and_args_t *disassembler_options_arm (void);
extern const disasm_options_and_args_t *disassembler_options_mips (void);
extern const disasm_options_and_args_t *disassembler_options_powerpc (void);
extern const disasm_options_and_args_t *disassembler_options_s390 (void);
+extern const disasm_options_and_args_t *disassembler_options_riscv (void);
/* Fetch the disassembler for a given architecture ARC, endianess (big
endian if BIG is true), bfd_mach value MACH, and ABFD, if that support
diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c
index c3d9bb8745..b49f565663 100644
--- a/opcodes/riscv-dis.c
+++ b/opcodes/riscv-dis.c
@@ -31,6 +31,7 @@
#include "bfd_stdint.h"
#include <ctype.h>
+#include <limits.h>
static enum riscv_priv_spec_class default_priv_spec = PRIV_SPEC_CLASS_NONE;
@@ -47,9 +48,14 @@ static const char * const *riscv_fpr_names;
/* Other options. */
static int no_aliases; /* If set disassemble as most general inst. */
+/* If false csr table needs (re-)initializing. */
+static bfd_boolean init_csr = FALSE;
+
static void
set_default_riscv_dis_options (void)
{
+ default_priv_spec = PRIV_SPEC_CLASS_NONE;
+ init_csr = FALSE;
riscv_gpr_names = riscv_gpr_names_abi;
riscv_fpr_names = riscv_fpr_names_abi;
no_aliases = 0;
@@ -372,7 +378,6 @@ print_insn_args (const char *d, insn_t l, bfd_vma pc, disassemble_info *info)
case 'E':
{
static const char *riscv_csr_hash[4096]; /* Total 2^12 CSR. */
- static bfd_boolean init_csr = FALSE;
unsigned int csr = EXTRACT_OPERAND (CSR, l);
if (!init_csr)
@@ -554,6 +559,9 @@ riscv_disassemble_insn (bfd_vma memaddr, insn_t word, disassemble_info *info)
return insnlen;
}
+/* Disassembler option string that has previously been parsed. */
+static char *previous_disassembler_options = NULL;
+
int
print_insn_riscv (bfd_vma memaddr, struct disassemble_info *info)
{
@@ -562,11 +570,26 @@ print_insn_riscv (bfd_vma memaddr, struct disassemble_info *info)
bfd_vma n;
int status;
- if (info->disassembler_options != NULL)
+ /* If the current disassembler options are not the same as those used
+ previously, reset disassembler options and parse the new string.
+ NULL is used as an empty string, so treat NULL and NULL as equal. */
+ if ((info->disassembler_options != previous_disassembler_options &&
+ (!info->disassembler_options || !previous_disassembler_options)) ||
+ (info->disassembler_options && previous_disassembler_options &&
+ strcmp(info->disassembler_options, previous_disassembler_options)))
{
- parse_riscv_dis_options (info->disassembler_options);
- /* Avoid repeatedly parsing the options. */
- info->disassembler_options = NULL;
+ if (previous_disassembler_options)
+ {
+ free (previous_disassembler_options);
+ previous_disassembler_options = NULL;
+ }
+ set_default_riscv_dis_options();
+ if (info->disassembler_options != NULL)
+ {
+ parse_riscv_dis_options (info->disassembler_options);
+ /* Avoid repeatedly parsing the options. */
+ previous_disassembler_options = xstrdup(info->disassembler_options);
+ }
}
else if (riscv_gpr_names == NULL)
set_default_riscv_dis_options ();
@@ -630,23 +653,143 @@ riscv_symbol_is_valid (asymbol * sym,
return (strcmp (name, RISCV_FAKE_LABEL_NAME) != 0);
}
+/* Valid RISC-V diassembler options. */
+static struct
+{
+ const char *name;
+ const char *description;
+ /* This corresponds to the row in RISCV_OPTION_ARGS holding valid arguments
+ for this option. */
+ unsigned arg;
+} riscv_options[] =
+{
+ { "no-aliases", "Disassemble only into canonical instructions, rather than \
+into pseudoinstructions.\n", UINT_MAX },
+ { "numeric", "Print numeric register names, rather than ABI names.\n", UINT_MAX },
+ { "priv-spec=", "Print the CSRs according to the chosen privilege spec \
+(1.9.1, 1.10, 1.11).", 0}
+};
+
+/* Valid RISC-V disassembler arguments. Note value lists must be NULL
+ terminated. */
+static const char *
+riscv_option_args_privspec[] = {"1.9.1", "1.10", "1.11", NULL};
+static struct
+{
+ const char *name;
+ const char **values;
+} riscv_option_args[] =
+{
+ {"PRIV", (const char**)&riscv_option_args_privspec}
+};
+
+const disasm_options_and_args_t *
+disassembler_options_riscv (void)
+{
+ static disasm_options_and_args_t *opts_and_args;
+
+ if (opts_and_args == NULL)
+ {
+ unsigned num_options = ARRAY_SIZE(riscv_options);
+ unsigned num_args = ARRAY_SIZE(riscv_option_args);
+ disasm_option_arg_t *args;
+ disasm_options_t *opts;
+ unsigned i;
+
+ args = XNEWVEC (disasm_option_arg_t, num_args + 1);
+
+ /* Array must be NULL terminated. */
+ args[num_args].name = NULL;
+ args[num_args].values = NULL;
+
+ for (i = 0; i < num_args; i++)
+ {
+ args[i].name = riscv_option_args[i].name;
+ args[i].values = riscv_option_args[i].values;
+ }
+
+ opts_and_args = XNEW (disasm_options_and_args_t);
+ opts_and_args->args = args;
+
+ opts = &opts_and_args->options;
+ opts->name = XNEWVEC (const char *, num_options + 1);
+ opts->description = XNEWVEC (const char *, num_options + 1);
+ opts->arg = XNEWVEC (const disasm_option_arg_t *, num_options + 1);
+
+ /* Last option must be NULL */
+ opts->name[num_options] = NULL;
+ opts->description[num_options] = NULL;
+ opts->arg[num_options] = NULL;
+
+ for (i = 0; i < num_options; i++)
+ {
+ opts->name[i] = riscv_options[i].name;
+ opts->description[i] = _(riscv_options[i].description);
+ if (riscv_options[i].arg != UINT_MAX)
+ opts->arg[i] = &args[riscv_options[i].arg];
+ else
+ opts->arg[i] = NULL;
+ }
+ }
+
+ return opts_and_args;
+}
+
void
print_riscv_disassembler_options (FILE *stream)
{
+ const disasm_options_and_args_t *opts_and_args;
+ const disasm_option_arg_t *args;
+ const disasm_options_t *opts;
+ size_t max_len = 0;
+ size_t i;
+ size_t j;
+
+ opts_and_args = disassembler_options_riscv ();
+ opts = &opts_and_args->options;
+ args = opts_and_args->args;
+
fprintf (stream, _("\n\
The following RISC-V-specific disassembler options are supported for use\n\
with the -M switch (multiple options should be separated by commas):\n"));
- fprintf (stream, _("\n\
- numeric Print numeric register names, rather than ABI names.\n"));
+ /* Compute the length of the longest option name. */
+ for (i = 0; opts->name[i] != NULL; i++)
+ {
+ size_t len = strlen (opts->name[i]);
- fprintf (stream, _("\n\
- no-aliases Disassemble only into canonical instructions, rather\n\
- than into pseudoinstructions.\n"));
+ if (opts->arg[i] != NULL)
+ len += strlen (opts->arg[i]->name);
+ if (max_len < len)
+ max_len = len;
+ }
- fprintf (stream, _("\n\
- priv-spec=PRIV Print the CSR according to the chosen privilege spec\n\
- (1.9, 1.9.1, 1.10, 1.11).\n"));
+ for (i = 0, max_len++; opts->name[i] != NULL; i++)
+ {
+ fprintf (stream, " %s", opts->name[i]);
+ if (opts->arg[i] != NULL)
+ fprintf (stream, "%s", opts->arg[i]->name);
+ if (opts->description[i] != NULL)
+ {
+ size_t len = strlen (opts->name[i]);
+
+ if (opts->arg[i] != NULL)
+ len += strlen (opts->arg[i]->name);
+ fprintf (stream,
+ "%*c %s", (int) (max_len - len), ' ', opts->description[i]);
+ }
+ fprintf (stream, _("\n"));
+ }
+
+ for (i = 0; args[i].name != NULL; i++)
+ {
+ fprintf (stream, _("\n\
+ For the options above, the following values are supported for \"%s\":\n "),
+ args[i].name);
+ for (j = 0; args[i].values[j] != NULL; j++)
+ fprintf (stream, " %s", args[i].values[j]);
+ fprintf (stream, _("\n"));
+ }
fprintf (stream, _("\n"));
}
--
2.24.3 (Apple Git-128)
More information about the Binutils
mailing list