This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Re: [PATCH] decoded output of .debug_line from readelf -wL
- From: "Torleif Sandnes" <torleif dot sandnes at gmail dot com>
- To: "Nick Clifton" <nickc at redhat dot com>
- Cc: binutils at sourceware dot org
- Date: Wed, 9 Apr 2008 11:51:06 +0200
- Subject: Re: [PATCH] decoded output of .debug_line from readelf -wL
- References: <dfb869080702250731l324dd289mdff9dc34ce4dcedd@mail.gmail.com> <45E426FA.8090401@redhat.com>
Hi again Nick.
I finally sat down to implement your proposals:
> *) Please use xmalloc() rather than malloc(). That way you also do
> not need to check for a NULL return pointer.
Done. I wasn't aware of xmalloc().
> *) Please read the GNU Coding Standard:
Done. It would have been nice if
> *) The -wL and -wl command line options ought to display different
> comments at the start of their output, and if both switches are
> used then both comments should be displayed. eg:
Done.
> *) The new command line option needs to be documented in
> binutils/binutils.texi and mentioned in binutils/NEWS.
Done.
> *) It would be nice to have an option to suppress the output of the
> column number, as this can be rather confusing. It would be
> especially nice if this was done automatically if the column is
> always zero, unless the user has used a command line option to
> specifically request column numbers.
I just removed the column number from the output as gcc doesn't seem
to support it yet anyway.
> *) It would be nice if there were headings at the start of the
> decoded information for each compilation unit, and if you used a
> little more white space. eg:
Done.
> You might also want to restrict the number of characters printed
> for the file name unless the --wide command line option has been
> enabled.
Done
Torleif
? config.log
? decodedline.patch
? serdep.tmp
? bfd/.libs
? bfd/Makefile
? bfd/aout32.lo
? bfd/archive.lo
? bfd/archures.lo
? bfd/bfd-in3.h
? bfd/bfd.h
? bfd/bfd.lo
? bfd/bfd_stdint.h
? bfd/bfdio.lo
? bfd/bfdver.h
? bfd/bfdwin.lo
? bfd/binary.lo
? bfd/cache.lo
? bfd/coffgen.lo
? bfd/cofflink.lo
? bfd/config.cache
? bfd/config.h
? bfd/config.log
? bfd/config.status
? bfd/corefile.lo
? bfd/cpu-i386.lo
? bfd/dwarf1.lo
? bfd/dwarf2.lo
? bfd/efi-app-ia32.lo
? bfd/efi-bsdrv-ia32.lo
? bfd/efi-rtdrv-ia32.lo
? bfd/elf-attrs.lo
? bfd/elf-eh-frame.lo
? bfd/elf-strtab.lo
? bfd/elf-vxworks.lo
? bfd/elf.lo
? bfd/elf32-gen.lo
? bfd/elf32-i386.lo
? bfd/elf32-target.h
? bfd/elf32.lo
? bfd/elflink.lo
? bfd/format.lo
? bfd/hash.lo
? bfd/i386linux.lo
? bfd/ihex.lo
? bfd/init.lo
? bfd/libbfd.la
? bfd/libbfd.lo
? bfd/libtool
? bfd/libtool-soversion
? bfd/linker.lo
? bfd/merge.lo
? bfd/ofiles
? bfd/opncls.lo
? bfd/peigen.c
? bfd/peigen.lo
? bfd/reloc.lo
? bfd/section.lo
? bfd/simple.lo
? bfd/srec.lo
? bfd/stab-syms.lo
? bfd/stabs.lo
? bfd/stamp-h1
? bfd/stamp-lib
? bfd/stamp-ofiles
? bfd/stmp-bfd-h
? bfd/syms.lo
? bfd/targets.lo
? bfd/targmatch.h
? bfd/tekhex.lo
? bfd/trad-core.lo
? bfd/doc/Makefile
? bfd/doc/aoutx.texi
? bfd/doc/archive.texi
? bfd/doc/archures.texi
? bfd/doc/bfd.info
? bfd/doc/bfdio.texi
? bfd/doc/bfdt.texi
? bfd/doc/bfdver.texi
? bfd/doc/bfdwin.texi
? bfd/doc/cache.texi
? bfd/doc/chew
? bfd/doc/coffcode.texi
? bfd/doc/core.texi
? bfd/doc/elf.texi
? bfd/doc/elfcode.texi
? bfd/doc/format.texi
? bfd/doc/hash.texi
? bfd/doc/init.texi
? bfd/doc/libbfd.texi
? bfd/doc/linker.texi
? bfd/doc/mmo.texi
? bfd/doc/opncls.texi
? bfd/doc/reloc.texi
? bfd/doc/section.texi
? bfd/doc/syms.texi
? bfd/doc/targets.texi
? bfd/po/BLD-POTFILES
? bfd/po/Makefile
? bfd/po/Makefile.in
? bfd/po/SRC-POTFILES
? binutils/.libs
? binutils/Makefile
? binutils/arlex.c
? binutils/arparse.c
? binutils/arparse.h
? binutils/config.cache
? binutils/config.h
? binutils/config.log
? binutils/config.status
? binutils/libtool
? binutils/objdump
? binutils/readelf
? binutils/size
? binutils/stamp-h1
? binutils/doc/Makefile
? binutils/doc/addr2line.1
? binutils/doc/ar.1
? binutils/doc/binutils.info
? binutils/doc/c++filt.1
? binutils/doc/cxxfilt.man
? binutils/doc/dlltool.1
? binutils/doc/nlmconv.1
? binutils/doc/nm.1
? binutils/doc/objcopy.1
? binutils/doc/objdump.1
? binutils/doc/ranlib.1
? binutils/doc/readelf.1
? binutils/doc/size.1
? binutils/doc/strings.1
? binutils/doc/strip.1
? binutils/doc/windmc.1
? binutils/doc/windres.1
? binutils/po/Makefile
? binutils/po/Makefile.in
? binutils/po/POTFILES
? intl/Makefile
? intl/config.cache
? intl/config.h
? intl/config.intl
? intl/config.log
? intl/config.status
? libiberty/Makefile
? libiberty/config.cache
? libiberty/config.h
? libiberty/config.log
? libiberty/config.status
? libiberty/needed-list
? libiberty/required-list
? libiberty/stamp-h
? libiberty/stamp-picdir
? libiberty/xhost-mkfrag
? libiberty/testsuite/Makefile
? opcodes/.libs
? opcodes/Makefile
? opcodes/config.cache
? opcodes/config.h
? opcodes/config.log
? opcodes/config.status
? opcodes/dis-buf.lo
? opcodes/dis-init.lo
? opcodes/disassemble.lo
? opcodes/i386-dis.lo
? opcodes/i386-opc.lo
? opcodes/libopcodes.la
? opcodes/libtool
? opcodes/stamp-h1
? opcodes/stamp-lib
? opcodes/po/Makefile
? opcodes/po/Makefile.in
? opcodes/po/POTFILES
Index: binutils/NEWS
===================================================================
RCS file: /cvs/src/src/binutils/NEWS,v
retrieving revision 1.69
diff -r1.69 NEWS
1a2,3
> * Added -wL switch to dump decoded contents of .debug_line.
>
Index: binutils/dwarf.c
===================================================================
RCS file: /cvs/src/src/binutils/dwarf.c,v
retrieving revision 1.32
diff -r1.32 dwarf.c
46a47
> int do_debug_lines_addresses;
54a56
> int do_wide;
2054,2068c2056,2060
< display_debug_lines (struct dwarf_section *section, void *file)
< {
< 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;
< }
---
> display_debug_lines_raw (struct dwarf_section *section, unsigned char *data,
> unsigned char *end)
> {
> printf (_("Raw dump of debug contents of section %s:\n\n"),
> section->name);
2069a2062
> unsigned char *start = section->start;
2334a2328,2740
> 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 = (unsigned char **)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(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;
> printf (_("\n%s/%s:\n"),
> directory_table[file_table[state_machine_regs.file - 1].directory_index],
> 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);
> }
> 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_addresses)
> retValDecoded = display_debug_lines_decoded (section, data, end);
>
> if ( (do_debug_lines && !retValRaw) ||
> (do_debug_lines_addresses && !retValDecoded) )
> {
> return 0;
> }
> else
> return 1;
> }
>
Index: binutils/dwarf.h
===================================================================
RCS file: /cvs/src/src/binutils/dwarf.h,v
retrieving revision 1.5
diff -r1.5 dwarf.h
98a99
> extern int do_debug_lines_addresses;
Index: binutils/readelf.c
===================================================================
RCS file: /cvs/src/src/binutils/readelf.c,v
retrieving revision 1.405
diff -r1.405 readelf.c
163a164
> int do_wide;
200d200
< static int do_wide;
2884c2884
< --debug-dump[=line,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=str,=loc,=Ranges]\n\
---
> --debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=str,=loc,=Ranges]\n\
3085a3086,3089
> case 'L':
> do_debug_lines_addresses = 1;
> break;
>
4306,4308c4310,4312
< || 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_addresses || do_debug_pubnames
> || do_debug_aranges || do_debug_frames || do_debug_macinfo
> || do_debug_str || do_debug_loc || do_debug_ranges)
4316c4320,4321
< || (do_debug_lines && streq (name, "line"))
---
> || ( (do_debug_lines || do_debug_lines_addresses)
> && streq (name, "line"))
Index: binutils/doc/binutils.texi
===================================================================
RCS file: /cvs/src/src/binutils/doc/binutils.texi,v
retrieving revision 1.126
diff -r1.126 binutils.texi
3708,3709c3708,3709
< [@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,line,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges]]
3846,3847c3846,3847
< @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]