This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Jump visualization feature for objdump.


> Hi Thomas,

Hi there!

> OK, so just to be clear, we are interested in having such a feature
> inside objdump and if you are willing to create a patch or two we
> would be very happy to review them.

I have attached the current version of my patches.
It's possible to create the visualization using

objdump -wzd --visualize-jumps --extended-color /bin/bash

There are still some fixmes and I guess the code still needs tweaks. I
tried using the same formatting as the code around it, but I cannot
guarantee that it is consistent, but here you go.

Cheers!
Thomas.
diff --git a/binutils/NEWS b/binutils/NEWS
index e6afc4b..7ba2ff7 100644
--- a/binutils/NEWS
+++ b/binutils/NEWS
@@ -30,6 +30,9 @@ Changes in 2.33:
  * Add support for dumping types encoded in the Compact Type Format
    to objdump and readelf.

+ * Add visualization of jumps inside a function by drawing a graph
+   between the address and the disassembler column.
+
 Changes in 2.32:

 * The addr2line, c++filt, nm and objdump tools now have a limit on the
diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
index 059d61c..8c99607 100644
--- a/binutils/doc/binutils.texi
+++ b/binutils/doc/binutils.texi
@@ -2142,6 +2142,9 @@ objdump [@option{-a}|@option{--archive-headers}]
         [@option{--prefix=}@var{prefix}]
         [@option{--prefix-strip=}@var{level}]
         [@option{--insn-width=}@var{width}]
+        [@option{--visualize-jumps}]
+        [@option{--color}]
+        [@option{--extended-color}]
         [@option{-V}|@option{--version}]
         [@option{-H}|@option{--help}]
         @var{objfile}@dots{}
@@ -2654,6 +2657,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
+Visualize jumps that stay inside a function by drawing ASCII art between
+the start and target addresses.
+
+@item --color
+Colorize output of visualized jumps with simple terminal colors.
+
+@item --extended-color
+Colorize output of visualized jumps using 8bit
+color.  Might not work on all terminals.
+
 @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
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 33d5d72..b2ee4c6 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -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;             /* --color.  */
+static bfd_boolean extended_color_output = FALSE;    /* --extended-color.  */

 static int demangle_flags = DMGL_ANSI | DMGL_PARAMS;

@@ -199,6 +202,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;
 
 static void usage (FILE *, int) ATTRIBUTE_NORETURN;
 static void
@@ -279,7 +285,10 @@ 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\
+      --color                    colorize output\n\
+      --extended-color           use extended 8bit color codes\n\n"));
       list_supported_targets (program_name, stream);
       list_supported_architectures (program_name, stream);

@@ -317,7 +326,10 @@ enum option_values
     OPTION_INLINES,
     OPTION_SOURCE_COMMENT,
     OPTION_CTF,
-    OPTION_CTF_PARENT
+    OPTION_CTF_PARENT,
+    OPTION_VISUALIZE_JUMPS,
+    OPTION_COLOR_OUTPUT,
+    OPTION_EXTENDED_COLOR_OUTPUT
   };

 static struct option long_options[]=
@@ -377,6 +389,9 @@ 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", no_argument, 0, OPTION_VISUALIZE_JUMPS},
+  {"color", no_argument, 0, OPTION_COLOR_OUTPUT},
+  {"extended-color", no_argument, 0, OPTION_EXTENDED_COLOR_OUTPUT},
   {0, no_argument, 0, 0}
 };
 
@@ -1838,6 +1853,620 @@ objdump_sprintf (SFILE *f, const char *format, ...)

 /* Disassemble some data in memory between given values.  */

+#if 1
+/* Very simple hash function to map any address to an 8 bit value.  The main
+   purpose of this is to generate all colors in the case the input addresses
+   are all even or divisible by 4 or 8 or follow some other pattern. */
+
+static
+uint8_t
+hash_address (bfd_vma address)
+{
+	bfd_vma result = address;
+
+	result += result << 5;
+	result ^= result >> 7;
+
+	return result % 256;
+}
+#else
+/* This code was taken from
+ *
+ * https://en.wikipedia.org/wiki/Jenkins_hash_function
+ *
+ * TODO: Does this work with the GPL?
+ */
+static
+uint8_t
+hash_address (bfd_vma address)
+{
+  const uint8_t* ptr = (const uint8_t*) &address;
+  uint32_t hash = 0;
+
+  for (size_t i = 0; i < sizeof (address); ++i)
+    {
+      hash = ptr[i];
+      hash += hash << 10;
+      hash ^= hash >> 6;
+    }
+
+  hash += hash << 3;
+  hash ^= hash >> 11;
+  hash += hash << 15;
+
+  return hash % 256;
+}
+#endif
+
+/* Store 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;
+  for (size_t 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;
+  for (size_t 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;
+
+  for (size_t 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)
+{
+  for (struct jump_info *a = *base; a; a = a->next)
+    {
+      for (struct jump_info *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;
+	      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 (size_t 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;
+
+  /* Clear line buffer. */
+  memset(line_buffer, ' ', len);
+  memset(color_buffer, 0, len);
+
+  /* Iterate over jumps and add their ASCII art. */
+  for (const struct jump_info *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. */
+	  uint8_t color = hash_address (jump_info_end_address (ji));
+	  /* 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 = aux->sec;
+
+  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;
+	  for (struct jump_info *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;
+}
+
 static void
 disassemble_bytes (struct disassemble_info * inf,
 		   disassembler_ftype        disassemble_fn,
@@ -1898,6 +2527,27 @@ 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;
+  for (struct jump_info *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)
     {
@@ -1972,6 +2622,42 @@ 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;
@@ -2245,6 +2931,13 @@ disassemble_bytes (struct disassemble_info * inf,
     }

   free (sfile.buffer);
+
+  /* Free line buffer. */
+  if (line_buffer)
+    free(line_buffer);
+  /* Free color buffer. */
+  if (color_buffer)
+    free(color_buffer);
 }

 static void
@@ -2562,9 +3255,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;
@@ -4383,6 +5109,17 @@ main (int argc, char **argv)
 	case OPTION_INLINES:
 	  unwind_inlines = TRUE;
 	  break;
+	case OPTION_VISUALIZE_JUMPS:
+	  visualize_jumps = TRUE;
+	  break;
+	case OPTION_COLOR_OUTPUT:
+	  color_output = TRUE;
+	  break;
+	case OPTION_EXTENDED_COLOR_OUTPUT:
+	  /* enable color output. */
+	  color_output = TRUE;
+	  extended_color_output = TRUE;
+	  break;
 	case 'E':
 	  if (strcmp (optarg, "B") == 0)
 	    endian = BFD_ENDIAN_BIG;
diff --git a/opcodes/arm-dis.c b/opcodes/arm-dis.c
index 8ff86bf..e862cf4 100644
--- a/opcodes/arm-dis.c
+++ b/opcodes/arm-dis.c
@@ -9810,7 +9810,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;

@@ -9948,6 +9954,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;

@@ -10312,6 +10323,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;

@@ -10385,8 +10401,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':
@@ -10940,7 +10962,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;

@@ -10964,6 +10992,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;

@@ -11636,6 +11669,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);
diff --git a/opcodes/i386-dis.c b/opcodes/i386-dis.c
index dc81420..24577ee 100644
--- a/opcodes/i386-dis.c
+++ b/opcodes/i386-dis.c
@@ -11052,6 +11052,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 int op_is_jump = 0;
+
 static int
 ckprefix (void)
 {
@@ -12126,6 +12129,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 = 0;
+
+  {
+    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 = 1;
+	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)
@@ -12239,7 +12286,18 @@ 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;

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]