[1/2] Add support for setting disassembler-options in GDB for POWER, ARM and S390

Message ID b6037acd-9036-7e69-3350-8da5f0ec981b@vnet.ibm.com
State New, archived
Headers

Commit Message

Peter Bergner Nov. 17, 2016, 7:18 p.m. UTC
  Ping.  This is the same as my last patch posted here:

    https://sourceware.org/ml/binutils/2016-11/msg00029.html

...except for that we no longer output "default" for a NULL
disassembler_options value, and instead just output ''.
I will note that doing "set disassembler-options" does reset
the disassembler_options value to NULL.

I'll shortly post a follow-on patch that splits the huge ppc
disassembly test case into multiple test cases which uses the
"set disassembler-options ..." command to correctly choose
the disassembly cpu/ISA to disassemble the tests with.

Is this version ok?

Peter

include/
	* dis-asm.h (parse_arm_disassembler_option): Remove prototype.
	(set_arm_regname_option): Likewise.
	(disassemble_init_s390): New prototype.
	(disassembler_options_names_powerpc): Likewise.
	(disassembler_options_names_arm): Likewise.
	(disassembler_options_desc_arm): Likewise.
	(disassembler_options_names_s390): Likewise.
	(disassembler_options_desc_s390): Likewise.

opcodes/
	* disassemble.c (disassemble_init_for_target): Handle s390 init.
	* ppc-dis.c: Include "libiberty.h".
	(ppc_opts): Add "32" and "64" entries.
	(parse_ppc_dis_option): New function.
	(disassembler_options_names_powerpc): Likewise.
	(powerpc_init_dialect): Use parse_ppc_dis_option().
	Add break to switch statement.
	(print_ppc_disassembler_options): Remove printing of "32" and "64".
	* arm-dis.c: Include "libiberty.h".
	(struct arm_regname): Add 'long_name' field.
	(regnames): Initialize it.
	(set_arm_regname_option): Remove function.
	(parse_arm_disassembler_option): Make static.
	(disassembler_options_names_arm): New function.
	(disassembler_options_desc_arm): Likewise.
	* s390-dis.c: Include "libiberty.h".
	(struct options_t): New structure type.
	(options): New structure.
	(init_disasm): Rename from this...
	(disassemble_init_s390): ...to this.  Add initializations for
	current_arch_mask and option_use_insn_len_bits_p.  Remove init_flag.
	(disassembler_options_names_s390): New function.
	(disassembler_options_desc_s390): Likewise.
	(print_s390_disassembler_options): Print using information from
	struct 'options'.

gdb/
	* gdbarch.sh (gdbarch_disassembler_options): New variable.
	(gdbarch_disassembler_options_names): Likewise.
	(gdbarch_disassembler_options_descriptions): Likewise.
	* gdbarch.c: Regenerate.
	* gdbarch.h: Likewise.
	* disasm.c: Include "arch-utils.h", "gdbcmd.h", "gdbcmd.h" and
	"safe-ctype.h".
	(gdb_disassemble_info): Initilize di.disassembler_options.
	(gdb_buffered_insn_length_init_dis): Initilize di->application_data
	and di->disassembler_options.
	(cleanup_disassembler_options): New function.
	(parse_disassembler_options): Likewise.
	(set_disassembler_options): Likewise.
	(show_disassembler_options): Likewise.
	(disassembler_options_completer): Likewise.
	(_initialize_disasm): Likewise.
	* disasm.h (set_disassembler_options): New prototype.
	(show_disassembler_options): Likewise.
	* rs6000-tdep.c (rs6000_gdbarch_init): Call
	set_gdbarch_disassembler_options_names.
	* arm-tdep.c: Include "disasm.h" and "cli/cli-decode.h".
	(disassembly_style): Delete static variable.
	(set_disassembly_style): Delete function and prototype.
	(show_disassembly_style_sfunc): New function.
	(set_disassembly_style_sfunc): Call set_disassembler_options.
	(arm_gdbarch_init): Call set_gdbarch_disassembler_options,
	set_gdbarch_disassembler_options_names and
	set_gdbarch_disassembler_options_descriptions.
	(_initialize_arm_tdep): New static variable 'disassembly_style';
	Remove calls to parse_arm_disassembler_option & set_arm_regname_option.
	Pass show_disassembly_style_sfunc to the "disassembler" setshow command.
	* s390-tdep.c (s390_gdbarch_init): Call functions
	set_gdbarch_disassembler_options_names and
	set_gdbarch_disassembler_options_descriptions.
  

Comments

Peter Bergner Nov. 17, 2016, 7:51 p.m. UTC | #1
On 11/17/16 1:18 PM, Peter Bergner wrote:
> Ping.  This is the same as my last patch posted here:
>
>     https://sourceware.org/ml/binutils/2016-11/msg00029.html
>
> ...except for that we no longer output "default" for a NULL
> disassembler_options value, and instead just output ''.
> I will note that doing "set disassembler-options" does reset
> the disassembler_options value to NULL.

I will note I only tested ARM and S390 using a cross debugger,
but they seem to work fine.  In addition, I was able to confirm
that using "set disassembler-options ..." and "set arm disassembler ..."
together works fine, meaning you can use them interchangeably.
The only "gotcha", is that "set arm disassembler ..." uses short
option names, while the "set disassebler-options ..." has to use
the canonical long names (eg, "set arm disassembler atpcs" versus
"set disassembler-options reg-names-atpcs").

That above said, a little testing of ARM and S390 by others would
be appreciated.

I did try and take a stab converting i386 over as well, but
opcodes/i386-dis.c:print_insn() contains a lot of global
state that really should be moved into a disassemble_init_i386()
similar to what I did for S390.  There really is no reason to
parse info->disassembler_options for each insn we print.
However, I didn't trust myself at getting that all correct, so
I'll leave it to someone more qualified to do.

Peter
  
Nick Clifton Nov. 18, 2016, 10:25 a.m. UTC | #2
Hi Peter,

> include/
> 	* dis-asm.h (parse_arm_disassembler_option): Remove prototype.
> 	(set_arm_regname_option): Likewise.
> 	(disassemble_init_s390): New prototype.
> 	(disassembler_options_names_powerpc): Likewise.
> 	(disassembler_options_names_arm): Likewise.
> 	(disassembler_options_desc_arm): Likewise.
> 	(disassembler_options_names_s390): Likewise.
> 	(disassembler_options_desc_s390): Likewise.
> 
> opcodes/
> 	* disassemble.c (disassemble_init_for_target): Handle s390 init.
> 	* ppc-dis.c: Include "libiberty.h".
> 	(ppc_opts): Add "32" and "64" entries.
> 	(parse_ppc_dis_option): New function.
> 	(disassembler_options_names_powerpc): Likewise.
> 	(powerpc_init_dialect): Use parse_ppc_dis_option().
> 	Add break to switch statement.
> 	(print_ppc_disassembler_options): Remove printing of "32" and "64".
> 	* arm-dis.c: Include "libiberty.h".
> 	(struct arm_regname): Add 'long_name' field.
> 	(regnames): Initialize it.
> 	(set_arm_regname_option): Remove function.
> 	(parse_arm_disassembler_option): Make static.
> 	(disassembler_options_names_arm): New function.
> 	(disassembler_options_desc_arm): Likewise.
> 	* s390-dis.c: Include "libiberty.h".
> 	(struct options_t): New structure type.
> 	(options): New structure.
> 	(init_disasm): Rename from this...
> 	(disassemble_init_s390): ...to this.  Add initializations for
> 	current_arch_mask and option_use_insn_len_bits_p.  Remove init_flag.
> 	(disassembler_options_names_s390): New function.
> 	(disassembler_options_desc_s390): Likewise.
> 	(print_s390_disassembler_options): Print using information from
> 	struct 'options'.
> 
> gdb/
> 	* gdbarch.sh (gdbarch_disassembler_options): New variable.
> 	(gdbarch_disassembler_options_names): Likewise.
> 	(gdbarch_disassembler_options_descriptions): Likewise.
> 	* gdbarch.c: Regenerate.
> 	* gdbarch.h: Likewise.
> 	* disasm.c: Include "arch-utils.h", "gdbcmd.h", "gdbcmd.h" and
> 	"safe-ctype.h".
> 	(gdb_disassemble_info): Initilize di.disassembler_options.
> 	(gdb_buffered_insn_length_init_dis): Initilize di->application_data
> 	and di->disassembler_options.
> 	(cleanup_disassembler_options): New function.
> 	(parse_disassembler_options): Likewise.
> 	(set_disassembler_options): Likewise.
> 	(show_disassembler_options): Likewise.
> 	(disassembler_options_completer): Likewise.
> 	(_initialize_disasm): Likewise.
> 	* disasm.h (set_disassembler_options): New prototype.
> 	(show_disassembler_options): Likewise.
> 	* rs6000-tdep.c (rs6000_gdbarch_init): Call
> 	set_gdbarch_disassembler_options_names.
> 	* arm-tdep.c: Include "disasm.h" and "cli/cli-decode.h".
> 	(disassembly_style): Delete static variable.
> 	(set_disassembly_style): Delete function and prototype.
> 	(show_disassembly_style_sfunc): New function.
> 	(set_disassembly_style_sfunc): Call set_disassembler_options.
> 	(arm_gdbarch_init): Call set_gdbarch_disassembler_options,
> 	set_gdbarch_disassembler_options_names and
> 	set_gdbarch_disassembler_options_descriptions.
> 	(_initialize_arm_tdep): New static variable 'disassembly_style';
> 	Remove calls to parse_arm_disassembler_option & set_arm_regname_option.
> 	Pass show_disassembly_style_sfunc to the "disassembler" setshow command.
> 	* s390-tdep.c (s390_gdbarch_init): Call functions
> 	set_gdbarch_disassembler_options_names and
> 	set_gdbarch_disassembler_options_descriptions.

The binutils parts are approved.

Question: Is this worth a mention in the NEWS file ? (GDB presumably, although maybe binutils as well ?)

Cheers
  Nick
  

Patch

diff --git a/include/dis-asm.h b/include/dis-asm.h
index 2cefff4..a6b61e2 100644
--- a/include/dis-asm.h
+++ b/include/dis-asm.h
@@ -331,14 +331,18 @@  extern void print_ppc_disassembler_options (FILE *);
 extern void print_riscv_disassembler_options (FILE *);
 extern void print_arm_disassembler_options (FILE *);
 extern void print_arc_disassembler_options (FILE *);
-extern void parse_arm_disassembler_option (char *);
 extern void print_s390_disassembler_options (FILE *);
 extern int  get_arm_regname_num_options (void);
-extern int  set_arm_regname_option (int);
 extern int  get_arm_regnames (int, const char **, const char **, const char *const **);
 extern bfd_boolean aarch64_symbol_is_valid (asymbol *, struct disassemble_info *);
 extern bfd_boolean arm_symbol_is_valid (asymbol *, struct disassemble_info *);
 extern void disassemble_init_powerpc (struct disassemble_info *);
+extern void disassemble_init_s390 (struct disassemble_info *);
+extern const char **disassembler_options_names_powerpc (void);
+extern const char **disassembler_options_names_arm (void);
+extern const char **disassembler_options_desc_arm (void);
+extern const char **disassembler_options_names_s390 (void);
+extern const char **disassembler_options_desc_s390 (void);
 
 /* Fetch the disassembler for a given BFD, if that support is available.  */
 extern disassembler_ftype disassembler (bfd *);
diff --git a/opcodes/disassemble.c b/opcodes/disassemble.c
index e1fb65c..6418ada 100644
--- a/opcodes/disassemble.c
+++ b/opcodes/disassemble.c
@@ -643,6 +643,11 @@  disassemble_init_for_target (struct disassemble_info * info)
       disassemble_init_powerpc (info);
       break;
 #endif
+#ifdef ARCH_s390
+    case bfd_arch_s390:
+      disassemble_init_s390 (info);
+      break;
+#endif
     default:
       break;
     }
diff --git a/opcodes/ppc-dis.c b/opcodes/ppc-dis.c
index da1301e..af8c756 100644
--- a/opcodes/ppc-dis.c
+++ b/opcodes/ppc-dis.c
@@ -26,6 +26,7 @@ 
 #include "elf/ppc.h"
 #include "opintl.h"
 #include "opcode/ppc.h"
+#include "libiberty.h"
 
 /* This file provides several disassembler functions, all of which use
    the disassembler interface defined in dis-asm.h.  Several functions
@@ -172,8 +173,12 @@  struct ppc_mopt ppc_opts[] = {
     0 },
   { "ppc32",   PPC_OPCODE_PPC,
     0 },
+  { "32",      PPC_OPCODE_PPC,
+    0 },
   { "ppc64",   PPC_OPCODE_PPC | PPC_OPCODE_64,
     0 },
+  { "64",      PPC_OPCODE_PPC | PPC_OPCODE_64,
+    0 },
   { "ppc64bridge", PPC_OPCODE_PPC | PPC_OPCODE_64_BRIDGE,
     0 },
   { "ppcps",   PPC_OPCODE_PPC | PPC_OPCODE_PPCPS,
@@ -271,6 +276,27 @@  ppc_parse_cpu (ppc_cpu_t ppc_cpu, ppc_cpu_t *sticky, const char *arg)
   return ppc_cpu;
 }
 
+/* Parse the OPTIONS argument looking for ',' seperated cpu names.
+   Return the length of the current option, or 0 if there are no more.
+   Update OPTIONS to point to the next option, or NULL otherwise.  */
+
+static char *
+parse_ppc_dis_option (char *cpu, const char *options)
+{
+  char *next = strchr (options, ',');
+
+  if (next != NULL)
+    {
+      strncpy (cpu, options, (size_t) (next - options));
+      cpu[(size_t) (next - options)] = 0;
+      next++;
+    }
+  else
+    strcpy (cpu, options);
+
+  return next;
+}
+
 /* Determine which set of machines to disassemble for.  */
 
 static void
@@ -324,29 +350,24 @@  powerpc_init_dialect (struct disassemble_info *info)
       break;
     default:
       dialect = ppc_parse_cpu (dialect, &sticky, "power9") | PPC_OPCODE_ANY;
+      break;
     }
 
   arg = info->disassembler_options;
   while (arg != NULL)
     {
       ppc_cpu_t new_cpu = 0;
-      char *end = strchr (arg, ',');
+      char opt[64];
+      arg = parse_ppc_dis_option (opt, arg);
 
-      if (end != NULL)
-	*end = 0;
-
-      if ((new_cpu = ppc_parse_cpu (dialect, &sticky, arg)) != 0)
-	dialect = new_cpu;
-      else if (strcmp (arg, "32") == 0)
+      if (strcmp (opt, "32") == 0)
 	dialect &= ~(ppc_cpu_t) PPC_OPCODE_64;
-      else if (strcmp (arg, "64") == 0)
+      else if (strcmp (opt, "64") == 0)
 	dialect |= PPC_OPCODE_64;
+      else if ((new_cpu = ppc_parse_cpu (dialect, &sticky, opt)) != 0)
+	dialect = new_cpu;
       else
-	fprintf (stderr, _("warning: ignoring unknown -M%s option\n"), arg);
-
-      if (end != NULL)
-	*end++ = ',';
-      arg = end;
+	fprintf (stderr, _("warning: ignoring unknown -M%s option\n"), opt);
     }
 
   info->private_data = priv;
@@ -767,6 +788,24 @@  print_insn_powerpc (bfd_vma memaddr,
   return 4;
 }
 
+const char **
+disassembler_options_names_powerpc (void)
+{
+  static const char **options = NULL;
+
+  if (options == NULL)
+    {
+      size_t i;
+      size_t num_options = sizeof (ppc_opts) / sizeof (ppc_opts[0]);
+      options = XNEWVEC (const char *, num_options + 1);
+      for (i = 0; i < num_options; i++)
+	options[i] = ppc_opts[i].opt;
+      options[i] = NULL;
+    }
+
+  return options;
+}
+
 void
 print_ppc_disassembler_options (FILE *stream)
 {
@@ -785,5 +824,5 @@  the -M switch:\n"));
 	  col = 0;
 	}
     }
-  fprintf (stream, " 32, 64\n");
+  fprintf (stream, "\n");
 }
diff --git a/opcodes/arm-dis.c b/opcodes/arm-dis.c
index 87d4930..939005d 100644
--- a/opcodes/arm-dis.c
+++ b/opcodes/arm-dis.c
@@ -26,6 +26,7 @@ 
 #include "opcode/arm.h"
 #include "opintl.h"
 #include "safe-ctype.h"
+#include "libiberty.h"
 #include "floatformat.h"
 
 /* FIXME: This shouldn't be done here.  */
@@ -3164,6 +3165,7 @@  static const char *const arm_shift[] =
 typedef struct
 {
   const char *name;
+  const char *long_name;
   const char *description;
   const char *reg_names[16];
 }
@@ -3171,17 +3173,17 @@  arm_regname;
 
 static const arm_regname regnames[] =
 {
-  { "raw" , "Select raw register names",
+  { "raw" , "reg-names-raw", "Select raw register names",
     { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}},
-  { "gcc",  "Select register names used by GCC",
+  { "gcc",  "reg-names-gcc", "Select register names used by GCC",
     { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "sl",  "fp",  "ip",  "sp",  "lr",  "pc" }},
-  { "std",  "Select register names used in ARM's ISA documentation",
+  { "std",  "reg-names-std", "Select register names used in ARM's ISA documentation",
     { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "sp",  "lr",  "pc" }},
-  { "apcs", "Select register names used in the APCS",
+  { "apcs", "reg-names-apcs", "Select register names used in the APCS",
     { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "sl",  "fp",  "ip",  "sp",  "lr",  "pc" }},
-  { "atpcs", "Select register names used in the ATPCS",
+  { "atpcs", "reg-names-atpcs", "Select register names used in the ATPCS",
     { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "v7",  "v8",  "IP",  "SP",  "LR",  "PC" }},
-  { "special-atpcs", "Select special register names used in the ATPCS",
+  { "special-atpcs", "reg-names-special-atpcs", "Select special register names used in the ATPCS",
     { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "WR", "v5", "SB", "SL",  "FP",  "IP",  "SP",  "LR",  "PC" }},
 };
 
@@ -3234,14 +3236,6 @@  get_arm_regname_num_options (void)
 }
 
 int
-set_arm_regname_option (int option)
-{
-  int old = regname_selected;
-  regname_selected = option;
-  return old;
-}
-
-int
 get_arm_regnames (int option,
 		  const char **setname,
 		  const char **setdescription,
@@ -6094,7 +6088,7 @@  arm_symbol_is_valid (asymbol * sym,
 
 /* Parse an individual disassembler option.  */
 
-void
+static void
 parse_arm_disassembler_option (char *option)
 {
   if (option == NULL)
@@ -6810,6 +6804,46 @@  print_insn_little_arm (bfd_vma pc, struct disassemble_info *info)
   return print_insn (pc, info, TRUE);
 }
 
+const char **
+disassembler_options_names_arm (void)
+{
+  static const char **options = NULL;
+
+  if (options == NULL)
+    {
+      size_t i;
+      size_t num_options = NUM_ARM_REGNAMES + 2;
+      options = XNEWVEC (const char *, num_options + 1);
+      for (i = 0; i < NUM_ARM_REGNAMES; i++)
+	options[i] = regnames[i].long_name;
+      options[i++] = "force-thumb";
+      options[i++] = "no-force-thumb";
+      options[i] = NULL;
+    }
+
+  return options;
+}
+
+const char **
+disassembler_options_desc_arm (void)
+{
+  static const char **desc = NULL;
+
+  if (desc == NULL)
+    {
+      size_t i;
+      size_t num_desc = NUM_ARM_REGNAMES + 2;
+      desc = XNEWVEC (const char *, num_desc + 1);
+      for (i = 0; i < NUM_ARM_REGNAMES; i++)
+	desc[i] = regnames[i].description;
+      desc[i++] = "Assume all insns are Thumb insns";
+      desc[i++] = "Examine preceding label to determine an insn's type";
+      desc[i] = NULL;
+    }
+
+  return desc;
+}
+
 void
 print_arm_disassembler_options (FILE *stream)
 {
diff --git a/opcodes/s390-dis.c b/opcodes/s390-dis.c
index 8134073..f6e85de 100644
--- a/opcodes/s390-dis.c
+++ b/opcodes/s390-dis.c
@@ -25,16 +25,30 @@ 
 #include "dis-asm.h"
 #include "opintl.h"
 #include "opcode/s390.h"
+#include "libiberty.h"
 
-static int init_flag = 0;
 static int opc_index[256];
 static int current_arch_mask = 0;
 static int option_use_insn_len_bits_p = 0;
 
+typedef struct
+{
+  const char *name;
+  const char *description;
+} options_t;
+
+static const options_t options[] =
+{
+  { "esa" ,       "Disassemble in ESA architecture mode" },
+  { "zarch",      "Disassemble in z/Architecture mode" },
+  { "insnlength", "Print unknown instructions according to "
+		  "length from first two bits" }
+};
+
 /* Set up index table for first opcode byte.  */
 
-static void
-init_disasm (struct disassemble_info *info)
+void
+disassemble_init_s390 (struct disassemble_info *info)
 {
   int i;
   const char *p;
@@ -46,6 +60,9 @@  init_disasm (struct disassemble_info *info)
   for (i = s390_num_opcodes; i--; )
     opc_index[s390_opcodes[i].opcode[0]] = i;
 
+  current_arch_mask = 1 << S390_OPCODE_ZARCH;
+  option_use_insn_len_bits_p = 0;
+
   for (p = info->disassembler_options; p != NULL; )
     {
       if (CONST_STRNEQ (p, "esa"))
@@ -61,11 +78,6 @@  init_disasm (struct disassemble_info *info)
       if (p != NULL)
 	p++;
     }
-
-  if (!current_arch_mask)
-    current_arch_mask = 1 << S390_OPCODE_ZARCH;
-
-  init_flag = 1;
 }
 
 /* Derive the length of an instruction from its first byte.  */
@@ -266,9 +278,6 @@  print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info)
   unsigned int value;
   int status, opsize, bufsize, bytes_to_dump, i;
 
-  if (init_flag == 0)
-    init_disasm (info);
-
   /* The output looks better if we put 6 bytes on a line.  */
   info->bytes_per_line = 6;
 
@@ -360,15 +369,58 @@  print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info)
   return 0;
 }
 
+const char **
+disassembler_options_names_s390 (void)
+{
+  static const char **opts = NULL;
+
+  if (opts == NULL)
+    {
+      size_t i, num_options = sizeof (options) / sizeof (options[0]);
+      opts = XNEWVEC (const char *, num_options);
+      for (i = 0; i < num_options; i++)
+	opts[i] = options[i].name;
+      opts[i] = NULL;
+    }
+
+  return opts;
+}
+
+const char **
+disassembler_options_desc_s390 (void)
+{
+  static const char **desc = NULL;
+
+  if (desc == NULL)
+    {
+      size_t i, num_options = sizeof (options) / sizeof (options[0]);
+      desc = XNEWVEC (const char *, num_options + 1);
+      for (i = 0; i < num_options; i++)
+	desc[i] = options[i].description;
+      desc[i] = NULL;
+    }
+
+  return desc;
+}
+
 void
 print_s390_disassembler_options (FILE *stream)
 {
+  unsigned int i, max_len = 0;
   fprintf (stream, _("\n\
 The following S/390 specific disassembler options are supported for use\n\
 with the -M switch (multiple options should be separated by commas):\n"));
 
-  fprintf (stream, _("  esa         Disassemble in ESA architecture mode\n"));
-  fprintf (stream, _("  zarch       Disassemble in z/Architecture mode\n"));
-  fprintf (stream, _("  insnlength  Print unknown instructions according "
-		     "to length from first two bits\n"));
+  for (i = 0; sizeof (options) / sizeof (options[0]); i++)
+    {
+      unsigned int len = strlen (options[i].name);
+      if (max_len < len)
+	max_len = len;
+    }
+
+  for (i = 0, max_len++; sizeof (options) / sizeof (options[0]); i++)
+    fprintf (stream, "  %s%*c %s",
+	     options[i].name,
+	     (int)(max_len - strlen (options[i].name)), ' ',
+	     options[i].description);
 }
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index 28a3478..deba9db 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -1163,6 +1163,11 @@  m:const char *:gnu_triplet_regexp:void:::default_gnu_triplet_regexp::0
 # each address in memory.
 m:int:addressable_memory_unit_size:void:::default_addressable_memory_unit_size::0
 
+# Functions for allowing a target to modify its disassembler options.
+v:char *:disassembler_options:::0:0::0:pstring (gdbarch->disassembler_options)
+v:const char **:disassembler_options_names:::0:0::0:pstring (*gdbarch->disassembler_options_names)
+v:const char **:disassembler_options_descriptions:::0:0::0:pstring (*gdbarch->disassembler_options_descriptions)
+
 EOF
 }
 
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 07b3ce5..7bc9806 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -339,6 +339,9 @@  struct gdbarch
   gdbarch_gcc_target_options_ftype *gcc_target_options;
   gdbarch_gnu_triplet_regexp_ftype *gnu_triplet_regexp;
   gdbarch_addressable_memory_unit_size_ftype *addressable_memory_unit_size;
+  char * disassembler_options;
+  const char ** disassembler_options_names;
+  const char ** disassembler_options_descriptions;
 };
 
 /* Create a new ``struct gdbarch'' based on information provided by
@@ -696,6 +699,9 @@  verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of gcc_target_options, invalid_p == 0 */
   /* Skip verify of gnu_triplet_regexp, invalid_p == 0 */
   /* Skip verify of addressable_memory_unit_size, invalid_p == 0 */
+  /* Skip verify of disassembler_options, invalid_p == 0 */
+  /* Skip verify of disassembler_options_names, invalid_p == 0 */
+  /* Skip verify of disassembler_options_descriptions, invalid_p == 0 */
   std::string buf = ui_file_as_string (log);
   if (!buf.empty ())
     internal_error (__FILE__, __LINE__,
@@ -881,6 +887,15 @@  gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: deprecated_function_start_offset = %s\n",
                       core_addr_to_string_nz (gdbarch->deprecated_function_start_offset));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: disassembler_options = %s\n",
+                      pstring (gdbarch->disassembler_options));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: disassembler_options_descriptions = %s\n",
+                      pstring (*gdbarch->disassembler_options_descriptions));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: disassembler_options_names = %s\n",
+                      pstring (*gdbarch->disassembler_options_names));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: gdbarch_displaced_step_copy_insn_p() = %d\n",
                       gdbarch_displaced_step_copy_insn_p (gdbarch));
   fprintf_unfiltered (file,
@@ -4962,6 +4977,57 @@  set_gdbarch_addressable_memory_unit_size (struct gdbarch *gdbarch,
   gdbarch->addressable_memory_unit_size = addressable_memory_unit_size;
 }
 
+char *
+gdbarch_disassembler_options (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of disassembler_options, invalid_p == 0 */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_disassembler_options called\n");
+  return gdbarch->disassembler_options;
+}
+
+void
+set_gdbarch_disassembler_options (struct gdbarch *gdbarch,
+                                  char * disassembler_options)
+{
+  gdbarch->disassembler_options = disassembler_options;
+}
+
+const char **
+gdbarch_disassembler_options_names (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of disassembler_options_names, invalid_p == 0 */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_disassembler_options_names called\n");
+  return gdbarch->disassembler_options_names;
+}
+
+void
+set_gdbarch_disassembler_options_names (struct gdbarch *gdbarch,
+                                        const char ** disassembler_options_names)
+{
+  gdbarch->disassembler_options_names = disassembler_options_names;
+}
+
+const char **
+gdbarch_disassembler_options_descriptions (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of disassembler_options_descriptions, invalid_p == 0 */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_disassembler_options_descriptions called\n");
+  return gdbarch->disassembler_options_descriptions;
+}
+
+void
+set_gdbarch_disassembler_options_descriptions (struct gdbarch *gdbarch,
+                                               const char ** disassembler_options_descriptions)
+{
+  gdbarch->disassembler_options_descriptions = disassembler_options_descriptions;
+}
+
 
 /* Keep a registry of per-architecture data-pointers required by GDB
    modules.  */
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index cc95914..ee82677 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -1545,6 +1545,17 @@  typedef int (gdbarch_addressable_memory_unit_size_ftype) (struct gdbarch *gdbarc
 extern int gdbarch_addressable_memory_unit_size (struct gdbarch *gdbarch);
 extern void set_gdbarch_addressable_memory_unit_size (struct gdbarch *gdbarch, gdbarch_addressable_memory_unit_size_ftype *addressable_memory_unit_size);
 
+/* Functions for allowing a target to modify its disassembler options. */
+
+extern char * gdbarch_disassembler_options (struct gdbarch *gdbarch);
+extern void set_gdbarch_disassembler_options (struct gdbarch *gdbarch, char * disassembler_options);
+
+extern const char ** gdbarch_disassembler_options_names (struct gdbarch *gdbarch);
+extern void set_gdbarch_disassembler_options_names (struct gdbarch *gdbarch, const char ** disassembler_options_names);
+
+extern const char ** gdbarch_disassembler_options_descriptions (struct gdbarch *gdbarch);
+extern void set_gdbarch_disassembler_options_descriptions (struct gdbarch *gdbarch, const char ** disassembler_options_descriptions);
+
 /* Definition for an unknown syscall, used basically in error-cases.  */
 #define UNKNOWN_SYSCALL (-1)
 
diff --git a/gdb/disasm.c b/gdb/disasm.c
index 07c3abe..cb039bab 100644
--- a/gdb/disasm.c
+++ b/gdb/disasm.c
@@ -18,13 +18,17 @@ 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "arch-utils.h"
 #include "target.h"
 #include "value.h"
 #include "ui-out.h"
 #include "disasm.h"
 #include "gdbcore.h"
+#include "gdbcmd.h"
 #include "dis-asm.h"
+#include "cli/cli-decode.h"
 #include "source.h"
+#include "safe-ctype.h"
 #include <algorithm>
 
 /* Disassemble functions.
@@ -785,6 +789,7 @@  gdb_disassemble_info (struct gdbarch *gdbarch, struct ui_file *file)
   di.endian = gdbarch_byte_order (gdbarch);
   di.endian_code = gdbarch_byte_order_for_code (gdbarch);
   di.application_data = gdbarch;
+  di.disassembler_options = gdbarch_disassembler_options (gdbarch);
   disassemble_init_for_target (&di);
   return di;
 }
@@ -901,6 +906,8 @@  gdb_buffered_insn_length_init_dis (struct gdbarch *gdbarch,
   di->endian = gdbarch_byte_order (gdbarch);
   di->endian_code = gdbarch_byte_order_for_code (gdbarch);
 
+  di->application_data = gdbarch;
+  di->disassembler_options = gdbarch_disassembler_options (gdbarch);
   disassemble_init_for_target (di);
 }
 
@@ -917,3 +924,222 @@  gdb_buffered_insn_length (struct gdbarch *gdbarch,
 
   return gdbarch_print_insn (gdbarch, addr, &di);
 }
+
+/* Remove whitespace and consecutive commas from OPTIONS.  */
+
+char *
+cleanup_disassembler_options (char *options)
+{
+  char *str;
+  size_t i, len;
+
+  if (options == NULL)
+    return NULL;
+
+  /* Strip off all trailing whitespace and commas.  */
+  for (len = strlen (options); len > 0; len--)
+    {
+      if (!ISSPACE (options[len - 1]) && options[len - 1] != ',')
+	break;
+      options[len - 1] = '\0';
+    }
+
+  /* Convert all remaining whitespace to commas.  */
+  for (i = 0; options[i] != '\0'; i++)
+    if (ISSPACE (options[i]))
+      options[i] = ',';
+
+  /* Remove consecutive commas.  */
+  for (str = options; *str != '\0'; str++)
+    if (*str == ',' && *(str + 1) == ',')
+      {
+	char *next = str++;
+	while (*next == ',')
+	  next++;
+	len = strlen (next);
+	memmove (str, next, len);
+	next[len - (size_t)(next - str)] = '\0';
+      }
+  return (strlen (options) != 0) ? options : NULL;
+}
+
+/* Parse OPTIONS looking for ',' seperated disassembler options and
+   verify each option is valid.  Return NULL if all options are valid.
+   Otherwise, return a pointer to the first invalid disassembler option.  */
+
+static char *
+parse_disassembler_options (const char *options, const char **valid_names)
+{
+  static char opt[256];
+  size_t i;
+
+  if (options == NULL)
+    return NULL;
+
+  while (*options)
+    {
+      /* Copy the current disassembler option into OPT.  */
+      const char *separator = strchr (options, ',');
+      if (separator != NULL)
+	{
+	  strncpy (opt, options, (size_t) (separator - options));
+	  opt[(size_t) (separator - options)] = 0;
+	  options = separator;
+	  /* Skip to the next disassembler option.  */
+	  while (*options == ',')
+	    options++;
+	}
+      else
+	options = stpcpy (opt, options);
+
+      /* Verify OPT is a valid disassembler option.  */
+      for (i = 0; valid_names[i] != NULL; i++)
+	if (strcmp (opt, valid_names[i]) == 0)
+	  break;
+      if (valid_names[i] == NULL)
+	return opt;
+    }
+
+  return NULL;
+}
+
+void
+set_disassembler_options (char *args, int from_tty, struct cmd_list_element *c)
+{
+  struct gdbarch *gdbarch = get_current_arch ();
+  const char **valid_names = gdbarch_disassembler_options_names (gdbarch);
+  if (valid_names  == NULL)
+    {
+      fprintf_filtered (gdb_stdlog, _("\
+'set disassembler' is not supported on this architecture.\n"));
+      return;
+    }
+
+  char *options = *(char **)c->var;
+  options = cleanup_disassembler_options (options);
+  char *opt = parse_disassembler_options (options, valid_names);
+  if (opt != NULL)
+    {
+      fprintf_filtered (gdb_stdlog,
+			_("Invalid disassembler option value: '%s'.\n"), opt);
+      return;
+    }
+
+  free (gdbarch_disassembler_options (gdbarch));
+  if (options != NULL)
+    set_gdbarch_disassembler_options (gdbarch, xstrdup (options));
+  else
+    set_gdbarch_disassembler_options (gdbarch, NULL);
+}
+
+void
+show_disassembler_options (struct ui_file *file, int from_tty,
+			   struct cmd_list_element *c, const char *value)
+{
+  struct gdbarch *gdbarch = get_current_arch ();
+
+  const char *options = gdbarch_disassembler_options (gdbarch);
+  if (options == NULL)
+    options = "";
+
+  fprintf_filtered (file, _("The current disassembler options are '%s'\n"),
+		    options);
+
+  const char **names = gdbarch_disassembler_options_names (gdbarch);
+  const char **desc = gdbarch_disassembler_options_descriptions (gdbarch);
+
+  if (names == NULL)
+    return;
+
+  fprintf_filtered (file, _("\n\
+The following disassembler options are supported for use with\n\
+the 'set disassembler-options <option>[,<option>...]' command:\n"));
+
+  if (desc != NULL)
+    {
+      size_t i, max_len = 0;
+      for (i = 0; names[i] != NULL; i++)
+	{
+	  size_t len = strlen (names[i]);
+	  if (max_len < len)
+	    max_len = len;
+	}
+
+      for (i = 0, max_len++; names[i] != NULL; i++)
+	{
+	  fprintf_filtered (file, "  %s", names[i]);
+	  if (desc[i] != NULL)
+	    fprintf_filtered (file, "%*c %s",
+			      (int)(max_len - strlen (names[i])), ' ',
+			      desc[i]);
+	  fprintf_filtered (file, "\n");
+	}
+    }
+  else
+    {
+      size_t i, col;
+      for (i = 0, col = 0; names[i] != NULL; i++)
+	{
+	  /* Include the " " and "," we print below.  */
+	  size_t len = strlen (names[i]) + 2;
+	  if (col + len > 80)
+	    {
+	      fprintf_filtered (file, "\n");
+	      col = 0;
+	    }
+	  if (col == 0)
+	    fprintf_filtered (file, "  %s", names[i]);
+	  else
+	    fprintf_filtered (file, ", %s", names[i]);
+	  col += len;
+	}
+      fprintf_filtered (file, "\n");
+    }
+}
+
+/* A completion function for "set disassembler".  */
+
+static VEC (char_ptr) *
+disassembler_options_completer (struct cmd_list_element *ignore,
+				const char *text, const char *word)
+{
+  struct gdbarch *gdbarch = get_current_arch ();
+  const char **options = gdbarch_disassembler_options_names (gdbarch);
+
+  if (options != NULL)
+    {
+      /* Only attempt to complete on the last option text.  */
+      const char *separator = strrchr (text, ',');
+      if (separator != NULL)
+	text = separator + 1;
+      while (ISSPACE (*text))
+	text++;
+      return complete_on_enum (options, text, word);
+    }
+  return NULL;
+}
+
+
+/* Initialization code.  */
+
+/* -Wmissing-prototypes */
+extern initialize_file_ftype _initialize_disasm;
+
+void
+_initialize_disasm (void)
+{
+  static char *prospective_options = NULL;
+  struct cmd_list_element *cmd;
+
+  /* Add the command that controls the disassembler options.  */
+  cmd = add_setshow_string_noescape_cmd ("disassembler-options", no_class,
+					 &prospective_options, _("\
+Set the disassembler options.\n\
+Usage: set disassembler <options>\n\n\
+See: show disassembler' for valid option values.\n"), _("\
+Show the disassembler options."), NULL,
+					 set_disassembler_options,
+					 show_disassembler_options,
+					 &setlist, &showlist);
+  set_cmd_completer (cmd, disassembler_options_completer);
+}
diff --git a/gdb/disasm.h b/gdb/disasm.h
index a2b72b9..ec1de8b 100644
--- a/gdb/disasm.h
+++ b/gdb/disasm.h
@@ -83,4 +83,8 @@  extern int gdb_buffered_insn_length (struct gdbarch *gdbarch,
 				     const gdb_byte *insn, int max_len,
 				     CORE_ADDR memaddr);
 
+extern void set_disassembler_options (char *, int, struct cmd_list_element *);
+extern void show_disassembler_options (struct ui_file *, int,
+				       struct cmd_list_element *, const char *);
+
 #endif
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 5e10893..8262b76 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -6594,6 +6594,9 @@  rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   else
     register_ppc_ravenscar_ops (gdbarch);
 
+  set_gdbarch_disassembler_options_names
+    (gdbarch, disassembler_options_names_powerpc ());
+
   return gdbarch;
 }
 
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 78fc264..7685977 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -27,6 +27,8 @@ 
 #include "gdbcmd.h"
 #include "gdbcore.h"
 #include "dis-asm.h"		/* For register styles.  */
+#include "disasm.h"
+#include "cli/cli-decode.h"
 #include "regcache.h"
 #include "reggroups.h"
 #include "doublest.h"
@@ -210,14 +212,13 @@  static const char *const arm_register_names[] =
 /* Valid register name styles.  */
 static const char **valid_disassembly_styles;
 
-/* Disassembly style to use. Default to "std" register names.  */
-static const char *disassembly_style;
-
 /* This is used to keep the bfd arch_info in sync with the disassembly
    style.  */
 static void set_disassembly_style_sfunc(char *, int,
 					 struct cmd_list_element *);
-static void set_disassembly_style (void);
+static void show_disassembly_style_sfunc (struct ui_file *, int,
+					  struct cmd_list_element *,
+					  const char *);
 
 static void convert_from_extended (const struct floatformat *, const void *,
 				   void *, int);
@@ -8536,9 +8537,27 @@  arm_show_force_mode (struct ui_file *file, int from_tty,
 
 static void
 set_disassembly_style_sfunc (char *args, int from_tty,
-			      struct cmd_list_element *c)
+			     struct cmd_list_element *c)
+{
+  /* Convert the short style name into the long style name (eg, reg-names-*)
+     before calling the generic set_disassembler_options() function.  */
+  char long_name[256], *style = long_name;
+  snprintf (style, 256, "reg-names-%s", *(char **)c->var);
+  c->var = &style;
+  set_disassembler_options (args, from_tty, c);
+}
+
+static void
+show_disassembly_style_sfunc (struct ui_file *file, int from_tty,
+			      struct cmd_list_element *c, const char *value)
 {
-  set_disassembly_style ();
+  struct gdbarch *gdbarch = get_current_arch ();
+  const char *style = gdbarch_disassembler_options (gdbarch);
+  if (style == NULL)
+    style = "default";
+  else if (CONST_STRNEQ (style, "reg-names-"))
+    style += strlen ("reg-names-");
+  fprintf_unfiltered (file, "The disassembly style is \"%s\".\n", style);
 }
 
 /* Return the ARM register name corresponding to register I.  */
@@ -8579,21 +8598,6 @@  arm_register_name (struct gdbarch *gdbarch, int i)
   return arm_register_names[i];
 }
 
-static void
-set_disassembly_style (void)
-{
-  int current;
-
-  /* Find the style that the user wants.  */
-  for (current = 0; current < num_disassembly_options; current++)
-    if (disassembly_style == valid_disassembly_styles[current])
-      break;
-  gdb_assert (current < num_disassembly_options);
-
-  /* Synchronize the disassembler.  */
-  set_arm_regname_option (current);
-}
-
 /* Test whether the coff symbol specific value corresponds to a Thumb
    function.  */
 
@@ -9558,6 +9562,12 @@  arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
     user_reg_add (gdbarch, arm_register_aliases[i].name,
 		  value_of_arm_user_reg, &arm_register_aliases[i].regnum);
 
+  set_gdbarch_disassembler_options (gdbarch, xstrdup ("reg-names-std"));
+  set_gdbarch_disassembler_options_names (gdbarch,
+					  disassembler_options_names_arm ());
+  set_gdbarch_disassembler_options_descriptions
+    (gdbarch, disassembler_options_desc_arm ());
+
   return gdbarch;
 }
 
@@ -9578,6 +9588,9 @@  extern initialize_file_ftype _initialize_arm_tdep; /* -Wmissing-prototypes */
 void
 _initialize_arm_tdep (void)
 {
+  /* Disassembly style to use. Default to "std" register names.  */
+  static const char *disassembly_style;
+
   struct ui_file *stb;
   long length;
   const char *setname;
@@ -9624,9 +9637,6 @@  _initialize_arm_tdep (void)
 		  _("Various ARM-specific commands."),
 		  &showarmcmdlist, "show arm ", 0, &showlist);
 
-  /* Sync the opcode insn printer with our register viewer.  */
-  parse_arm_disassembler_option ("reg-names-std");
-
   /* Initialize the array that will be passed to
      add_setshow_enum_cmd().  */
   valid_disassembly_styles = XNEWVEC (const char *,
@@ -9638,13 +9648,6 @@  _initialize_arm_tdep (void)
       length = snprintf (rdptr, rest, "%s - %s\n", setname, setdesc);
       rdptr += length;
       rest -= length;
-      /* When we find the default names, tell the disassembler to use
-	 them.  */
-      if (!strcmp (setname, "std"))
-	{
-          disassembly_style = setname;
-          set_arm_regname_option (i);
-	}
     }
   /* Mark the end of valid options.  */
   valid_disassembly_styles[num_disassembly_options] = NULL;
@@ -9664,8 +9667,7 @@  _initialize_arm_tdep (void)
 		       _("Show the disassembly style."),
 		       helptext.c_str (),
 		       set_disassembly_style_sfunc,
-		       NULL, /* FIXME: i18n: The disassembly style is
-				\"%s\".  */
+		       show_disassembly_style_sfunc,
 		       &setarmcmdlist, &showarmcmdlist);
 
   add_setshow_boolean_cmd ("apcs32", no_class, &arm_apcs_32,
diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
index 885aadd..f2305c5 100644
--- a/gdb/s390-linux-tdep.c
+++ b/gdb/s390-linux-tdep.c
@@ -8114,6 +8114,11 @@  s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   s390_init_linux_record_tdep (&s390_linux_record_tdep, ABI_LINUX_S390);
   s390_init_linux_record_tdep (&s390x_linux_record_tdep, ABI_LINUX_ZSERIES);
 
+  set_gdbarch_disassembler_options_names (gdbarch,
+					  disassembler_options_names_s390 ());
+  set_gdbarch_disassembler_options_descriptions
+    (gdbarch, disassembler_options_desc_s390 ());
+
   return gdbarch;
 }