* dwarf.c (display_debug_lines): Rename to

display_debug_lines_raw.
        (display_debug_lines_decoded): New function.  Displays the
        interpreted contents of a .debug_line section.
        (display_debug_lines): New function: Selects either a raw dump or
        a decoded dump (or both) as requested by the user.
        * dwarf.h (do_debug_lines_decoded): New extern.
        * readelf.c: Add support for -wL or --debug-dump=decodedline
        option to display the decoded contents of a .debug_line section.
        * doc/binutils.texi: Document the new option.
        * NEWS: Mention the new feature.
This commit is contained in:
Nick Clifton 2008-04-11 09:04:17 +00:00
parent 5bcb0e6433
commit a262ae964e
6 changed files with 486 additions and 26 deletions

View File

@ -1,3 +1,17 @@
2008-04-11 Torleif Sandnes <torleif.sandnes@gmail.com>
* dwarf.c (display_debug_lines): Rename to
display_debug_lines_raw.
(display_debug_lines_decoded): New function. Displays the
interpreted contents of a .debug_line section.
(display_debug_lines): New function: Selects either a raw dump or
a decoded dump (or both) as requested by the user.
* dwarf.h (do_debug_lines_decoded): New extern.
* readelf.c: Add support for -wL or --debug-dump=decodedline
option to display the decoded contents of a .debug_line section.
* doc/binutils.texi: Document the new option.
* NEWS: Mention the new feature.
2008-04-08 Alan Modra <amodra@bigpond.net.au>
* dwarf.c: Remove trailing whitespace throughout file.

View File

@ -1,4 +1,6 @@
-*- text -*-
* Added -wL switch to dump decoded contents of .debug_line.
* Added support for "thin" archives which contain pathnames pointing to
object files rather than the files themselves and which contain a
flattened symbol index for all objects, and archives, which have been

View File

@ -3705,8 +3705,8 @@ readelf [@option{-a}|@option{--all}]
[@option{-x} <number or name>|@option{--hex-dump=}<number or name>]
[@option{-p} <number or name>|@option{--string-dump=}<number or name>]
[@option{-c}|@option{--archive-index}]
[@option{-w[liaprmfFsoR]}|
@option{--debug-dump}[=line,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges]]
[@option{-w[lLiaprmfFsoR]}|
@option{--debug-dump}[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges]]
[@option{-I}|@option{-histogram}]
[@option{-v}|@option{--version}]
[@option{-W}|@option{--wide}]
@ -3843,12 +3843,16 @@ Displays the file symbol index infomation contained in the header part
of binary archives. Performs the same function as the @option{t}
command to @command{ar}, but without using the BFD library. @xref{ar}.
@item -w[liaprmfFsoR]
@itemx --debug-dump[=line,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges]
@item -w[lLiaprmfFsoR]
@itemx --debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges]
Displays the contents of the debug sections in the file, if any are
present. If one of the optional letters or words follows the switch
then only data found in those specific sections will be dumped.
Note: the @option{=decodedline} option will display the interpreted
contents of a .debug_line section whereas the @option{=rawline} option
dumps the contents in a raw format.
@item -I
@itemx --histogram
Display a histogram of bucket list lengths when displaying the contents

View File

@ -44,6 +44,7 @@ int eh_addr_size;
int do_debug_info;
int do_debug_abbrevs;
int do_debug_lines;
int do_debug_lines_decoded;
int do_debug_pubnames;
int do_debug_aranges;
int do_debug_ranges;
@ -52,6 +53,7 @@ int do_debug_frames_interp;
int do_debug_macinfo;
int do_debug_str;
int do_debug_loc;
int do_wide;
dwarf_vma (*byte_get) (unsigned char *, int);
@ -2051,21 +2053,14 @@ load_debug_info (void * file)
}
static int
display_debug_lines (struct dwarf_section *section, void *file)
display_debug_lines_raw (struct dwarf_section *section,
unsigned char *data,
unsigned char *end)
{
unsigned char *start = section->start;
unsigned char *data = start;
unsigned char *end = start + section->size;
printf (_("\nDump of debug contents of section %s:\n\n"),
section->name);
if (load_debug_info (file) == 0)
{
warn (_("Unable to load/parse the .debug_info section, so cannot interpret the %s section.\n"),
section->name);
return 0;
}
printf (_("Raw dump of debug contents of section %s:\n\n"),
section->name);
while (data < end)
{
@ -2332,6 +2327,443 @@ display_debug_lines (struct dwarf_section *section, void *file)
return 1;
}
typedef struct
{
unsigned char *name;
unsigned int directory_index;
unsigned int modification_date;
unsigned int length;
} File_Entry;
/* Output a decoded representation of the .debug_line section. */
static int
display_debug_lines_decoded (struct dwarf_section *section,
unsigned char *data,
unsigned char *end)
{
printf (_("Decoded dump of debug contents of section %s:\n\n"),
section->name);
while (data < end)
{
/* This loop amounts to one iteration per compilation unit. */
DWARF2_Internal_LineInfo info;
unsigned char *standard_opcodes;
unsigned char *end_of_sequence;
unsigned char *hdrptr;
int initial_length_size;
int offset_size;
int i;
File_Entry *file_table = NULL;
unsigned char **directory_table = NULL;
unsigned int prev_line = 0;
hdrptr = data;
/* Extract information from the Line Number Program Header.
(section 6.2.4 in the Dwarf3 doc). */
/* Get the length of this CU's line number information block. */
info.li_length = byte_get (hdrptr, 4);
hdrptr += 4;
if (info.li_length == 0xffffffff)
{
/* This section is 64-bit DWARF 3. */
info.li_length = byte_get (hdrptr, 8);
hdrptr += 8;
offset_size = 8;
initial_length_size = 12;
}
else
{
offset_size = 4;
initial_length_size = 4;
}
if (info.li_length + initial_length_size > section->size)
{
warn (_("The line info appears to be corrupt - "
"the section is too small\n"));
return 0;
}
/* Get this CU's Line Number Block version number. */
info.li_version = byte_get (hdrptr, 2);
hdrptr += 2;
if (info.li_version != 2 && info.li_version != 3)
{
warn (_("Only DWARF version 2 and 3 line info is currently "
"supported.\n"));
return 0;
}
info.li_prologue_length = byte_get (hdrptr, offset_size);
hdrptr += offset_size;
info.li_min_insn_length = byte_get (hdrptr, 1);
hdrptr++;
info.li_default_is_stmt = byte_get (hdrptr, 1);
hdrptr++;
info.li_line_base = byte_get (hdrptr, 1);
hdrptr++;
info.li_line_range = byte_get (hdrptr, 1);
hdrptr++;
info.li_opcode_base = byte_get (hdrptr, 1);
hdrptr++;
/* Sign extend the line base field. */
info.li_line_base <<= 24;
info.li_line_base >>= 24;
/* Find the end of this CU's Line Number Information Block. */
end_of_sequence = data + info.li_length + initial_length_size;
reset_state_machine (info.li_default_is_stmt);
/* Save a pointer to the contents of the Opcodes table. */
standard_opcodes = hdrptr;
/* Traverse the Directory table just to count entries. */
data = standard_opcodes + info.li_opcode_base - 1;
if (*data != 0)
{
unsigned int n_directories = 0;
unsigned char *ptr_directory_table = data;
int i;
while (*data != 0)
{
data += strlen ((char *) data) + 1;
n_directories++;
}
/* Go through the directory table again to save the directories. */
directory_table = xmalloc (n_directories * sizeof (unsigned char *));
i = 0;
while (*ptr_directory_table != 0)
{
directory_table[i] = ptr_directory_table;
ptr_directory_table += strlen ((char *) ptr_directory_table) + 1;
i++;
}
}
/* Skip the NUL at the end of the table. */
data++;
/* Traverse the File Name table just to count the entries. */
if (*data != 0)
{
unsigned int n_files = 0;
unsigned char *ptr_file_name_table = data;
int i;
while (*data != 0)
{
unsigned int bytes_read;
/* Skip Name, directory index, last modification time and length
of file. */
data += strlen ((char *) data) + 1;
read_leb128 (data, & bytes_read, 0);
data += bytes_read;
read_leb128 (data, & bytes_read, 0);
data += bytes_read;
read_leb128 (data, & bytes_read, 0);
data += bytes_read;
n_files++;
}
/* Go through the file table again to save the strings. */
file_table = xmalloc (n_files * sizeof (File_Entry));
i = 0;
while (*ptr_file_name_table != 0)
{
unsigned int bytes_read;
file_table[i].name = ptr_file_name_table;
ptr_file_name_table += strlen ((char *) ptr_file_name_table) + 1;
/* We are not interested in directory, time or size. */
file_table[i].directory_index = read_leb128 (ptr_file_name_table,
& bytes_read, 0);
ptr_file_name_table += bytes_read;
file_table[i].modification_date = read_leb128 (ptr_file_name_table,
& bytes_read, 0);
ptr_file_name_table += bytes_read;
file_table[i].length = read_leb128 (ptr_file_name_table, & bytes_read, 0);
ptr_file_name_table += bytes_read;
i++;
}
i = 0;
/* Print the Compilation Unit's name and a header. */
if (directory_table == NULL)
{
printf (_("CU: %s:\n"), file_table[0].name);
printf (_("File name Line number Starting address\n"));
}
else
{
if (do_wide || strlen ((char *) directory_table[0]) < 76)
{
printf (_("CU: %s/%s:\n"), directory_table[0],
file_table[0].name);
}
else
{
printf (_("%s:\n"), file_table[0].name);
}
printf (_("File name Line number Starting address\n"));
}
}
/* Skip the NUL at the end of the table. */
data++;
/* This loop iterates through the Dwarf Line Number Program. */
while (data < end_of_sequence)
{
unsigned char op_code;
int adv;
unsigned long int uladv;
unsigned int bytes_read;
int is_special_opcode = 0;
op_code = *data++;
prev_line = state_machine_regs.line;
if (op_code >= info.li_opcode_base)
{
op_code -= info.li_opcode_base;
uladv = (op_code / info.li_line_range) * info.li_min_insn_length;
state_machine_regs.address += uladv;
adv = (op_code % info.li_line_range) + info.li_line_base;
state_machine_regs.line += adv;
is_special_opcode = 1;
}
else switch (op_code)
{
case DW_LNS_extended_op:
{
unsigned int ext_op_code_len;
unsigned int bytes_read;
unsigned char ext_op_code;
unsigned char *op_code_data = data;
ext_op_code_len = read_leb128 (op_code_data, &bytes_read, 0);
op_code_data += bytes_read;
if (ext_op_code_len == 0)
{
warn (_("badly formed extended line op encountered!\n"));
break;
}
ext_op_code_len += bytes_read;
ext_op_code = *op_code_data++;
switch (ext_op_code)
{
case DW_LNE_end_sequence:
reset_state_machine (info.li_default_is_stmt);
break;
case DW_LNE_set_address:
state_machine_regs.address =
byte_get (op_code_data, ext_op_code_len - bytes_read - 1);
break;
case DW_LNE_define_file:
{
unsigned int dir_index = 0;
++state_machine_regs.last_file_entry;
op_code_data += strlen ((char *) op_code_data) + 1;
dir_index = read_leb128 (op_code_data, & bytes_read, 0);
op_code_data += bytes_read;
read_leb128 (op_code_data, & bytes_read, 0);
op_code_data += bytes_read;
read_leb128 (op_code_data, & bytes_read, 0);
printf (_("%s:\n"), directory_table[dir_index]);
break;
}
default:
printf (_("UNKNOWN: length %d\n"), ext_op_code_len - bytes_read);
break;
}
data += ext_op_code_len;
break;
}
case DW_LNS_copy:
break;
case DW_LNS_advance_pc:
uladv = read_leb128 (data, & bytes_read, 0);
uladv *= info.li_min_insn_length;
data += bytes_read;
state_machine_regs.address += uladv;
break;
case DW_LNS_advance_line:
adv = read_leb128 (data, & bytes_read, 1);
data += bytes_read;
state_machine_regs.line += adv;
break;
case DW_LNS_set_file:
adv = read_leb128 (data, & bytes_read, 0);
data += bytes_read;
state_machine_regs.file = adv;
if (file_table[state_machine_regs.file - 1].directory_index == 0)
{
/* If directory index is 0, that means current directory. */
printf (_("\n./%s:[++]\n"),
file_table[state_machine_regs.file - 1].name);
}
else
{
/* The directory index starts counting at 1. */
printf (_("\n%s/%s:\n"),
directory_table[file_table[state_machine_regs.file - 1].directory_index - 1],
file_table[state_machine_regs.file - 1].name);
}
break;
case DW_LNS_set_column:
uladv = read_leb128 (data, & bytes_read, 0);
data += bytes_read;
state_machine_regs.column = uladv;
break;
case DW_LNS_negate_stmt:
adv = state_machine_regs.is_stmt;
adv = ! adv;
state_machine_regs.is_stmt = adv;
break;
case DW_LNS_set_basic_block:
state_machine_regs.basic_block = 1;
break;
case DW_LNS_const_add_pc:
uladv = (((255 - info.li_opcode_base) / info.li_line_range)
* info.li_min_insn_length);
state_machine_regs.address += uladv;
break;
case DW_LNS_fixed_advance_pc:
uladv = byte_get (data, 2);
data += 2;
state_machine_regs.address += uladv;
break;
case DW_LNS_set_prologue_end:
break;
case DW_LNS_set_epilogue_begin:
break;
case DW_LNS_set_isa:
uladv = read_leb128 (data, & bytes_read, 0);
data += bytes_read;
printf (_(" Set ISA to %lu\n"), uladv);
break;
default:
printf (_(" Unknown opcode %d with operands: "), op_code);
for (i = standard_opcodes[op_code - 1]; i > 0 ; --i)
{
printf ("0x%lx%s", read_leb128 (data, &bytes_read, 0),
i == 1 ? "" : ", ");
data += bytes_read;
}
putchar ('\n');
break;
}
/* Only Special opcodes, DW_LNS_copy and DW_LNE_end_sequence adds a row
to the DWARF address/line matrix. */
if ((is_special_opcode) || (op_code == DW_LNE_end_sequence)
|| (op_code == DW_LNS_copy))
{
const unsigned int MAX_FILENAME_LENGTH = 35;
char *fileName = (char *)file_table[state_machine_regs.file - 1].name;
char *newFileName = NULL;
size_t fileNameLength = strlen (fileName);
if ((fileNameLength > MAX_FILENAME_LENGTH) && (!do_wide))
{
newFileName = xmalloc (MAX_FILENAME_LENGTH + 1);
/* Truncate file name */
strncpy (newFileName,
fileName + fileNameLength - MAX_FILENAME_LENGTH,
MAX_FILENAME_LENGTH + 1);
}
else
{
newFileName = xmalloc (fileNameLength + 1);
strncpy (newFileName, fileName, fileNameLength + 1);
}
if (!do_wide || (fileNameLength <= MAX_FILENAME_LENGTH))
{
printf (_("%-35s %11d %#18lx\n"), newFileName,
state_machine_regs.line, state_machine_regs.address);
}
else
{
printf (_("%s %11d %#18lx\n"), newFileName,
state_machine_regs.line, state_machine_regs.address);
}
if (op_code == DW_LNE_end_sequence)
printf ("\n");
free (newFileName);
}
}
free (file_table);
file_table = NULL;
free (directory_table);
directory_table = NULL;
putchar ('\n');
}
return 1;
}
static int
display_debug_lines (struct dwarf_section *section, void *file)
{
unsigned char *data = section->start;
unsigned char *end = data + section->size;
int retValRaw = 0;
int retValDecoded = 0;
if (load_debug_info (file) == 0)
{
warn (_("Unable to load/parse the .debug_info section, so cannot interpret the %s section.\n"),
section->name);
return 0;
}
if (do_debug_lines)
retValRaw = display_debug_lines_raw (section, data, end);
if (do_debug_lines_decoded)
retValDecoded = display_debug_lines_decoded (section, data, end);
if ((do_debug_lines && !retValRaw)
|| (do_debug_lines_decoded && !retValDecoded))
return 0;
return 1;
}
static debug_info *
find_debug_info_for_offset (unsigned long offset)
{

View File

@ -1,5 +1,5 @@
/* dwwrf.h - DWARF support header file
Copyright 2005, 2007
Copyright 2005, 2007, 2008
Free Software Foundation, Inc.
This file is part of GNU Binutils.
@ -96,6 +96,7 @@ extern int eh_addr_size;
extern int do_debug_info;
extern int do_debug_abbrevs;
extern int do_debug_lines;
extern int do_debug_lines_decoded;
extern int do_debug_pubnames;
extern int do_debug_aranges;
extern int do_debug_ranges;

View File

@ -161,6 +161,7 @@
#include "safe-ctype.h"
char *program_name = "readelf";
int do_wide;
static long archive_file_offset;
static unsigned long archive_file_size;
static unsigned long dynamic_addr;
@ -197,7 +198,6 @@ static int do_using_dynamic;
static int do_header;
static int do_dump;
static int do_version;
static int do_wide;
static int do_histogram;
static int do_debugging;
static int do_arch;
@ -2880,8 +2880,8 @@ usage (FILE *stream)
Dump the contents of section <number|name> as bytes\n\
-p --string-dump=<number|name>\n\
Dump the contents of section <number|name> as strings\n\
-w[liaprmfFsoR] or\n\
--debug-dump[=line,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=str,=loc,=Ranges]\n\
-w[lLiaprmfFsoR] or\n\
--debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=str,=loc,=Ranges]\n\
Display the contents of DWARF2 debug sections\n"));
#ifdef SUPPORT_DISASSEMBLY
fprintf (stream, _("\
@ -3083,6 +3083,10 @@ parse_args (int argc, char **argv)
do_debug_lines = 1;
break;
case 'L':
do_debug_lines_decoded = 1;
break;
case 'p':
do_debug_pubnames = 1;
break;
@ -3141,7 +3145,9 @@ parse_args (int argc, char **argv)
{ "frames", & do_debug_frames },
{ "frames-interp", & do_debug_frames_interp },
{ "info", & do_debug_info },
{ "line", & do_debug_lines },
{ "line", & do_debug_lines }, /* For backwards compatibility. */
{ "rawline", & do_debug_lines },
{ "decodedline", & do_debug_lines_decoded },
{ "loc", & do_debug_loc },
{ "macro", & do_debug_macinfo },
{ "pubnames", & do_debug_pubnames },
@ -4303,9 +4309,9 @@ process_section_headers (FILE *file)
else if (section->sh_type == SHT_RELA)
CHECK_ENTSIZE (section, i, Rela);
else if ((do_debugging || do_debug_info || do_debug_abbrevs
|| do_debug_lines || do_debug_pubnames || do_debug_aranges
|| do_debug_frames || do_debug_macinfo || do_debug_str
|| do_debug_loc || do_debug_ranges)
|| do_debug_lines || do_debug_lines_decoded || do_debug_pubnames
|| do_debug_aranges || do_debug_frames || do_debug_macinfo
|| do_debug_str || do_debug_loc || do_debug_ranges)
&& const_strneq (name, ".debug_"))
{
name += 7;
@ -4313,7 +4319,8 @@ process_section_headers (FILE *file)
if (do_debugging
|| (do_debug_info && streq (name, "info"))
|| (do_debug_abbrevs && streq (name, "abbrev"))
|| (do_debug_lines && streq (name, "line"))
|| ((do_debug_lines || do_debug_lines_decoded)
&& streq (name, "line"))
|| (do_debug_pubnames && streq (name, "pubnames"))
|| (do_debug_aranges && streq (name, "aranges"))
|| (do_debug_ranges && streq (name, "ranges"))
@ -4324,7 +4331,7 @@ process_section_headers (FILE *file)
)
request_dump_bynumber (i, DEBUG_DUMP);
}
/* linkonce section to be combined with .debug_info at link time. */
/* Linkonce section to be combined with .debug_info at link time. */
else if ((do_debugging || do_debug_info)
&& const_strneq (name, ".gnu.linkonce.wi."))
request_dump_bynumber (i, DEBUG_DUMP);