Add an option to objdump's disassembler to generate ascii art diagrams showing the destinations of flow control instructions.

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.
This commit is contained in:
Thomas Troeger 2020-01-13 12:36:55 +00:00 committed by Nick Clifton
parent a4f2b7c5d9
commit 1d67fe3b6e
7 changed files with 877 additions and 9 deletions

View 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

View File

@ -15,6 +15,25 @@
* 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,

View 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

View 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;
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}
};
@ -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;

View 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.

View 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);

View 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;