PR29262, memory leak in pr_function_type
[binutils-gdb.git] / gdb / disasm.c
index 11793df67c0da2459f327f85c118aeaca3763065..c6edc92930d66db7c77068679fda81aa5456ae53 100644 (file)
@@ -1,6 +1,6 @@
 /* Disassemble support for GDB.
 
-   Copyright (C) 2000-2018 Free Software Foundation, Inc.
+   Copyright (C) 2000-2022 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -29,8 +29,9 @@
 #include "source.h"
 #include "safe-ctype.h"
 #include <algorithm>
-#include "common/gdb_optional.h"
+#include "gdbsupport/gdb_optional.h"
 #include "valprint.h"
+#include "cli/cli-style.h"
 
 /* Disassemble functions.
    FIXME: We should get rid of all the duplicate code in gdb that does
@@ -38,7 +39,7 @@
 
 /* This variable is used to hold the prospective disassembler_options value
    which is set by the "set disassembler_options" command.  */
-static char *prospective_options = NULL;
+static std::string prospective_options;
 
 /* This structure is used to store line number information for the
    deprecated /m option.
@@ -131,9 +132,9 @@ line_has_code_p (htab_t table, struct symtab *symtab, int line)
 /* Wrapper of target_read_code.  */
 
 int
-gdb_disassembler::dis_asm_read_memory (bfd_vma memaddr, gdb_byte *myaddr,
-                                      unsigned int len,
-                                      struct disassemble_info *info)
+gdb_disassembler_memory_reader::dis_asm_read_memory
+  (bfd_vma memaddr, gdb_byte *myaddr, unsigned int len,
+   struct disassemble_info *info)
 {
   return target_read_code (memaddr, myaddr, len);
 }
@@ -147,7 +148,7 @@ gdb_disassembler::dis_asm_memory_error (int err, bfd_vma memaddr,
   gdb_disassembler *self
     = static_cast<gdb_disassembler *>(info->application_data);
 
-  self->m_err_memaddr = memaddr;
+  self->m_err_memaddr.emplace (memaddr);
 }
 
 /* Wrapper of print_address.  */
@@ -162,28 +163,58 @@ gdb_disassembler::dis_asm_print_address (bfd_vma addr,
   print_address (self->arch (), addr, self->stream ());
 }
 
-static int
-compare_lines (const void *mle1p, const void *mle2p)
+/* Format disassembler output to STREAM.  */
+
+int
+gdb_printing_disassembler::fprintf_func (void *stream,
+                                        const char *format, ...)
 {
-  struct deprecated_dis_line_entry *mle1, *mle2;
-  int val;
+  va_list args;
+
+  va_start (args, format);
+  gdb_vprintf ((struct ui_file *) stream, format, args);
+  va_end (args);
+  /* Something non -ve.  */
+  return 0;
+}
+
+/* See disasm.h.  */
 
-  mle1 = (struct deprecated_dis_line_entry *) mle1p;
-  mle2 = (struct deprecated_dis_line_entry *) mle2p;
+int
+gdb_printing_disassembler::fprintf_styled_func (void *stream,
+                                               enum disassembler_style style,
+                                               const char *format, ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  gdb_vprintf ((struct ui_file *) stream, format, args);
+  va_end (args);
+  /* Something non -ve.  */
+  return 0;
+}
+
+static bool
+line_is_less_than (const deprecated_dis_line_entry &mle1,
+                  const deprecated_dis_line_entry &mle2)
+{
+  bool val;
 
   /* End of sequence markers have a line number of 0 but don't want to
      be sorted to the head of the list, instead sort by PC.  */
-  if (mle1->line == 0 || mle2->line == 0)
+  if (mle1.line == 0 || mle2.line == 0)
     {
-      val = mle1->start_pc - mle2->start_pc;
-      if (val == 0)
-        val = mle1->line - mle2->line;
+      if (mle1.start_pc != mle2.start_pc)
+       val = mle1.start_pc < mle2.start_pc;
+      else
+       val = mle1.line < mle2.line;
     }
   else
     {
-      val = mle1->line - mle2->line;
-      if (val == 0)
-        val = mle1->start_pc - mle2->start_pc;
+      if (mle1.line != mle2.line)
+       val = mle1.line < mle2.line;
+      else
+       val = mle1.start_pc < mle2.start_pc;
     }
   return val;
 }
@@ -191,8 +222,7 @@ compare_lines (const void *mle1p, const void *mle2p)
 /* See disasm.h.  */
 
 int
-gdb_pretty_print_disassembler::pretty_print_insn (struct ui_out *uiout,
-                                                 const struct disasm_insn *insn,
+gdb_pretty_print_disassembler::pretty_print_insn (const struct disasm_insn *insn,
                                                  gdb_disassembly_flags flags)
 {
   /* parts of the symbolic representation of the address */
@@ -204,56 +234,93 @@ gdb_pretty_print_disassembler::pretty_print_insn (struct ui_out *uiout,
   struct gdbarch *gdbarch = arch ();
 
   {
-    ui_out_emit_tuple tuple_emitter (uiout, NULL);
+    ui_out_emit_tuple tuple_emitter (m_uiout, NULL);
     pc = insn->addr;
 
     if (insn->number != 0)
       {
-       uiout->field_fmt ("insn-number", "%u", insn->number);
-       uiout->text ("\t");
+       m_uiout->field_unsigned ("insn-number", insn->number);
+       m_uiout->text ("\t");
       }
 
     if ((flags & DISASSEMBLY_SPECULATIVE) != 0)
       {
        if (insn->is_speculative)
          {
-           uiout->field_string ("is-speculative", "?");
+           m_uiout->field_string ("is-speculative", "?");
 
            /* The speculative execution indication overwrites the first
               character of the PC prefix.
               We assume a PC prefix length of 3 characters.  */
            if ((flags & DISASSEMBLY_OMIT_PC) == 0)
-             uiout->text (pc_prefix (pc) + 1);
+             m_uiout->text (pc_prefix (pc) + 1);
            else
-             uiout->text ("  ");
+             m_uiout->text ("  ");
          }
        else if ((flags & DISASSEMBLY_OMIT_PC) == 0)
-         uiout->text (pc_prefix (pc));
+         m_uiout->text (pc_prefix (pc));
        else
-         uiout->text ("   ");
+         m_uiout->text ("   ");
       }
     else if ((flags & DISASSEMBLY_OMIT_PC) == 0)
-      uiout->text (pc_prefix (pc));
-    uiout->field_core_addr ("address", gdbarch, pc);
+      m_uiout->text (pc_prefix (pc));
+    m_uiout->field_core_addr ("address", gdbarch, pc);
 
     std::string name, filename;
-    if (!build_address_symbolic (gdbarch, pc, 0, &name, &offset, &filename,
-                                &line, &unmapped))
+    bool omit_fname = ((flags & DISASSEMBLY_OMIT_FNAME) != 0);
+    if (!build_address_symbolic (gdbarch, pc, false, omit_fname, &name,
+                                &offset, &filename, &line, &unmapped))
       {
        /* We don't care now about line, filename and unmapped.  But we might in
           the future.  */
-       uiout->text (" <");
-       if ((flags & DISASSEMBLY_OMIT_FNAME) == 0)
-         uiout->field_string ("func-name", name.c_str ());
-       uiout->text ("+");
-       uiout->field_int ("offset", offset);
-       uiout->text (">:\t");
+       m_uiout->text (" <");
+       if (!omit_fname)
+         m_uiout->field_string ("func-name", name,
+                                function_name_style.style ());
+       /* For negative offsets, avoid displaying them as +-N; the sign of
+          the offset takes the place of the "+" here.  */
+       if (offset >= 0)
+         m_uiout->text ("+");
+       m_uiout->field_signed ("offset", offset);
+       m_uiout->text (">:\t");
       }
     else
-      uiout->text (":\t");
+      m_uiout->text (":\t");
 
+    /* Clear the buffer into which we will disassemble the instruction.  */
     m_insn_stb.clear ();
 
+    /* A helper function to write the M_INSN_STB buffer, followed by a
+       newline.  This can be called in a couple of situations.  */
+    auto write_out_insn_buffer = [&] ()
+    {
+      m_uiout->field_stream ("inst", m_insn_stb);
+      m_uiout->text ("\n");
+    };
+
+    try
+      {
+       /* Now we can disassemble the instruction.  If the disassembler
+          returns a negative value this indicates an error and is handled
+          within the print_insn call, resulting in an exception being
+          thrown.  Returning zero makes no sense, as this indicates we
+          disassembled something successfully, but it was something of no
+          size?  */
+       size = m_di.print_insn (pc);
+       gdb_assert (size > 0);
+      }
+    catch (const gdb_exception &ex)
+      {
+       /* An exception was thrown while disassembling the instruction.
+          However, the disassembler might still have written something
+          out, so ensure that we flush the instruction buffer before
+          rethrowing the exception.  We can't perform this write from an
+          object destructor as the write itself might throw an exception
+          if the pager kicks in, and the user selects quit.  */
+       write_out_insn_buffer ();
+       throw ex;
+      }
+
     if (flags & DISASSEMBLY_RAW_INSN)
       {
        CORE_ADDR end_pc;
@@ -264,7 +331,6 @@ gdb_pretty_print_disassembler::pretty_print_insn (struct ui_out *uiout,
           write them out in a single go for the MI.  */
        m_opcode_stb.clear ();
 
-       size = m_di.print_insn (pc);
        end_pc = pc + size;
 
        for (;pc < end_pc; ++pc)
@@ -274,15 +340,13 @@ gdb_pretty_print_disassembler::pretty_print_insn (struct ui_out *uiout,
            spacer = " ";
          }
 
-       uiout->field_stream ("opcodes", m_opcode_stb);
-       uiout->text ("\t");
+       m_uiout->field_stream ("opcodes", m_opcode_stb);
+       m_uiout->text ("\t");
       }
-    else
-      size = m_di.print_insn (pc);
 
-    uiout->field_stream ("inst", m_insn_stb);
+    /* Disassembly was a success, write out the instruction buffer.  */
+    write_out_insn_buffer ();
   }
-  uiout->text ("\n");
 
   return size;
 }
@@ -298,13 +362,13 @@ dump_insns (struct gdbarch *gdbarch,
   memset (&insn, 0, sizeof (insn));
   insn.addr = low;
 
-  gdb_pretty_print_disassembler disasm (gdbarch);
+  gdb_pretty_print_disassembler disasm (gdbarch, uiout);
 
   while (insn.addr < high && (how_many < 0 || num_displayed < how_many))
     {
       int size;
 
-      size = disasm.pretty_print_insn (uiout, &insn, flags);
+      size = disasm.pretty_print_insn (&insn, flags);
       if (size <= 0)
        break;
 
@@ -346,10 +410,10 @@ do_mixed_source_and_assembly_deprecated
   int num_displayed = 0;
   print_source_lines_flags psl_flags = 0;
 
-  gdb_assert (symtab != NULL && SYMTAB_LINETABLE (symtab) != NULL);
+  gdb_assert (symtab != nullptr && symtab->linetable () != nullptr);
 
-  nlines = SYMTAB_LINETABLE (symtab)->nitems;
-  le = SYMTAB_LINETABLE (symtab)->item;
+  nlines = symtab->linetable ()->nitems;
+  le = symtab->linetable ()->item;
 
   if (flags & DISASSEMBLY_FILENAME)
     psl_flags |= PRINT_SOURCE_LINES_FILENAME;
@@ -399,8 +463,7 @@ do_mixed_source_and_assembly_deprecated
   /* Now, sort mle by line #s (and, then by addresses within lines).  */
 
   if (out_of_order)
-    qsort (mle, newlines, sizeof (struct deprecated_dis_line_entry),
-          compare_lines);
+    std::sort (mle, mle + newlines, line_is_less_than);
 
   /* Now, for each line entry, emit the specified lines (unless
      they have been emitted before), followed by the assembly code
@@ -433,8 +496,8 @@ do_mixed_source_and_assembly_deprecated
                                                       "src_and_asm_line");
                      print_source_lines (symtab, next_line, next_line + 1,
                                          psl_flags);
-                     ui_out_emit_list inner_list_emitter (uiout,
-                                                          "line_asm_insn");
+                     ui_out_emit_list temp_list_emitter (uiout,
+                                                         "line_asm_insn");
                    }
                  /* Print the last line and leave list open for
                     asm instructions to be added.  */
@@ -457,7 +520,7 @@ do_mixed_source_and_assembly_deprecated
                                   how_many, flags, NULL);
 
       /* When we've reached the end of the mle array, or we've seen the last
-         assembly range for this source line, close out the list/tuple.  */
+        assembly range for this source line, close out the list/tuple.  */
       if (i == (newlines - 1) || mle[i + 1].line > mle[i].line)
        {
          inner_list_emitter.reset ();
@@ -489,7 +552,7 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch,
   struct symtab *last_symtab;
   int last_line;
 
-  gdb_assert (main_symtab != NULL && SYMTAB_LINETABLE (main_symtab) != NULL);
+  gdb_assert (main_symtab != NULL && main_symtab->linetable () != NULL);
 
   /* First pass: collect the list of all source files and lines.
      We do this so that we can only print lines containing code once.
@@ -507,8 +570,8 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch,
      line after the opening brace.  We still want to print this opening brace.
      first_le is used to implement this.  */
 
-  nlines = SYMTAB_LINETABLE (main_symtab)->nitems;
-  le = SYMTAB_LINETABLE (main_symtab)->item;
+  nlines = main_symtab->linetable ()->nitems;
+  le = main_symtab->linetable ()->item;
   first_le = NULL;
 
   /* Skip all the preceding functions.  */
@@ -660,7 +723,8 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch,
                   l < end_preceding_line_to_display;
                   ++l)
                {
-                 ui_out_emit_tuple tuple_emitter (uiout, "src_and_asm_line");
+                 ui_out_emit_tuple line_tuple_emitter (uiout,
+                                                       "src_and_asm_line");
                  print_source_lines (sal.symtab, l, l + 1, psl_flags);
                  ui_out_emit_list chain_line_emitter (uiout, "line_asm_insn");
                }
@@ -707,21 +771,6 @@ do_assembly_only (struct gdbarch *gdbarch, struct ui_out *uiout,
   dump_insns (gdbarch, uiout, low, high, how_many, flags, NULL);
 }
 
-/* Initialize the disassemble info struct ready for the specified
-   stream.  */
-
-static int ATTRIBUTE_PRINTF (2, 3)
-fprintf_disasm (void *stream, const char *format, ...)
-{
-  va_list args;
-
-  va_start (args, format);
-  vfprintf_filtered ((struct ui_file *) stream, format, args);
-  va_end (args);
-  /* Something non -ve.  */
-  return 0;
-}
-
 /* Combine implicit and user disassembler options and return them
    in a newly-created string.  */
 
@@ -749,23 +798,41 @@ get_all_disassembler_options (struct gdbarch *gdbarch)
 
 gdb_disassembler::gdb_disassembler (struct gdbarch *gdbarch,
                                    struct ui_file *file,
-                                   di_read_memory_ftype read_memory_func)
-  : m_gdbarch (gdbarch),
-    m_err_memaddr (0)
+                                   read_memory_ftype func)
+  : gdb_printing_disassembler (gdbarch, &m_buffer, func,
+                              dis_asm_memory_error, dis_asm_print_address),
+    m_buffer (!use_ext_lang_colorization_p && disassembler_styling
+             && file->can_emit_style_escape ()),
+    m_dest (file)
+{ /* Nothing.  */ }
+
+/* See disasm.h.  */
+
+gdb_disassemble_info::gdb_disassemble_info
+  (struct gdbarch *gdbarch, struct ui_file *stream,
+   read_memory_ftype read_memory_func, memory_error_ftype memory_error_func,
+   print_address_ftype print_address_func, fprintf_ftype fprintf_func,
+   fprintf_styled_ftype fprintf_styled_func)
+    : m_gdbarch (gdbarch)
 {
-  init_disassemble_info (&m_di, file, fprintf_disasm);
+  gdb_assert (fprintf_func != nullptr);
+  gdb_assert (fprintf_styled_func != nullptr);
+  init_disassemble_info (&m_di, stream, fprintf_func,
+                        fprintf_styled_func);
   m_di.flavour = bfd_target_unknown_flavour;
-  m_di.memory_error_func = dis_asm_memory_error;
-  m_di.print_address_func = dis_asm_print_address;
-  /* NOTE: cagney/2003-04-28: The original code, from the old Insight
-     disassembler had a local optomization here.  By default it would
-     access the executable file, instead of the target memory (there
-     was a growing list of exceptions though).  Unfortunately, the
-     heuristic was flawed.  Commands like "disassemble &variable"
-     didn't work as they relied on the access going to the target.
-     Further, it has been supperseeded by trust-read-only-sections
-     (although that should be superseeded by target_trust..._p()).  */
-  m_di.read_memory_func = read_memory_func;
+
+  /* The memory_error_func, print_address_func, and read_memory_func are
+     all initialized to a default (non-nullptr) value by the call to
+     init_disassemble_info above.  If the user is overriding these fields
+     (by passing non-nullptr values) then do that now, otherwise, leave
+     these fields as the defaults.  */
+  if (memory_error_func != nullptr)
+    m_di.memory_error_func = memory_error_func;
+  if (print_address_func != nullptr)
+    m_di.print_address_func = print_address_func;
+  if (read_memory_func != nullptr)
+    m_di.read_memory_func = read_memory_func;
+
   m_di.arch = gdbarch_bfd_arch_info (gdbarch)->arch;
   m_di.mach = gdbarch_bfd_arch_info (gdbarch)->mach;
   m_di.endian = gdbarch_byte_order (gdbarch);
@@ -777,16 +844,102 @@ gdb_disassembler::gdb_disassembler (struct gdbarch *gdbarch,
   disassemble_init_for_target (&m_di);
 }
 
+/* See disasm.h.  */
+
+gdb_disassemble_info::~gdb_disassemble_info ()
+{
+  disassemble_free_target (&m_di);
+}
+
+/* Wrapper around calling gdbarch_print_insn.  This function takes care of
+   first calling the extension language hooks for print_insn, and, if none
+   of the extension languages can print this instruction, calls
+   gdbarch_print_insn to do the work.
+
+   GDBARCH is the architecture to disassemble in, VMA is the address of the
+   instruction being disassembled, and INFO is the libopcodes disassembler
+   related information.  */
+
+static int
+gdb_print_insn_1 (struct gdbarch *gdbarch, CORE_ADDR vma,
+                 struct disassemble_info *info)
+{
+  /* Call into the extension languages to do the disassembly.  */
+  gdb::optional<int> length = ext_lang_print_insn (gdbarch, vma, info);
+  if (length.has_value ())
+    return *length;
+
+  /* No extension language wanted to do the disassembly, so do it
+     manually.  */
+  return gdbarch_print_insn (gdbarch, vma, info);
+}
+
+/* See disasm.h.  */
+
+bool gdb_disassembler::use_ext_lang_colorization_p = true;
+
+/* See disasm.h.  */
+
 int
 gdb_disassembler::print_insn (CORE_ADDR memaddr,
                              int *branch_delay_insns)
 {
-  m_err_memaddr = 0;
+  m_err_memaddr.reset ();
+  m_buffer.clear ();
+
+  int length = gdb_print_insn_1 (arch (), memaddr, &m_di);
+
+  /* If we have successfully disassembled an instruction, styling is on, we
+     think that the extension language might be able to perform styling for
+     us, and the destination can support styling, then lets call into the
+     extension languages in order to style this output.  */
+  if (length > 0 && disassembler_styling
+      && use_ext_lang_colorization_p
+      && m_dest->can_emit_style_escape ())
+    {
+      gdb::optional<std::string> ext_contents;
+      ext_contents = ext_lang_colorize_disasm (m_buffer.string (), arch ());
+      if (ext_contents.has_value ())
+       m_buffer = std::move (*ext_contents);
+      else
+       {
+         /* The extension language failed to add styling to the
+            disassembly output.  Set the static flag so that next time we
+            disassemble we don't even bother attempting to use the
+            extension language for styling.  */
+         use_ext_lang_colorization_p = false;
+
+         /* The instruction we just disassembled, and the extension
+            languages failed to style, might have otherwise had some
+            minimal styling applied by GDB.  To regain that styling we
+            need to recreate m_buffer, but this time with styling support.
+
+            To do this we perform an in-place new, but this time turn on
+            the styling support, then we can re-disassembly the
+            instruction, and gain any minimal styling GDB might add.  */
+         gdb_static_assert ((std::is_same<decltype (m_buffer),
+                             string_file>::value));
+         gdb_assert (!m_buffer.term_out ());
+         m_buffer.~string_file ();
+         new (&m_buffer) string_file (true);
+         length = gdb_print_insn_1 (arch (), memaddr, &m_di);
+         gdb_assert (length > 0);
+       }
+    }
 
-  int length = gdbarch_print_insn (arch (), memaddr, &m_di);
+  /* Push any disassemble output to the real destination stream.  We do
+     this even if the disassembler reported failure (-1) as the
+     disassembler may have printed something to its output stream.  */
+  m_di.fprintf_func (m_dest, "%s", m_buffer.c_str ());
 
+  /* If the disassembler failed then report an appropriate error.  */
   if (length < 0)
-    memory_error (TARGET_XFER_E_IO, m_err_memaddr);
+    {
+      if (m_err_memaddr.has_value ())
+       memory_error (TARGET_XFER_E_IO, *m_err_memaddr);
+      else
+       error (_("unknown disassembler error (error = %d)"), length);
+    }
 
   if (branch_delay_insns != NULL)
     {
@@ -809,8 +962,8 @@ gdb_disassembly (struct gdbarch *gdbarch, struct ui_out *uiout,
   /* Assume symtab is valid for whole PC range.  */
   symtab = find_pc_line_symtab (low);
 
-  if (symtab != NULL && SYMTAB_LINETABLE (symtab) != NULL)
-    nlines = SYMTAB_LINETABLE (symtab)->nitems;
+  if (symtab != NULL && symtab->linetable () != NULL)
+    nlines = symtab->linetable ()->nitems;
 
   if (!(flags & (DISASSEMBLY_SOURCE_DEPRECATED | DISASSEMBLY_SOURCE))
       || nlines <= 0)
@@ -850,47 +1003,47 @@ gdb_insn_length (struct gdbarch *gdbarch, CORE_ADDR addr)
   return gdb_print_insn (gdbarch, addr, &null_stream, NULL);
 }
 
-/* fprintf-function for gdb_buffered_insn_length.  This function is a
-   nop, we don't want to print anything, we just want to compute the
-   length of the insn.  */
+/* See disasm.h.  */
 
-static int ATTRIBUTE_PRINTF (2, 3)
-gdb_buffered_insn_length_fprintf (void *stream, const char *format, ...)
+int
+gdb_non_printing_disassembler::null_fprintf_func (void *stream,
+                                                 const char *format, ...)
 {
   return 0;
 }
 
-/* Initialize a struct disassemble_info for gdb_buffered_insn_length.
-   Upon return, *DISASSEMBLER_OPTIONS_HOLDER owns the string pointed
-   to by DI.DISASSEMBLER_OPTIONS.  */
+/* See disasm.h.  */
 
-static void
-gdb_buffered_insn_length_init_dis (struct gdbarch *gdbarch,
-                                  struct disassemble_info *di,
-                                  const gdb_byte *insn, int max_len,
-                                  CORE_ADDR addr,
-                                  std::string *disassembler_options_holder)
+int
+gdb_non_printing_disassembler::null_fprintf_styled_func
+  (void *stream, enum disassembler_style style, const char *format, ...)
 {
-  init_disassemble_info (di, NULL, gdb_buffered_insn_length_fprintf);
-
-  /* init_disassemble_info installs buffer_read_memory, etc.
-     so we don't need to do that here.
-     The cast is necessary until disassemble_info is const-ified.  */
-  di->buffer = (gdb_byte *) insn;
-  di->buffer_length = max_len;
-  di->buffer_vma = addr;
-
-  di->arch = gdbarch_bfd_arch_info (gdbarch)->arch;
-  di->mach = gdbarch_bfd_arch_info (gdbarch)->mach;
-  di->endian = gdbarch_byte_order (gdbarch);
-  di->endian_code = gdbarch_byte_order_for_code (gdbarch);
-
-  *disassembler_options_holder = get_all_disassembler_options (gdbarch);
-  if (!disassembler_options_holder->empty ())
-    di->disassembler_options = disassembler_options_holder->c_str ();
-  disassemble_init_for_target (di);
+  return 0;
 }
 
+/* A non-printing disassemble_info management class.  The disassemble_info
+   setup by this class will not print anything to the output stream (there
+   is no output stream), and the instruction to be disassembled will be
+   read from a buffer passed to the constructor.  */
+
+struct gdb_non_printing_buffer_disassembler
+  : public gdb_non_printing_disassembler
+{
+  /* Constructor.  GDBARCH is the architecture to disassemble for, BUFFER
+     contains the instruction to disassemble, and INSN_ADDRESS is the
+     address (in target memory) of the instruction to disassemble.  */
+  gdb_non_printing_buffer_disassembler (struct gdbarch *gdbarch,
+                                       gdb::array_view<const gdb_byte> buffer,
+                                       CORE_ADDR insn_address)
+    : gdb_non_printing_disassembler (gdbarch, nullptr)
+  {
+    /* The cast is necessary until disassemble_info is const-ified.  */
+    m_di.buffer = (gdb_byte *) buffer.data ();
+    m_di.buffer_length = buffer.size ();
+    m_di.buffer_vma = insn_address;
+  }
+};
+
 /* Return the length in bytes of INSN.  MAX_LEN is the size of the
    buffer containing INSN.  */
 
@@ -898,13 +1051,11 @@ int
 gdb_buffered_insn_length (struct gdbarch *gdbarch,
                          const gdb_byte *insn, int max_len, CORE_ADDR addr)
 {
-  struct disassemble_info di;
-  std::string disassembler_options_holder;
-
-  gdb_buffered_insn_length_init_dis (gdbarch, &di, insn, max_len, addr,
-                                    &disassembler_options_holder);
-
-  return gdbarch_print_insn (gdbarch, addr, &di);
+  gdb::array_view<const gdb_byte> buffer
+    = gdb::make_array_view (insn, max_len);
+  gdb_non_printing_buffer_disassembler dis (gdbarch, buffer, addr);
+  int result = gdb_print_insn_1 (gdbarch, addr, dis.disasm_info ());
+  return result;
 }
 
 char *
@@ -917,13 +1068,16 @@ get_disassembler_options (struct gdbarch *gdbarch)
 }
 
 void
-set_disassembler_options (char *prospective_options)
+set_disassembler_options (const char *prospective_options)
 {
   struct gdbarch *gdbarch = get_current_arch ();
   char **disassembler_options = gdbarch_disassembler_options (gdbarch);
   const disasm_options_and_args_t *valid_options_and_args;
   const disasm_options_t *valid_options;
-  char *options = remove_whitespace_and_extra_commas (prospective_options);
+  gdb::unique_xmalloc_ptr<char> prospective_options_local
+    = make_unique_xstrdup (prospective_options);
+  char *options = remove_whitespace_and_extra_commas
+    (prospective_options_local.get ());
   const char *opt;
 
   /* Allow all architectures, even ones that do not support 'set disassembler',
@@ -941,7 +1095,7 @@ set_disassembler_options (char *prospective_options)
   valid_options_and_args = gdbarch_valid_disassembler_options (gdbarch);
   if (valid_options_and_args == NULL)
     {
-      fprintf_filtered (gdb_stdlog, _("\
+      gdb_printf (gdb_stderr, _("\
 'set disassembler-options ...' is not supported on this architecture.\n"));
       return;
     }
@@ -977,9 +1131,9 @@ set_disassembler_options (char *prospective_options)
          break;
       if (valid_options->name[i] == NULL)
        {
-         fprintf_filtered (gdb_stdlog,
-                           _("Invalid disassembler option value: '%s'.\n"),
-                           opt);
+         gdb_printf (gdb_stderr,
+                     _("Invalid disassembler option value: '%s'.\n"),
+                     opt);
          return;
        }
     }
@@ -992,7 +1146,7 @@ static void
 set_disassembler_options_sfunc (const char *args, int from_tty,
                                struct cmd_list_element *c)
 {
-  set_disassembler_options (prospective_options);
+  set_disassembler_options (prospective_options.c_str ());
 }
 
 static void
@@ -1008,25 +1162,30 @@ show_disassembler_options_sfunc (struct ui_file *file, int from_tty,
   if (options == NULL)
     options = "";
 
-  fprintf_filtered (file, _("The current disassembler options are '%s'\n"),
-                   options);
+  gdb_printf (file, _("The current disassembler options are '%s'\n\n"),
+             options);
 
   valid_options_and_args = gdbarch_valid_disassembler_options (gdbarch);
 
   if (valid_options_and_args == NULL)
-    return;
+    {
+      gdb_puts (_("There are no disassembler options available "
+                 "for this architecture.\n"),
+               file);
+      return;
+    }
 
   valid_options = &valid_options_and_args->options;
 
-  fprintf_filtered (file, _("\n\
+  gdb_printf (file, _("\
 The following disassembler options are supported for use with the\n\
-'set disassembler-options <option>[,<option>...]' command:\n"));
+'set disassembler-options OPTION [,OPTION]...' command:\n"));
 
   if (valid_options->description != NULL)
     {
       size_t i, max_len = 0;
 
-      fprintf_filtered (file, "\n");
+      gdb_printf (file, "\n");
 
       /* Compute the length of the longest option name.  */
       for (i = 0; valid_options->name[i] != NULL; i++)
@@ -1041,35 +1200,35 @@ The following disassembler options are supported for use with the\n\
 
       for (i = 0, max_len++; valid_options->name[i] != NULL; i++)
        {
-         fprintf_filtered (file, "  %s", valid_options->name[i]);
+         gdb_printf (file, "  %s", valid_options->name[i]);
          if (valid_options->arg != NULL && valid_options->arg[i] != NULL)
-           fprintf_filtered (file, "%s", valid_options->arg[i]->name);
+           gdb_printf (file, "%s", valid_options->arg[i]->name);
          if (valid_options->description[i] != NULL)
            {
              size_t len = strlen (valid_options->name[i]);
 
              if (valid_options->arg != NULL && valid_options->arg[i] != NULL)
                len += strlen (valid_options->arg[i]->name);
-             fprintf_filtered (file, "%*c %s", (int) (max_len - len), ' ',
-                               valid_options->description[i]);
+             gdb_printf (file, "%*c %s", (int) (max_len - len), ' ',
+                         valid_options->description[i]);
            }
-         fprintf_filtered (file, "\n");
+         gdb_printf (file, "\n");
        }
     }
   else
     {
       size_t i;
-      fprintf_filtered (file, "  ");
+      gdb_printf (file, "  ");
       for (i = 0; valid_options->name[i] != NULL; i++)
        {
-         fprintf_filtered (file, "%s", valid_options->name[i]);
+         gdb_printf (file, "%s", valid_options->name[i]);
          if (valid_options->arg != NULL && valid_options->arg[i] != NULL)
-           fprintf_filtered (file, "%s", valid_options->arg[i]->name);
+           gdb_printf (file, "%s", valid_options->arg[i]->name);
          if (valid_options->name[i + 1] != NULL)
-           fprintf_filtered (file, ", ");
-         wrap_here ("  ");
+           gdb_printf (file, ", ");
+         file->wrap_here (2);
        }
-      fprintf_filtered (file, "\n");
+      gdb_printf (file, "\n");
     }
 
   valid_args = valid_options_and_args->args;
@@ -1079,15 +1238,15 @@ The following disassembler options are supported for use with the\n\
 
       for (i = 0; valid_args[i].name != NULL; i++)
        {
-         fprintf_filtered (file, _("\n\
+         gdb_printf (file, _("\n\
   For the options above, the following values are supported for \"%s\":\n   "),
-                           valid_args[i].name);
+                     valid_args[i].name);
          for (j = 0; valid_args[i].values[j] != NULL; j++)
            {
-             fprintf_filtered (file, " %s", valid_args[i].values[j]);
-             wrap_here ("   ");
+             gdb_printf (file, " %s", valid_args[i].values[j]);
+             file->wrap_here (3);
            }
-         fprintf_filtered (file, "\n");
+         gdb_printf (file, "\n");
        }
     }
 }
@@ -1119,20 +1278,20 @@ disassembler_options_completer (struct cmd_list_element *ignore,
 
 /* Initialization code.  */
 
+void _initialize_disasm ();
 void
-_initialize_disasm (void)
+_initialize_disasm ()
 {
-  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_show_commands set_show_disas_opts
+    = add_setshow_string_noescape_cmd ("disassembler-options", no_class,
+                                      &prospective_options, _("\
 Set the disassembler options.\n\
 Usage: set disassembler-options OPTION [,OPTION]...\n\n\
-See: 'show disassembler-options' for valid option values.\n"), _("\
+See: 'show disassembler-options' for valid option values."), _("\
 Show the disassembler options."), NULL,
                                         set_disassembler_options_sfunc,
                                         show_disassembler_options_sfunc,
                                         &setlist, &showlist);
-  set_cmd_completer (cmd, disassembler_options_completer);
+  set_cmd_completer (set_show_disas_opts.set, disassembler_options_completer);
 }