This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Re: Jump visualization feature for objdump.
- From: Thomas Troeger <tstroege at gmx dot de>
- To: Nick Clifton <nickc at redhat dot com>
- Cc: binutils at sourceware dot org
- Date: Tue, 7 Jan 2020 16:24:19 +0100
- Subject: Re: Jump visualization feature for objdump.
- References: <20191106190915.GA2533@glenfiddich> <d319f868-ef8c-1e0e-b6d7-44abdce19d22@redhat.com> <20191107181549.GA2228@glenfiddich> <c96b1a8d-71ea-dbfd-40aa-85499818c892@redhat.com>
> 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;