From 33d751e5f5906957b74c4968ca4752fd329d1adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Wolker?= Date: Tue, 11 Jun 2024 19:56:43 +0200 Subject: [PATCH] objdump: UTF-8 jump visualization in disassembly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new option ‘--visualize-jumps-lines’ was added. It uses the same arguments like ‘--visualize-jumps’, but UTF-8 characters are used for rendering the jump visualization instead of plain ASCII characters. This also adds a way to customize jump visualization output using an environment variable OBJDUMP_JUMP_LINES that contains key-value pairs that change the strings used to print the line drawing characters. --- binutils/doc/binutils.texi | 41 ++++++++ binutils/objdump.c | 188 ++++++++++++++++++++++++++++++------- 2 files changed, 197 insertions(+), 32 deletions(-) diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi index c78c9e39..d68bb9b8 100644 --- a/binutils/doc/binutils.texi +++ b/binutils/doc/binutils.texi @@ -2321,6 +2321,7 @@ objdump [@option{-a}|@option{--archive-headers}] [@option{--prefix-strip=}@var{level}] [@option{--insn-width=}@var{width}] [@option{--visualize-jumps[=color|=extended-color|=off]} + [@option{--visualize-jumps-lines[=color|=extended-color|=off]} [@option{--disassembler-color=[off|terminal|on|extended]} [@option{-U} @var{method}] [@option{--unicode=}@var{method}] [@option{-V}|@option{--version}] @@ -2883,6 +2884,46 @@ If it is necessary to disable the @option{visualize-jumps} option after it has previously been enabled then use @option{visualize-jumps=off}. +Option @option{--visualize-jumps-lines} is the same, but uses box +drawing characters instead of plain ASCII to output the +lines. Environment variable @env{OBJDUMP_JUMP_LINES} allows +customization of these line drawing characters. This option contains +comma-separated key-value pairs, such as: + +@smallexample +OBJDUMP_JUMP_LINES="hor=-,arrow=>" +@end smallexample + +The objdump understands these keys: + +@table @code + +@item none +White space + +@item hor +Horizontal line + +@item ver +Vertical line + +@item dest +Arrow pointing rightwards, to the jump destination + +@item biarrow +Arrow pointing right and left + +@item topleft +Top left corner of the line + +@item branch +Vertical line branching to the right + +@item bottomleft +Bottom left corner of the line + +@end table + @item --disassembler-color=off @itemx --disassembler-color=terminal @itemx --disassembler-color=on|color|colour diff --git a/binutils/objdump.c b/binutils/objdump.c index 7182abda..21200e6c 100644 --- a/binutils/objdump.c +++ b/binutils/objdump.c @@ -136,6 +136,7 @@ static const char * source_comment; /* --source_comment. */ static bool visualize_jumps = false; /* --visualize-jumps. */ static bool color_output = false; /* --visualize-jumps=color. */ static bool extended_color_output = false; /* --visualize-jumps=extended-color. */ +static bool utf8_jumps = false; /* --visualize-jumps-utf8. */ static int process_links = false; /* --process-links. */ static int show_all_symbols; /* --show-all-symbols. */ static bool decompressed_dumps = false; /* -Z, --decompress. */ @@ -236,6 +237,53 @@ static const struct objdump_private_desc * const objdump_private_vectors[] = /* The list of detected jumps inside a function. */ static struct jump_info *detected_jumps = NULL; +/* Symbols used in the jump visualization. */ +enum jump_line + { + jump_line_none, + jump_line_hor, + jump_line_biarrow, + jump_line_topleft, + jump_line_branch, + jump_line_bottomleft, + jump_line_dest, + jump_line_ver, + }; +static const char *const jump_line_env_tokens[] = + { + [jump_line_none] = "none", + [jump_line_hor] = "hor", + [jump_line_biarrow] = "biarrow", + [jump_line_topleft] = "topleft", + [jump_line_branch] = "branch", + [jump_line_bottomleft] = "bottomleft", + [jump_line_dest] = "dest", + [jump_line_ver] = "ver", + NULL /* Sentinel value. */ + }; +static const char jump_lines[] = + { + [jump_line_none] = ' ', + [jump_line_hor] = '-', + [jump_line_biarrow] = 'X', + [jump_line_topleft] = ',', + [jump_line_branch] = '+', + [jump_line_bottomleft] = '\\', + [jump_line_dest] = '>', + [jump_line_ver] = '|', + }; +static const char *jump_lines_utf8[] = + { + [jump_line_none] = " ", + [jump_line_hor] = "─", + [jump_line_biarrow] = "X", + [jump_line_topleft] = "┌", + [jump_line_branch] = "├", + [jump_line_bottomleft] = "└", + [jump_line_dest] = "▶", + [jump_line_ver] = "│", + }; + typedef enum unicode_display_type { unicode_default = 0, @@ -430,6 +478,11 @@ usage (FILE *stream, int status) --visualize-jumps=extended-color\n\ Use extended 8-bit color codes\n")); fprintf (stream, _("\ + --visualize-jumps-lines Use box drawing characters for jumps visualization\n")); + fprintf (stream, _("\ + --visualize-jumps-lines={color|extended-color}\n\ + Use extended 8-bit color codes\n")); + fprintf (stream, _("\ --visualize-jumps=off Disable jump visualization\n")); #if DEFAULT_FOR_COLORED_DISASSEMBLY fprintf (stream, _("\ @@ -490,6 +543,7 @@ enum option_values #endif OPTION_SFRAME, OPTION_VISUALIZE_JUMPS, + OPTION_VISUALIZE_JUMPS_LINES, OPTION_DISASSEMBLER_COLOR }; @@ -559,6 +613,7 @@ static struct option long_options[]= {"unicode", required_argument, NULL, 'U'}, {"version", no_argument, NULL, 'V'}, {"visualize-jumps", optional_argument, 0, OPTION_VISUALIZE_JUMPS}, + {"visualize-jumps-lines", optional_argument, 0, OPTION_VISUALIZE_JUMPS_LINES}, {"wide", no_argument, NULL, 'w'}, {"disassembler-color", required_argument, NULL, OPTION_DISASSEMBLER_COLOR}, {NULL, no_argument, NULL, 0} @@ -2888,14 +2943,14 @@ jump_info_sort (struct jump_info **base) static void jump_info_visualize_address (bfd_vma address, int max_level, - char *line_buffer, + uint8_t *line_buffer, uint8_t *color_buffer) { struct jump_info *ji = detected_jumps; size_t len = (max_level + 1) * 3; /* Clear line buffer. */ - memset (line_buffer, ' ', len); + memset (line_buffer, jump_line_none, len); memset (color_buffer, 0, len); /* Iterate over jumps and add their ASCII art. */ @@ -2928,31 +2983,39 @@ jump_info_visualize_address (bfd_vma address, size_t i = offset + 1; for (; i < len - 1; ++i) - if (line_buffer[i] == ' ') + if (line_buffer[i] == jump_line_none) { - line_buffer[i] = '-'; + line_buffer[i] = jump_line_hor; color_buffer[i] = color; } - if (line_buffer[i] == ' ') + if (line_buffer[i] == jump_line_none) { - line_buffer[i] = '-'; + line_buffer[i] = jump_line_none; color_buffer[i] = color; } - else if (line_buffer[i] == '>') + else if (line_buffer[i] == jump_line_dest) { - line_buffer[i] = 'X'; + line_buffer[i] = jump_line_biarrow; color_buffer[i] = color; } - if (line_buffer[offset] == ' ') + if (line_buffer[offset] == jump_line_none) { if (address <= ji->end) - line_buffer[offset] = - (jump_info_min_address (ji) == address) ? ',': '+'; + { + if (jump_info_min_address (ji) == address) + line_buffer[offset] = jump_line_topleft; + else + line_buffer[offset] = jump_line_branch; + } else - line_buffer[offset] = - (jump_info_max_address (ji) == address) ? '\'': '+'; + { + if (jump_info_max_address (ji) == address) + line_buffer[offset] = jump_line_bottomleft; + else + line_buffer[offset] = jump_line_branch; + } color_buffer[offset] = color; } } @@ -2962,37 +3025,38 @@ jump_info_visualize_address (bfd_vma address, size_t i = offset + 1; for (; i < len - 1; ++i) - if (line_buffer[i] == ' ') + if (line_buffer[i] == jump_line_none) { - line_buffer[i] = '-'; + line_buffer[i] = jump_line_hor; color_buffer[i] = color; } - if (line_buffer[i] == ' ') + if (line_buffer[i] == jump_line_none) { - line_buffer[i] = '>'; + line_buffer[i] = jump_line_dest; color_buffer[i] = color; } - else if (line_buffer[i] == '-') + else if (line_buffer[i] == jump_line_hor) { - line_buffer[i] = 'X'; + line_buffer[i] = jump_line_biarrow; color_buffer[i] = color; } - if (line_buffer[offset] == ' ') + if (line_buffer[offset] == jump_line_none) { if (jump_info_min_address (ji) < address) line_buffer[offset] = - (jump_info_max_address (ji) > address) ? '>' : '\''; + (jump_info_max_address (ji) > address) + ? jump_line_branch : jump_line_bottomleft; else - line_buffer[offset] = ','; + line_buffer[offset] = jump_line_topleft; color_buffer[offset] = color; } } /* Draw intermediate line segment. */ - else if (line_buffer[offset] == ' ') + else if (line_buffer[offset] == jump_line_none) { - line_buffer[offset] = '|'; + line_buffer[offset] = jump_line_ver; color_buffer[offset] = color; } } @@ -3221,7 +3285,8 @@ null_styled_print (const void * stream ATTRIBUTE_UNUSED, /* Print out jump visualization. */ static void -print_jump_visualisation (bfd_vma addr, int max_level, char *line_buffer, +print_jump_visualisation (bfd_vma addr, int max_level, + uint8_t *line_buffer, size_t line_buffer_size, uint8_t *color_buffer) { if (!line_buffer) @@ -3229,7 +3294,6 @@ print_jump_visualisation (bfd_vma addr, int max_level, char *line_buffer, jump_info_visualize_address (addr, max_level, line_buffer, color_buffer); - size_t line_buffer_size = strlen (line_buffer); char last_color = 0; size_t i; @@ -3255,7 +3319,59 @@ print_jump_visualisation (bfd_vma addr, int max_level, char *line_buffer, last_color = color; } } - putchar ((i < line_buffer_size) ? line_buffer[i]: ' '); + if (i >= line_buffer_size) + putchar (' '); + else if (utf8_jumps) + fputs (jump_lines_utf8[line_buffer[i]], stdout); + else + putchar (jump_lines[line_buffer[i]]); + } +} + +static void +process_jumps_env_var(void) +{ + char *var = getenv("OBJDUMP_JUMP_LINES"); + + if (var == NULL || var[0] == '\0') + { + return; /* Not set, do nothing. */ + } + + /* When set, enable the UTF-8 lines and parse the variable to allow + customization. */ + utf8_jumps = true; + + char *name = var; + for (bool last = false; !last; ) + { + char *name_end = strchr(name, '='); + if (name_end == NULL) + break; + *name_end = '\0'; + + char *value = name_end + 1; + char *value_end = strchr(value, ','); + if (value_end != NULL) + { + *value_end = '\0'; + name = value_end + 1; + } + else + { + value_end = value + strlen(value); + last = true; + } + + /* Find the value name. */ + for (size_t ind = 0; jump_line_env_tokens[ind] != NULL; ind++) + { + if (strcmp(jump_line_env_tokens[ind], name) == 0) + { + jump_lines_utf8[ind] = value; + break; + } + } } } @@ -3323,7 +3439,8 @@ disassemble_bytes (struct disassemble_info *inf, /* Determine maximum level. */ uint8_t *color_buffer = NULL; - char *line_buffer = NULL; + uint8_t *line_buffer = NULL; + size_t line_buffer_size = -1; int max_level = -1; /* Some jumps were detected. */ @@ -3340,10 +3457,11 @@ disassemble_bytes (struct disassemble_info *inf, /* Allocate buffers. */ size_t len = (max_level + 1) * 3 + 1; + line_buffer_size = len - 1; line_buffer = xmalloc (len); - line_buffer[len - 1] = 0; + line_buffer[line_buffer_size] = 0; color_buffer = xmalloc (len); - color_buffer[len - 1] = 0; + color_buffer[line_buffer_size] = 0; } addr_offset = start_offset; @@ -3420,7 +3538,8 @@ disassemble_bytes (struct disassemble_info *inf, } print_jump_visualisation (section->vma + addr_offset, - max_level, line_buffer, + max_level, + line_buffer, line_buffer_size, color_buffer); if (insns) @@ -3629,7 +3748,8 @@ disassemble_bytes (struct disassemble_info *inf, } print_jump_visualisation (section->vma + j / opb, - max_level, line_buffer, + max_level, + line_buffer, line_buffer_size, color_buffer); pb += octets_per_line; @@ -6093,6 +6213,9 @@ main (int argc, char **argv) case OPTION_INLINES: unwind_inlines = true; break; + case OPTION_VISUALIZE_JUMPS_LINES: + utf8_jumps = true; + /* FALLTHROUGH */ case OPTION_VISUALIZE_JUMPS: visualize_jumps = true; color_output = false; @@ -6114,6 +6237,7 @@ main (int argc, char **argv) usage (stderr, 1); } } + process_jumps_env_var(); break; case OPTION_DISASSEMBLER_COLOR: if (streq (optarg, "off")) -- 2.44.2