Automatic date update in version.in
[deliverable/binutils-gdb.git] / gdb / disasm.c
index 25d87d70846df775195940e41a530d2e12367e9e..2b65c6acbbb0a170680e90e46ffb5629cc6b8356 100644 (file)
@@ -1,6 +1,6 @@
 /* Disassemble support for GDB.
 
-   Copyright (C) 2000-2005, 2007-2012 Free Software Foundation, Inc.
+   Copyright (C) 2000-2015 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 #include "target.h"
 #include "value.h"
 #include "ui-out.h"
-#include "gdb_string.h"
 #include "disasm.h"
 #include "gdbcore.h"
 #include "dis-asm.h"
+#include "source.h"
 
 /* Disassemble functions.
    FIXME: We should get rid of all the duplicate code in gdb that does
    the same thing: disassemble_command() and the gdbtk variation.  */
 
-/* This Structure is used to store line number information.
+/* This structure is used to store line number information for the
+   deprecated /m option.
    We need a different sort of line table from the normal one cuz we can't
    depend upon implicit line-end pc's for lines to do the
    reordering in this function.  */
 
-struct dis_line_entry
+struct deprecated_dis_line_entry
 {
   int line;
   CORE_ADDR start_pc;
   CORE_ADDR end_pc;
 };
 
+/* This Structure is used to store line number information.
+   We need a different sort of line table from the normal one cuz we can't
+   depend upon implicit line-end pc's for lines to do the
+   reordering in this function.  */
+
+struct dis_line_entry
+{
+  struct symtab *symtab;
+  int line;
+};
+
+/* Hash function for dis_line_entry.  */
+
+static hashval_t
+hash_dis_line_entry (const void *item)
+{
+  const struct dis_line_entry *dle = item;
+
+  return htab_hash_pointer (dle->symtab) + dle->line;
+}
+
+/* Equal function for dis_line_entry.  */
+
+static int
+eq_dis_line_entry (const void *item_lhs, const void *item_rhs)
+{
+  const struct dis_line_entry *lhs = item_lhs;
+  const struct dis_line_entry *rhs = item_rhs;
+
+  return (lhs->symtab == rhs->symtab
+         && lhs->line == rhs->line);
+}
+
+/* Create the table to manage lines for mixed source/disassembly.  */
+
+static htab_t
+allocate_dis_line_table (void)
+{
+  return htab_create_alloc (41,
+                           hash_dis_line_entry, eq_dis_line_entry,
+                           xfree, xcalloc, xfree);
+}
+
+/* Add DLE to TABLE.
+   Returns 1 if added, 0 if already present.  */
+
+static void
+maybe_add_dis_line_entry (htab_t table, struct symtab *symtab, int line)
+{
+  void **slot;
+  struct dis_line_entry dle, *dlep;
+
+  dle.symtab = symtab;
+  dle.line = line;
+  slot = htab_find_slot (table, &dle, INSERT);
+  if (*slot == NULL)
+    {
+      dlep = XNEW (struct dis_line_entry);
+      dlep->symtab = symtab;
+      dlep->line = line;
+      *slot = dlep;
+    }
+}
+
+/* Return non-zero if SYMTAB, LINE are in TABLE.  */
+
+static int
+line_has_code_p (htab_t table, struct symtab *symtab, int line)
+{
+  struct dis_line_entry dle;
+
+  dle.symtab = symtab;
+  dle.line = line;
+  return htab_find (table, &dle) != NULL;
+}
+
 /* Like target_read_memory, but slightly different parameters.  */
 static int
 dis_asm_read_memory (bfd_vma memaddr, gdb_byte *myaddr, unsigned int len,
                     struct disassemble_info *info)
 {
-  return target_read_memory (memaddr, myaddr, len);
+  return target_read_code (memaddr, myaddr, len);
 }
 
 /* Like memory_error with slightly different parameters.  */
@@ -70,11 +147,11 @@ dis_asm_print_address (bfd_vma addr, struct disassemble_info *info)
 static int
 compare_lines (const void *mle1p, const void *mle2p)
 {
-  struct dis_line_entry *mle1, *mle2;
+  struct deprecated_dis_line_entry *mle1, *mle2;
   int val;
 
-  mle1 = (struct dis_line_entry *) mle1p;
-  mle2 = (struct dis_line_entry *) mle2p;
+  mle1 = (struct deprecated_dis_line_entry *) mle1p;
+  mle2 = (struct deprecated_dis_line_entry *) mle2p;
 
   /* 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.  */
@@ -97,7 +174,8 @@ static int
 dump_insns (struct gdbarch *gdbarch, struct ui_out *uiout,
            struct disassemble_info * di,
            CORE_ADDR low, CORE_ADDR high,
-           int how_many, int flags, struct ui_file *stb)
+           int how_many, int flags, struct ui_file *stb,
+           CORE_ADDR *end_pc)
 {
   int num_displayed = 0;
   CORE_ADDR pc;
@@ -122,7 +200,9 @@ dump_insns (struct gdbarch *gdbarch, struct ui_out *uiout,
            num_displayed++;
        }
       ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
-      ui_out_text (uiout, pc_prefix (pc));
+
+      if ((flags & DISASSEMBLY_OMIT_PC) == 0)
+       ui_out_text (uiout, pc_prefix (pc));
       ui_out_field_core_addr (uiout, "address", gdbarch, pc);
 
       if (!build_address_symbolic (gdbarch, pc, 0, &name, &offset, &filename,
@@ -181,24 +261,30 @@ dump_insns (struct gdbarch *gdbarch, struct ui_out *uiout,
       do_cleanups (ui_out_chain);
       ui_out_text (uiout, "\n");
     }
+
+  if (end_pc != NULL)
+    *end_pc = pc;
   return num_displayed;
 }
 
 /* The idea here is to present a source-O-centric view of a
    function to the user.  This means that things are presented
    in source order, with (possibly) out of order assembly
-   immediately following.  */
+   immediately following.
+
+   N.B. This view is deprecated.  */
 
 static void
-do_mixed_source_and_assembly (struct gdbarch *gdbarch, struct ui_out *uiout,
-                             struct disassemble_info *di, int nlines,
-                             struct linetable_entry *le,
-                             CORE_ADDR low, CORE_ADDR high,
-                             struct symtab *symtab,
-                             int how_many, int flags, struct ui_file *stb)
+do_mixed_source_and_assembly_deprecated
+  (struct gdbarch *gdbarch, struct ui_out *uiout,
+   struct disassemble_info *di, struct symtab *symtab,
+   CORE_ADDR low, CORE_ADDR high,
+   int how_many, int flags, struct ui_file *stb)
 {
   int newlines = 0;
-  struct dis_line_entry *mle;
+  int nlines;
+  struct linetable_entry *le;
+  struct deprecated_dis_line_entry *mle;
   struct symtab_and_line sal;
   int i;
   int out_of_order = 0;
@@ -209,11 +295,16 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch, struct ui_out *uiout,
   struct cleanup *ui_out_tuple_chain = make_cleanup (null_cleanup, 0);
   struct cleanup *ui_out_list_chain = make_cleanup (null_cleanup, 0);
 
+  gdb_assert (symtab != NULL && SYMTAB_LINETABLE (symtab) != NULL);
+
+  nlines = SYMTAB_LINETABLE (symtab)->nitems;
+  le = SYMTAB_LINETABLE (symtab)->item;
+
   if (flags & DISASSEMBLY_FILENAME)
     psl_flags |= PRINT_SOURCE_LINES_FILENAME;
 
-  mle = (struct dis_line_entry *) alloca (nlines
-                                         * sizeof (struct dis_line_entry));
+  mle = (struct deprecated_dis_line_entry *)
+    alloca (nlines * sizeof (struct deprecated_dis_line_entry));
 
   /* Copy linetable entries for this function into our data
      structure, creating end_pc's and setting out_of_order as
@@ -254,11 +345,11 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch, struct ui_out *uiout,
       newlines++;
     }
 
-  /* Now, sort mle by line #s (and, then by addresses within
-     lines).  */
+  /* Now, sort mle by line #s (and, then by addresses within lines).  */
 
   if (out_of_order)
-    qsort (mle, newlines, sizeof (struct dis_line_entry), compare_lines);
+    qsort (mle, newlines, sizeof (struct deprecated_dis_line_entry),
+          compare_lines);
 
   /* Now, for each line entry, emit the specified lines (unless
      they have been emitted before), followed by the assembly code
@@ -323,7 +414,7 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch, struct ui_out *uiout,
 
       num_displayed += dump_insns (gdbarch, uiout, di,
                                   mle[i].start_pc, mle[i].end_pc,
-                                  how_many, flags, stb);
+                                  how_many, flags, stb, 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.  */
@@ -341,6 +432,260 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch, struct ui_out *uiout,
   do_cleanups (ui_out_chain);
 }
 
+/* The idea here is to present a source-O-centric view of a
+   function to the user.  This means that things are presented
+   in source order, with (possibly) out of order assembly
+   immediately following.  */
+
+static void
+do_mixed_source_and_assembly (struct gdbarch *gdbarch, struct ui_out *uiout,
+                             struct disassemble_info *di,
+                             struct symtab *main_symtab,
+                             CORE_ADDR low, CORE_ADDR high,
+                             int how_many, int flags, struct ui_file *stb)
+{
+  int newlines = 0;
+  const struct linetable_entry *le, *first_le;
+  struct symtab_and_line sal;
+  int i, nlines;
+  int out_of_order = 0;
+  int next_line = 0;
+  int num_displayed = 0;
+  enum print_source_lines_flags psl_flags = 0;
+  struct cleanup *cleanups;
+  struct cleanup *ui_out_chain;
+  struct cleanup *ui_out_tuple_chain;
+  struct cleanup *ui_out_list_chain;
+  CORE_ADDR pc;
+  struct symtab *last_symtab;
+  int last_line;
+  htab_t dis_line_table;
+
+  gdb_assert (main_symtab != NULL && SYMTAB_LINETABLE (main_symtab) != 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.
+     We try to print the source text leading up to the next instruction,
+     but if that text is for code that will be disassembled later, then
+     we'll want to defer printing it until later with its associated code.  */
+
+  dis_line_table = allocate_dis_line_table ();
+  cleanups = make_cleanup_htab_delete (dis_line_table);
+
+  pc = low;
+
+  /* The prologue may be empty, but there may still be a line number entry
+     for the opening brace which is distinct from the first line of code.
+     If the prologue has been eliminated find_pc_line may return the source
+     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;
+  first_le = NULL;
+
+  /* Skip all the preceding functions.  */
+  for (i = 0; i < nlines && le[i].pc < low; i++)
+    continue;
+
+  if (i < nlines && le[i].pc < high)
+    first_le = &le[i];
+
+  /* Add lines for every pc value.  */
+  while (pc < high)
+    {
+      struct symtab_and_line sal;
+      int length;
+
+      sal = find_pc_line (pc, 0);
+      length = gdb_insn_length (gdbarch, pc);
+      pc += length;
+
+      if (sal.symtab != NULL)
+       maybe_add_dis_line_entry (dis_line_table, sal.symtab, sal.line);
+    }
+
+  /* Second pass: print the disassembly.
+
+     Output format, from an MI perspective:
+       The result is a ui_out list, field name "asm_insns", where elements have
+       name "src_and_asm_line".
+       Each element is a tuple of source line specs (field names line, file,
+       fullname), and field "line_asm_insn" which contains the disassembly.
+       Field "line_asm_insn" is a list of tuples: address, func-name, offset,
+       opcodes, inst.
+
+     CLI output works on top of this because MI ignores ui_out_text output,
+     which is where we put file name and source line contents output.
+
+     Cleanup usage:
+     cleanups:
+       For things created at the beginning of this function and need to be
+       kept until the end of this function.
+     ui_out_chain
+       Handles the outer "asm_insns" list.
+     ui_out_tuple_chain
+       The tuples for each group of consecutive disassemblies.
+     ui_out_list_chain
+       List of consecutive source lines or disassembled insns.  */
+
+  if (flags & DISASSEMBLY_FILENAME)
+    psl_flags |= PRINT_SOURCE_LINES_FILENAME;
+
+  ui_out_chain = make_cleanup_ui_out_list_begin_end (uiout, "asm_insns");
+
+  ui_out_tuple_chain = NULL;
+  ui_out_list_chain = NULL;
+
+  last_symtab = NULL;
+  last_line = 0;
+  pc = low;
+
+  while (pc < high)
+    {
+      struct linetable_entry *le = NULL;
+      struct symtab_and_line sal;
+      CORE_ADDR end_pc;
+      int start_preceding_line_to_display = 0;
+      int end_preceding_line_to_display = 0;
+      int new_source_line = 0;
+
+      sal = find_pc_line (pc, 0);
+
+      if (sal.symtab != last_symtab)
+       {
+         /* New source file.  */
+         new_source_line = 1;
+
+         /* If this is the first line of output, check for any preceding
+            lines.  */
+         if (last_line == 0
+             && first_le != NULL
+             && first_le->line < sal.line)
+           {
+             start_preceding_line_to_display = first_le->line;
+             end_preceding_line_to_display = sal.line;
+           }
+       }
+      else
+       {
+         /* Same source file as last time.  */
+         if (sal.symtab != NULL)
+           {
+             if (sal.line > last_line + 1 && last_line != 0)
+               {
+                 int l;
+
+                 /* Several preceding source lines.  Print the trailing ones
+                    not associated with code that we'll print later.  */
+                 for (l = sal.line - 1; l > last_line; --l)
+                   {
+                     if (line_has_code_p (dis_line_table, sal.symtab, l))
+                       break;
+                   }
+                 if (l < sal.line - 1)
+                   {
+                     start_preceding_line_to_display = l + 1;
+                     end_preceding_line_to_display = sal.line;
+                   }
+               }
+             if (sal.line != last_line)
+               new_source_line = 1;
+             else
+               {
+                 /* Same source line as last time.  This can happen, depending
+                    on the debug info.  */
+               }
+           }
+       }
+
+      if (new_source_line)
+       {
+         /* Skip the newline if this is the first instruction.  */
+         if (pc > low)
+           ui_out_text (uiout, "\n");
+         if (ui_out_tuple_chain != NULL)
+           {
+             gdb_assert (ui_out_list_chain != NULL);
+             do_cleanups (ui_out_list_chain);
+             do_cleanups (ui_out_tuple_chain);
+           }
+         if (sal.symtab != last_symtab
+             && !(flags & DISASSEMBLY_FILENAME))
+           {
+             /* Remember MI ignores ui_out_text.
+                We don't have to do anything here for MI because MI
+                output includes the source specs for each line.  */
+             if (sal.symtab != NULL)
+               {
+                 ui_out_text (uiout,
+                              symtab_to_filename_for_display (sal.symtab));
+               }
+             else
+               ui_out_text (uiout, "unknown");
+             ui_out_text (uiout, ":\n");
+           }
+         if (start_preceding_line_to_display > 0)
+           {
+             /* Several source lines w/o asm instructions associated.
+                We need to preserve the structure of the output, so output
+                a bunch of line tuples with no asm entries.  */
+             int l;
+             struct cleanup *ui_out_list_chain_line;
+             struct cleanup *ui_out_tuple_chain_line;
+
+             gdb_assert (sal.symtab != NULL);
+             for (l = start_preceding_line_to_display;
+                  l < end_preceding_line_to_display;
+                  ++l)
+               {
+                 ui_out_tuple_chain_line
+                   = make_cleanup_ui_out_tuple_begin_end (uiout,
+                                                          "src_and_asm_line");
+                 print_source_lines (sal.symtab, l, l + 1, psl_flags);
+                 ui_out_list_chain_line
+                   = make_cleanup_ui_out_list_begin_end (uiout,
+                                                         "line_asm_insn");
+                 do_cleanups (ui_out_list_chain_line);
+                 do_cleanups (ui_out_tuple_chain_line);
+               }
+           }
+         ui_out_tuple_chain
+           = make_cleanup_ui_out_tuple_begin_end (uiout, "src_and_asm_line");
+         if (sal.symtab != NULL)
+           print_source_lines (sal.symtab, sal.line, sal.line + 1, psl_flags);
+         else
+           ui_out_text (uiout, _("--- no source info for this pc ---\n"));
+         ui_out_list_chain
+           = make_cleanup_ui_out_list_begin_end (uiout, "line_asm_insn");
+       }
+      else
+       {
+         /* Here we're appending instructions to an existing line.
+            By construction the very first insn will have a symtab
+            and follow the new_source_line path above.  */
+         gdb_assert (ui_out_tuple_chain != NULL);
+         gdb_assert (ui_out_list_chain != NULL);
+       }
+
+      if (sal.end != 0)
+       end_pc = min (sal.end, high);
+      else
+       end_pc = pc + 1;
+      num_displayed += dump_insns (gdbarch, uiout, di, pc, end_pc,
+                                  how_many, flags, stb, &end_pc);
+      pc = end_pc;
+
+      if (how_many >= 0 && num_displayed >= how_many)
+       break;
+
+      last_symtab = sal.symtab;
+      last_line = sal.line;
+    }
+
+  do_cleanups (ui_out_chain);
+  do_cleanups (cleanups);
+}
 
 static void
 do_assembly_only (struct gdbarch *gdbarch, struct ui_out *uiout,
@@ -354,7 +699,7 @@ do_assembly_only (struct gdbarch *gdbarch, struct ui_out *uiout,
   ui_out_chain = make_cleanup_ui_out_list_begin_end (uiout, "asm_insns");
 
   num_displayed = dump_insns (gdbarch, uiout, di, low, high, how_many,
-                              flags, stb);
+                              flags, stb, NULL);
 
   do_cleanups (ui_out_chain);
 }
@@ -374,7 +719,7 @@ fprintf_disasm (void *stream, const char *format, ...)
   return 0;
 }
 
-static struct disassemble_info
+struct disassemble_info
 gdb_disassemble_info (struct gdbarch *gdbarch, struct ui_file *file)
 {
   struct disassemble_info di;
@@ -409,28 +754,27 @@ gdb_disassembly (struct gdbarch *gdbarch, struct ui_out *uiout,
   struct ui_file *stb = mem_fileopen ();
   struct cleanup *cleanups = make_cleanup_ui_file_delete (stb);
   struct disassemble_info di = gdb_disassemble_info (gdbarch, stb);
-  /* To collect the instruction outputted from opcodes.  */
-  struct symtab *symtab = NULL;
+  struct symtab *symtab;
   struct linetable_entry *le = NULL;
   int nlines = -1;
 
   /* Assume symtab is valid for whole PC range.  */
-  symtab = find_pc_symtab (low);
+  symtab = find_pc_line_symtab (low);
 
-  if (symtab != NULL && symtab->linetable != NULL)
-    {
-      /* Convert the linetable to a bunch of my_line_entry's.  */
-      le = symtab->linetable->item;
-      nlines = symtab->linetable->nitems;
-    }
+  if (symtab != NULL && SYMTAB_LINETABLE (symtab) != NULL)
+    nlines = SYMTAB_LINETABLE (symtab)->nitems;
 
-  if (!(flags & DISASSEMBLY_SOURCE) || nlines <= 0
-      || symtab == NULL || symtab->linetable == NULL)
+  if (!(flags & (DISASSEMBLY_SOURCE_DEPRECATED | DISASSEMBLY_SOURCE))
+      || nlines <= 0)
     do_assembly_only (gdbarch, uiout, &di, low, high, how_many, flags, stb);
 
   else if (flags & DISASSEMBLY_SOURCE)
-    do_mixed_source_and_assembly (gdbarch, uiout, &di, nlines, le, low,
-                                 high, symtab, how_many, flags, stb);
+    do_mixed_source_and_assembly (gdbarch, uiout, &di, symtab, low, high,
+                                 how_many, flags, stb);
+
+  else if (flags & DISASSEMBLY_SOURCE_DEPRECATED)
+    do_mixed_source_and_assembly_deprecated (gdbarch, uiout, &di, symtab,
+                                            low, high, how_many, flags, stb);
 
   do_cleanups (cleanups);
   gdb_flush (gdb_stdout);
This page took 0.029223 seconds and 4 git commands to generate.