]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Add an option to objdump's disassembler to generate ascii art diagrams showing the...
authorThomas Troeger <tstroege@gmx.de>
Mon, 13 Jan 2020 12:36:55 +0000 (12:36 +0000)
committerNick Clifton <nickc@redhat.com>
Mon, 13 Jan 2020 12:36:55 +0000 (12:36 +0000)
binutils* objdump.c (visualize_jumps, color_output, extended_color_output)
(detected_jumps): New variables.
(usage): Add the new jump visualization options.
(option_values): Add new option value.
(long_options): Add the new option.
(jump_info_new, jump_info_free): New functions.
(jump_info_min_address, jump_info_max_address): Likewise.
(jump_info_end_address, jump_info_is_start_address): Likewise.
(jump_info_is_end_address, jump_info_size): Likewise.
(jump_info_unlink, jump_info_insert): Likewise.
(jump_info_add_front, jump_info_move_linked): Likewise.
(jump_info_intersect, jump_info_merge): Likewise.
(jump_info_sort, jump_info_visualize_address): Likewise.
(disassemble_jumps): New function - used to locate jumps.
(disassemble_bytes): Add ascii art generation.
(disassemble_section): Add scan to locate jumps.
(main): Parse the new visualization option.
* doc/binutils.texi: Document the new feature.
* NEWS: Mention the new feature.

opcodes * arm-dis.c (print_insn_arm): Fill in insn info fields for control
flow instructions.
(print_insn_thumb16, print_insn_thumb32): Likewise.
(print_insn): Initialize the insn info.
* i386-dis.c (print_insn): Initialize the insn info fields, and
detect jumps.

binutils/ChangeLog
binutils/NEWS
binutils/doc/binutils.texi
binutils/objdump.c
opcodes/ChangeLog
opcodes/arm-dis.c
opcodes/i386-dis.c

index a1a27c3a03f7d2049dea95cea10a89f55c171475..d496369636d3ed3e9779ec2477accaefb525117c 100644 (file)
@@ -1,3 +1,25 @@
+2020-01-13  Thomas Troeger  <tstroege@gmx.de>
+
+       * objdump.c (visualize_jumps, color_output, extended_color_output)
+       (detected_jumps): New variables.
+       (usage): Add the new jump visualization options.
+       (option_values): Add new option value.
+       (long_options): Add the new option.
+       (jump_info_new, jump_info_free): New functions.
+       (jump_info_min_address, jump_info_max_address): Likewise.
+       (jump_info_end_address, jump_info_is_start_address): Likewise.
+       (jump_info_is_end_address, jump_info_size): Likewise.
+       (jump_info_unlink, jump_info_insert): Likewise.
+       (jump_info_add_front, jump_info_move_linked): Likewise.
+       (jump_info_intersect, jump_info_merge): Likewise.
+       (jump_info_sort, jump_info_visualize_address): Likewise.
+       (disassemble_jumps): New function - used to locate jumps.
+       (disassemble_bytes): Add ascii art generation.
+       (disassemble_section): Add scan to locate jumps.
+       (main): Parse the new visualization option.
+       * doc/binutils.texi: Document the new feature.
+       * NEWS: Mention the new feature.
+
 2020-01-13  Alan Modra  <amodra@gmail.com>
 
        PR 25360
index 72a964424e2c7992b95f92e0bbe9e87a5c935644..92ec6bc07acd3c6c4d36d92d93daa3b07f6a2717 100644 (file)
 * Add --keep-section option to objcopy and strip.  This option keeps the
   specified section from being removed.
 
+ * Add visualization of jumps inside a function by drawing an ascii character
+   graph between the address and the disassembler column.  Enabled via the
+   --visualize-jumps command line option for objdump.  Currently supported by
+   the x86, x86_64, and ARM targets.  The output looks something like this:
+
+  c6:  |  |     \----------> be 00 00 00 00            mov    $0x0,%esi
+  cb:  |  |           /----> 48 8b 3d 00 00 00 00      mov    0x0(%rip),%rdi        # d2 <main+0xd2>
+  d2:  |  |           |      31 c0                     xor    %eax,%eax
+  d4:  |  |           |  /-- e8 00 00 00 00            callq  d9 <main+0xd9>
+  d9:  |  |           |  \-> bf 02 00 00 00            mov    $0x2,%edi
+  de:  |  +-----------|----- e8 00 00 00 00            callq  e3 <main+0xe3>
+  e3:  |  \-----------|----> 48 89 da                  mov    %rbx,%rdx
+  e6:  |              |      be 00 00 00 00            mov    $0x0,%esi
+  eb:  |              \----- eb de                     jmp    cb <main+0xcb>
+  ed:  \-------------------> 48 8b 16                  mov    (%rsi),%rdx
+
+  Additional arguments to the --visualize-jumps option add colors to the
+  output.
+
 Changes in 2.33:
 
 * Add --source-comment[=<txt>] option to objdump which if present,
index 71af6c5fcfb1b39ee5320eb4585a6b2fdfb261b7..669bee968f13e804f2f65f063ab1a8803947b311 100644 (file)
@@ -2169,6 +2169,7 @@ objdump [@option{-a}|@option{--archive-headers}]
         [@option{--prefix=}@var{prefix}]
         [@option{--prefix-strip=}@var{level}]
         [@option{--insn-width=}@var{width}]
+        [@option{--visualize-jumps[=color|=extended-color|=off]}
         [@option{-V}|@option{--version}]
         [@option{-H}|@option{--help}]
         @var{objfile}@dots{}
@@ -2681,6 +2682,17 @@ This is the default when @option{--prefix-addresses} is used.
 Display @var{width} bytes on a single line when disassembling
 instructions.
 
+@item --visualize-jumps[=color|=extended-color|=off]
+Visualize jumps that stay inside a function by drawing ASCII art between
+the start and target addresses.  The optional @option{=color} argument
+adds color to the output using simple terminal colors.  Alternatively
+the @option{=extended-color} argument will add color using 8bit
+colors, but these might not work on all terminals.
+
+If it is necessary to disable the @option{visualize-jumps} option
+after it has previously been enabled then use
+@option{visualize-jumps=off}.
+
 @item -W[lLiaprmfFsoRtUuTgAckK]
 @itemx --dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges,=pubtypes,=trace_info,=trace_abbrev,=trace_aranges,=gdb_index,=addr,=cu_index,=links,=follow-links]
 @include debug.options.texi
index 27b0fb6039404f8a3fe4266d155bbb47b4bce7f6..a031e9de12291e1d2025f753d92d44aae1e89215 100644 (file)
@@ -124,6 +124,9 @@ static size_t prefix_length;
 static bfd_boolean unwind_inlines;     /* --inlines.  */
 static const char * disasm_sym;                /* Disassembly start symbol.  */
 static const char * source_comment;     /* --source_comment.  */
+static bfd_boolean visualize_jumps = FALSE;          /* --visualize-jumps.  */
+static bfd_boolean color_output = FALSE;             /* --visualize-jumps=color.  */
+static bfd_boolean extended_color_output = FALSE;    /* --visualize-jumps=extended-color.  */
 
 static int demangle_flags = DMGL_ANSI | DMGL_PARAMS;
 
@@ -198,6 +201,9 @@ static const struct objdump_private_desc * const objdump_private_vectors[] =
     OBJDUMP_PRIVATE_VECTORS
     NULL
   };
+
+/* The list of detected jumps inside a function.  */
+static struct jump_info *detected_jumps = NULL;
 \f
 static void usage (FILE *, int) ATTRIBUTE_NORETURN;
 static void
@@ -278,7 +284,12 @@ usage (FILE *stream, int status)
                              or deeper\n\
       --dwarf-check          Make additional dwarf internal consistency checks.\
       \n\
-      --ctf-parent=SECTION     Use SECTION as the CTF parent\n\n"));
+      --ctf-parent=SECTION       Use SECTION as the CTF parent\n\
+      --visualize-jumps          Visualize jumps by drawing ASCII art lines\n\
+      --visualize-jumps=color    Use colors in the ASCII art\n\
+      --visualize-jumps=extended-color   Use extended 8-bit color codes\n\
+      --visualize-jumps=off      Disable jump visualization\n\n"));
+
       list_supported_targets (program_name, stream);
       list_supported_architectures (program_name, stream);
 
@@ -316,7 +327,8 @@ enum option_values
     OPTION_INLINES,
     OPTION_SOURCE_COMMENT,
     OPTION_CTF,
-    OPTION_CTF_PARENT
+    OPTION_CTF_PARENT,
+    OPTION_VISUALIZE_JUMPS
   };
 
 static struct option long_options[]=
@@ -376,6 +388,7 @@ static struct option long_options[]=
   {"dwarf-start", required_argument, 0, OPTION_DWARF_START},
   {"dwarf-check", no_argument, 0, OPTION_DWARF_CHECK},
   {"inlines", no_argument, 0, OPTION_INLINES},
+  {"visualize-jumps", optional_argument, 0, OPTION_VISUALIZE_JUMPS},
   {0, no_argument, 0, 0}
 };
 \f
@@ -1845,6 +1858,583 @@ objdump_sprintf (SFILE *f, const char *format, ...)
   return n;
 }
 
+/* Code for generating (colored) diagrams of control flow start and end
+   points.  */
+
+/* Structure used to store the properties of a jump.  */
+
+struct jump_info
+{
+  /* The next jump, or NULL if this is the last object.  */
+  struct jump_info *next;
+  /* The previous jump, or NULL if this is the first object.  */
+  struct jump_info *prev;
+  /* The start addresses of the jump.  */
+  struct
+    {
+      /* The list of start addresses.  */
+      bfd_vma *addresses;
+      /* The number of elements.  */
+      size_t count;
+      /* The maximum number of elements that fit into the array.  */
+      size_t max_count;
+    } start;
+  /* The end address of the jump.  */
+  bfd_vma end;
+  /* The drawing level of the jump.  */
+  int level;
+};
+
+/* Construct a jump object for a jump from start
+   to end with the corresponding level.  */
+
+static struct jump_info *
+jump_info_new (bfd_vma start, bfd_vma end, int level)
+{
+  struct jump_info *result = xmalloc (sizeof (struct jump_info));
+
+  result->next = NULL;
+  result->prev = NULL;
+  result->start.addresses = xmalloc (sizeof (bfd_vma *) * 2);
+  result->start.addresses[0] = start;
+  result->start.count = 1;
+  result->start.max_count = 2;
+  result->end = end;
+  result->level = level;
+
+  return result;
+}
+
+/* Free a jump object and return the next object
+   or NULL if this was the last one.  */
+
+static struct jump_info *
+jump_info_free (struct jump_info *ji)
+{
+  struct jump_info *result = NULL;
+
+  if (ji)
+    {
+      result = ji->next;
+      if (ji->start.addresses)
+       free (ji->start.addresses);
+      free (ji);
+    }
+
+  return result;
+}
+
+/* Get the smallest value of all start and end addresses.  */
+
+static bfd_vma
+jump_info_min_address (const struct jump_info *ji)
+{
+  bfd_vma min_address = ji->end;
+  size_t i;
+
+  for (i = ji->start.count; i-- > 0;)
+    if (ji->start.addresses[i] < min_address)
+      min_address = ji->start.addresses[i];
+  return min_address;
+}
+
+/* Get the largest value of all start and end addresses.  */
+
+static bfd_vma
+jump_info_max_address (const struct jump_info *ji)
+{
+  bfd_vma max_address = ji->end;
+  size_t i;
+
+  for (i = ji->start.count; i-- > 0;)
+    if (ji->start.addresses[i] > max_address)
+      max_address = ji->start.addresses[i];
+  return max_address;
+}
+
+/* Get the target address of a jump.  */
+
+static bfd_vma
+jump_info_end_address (const struct jump_info *ji)
+{
+  return ji->end;
+}
+
+/* Test if an address is one of the start addresses of a jump.  */
+
+static bfd_boolean
+jump_info_is_start_address (const struct jump_info *ji, bfd_vma address)
+{
+  bfd_boolean result = FALSE;
+  size_t i;
+
+  for (i = ji->start.count; i-- > 0;)
+    if (address == ji->start.addresses[i])
+      {
+       result = TRUE;
+       break;
+      }
+
+  return result;
+}
+
+/* Test if an address is the target address of a jump.  */
+
+static bfd_boolean
+jump_info_is_end_address (const struct jump_info *ji, bfd_vma address)
+{
+  return (address == ji->end);
+}
+
+/* Get the difference between the smallest and largest address of a jump.  */
+
+static bfd_vma
+jump_info_size (const struct jump_info *ji)
+{
+  return jump_info_max_address (ji) - jump_info_min_address (ji);
+}
+
+/* Unlink a jump object from a list.  */
+
+static void
+jump_info_unlink (struct jump_info *node,
+                 struct jump_info **base)
+{
+  if (node->next)
+    node->next->prev = node->prev;
+  if (node->prev)
+    node->prev->next = node->next;
+  else
+    *base = node->next;
+  node->next = NULL;
+  node->prev = NULL;
+}
+
+/* Insert unlinked jump info node into a list.  */
+
+static void
+jump_info_insert (struct jump_info *node,
+                 struct jump_info *target,
+                 struct jump_info **base)
+{
+  node->next = target;
+  node->prev = target->prev;
+  target->prev = node;
+  if (node->prev)
+    node->prev->next = node;
+  else
+    *base = node;
+}
+
+/* Add unlinked node to the front of a list.  */
+
+static void
+jump_info_add_front (struct jump_info *node,
+                    struct jump_info **base)
+{
+  node->next = *base;
+  if (node->next)
+    node->next->prev = node;
+  node->prev = NULL;
+  *base = node;
+}
+
+/* Move linked node to target position.  */
+
+static void
+jump_info_move_linked (struct jump_info *node,
+                      struct jump_info *target,
+                      struct jump_info **base)
+{
+  /* Unlink node.  */
+  jump_info_unlink (node, base);
+  /* Insert node at target position.  */
+  jump_info_insert (node, target, base);
+}
+
+/* Test if two jumps intersect.  */
+
+static bfd_boolean
+jump_info_intersect (const struct jump_info *a,
+                    const struct jump_info *b)
+{
+  return ((jump_info_max_address (a) >= jump_info_min_address (b))
+         && (jump_info_min_address (a) <= jump_info_max_address (b)));
+}
+
+/* Merge two compatible jump info objects.  */
+
+static void
+jump_info_merge (struct jump_info **base)
+{
+  struct jump_info *a;
+
+  for (a = *base; a; a = a->next)
+    {
+      struct jump_info *b;
+
+      for (b = a->next; b; b = b->next)
+       {
+         /* Merge both jumps into one.  */
+         if (a->end == b->end)
+           {
+             /* Reallocate addresses.  */
+             size_t needed_size = a->start.count + b->start.count;
+             size_t i;
+
+             if (needed_size > a->start.max_count)
+               {
+                 a->start.max_count += b->start.max_count;
+                 a->start.addresses =
+                   xrealloc (a->start.addresses,
+                             a->start.max_count * sizeof(bfd_vma *));
+               }
+
+             /* Append start addresses.  */
+             for (i = 0; i < b->start.count; ++i)
+               a->start.addresses[a->start.count++] =
+                 b->start.addresses[i];
+
+             /* Remove and delete jump.  */
+             struct jump_info *tmp = b->prev;
+             jump_info_unlink (b, base);
+             jump_info_free (b);
+             b = tmp;
+           }
+       }
+    }
+}
+
+/* Sort jumps by their size and starting point using a stable
+   minsort. This could be improved if sorting performance is
+   an issue, for example by using mergesort.  */
+
+static void
+jump_info_sort (struct jump_info **base)
+{
+  struct jump_info *current_element = *base;
+
+  while (current_element)
+    {
+      struct jump_info *best_match = current_element;
+      struct jump_info *runner = current_element->next;
+      bfd_vma best_size = jump_info_size (best_match);
+
+      while (runner)
+       {
+         bfd_vma runner_size = jump_info_size (runner);
+
+         if ((runner_size < best_size)
+             || ((runner_size == best_size)
+                 && (jump_info_min_address (runner)
+                     < jump_info_min_address (best_match))))
+           {
+             best_match = runner;
+             best_size = runner_size;
+           }
+
+         runner = runner->next;
+       }
+
+      if (best_match == current_element)
+       current_element = current_element->next;
+      else
+       jump_info_move_linked (best_match, current_element, base);
+    }
+}
+
+/* Visualize all jumps at a given address.  */
+
+static void
+jump_info_visualize_address (const struct jump_info *jumps,
+                            bfd_vma address,
+                            int max_level,
+                            char *line_buffer,
+                            uint8_t *color_buffer)
+{
+  size_t len = (max_level + 1) * 3;
+  const struct jump_info *ji;
+
+  /* Clear line buffer.  */
+  memset(line_buffer, ' ', len);
+  memset(color_buffer, 0, len);
+
+  /* Iterate over jumps and add their ASCII art.  */
+  for (ji = jumps; ji; ji = ji->next)
+    {
+      if ((jump_info_min_address (ji) <= address)
+         && (jump_info_max_address (ji) >= address))
+       {
+         /* Hash target address to get an even
+            distribution between all values.  */
+         bfd_vma hash_address = jump_info_end_address (ji);
+         uint8_t color = iterative_hash_object (hash_address, 0);
+         /* Fetch line offset.  */
+         int offset = (max_level - ji->level) * 3;
+
+         /* Draw start line.  */
+         if (jump_info_is_start_address (ji, address))
+           {
+             size_t i = offset + 1;
+
+             for (; i < len - 1; ++i)
+               if (line_buffer[i] == ' ')
+                 {
+                   line_buffer[i] = '-';
+                   color_buffer[i] = color;
+                 }
+
+             if (line_buffer[i] == ' ')
+               {
+                 line_buffer[i] = '-';
+                 color_buffer[i] = color;
+               }
+             else if (line_buffer[i] == '>')
+               {
+                 line_buffer[i] = 'X';
+                 color_buffer[i] = color;
+               }
+
+             if (line_buffer[offset] == ' ')
+               {
+                 if (address <= ji->end)
+                   line_buffer[offset] =
+                     (jump_info_min_address (ji) == address) ? '/': '+';
+                 else
+                   line_buffer[offset] =
+                     (jump_info_max_address (ji) == address) ? '\\': '+';
+                 color_buffer[offset] = color;
+               }
+           }
+         /* Draw jump target.  */
+         else if (jump_info_is_end_address (ji, address))
+           {
+             size_t i = offset + 1;
+
+             for (; i < len - 1; ++i)
+               if (line_buffer[i] == ' ')
+                 {
+                   line_buffer[i] = '-';
+                   color_buffer[i] = color;
+                 }
+
+             if (line_buffer[i] == ' ')
+               {
+                 line_buffer[i] = '>';
+                 color_buffer[i] = color;
+               }
+             else if (line_buffer[i] == '-')
+               {
+                 line_buffer[i] = 'X';
+                 color_buffer[i] = color;
+               }
+
+             if (line_buffer[offset] == ' ')
+               {
+                 if (jump_info_min_address (ji) < address)
+                   line_buffer[offset] =
+                     (jump_info_max_address (ji) > address) ? '>' : '\\';
+                 else
+                   line_buffer[offset] = '/';
+                 color_buffer[offset] = color;
+               }
+           }
+         /* Draw intermediate line segment.  */
+         else if (line_buffer[offset] == ' ')
+           {
+             line_buffer[offset] = '|';
+             color_buffer[offset] = color;
+           }
+       }
+    }
+}
+
+/* Clone of disassemble_bytes to detect jumps inside a function.  */
+/* FIXME: is this correct? Can we strip it down even further?  */
+
+static struct jump_info *
+disassemble_jumps (struct disassemble_info * inf,
+                  disassembler_ftype        disassemble_fn,
+                  bfd_vma                   start_offset,
+                  bfd_vma                   stop_offset,
+                  bfd_vma                   rel_offset,
+                  arelent ***               relppp,
+                  arelent **                relppend)
+{
+  struct objdump_disasm_info *aux;
+  struct jump_info *jumps = NULL;
+  asection *section;
+  bfd_vma addr_offset;
+  unsigned int opb = inf->octets_per_byte;
+  int octets = opb;
+  SFILE sfile;
+
+  aux = (struct objdump_disasm_info *) inf->application_data;
+  section = inf->section;
+
+  sfile.alloc = 120;
+  sfile.buffer = (char *) xmalloc (sfile.alloc);
+  sfile.pos = 0;
+
+  inf->insn_info_valid = 0;
+  inf->fprintf_func = (fprintf_ftype) objdump_sprintf;
+  inf->stream = &sfile;
+
+  addr_offset = start_offset;
+  while (addr_offset < stop_offset)
+    {
+      int previous_octets;
+
+      /* Remember the length of the previous instruction.  */
+      previous_octets = octets;
+      octets = 0;
+
+      sfile.pos = 0;
+      inf->bytes_per_line = 0;
+      inf->bytes_per_chunk = 0;
+      inf->flags = ((disassemble_all ? DISASSEMBLE_DATA : 0)
+        | (wide_output ? WIDE_OUTPUT : 0));
+      if (machine)
+       inf->flags |= USER_SPECIFIED_MACHINE_TYPE;
+
+      if (inf->disassembler_needs_relocs
+         && (bfd_get_file_flags (aux->abfd) & EXEC_P) == 0
+         && (bfd_get_file_flags (aux->abfd) & DYNAMIC) == 0
+         && *relppp < relppend)
+       {
+         bfd_signed_vma distance_to_rel;
+
+         distance_to_rel = (**relppp)->address - (rel_offset + addr_offset);
+
+         /* Check to see if the current reloc is associated with
+            the instruction that we are about to disassemble.  */
+         if (distance_to_rel == 0
+             /* FIXME: This is wrong.  We are trying to catch
+                relocs that are addressed part way through the
+                current instruction, as might happen with a packed
+                VLIW instruction.  Unfortunately we do not know the
+                length of the current instruction since we have not
+                disassembled it yet.  Instead we take a guess based
+                upon the length of the previous instruction.  The
+                proper solution is to have a new target-specific
+                disassembler function which just returns the length
+                of an instruction at a given address without trying
+                to display its disassembly. */
+             || (distance_to_rel > 0
+               && distance_to_rel < (bfd_signed_vma) (previous_octets/ opb)))
+           {
+             inf->flags |= INSN_HAS_RELOC;
+           }
+       }
+
+      if (! disassemble_all
+         && (section->flags & (SEC_CODE | SEC_HAS_CONTENTS))
+         == (SEC_CODE | SEC_HAS_CONTENTS))
+       /* Set a stop_vma so that the disassembler will not read
+          beyond the next symbol.  We assume that symbols appear on
+          the boundaries between instructions.  We only do this when
+          disassembling code of course, and when -D is in effect.  */
+       inf->stop_vma = section->vma + stop_offset;
+
+      inf->stop_offset = stop_offset;
+
+      /* Extract jump information.  */
+      inf->insn_info_valid = 0;
+      octets = (*disassemble_fn) (section->vma + addr_offset, inf);
+      /* Test if a jump was detected.  */
+      if (inf->insn_info_valid
+         && ((inf->insn_type == dis_branch)
+             || (inf->insn_type == dis_condbranch)
+             || (inf->insn_type == dis_jsr)
+             || (inf->insn_type == dis_condjsr))
+         && (inf->target >= section->vma + start_offset)
+         && (inf->target < section->vma + stop_offset))
+       {
+         struct jump_info *ji =
+           jump_info_new (section->vma + addr_offset, inf->target, -1);
+         jump_info_add_front (ji, &jumps);
+       }
+
+      inf->stop_vma = 0;
+
+      addr_offset += octets / opb;
+    }
+
+  inf->fprintf_func = (fprintf_ftype) fprintf;
+  inf->stream = stdout;
+
+  free (sfile.buffer);
+
+  /* Merge jumps.  */
+  jump_info_merge (&jumps);
+  /* Process jumps.  */
+  jump_info_sort (&jumps);
+
+  /* Group jumps by level.  */
+  struct jump_info *last_jump = jumps;
+  int max_level = -1;
+
+  while (last_jump)
+    {
+      /* The last jump is part of the next group.  */
+      struct jump_info *base = last_jump;
+      /* Increment level.  */
+      base->level = ++max_level;
+
+      /* Find jumps that can be combined on the same
+        level, with the largest jumps tested first.
+        This has the advantage that large jumps are on
+        lower levels and do not intersect with small
+        jumps that get grouped on higher levels.  */
+      struct jump_info *exchange_item = last_jump->next;
+      struct jump_info *it = exchange_item;
+
+      for (; it; it = it->next)
+       {
+         /* Test if the jump intersects with any
+            jump from current group.  */
+         bfd_boolean ok = TRUE;
+         struct jump_info *it_collision;
+
+         for (it_collision = base;
+              it_collision != exchange_item;
+              it_collision = it_collision->next)
+           {
+             /* This jump intersects so we leave it out.  */
+             if (jump_info_intersect (it_collision, it))
+               {
+                 ok = FALSE;
+                 break;
+               }
+           }
+
+         /* Add jump to group.  */
+         if (ok)
+           {
+             /* Move current element to the front.  */
+             if (it != exchange_item)
+               {
+                 struct jump_info *save = it->prev;
+                 jump_info_move_linked (it, exchange_item, &jumps);
+                 last_jump = it;
+                 it = save;
+               }
+             else
+               {
+                 last_jump = exchange_item;
+                 exchange_item = exchange_item->next;
+               }
+             last_jump->level = max_level;
+           }
+       }
+
+      /* Move to next group.  */
+      last_jump = exchange_item;
+    }
+
+  return jumps;
+}
+
 /* The number of zeroes we want to see before we start skipping them.
    The number is arbitrarily chosen.  */
 
@@ -1927,6 +2517,30 @@ disassemble_bytes (struct disassemble_info * inf,
 
   inf->insn_info_valid = 0;
 
+  /* Determine maximum level. */
+  int max_level = -1;
+  struct jump_info *base = detected_jumps ? detected_jumps : NULL;
+  struct jump_info *ji;
+
+  for (ji = base; ji; ji = ji->next)
+    {
+      if (ji->level > max_level)
+       {
+         max_level = ji->level;
+       }
+    }
+
+  /* Allocate line buffer if there are any jumps.  */
+  size_t len = (max_level + 1) * 3 + 1;
+  char *line_buffer = (max_level >= 0) ? xmalloc(len): NULL;
+  uint8_t *color_buffer = (max_level >= 0) ? xmalloc(len): NULL;
+
+  if (line_buffer)
+    {
+      line_buffer[len - 1] = 0;
+      color_buffer[len - 1] = 0;
+    }
+
   addr_offset = start_offset;
   while (addr_offset < stop_offset)
     {
@@ -1998,6 +2612,44 @@ disassemble_bytes (struct disassemble_info * inf,
              putchar (' ');
            }
 
+         /* Visualize jumps. */
+         if (line_buffer)
+           {
+             jump_info_visualize_address (base,
+                                          section->vma + addr_offset,
+                                          max_level,
+                                          line_buffer,
+                                          color_buffer);
+
+             size_t line_buffer_size = strlen (line_buffer);
+             char last_color = 0;
+
+             for (size_t i = 0; i <= line_buffer_size; ++i)
+               {
+                 if (color_output)
+                   {
+                     uint8_t color = (i < line_buffer_size) ? color_buffer[i]: 0;
+
+                     if (color != last_color)
+                       {
+                         if (color)
+                           if (extended_color_output)
+                             /* Use extended 8bit color, but
+                                do not choose dark colors.  */
+                             printf ("\033[38;5;%dm", 124 + (color % 108));
+                           else
+                             /* Use simple terminal colors.  */
+                             printf ("\033[%dm", 31 + (color % 7));
+                         else
+                           /* Clear color.  */
+                           printf ("\033[0m");
+                         last_color = color;
+                       }
+                   }
+                 putchar ((i < line_buffer_size) ? line_buffer[i]: ' ');
+               }
+           }
+
          if (insns)
            {
              sfile.pos = 0;
@@ -2291,6 +2943,8 @@ disassemble_bytes (struct disassemble_info * inf,
     }
 
   free (sfile.buffer);
+  free (line_buffer);
+  free (color_buffer);
 }
 
 static void
@@ -2611,9 +3265,42 @@ disassemble_section (bfd *abfd, asection *section, void *inf)
        insns = FALSE;
 
       if (do_print)
-       disassemble_bytes (pinfo, paux->disassemble_fn, insns, data,
-                          addr_offset, nextstop_offset,
-                          rel_offset, &rel_pp, rel_ppend);
+       {
+         /* Resolve symbol name.  */
+         if (visualize_jumps && abfd && sym && sym->name)
+           {
+             struct disassemble_info di;
+             SFILE sf;
+
+             sf.alloc = strlen (sym->name) + 40;
+             sf.buffer = (char*) xmalloc (sf.alloc);
+             sf.pos = 0;
+             di.fprintf_func = (fprintf_ftype) objdump_sprintf;
+             di.stream = &sf;
+
+             objdump_print_symname (abfd, &di, sym);
+
+             /* Fetch jump information.  */
+             detected_jumps = disassemble_jumps
+               (pinfo, paux->disassemble_fn,
+                addr_offset, nextstop_offset,
+                rel_offset, &rel_pp, rel_ppend);
+
+             /* Free symbol name.  */
+             free (sf.buffer);
+           }
+
+         /* Add jumps to output.  */
+         disassemble_bytes (pinfo, paux->disassemble_fn, insns, data,
+                            addr_offset, nextstop_offset,
+                            rel_offset, &rel_pp, rel_ppend);
+
+         /* Free jumps.  */
+         while (detected_jumps)
+           {
+             detected_jumps = jump_info_free (detected_jumps);
+           }
+       }
 
       addr_offset = nextstop_offset;
       sym = nextsym;
@@ -4437,6 +5124,25 @@ main (int argc, char **argv)
        case OPTION_INLINES:
          unwind_inlines = TRUE;
          break;
+       case OPTION_VISUALIZE_JUMPS:
+         visualize_jumps = TRUE;
+         color_output = FALSE;
+         extended_color_output = FALSE;
+         if (optarg != NULL)
+           {
+             if (streq (optarg, "color"))
+               color_output = TRUE;
+             else if (streq (optarg, "extended-color"))
+               {
+                 color_output = TRUE;
+                 extended_color_output = TRUE;
+               }
+             else if (streq (optarg, "off"))
+               visualize_jumps = FALSE;
+             else
+               nonfatal (_("unrecognized argument to --visualize-option"));
+           }
+         break;
        case 'E':
          if (strcmp (optarg, "B") == 0)
            endian = BFD_ENDIAN_BIG;
index bb235dce4cae859a6199c037630f2ca3b8a355d9..8f3f94414b2e73a8ee5b5c4683916fa66036328a 100644 (file)
@@ -1,3 +1,12 @@
+2020-01-13  Thomas Troeger  <tstroege@gmx.de>
+
+       * arm-dis.c (print_insn_arm): Fill in insn info fields for control
+       flow instructions.
+       (print_insn_thumb16, print_insn_thumb32): Likewise.
+       (print_insn): Initialize the insn info.
+       * i386-dis.c (print_insn): Initialize the insn info fields, and
+       detect jumps.
+
 2012-01-13  Claudiu Zissulescu <claziss@gmail.com>
 
        * arc-opc.c (C_NE): Make it required.
index 55ec321bedf6a2e3beb6ff7b08cf93213f1b9d50..b174f8335cdf7c3f90cafcb35995bf624512d719 100644 (file)
@@ -9886,7 +9886,13 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info, long given)
                    case 'b':
                      {
                        bfd_vma disp = (((given & 0xffffff) ^ 0x800000) - 0x800000);
-                       info->print_address_func (disp * 4 + pc + 8, info);
+                       bfd_vma target = disp * 4 + pc + 8;
+                       info->print_address_func (target, info);
+
+                       /* Fill in instruction information.  */
+                       info->insn_info_valid = 1;
+                       info->insn_type = dis_branch;
+                       info->target = target;
                      }
                      break;
 
@@ -10024,6 +10030,11 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info, long given)
                          address += 2;
 
                        info->print_address_func (address, info);
+
+                       /* Fill in instruction information.  */
+                       info->insn_info_valid = 1;
+                       info->insn_type = dis_branch;
+                       info->target = address;
                      }
                      break;
 
@@ -10388,6 +10399,11 @@ print_insn_thumb16 (bfd_vma pc, struct disassemble_info *info, long given)
                                     + ((given & 0x00f8) >> 2)
                                     + ((given & 0x0200) >> 3));
                  info->print_address_func (address, info);
+
+                 /* Fill in instruction information.  */
+                 info->insn_info_valid = 1;
+                 info->insn_type = dis_branch;
+                 info->target = address;
                }
                break;
 
@@ -10461,8 +10477,14 @@ print_insn_thumb16 (bfd_vma pc, struct disassemble_info *info, long given)
 
                          case 'B':
                            reg = ((reg ^ (1 << bitend)) - (1 << bitend));
-                           info->print_address_func (reg * 2 + pc + 4, info);
+                           bfd_vma target = reg * 2 + pc + 4;
+                           info->print_address_func (target, info);
                            value_in_comment = 0;
+
+                           /* Fill in instruction information.  */
+                           info->insn_info_valid = 1;
+                           info->insn_type = dis_branch;
+                           info->target = target;
                            break;
 
                          case 'c':
@@ -11019,7 +11041,13 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given)
                  offset |= (given & 0x000007ff) << 1;
                  offset -= (1 << 20);
 
-                 info->print_address_func (pc + 4 + offset, info);
+                 bfd_vma target = pc + 4 + offset;
+                 info->print_address_func (target, info);
+
+                 /* Fill in instruction information.  */
+                 info->insn_info_valid = 1;
+                 info->insn_type = dis_branch;
+                 info->target = target;
                }
                break;
 
@@ -11043,6 +11071,11 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given)
                      offset &= ~2u;
 
                  info->print_address_func (offset, info);
+
+                 /* Fill in instruction information.  */
+                 info->insn_info_valid = 1;
+                 info->insn_type = dis_branch;
+                 info->target = offset;
                }
                break;
 
@@ -11715,6 +11748,14 @@ print_insn (bfd_vma pc, struct disassemble_info *info, bfd_boolean little)
   bfd_boolean   found = FALSE;
   struct arm_private_data *private_data;
 
+  /* Clear instruction information field.  */
+  info->insn_info_valid = 0;
+  info->branch_delay_insns = 0;
+  info->data_size = 0;
+  info->insn_type = dis_noninsn;
+  info->target = 0;
+  info->target2 = 0;
+
   if (info->disassembler_options)
     {
       parse_arm_disassembler_options (info->disassembler_options);
index 5d24fb5cecf42ef5d4b28d2845d13b862e99933d..c73e964b5469b6f43c6a54f2a3227ad37dd71960 100644 (file)
@@ -11069,6 +11069,9 @@ static const struct dis386 rm_table[][8] = {
 #define BND_PREFIX     (0xf2 | 0x400)
 #define NOTRACK_PREFIX (0x3e | 0x100)
 
+/* Remember if the current op is a jump instruction.  */
+static bfd_boolean op_is_jump = FALSE;
+
 static int
 ckprefix (void)
 {
@@ -12143,6 +12146,50 @@ print_insn (bfd_vma pc, disassemble_info *info)
        }
     }
 
+  /* Clear instruction information.  */
+  if (the_info)
+    {
+      the_info->insn_info_valid = 0;
+      the_info->branch_delay_insns = 0;
+      the_info->data_size = 0;
+      the_info->insn_type = dis_noninsn;
+      the_info->target = 0;
+      the_info->target2 = 0;
+    }
+
+  /* Reset jump operation indicator.  */
+  op_is_jump = FALSE;
+
+  {
+    int jump_detection = 0;
+
+    /* Extract flags.  */
+    for (i = 0; i < MAX_OPERANDS; ++i)
+      {
+       if ((dp->op[i].rtn == OP_J)
+           || (dp->op[i].rtn == OP_indirE))
+         jump_detection |= 1;
+       else if ((dp->op[i].rtn == BND_Fixup)
+                || (!dp->op[i].rtn && !dp->op[i].bytemode))
+         jump_detection |= 2;
+       else if ((dp->op[i].bytemode == cond_jump_mode)
+                || (dp->op[i].bytemode == loop_jcxz_mode))
+         jump_detection |= 4;
+      }
+
+    /* Determine if this is a jump or branch.  */
+    if ((jump_detection & 0x3) == 0x3)
+      {
+       op_is_jump = TRUE;
+       if (jump_detection & 0x4)
+         the_info->insn_type = dis_condbranch;
+       else
+         the_info->insn_type =
+           (dp->name && !strncmp(dp->name, "call", 4))
+           ? dis_jsr : dis_branch;
+      }
+  }
+
   /* If VEX.vvvv and EVEX.vvvv are unused, they must be all 1s, which
      are all 0s in inverted form.  */
   if (need_vex && vex.register_specifier != 0)
@@ -12256,7 +12303,19 @@ print_insn (bfd_vma pc, disassemble_info *info)
        if (needcomma)
          (*info->fprintf_func) (info->stream, ",");
        if (op_index[i] != -1 && !op_riprel[i])
-         (*info->print_address_func) ((bfd_vma) op_address[op_index[i]], info);
+         {
+           bfd_vma target = (bfd_vma) op_address[op_index[i]];
+
+           if (the_info && op_is_jump)
+             {
+               the_info->insn_info_valid = 1;
+               the_info->branch_delay_insns = 0;
+               the_info->data_size = 0;
+               the_info->target = target;
+               the_info->target2 = 0;
+             }
+           (*info->print_address_func) (target, info);
+         }
        else
          (*info->fprintf_func) (info->stream, "%s", op_txt[i]);
        needcomma = 1;