[v3,1/3] RISC-V: Make "priv-spec" overridable

Message ID 94304c8d9174ae7e9cf52abc3af6ccf5e3e0ecd9.1668910970.git.research_trasio@irq.a4lg.com
State New
Headers
Series RISC-V: Add overridable "priv-spec" and "arch" disassembler options |

Commit Message

Tsukasa OI Nov. 20, 2022, 2:23 a.m. UTC
  From: Tsukasa OI <research_trasio@irq.a4lg.com>

This commit makes existing disassembler option "priv-spec" overridable on
ELF files with attributes.

Existing implementation is helpful on debugging binary files but on ELF
files, the value specified by "priv-spec" option must match the attributes.

However, there's a case where privileged specification ELF attributes and
actual CSRs as used in the program differs.  For instance, OpenSBI is the
prime example.

This commit enables objdump and GDB to ignore ELF attributes but instead
use custom specification (e.g. even if OpenSBI is compiled with priv-spec
1.11 toolchain, we can correctly disassemble with priv-spec=1.12 option).

gas/ChangeLog:

	* testsuite/gas/riscv/dis-priv-spec-override.s: New privileged
	specification override tests.
	* testsuite/gas/riscv/dis-priv-spec-override-1.d: Likewise.
	* testsuite/gas/riscv/dis-priv-spec-override-2.d: Likewise.

opcodes/ChangeLog:

	* riscv-dis.c (default_priv_spec) Set to initial default version.
	(priv_spec, is_custom_priv_spec) New.
	(is_init_csr): Define as a file-scope variable instead a local
	variable of print_insn_args.
	(init_riscv_dis_state_for_arch_and_options): Keep track of
	"priv-spec" changes.
	(set_default_riscv_dis_options): Initialize "priv-spec".
	(parse_riscv_dis_option): Make "priv-spec" overridable.
	(print_insn_args): Use file-scope is_init_csr variable.
	(riscv_get_disassembler): Move fallback of default_priv_spec
	from print_insn_args.  Initialize "priv-spec".
---
 .../gas/riscv/dis-priv-spec-override-1.d      | 10 +++
 .../gas/riscv/dis-priv-spec-override-2.d      | 10 +++
 .../gas/riscv/dis-priv-spec-override.s        |  2 +
 opcodes/riscv-dis.c                           | 70 ++++++++++++-------
 4 files changed, 68 insertions(+), 24 deletions(-)
 create mode 100644 gas/testsuite/gas/riscv/dis-priv-spec-override-1.d
 create mode 100644 gas/testsuite/gas/riscv/dis-priv-spec-override-2.d
 create mode 100644 gas/testsuite/gas/riscv/dis-priv-spec-override.s
  

Patch

diff --git a/gas/testsuite/gas/riscv/dis-priv-spec-override-1.d b/gas/testsuite/gas/riscv/dis-priv-spec-override-1.d
new file mode 100644
index 000000000000..2cceebbf35a4
--- /dev/null
+++ b/gas/testsuite/gas/riscv/dis-priv-spec-override-1.d
@@ -0,0 +1,10 @@ 
+#as: -march=rv64i_zicsr -mpriv-spec=1.11
+#source: dis-priv-spec-override.s
+#objdump: -d -M priv-spec=1.12
+
+.*:[ 	]+file format .*
+
+Disassembly of section .text:
+
+0+000 <target>:
+[ 	]+[0-9a-f]+:[ 	]+3d002573[ 	]+csrr[ 	]+a0,pmpaddr32
diff --git a/gas/testsuite/gas/riscv/dis-priv-spec-override-2.d b/gas/testsuite/gas/riscv/dis-priv-spec-override-2.d
new file mode 100644
index 000000000000..200955e0ffe5
--- /dev/null
+++ b/gas/testsuite/gas/riscv/dis-priv-spec-override-2.d
@@ -0,0 +1,10 @@ 
+#as: -march=rv64i_zicsr -mpriv-spec=1.12
+#source: dis-priv-spec-override.s
+#objdump: -d -M priv-spec=1.11
+
+.*:[ 	]+file format .*
+
+Disassembly of section .text:
+
+0+000 <target>:
+[ 	]+[0-9a-f]+:[ 	]+3d002573[ 	]+csrr[ 	]+a0,0x3d0
diff --git a/gas/testsuite/gas/riscv/dis-priv-spec-override.s b/gas/testsuite/gas/riscv/dis-priv-spec-override.s
new file mode 100644
index 000000000000..c3d1726e0b5a
--- /dev/null
+++ b/gas/testsuite/gas/riscv/dis-priv-spec-override.s
@@ -0,0 +1,2 @@ 
+target:
+	csrr	a0, 0x3d0
diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c
index 328a34501549..e7dded63c402 100644
--- a/opcodes/riscv-dis.c
+++ b/opcodes/riscv-dis.c
@@ -49,7 +49,16 @@  static enum riscv_spec_class default_isa_spec = ISA_SPEC_CLASS_DRAFT - 1;
 
 /* Default privileged specification
    (as specified by the ELF attributes or the `priv-spec' option).  */
-static enum riscv_spec_class default_priv_spec = PRIV_SPEC_CLASS_NONE;
+static enum riscv_spec_class default_priv_spec = PRIV_SPEC_CLASS_DRAFT - 1;
+
+/* Current privileged specification version.  */
+static enum riscv_spec_class priv_spec = PRIV_SPEC_CLASS_DRAFT - 1;
+
+/* If set, a custom privileged specification is specified.  */
+static bool is_custom_priv_spec = false;
+
+/* Reset when reinitializing the CSR hash table is required.  */
+static bool is_init_csr = false;
 
 /* RISC-V disassembler architecture context type.  */
 typedef struct
@@ -205,6 +214,18 @@  init_riscv_dis_state_for_arch (void)
 static void
 init_riscv_dis_state_for_arch_and_options (void)
 {
+  static bool init = false;
+  static enum riscv_spec_class prev_priv_spec;
+  /* Set current privileged specification.  */
+  if (!is_custom_priv_spec)
+    priv_spec = default_priv_spec;
+  /* First call to this function.  */
+  if (!init)
+    {
+      init = true;
+      /* Save initial options.  */
+      prev_priv_spec = priv_spec;
+    }
   /* If the architecture string is changed, update XLEN.  */
   if (is_arch_changed)
     update_riscv_dis_xlen (NULL);
@@ -215,7 +236,12 @@  init_riscv_dis_state_for_arch_and_options (void)
       = !riscv_subset_supports (&riscv_rps_dis, "zfinx")
 	    ? (is_numeric ? riscv_fpr_names_numeric : riscv_fpr_names_abi)
 	    : riscv_gpr_names;
+  /* Reset CSR hash table if either the architecture or the privileged
+     specification version is changed.  */
+  if (prev_priv_spec != priv_spec)
+    is_init_csr = false;
   /* Save previous options and mark them "unchanged".  */
+  prev_priv_spec = priv_spec;
   is_arch_changed = false;
 }
 
@@ -267,6 +293,7 @@  set_default_riscv_dis_options (void)
 {
   no_aliases = false;
   is_numeric = false;
+  is_custom_priv_spec = false;
 }
 
 /* Parse RISC-V disassembler option (without arguments).  */
@@ -314,21 +341,18 @@  parse_riscv_dis_option (const char *option)
   value = equal + 1;
   if (strcmp (option, "priv-spec") == 0)
     {
-      enum riscv_spec_class priv_spec = PRIV_SPEC_CLASS_NONE;
-      const char *name = NULL;
-
-      RISCV_GET_PRIV_SPEC_CLASS (value, priv_spec);
-      if (priv_spec == PRIV_SPEC_CLASS_NONE)
-	opcodes_error_handler (_("unknown privileged spec set by %s=%s"),
-			       option, value);
-      else if (default_priv_spec == PRIV_SPEC_CLASS_NONE)
-	default_priv_spec = priv_spec;
-      else if (default_priv_spec != priv_spec)
+      enum riscv_spec_class priv_spec_new = PRIV_SPEC_CLASS_NONE;
+      RISCV_GET_PRIV_SPEC_CLASS (value, priv_spec_new);
+      if (priv_spec_new == PRIV_SPEC_CLASS_NONE)
 	{
-	  RISCV_GET_PRIV_SPEC_NAME (name, default_priv_spec);
-	  opcodes_error_handler (_("mis-matched privilege spec set by %s=%s, "
-				   "the elf privilege attribute is %s"),
-				 option, value, name);
+	  opcodes_error_handler (_ ("unknown privileged spec set by %s=%s."
+				    "not overriding"),
+				 option, value);
+	}
+      else
+	{
+	  is_custom_priv_spec = true;
+	  priv_spec = priv_spec_new;
 	}
     }
   else
@@ -721,31 +745,26 @@  print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info
 	case 'E':
 	  {
 	    static const char *riscv_csr_hash[4096]; /* Total 2^12 CSRs.  */
-	    static bool init_csr = false;
 	    unsigned int csr = EXTRACT_OPERAND (CSR, l);
 
-	    if (!init_csr)
+	    if (!is_init_csr)
 	      {
 		unsigned int i;
 		for (i = 0; i < 4096; i++)
 		  riscv_csr_hash[i] = NULL;
 
-		/* Set to the newest privileged version.  */
-		if (default_priv_spec == PRIV_SPEC_CLASS_NONE)
-		  default_priv_spec = PRIV_SPEC_CLASS_DRAFT - 1;
-
 #define DECLARE_CSR(name, num, class, define_version, abort_version)	\
 		if (riscv_csr_hash[num] == NULL 			\
 		    && ((define_version == PRIV_SPEC_CLASS_NONE 	\
 			 && abort_version == PRIV_SPEC_CLASS_NONE)	\
-			|| (default_priv_spec >= define_version 	\
-			    && default_priv_spec < abort_version)))	\
+			|| (priv_spec >= define_version 	\
+			    && priv_spec < abort_version)))	\
 		  riscv_csr_hash[num] = #name;
 #define DECLARE_CSR_ALIAS(name, num, class, define_version, abort_version) \
 		DECLARE_CSR (name, num, class, define_version, abort_version)
 #include "opcode/riscv-opc.h"
 #undef DECLARE_CSR
-		init_csr = true;
+		is_init_csr = true;
 	      }
 
 	    if (riscv_csr_hash[csr] != NULL)
@@ -1283,6 +1302,9 @@  riscv_get_disassembler (bfd *abfd)
 	     in the attributes, use the default value.  */
 	  if (!default_arch)
 	    default_arch = initial_default_arch;
+	  /* By default, set to the newest privileged version.  */
+	  if (default_priv_spec == PRIV_SPEC_CLASS_NONE)
+	    default_priv_spec = PRIV_SPEC_CLASS_DRAFT - 1;
 	}
     }