From 5b18a4bc0911700a6c81bb39cf4397ef02849131 Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Tue, 4 Jan 2005 11:20:45 +0000 Subject: [PATCH] BZ 615 * readelf.c (process_debug_info): New. (debug_info): Add num_loc_offsets, loc_offsets and last_loc_offset_p. (get_debug_info): Use process_debug_info. (display_debug_loc): Properly handle location list. Warn if bad location lists are encoutnered. (read_and_process_attr_value): New. (read_and_display_attr_value): Use "%lx" for DW_FORM_data4. (display_debug_info): Use process_debug_info. (process_object): Also free loc_offsets in debug_information. --- binutils/ChangeLog | 33 + binutils/readelf.c | 3789 +++++++++++++++++++++++++++------------------------- 2 files changed, 2023 insertions(+), 1799 deletions(-) diff --git a/binutils/ChangeLog b/binutils/ChangeLog index e2bbebf..ad750eb 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,17 @@ +2005-01-04 H.J. Lu + + BZ 615 + * readelf.c (process_debug_info): New. + (debug_info): Add num_loc_offsets, loc_offsets and + last_loc_offset_p. + (get_debug_info): Use process_debug_info. + (display_debug_loc): Properly handle location list. Warn if bad + location lists are encoutnered. + (read_and_process_attr_value): New. + (read_and_display_attr_value): Use "%lx" for DW_FORM_data4. + (display_debug_info): Use process_debug_info. + (process_object): Also free loc_offsets in debug_information. + 2004-12-31 Alan Modra * objdump.c (remove_useless_symbols): Discard section symbols. @@ -6,6 +20,25 @@ * readelf.c (read_leb128): Support 64bit host. +2004-12-23 Nick Clifton + + PR binutils/616 + * readelf.c (debug_info): Add 'cu_offset' field. + (get+pointer_size_of_comp_unit): Rename to + 'get_pointer_size_and_offset_of_comp_unit'. Add code to return + the offset of the comp_unit if requested. + (get_debug_info): Record comp_unit offsets as well. + (display_debug_lines): Call get_pointer_size_of_comp_unit. + (decode_location_expression): Add an extra parameter - the offset + of the current comp-unit. Use this when decoding the DW_OP_call2 + and DW_OP_call4 operators. + (display_debug_loc): Call get_pointer_size_of_comp_unit, pass the + comp_unit offset on the decode_location_expression. + (read_and_display_attr_value): Pass the comp_unit offset on the + decode_location_expression. + (display_debug_frames): Pass 0 as the comp_unit offset to + decode_location_expression. + 2004-12-22 Nick Clifton * readelf.c (last_pointer_size, warned_about_missing_comp_units): diff --git a/binutils/readelf.c b/binutils/readelf.c index 6cecdc3..d81907c 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -1,5 +1,5 @@ /* readelf.c -- display contents of an ELF format file - Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004 + Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Originally developed by Eric Youngdale @@ -7068,527 +7068,360 @@ find_section (const char * name) return NULL; } -/* This could just be an array of unsigned integers, but I expect - that we will want to extend the structure to contain other - information. */ -typedef struct +static const char *debug_str_contents; +static bfd_vma debug_str_size; + +static void +load_debug_str (FILE *file) { - unsigned int pointer_size; -} -debug_info; + Elf_Internal_Shdr *sec; -static debug_info * debug_information = NULL; -static unsigned int num_debug_info_entries = 0; -static unsigned int last_pointer_size = 0; -static int warned_about_missing_comp_units = FALSE; + /* If it is already loaded, do nothing. */ + if (debug_str_contents != NULL) + return; -static unsigned int -get_pointer_size_of_comp_unit (unsigned int comp_unit, - const char * section_name) + /* Locate the .debug_str section. */ + sec = find_section (".debug_str"); + if (sec == NULL) + return; + + debug_str_size = sec->sh_size; + + debug_str_contents = get_data (NULL, file, sec->sh_offset, sec->sh_size, + _("debug_str section data")); +} + +static void +free_debug_str (void) { - if (num_debug_info_entries == 0) - { - error (_("%s section needs a populated .debug_info section\n"), - section_name); - return 0; - } + if (debug_str_contents == NULL) + return; - if (comp_unit >= num_debug_info_entries) - { - if (!warned_about_missing_comp_units) - { - warn (_("%s section has more comp units than .debug_info section\n"), - section_name); - warn (_("assuming that the pointer size is %d, from the last comp unit in .debug_info\n\n"), - last_pointer_size); - warned_about_missing_comp_units = TRUE; - } - } - else - last_pointer_size = debug_information [comp_unit].pointer_size; + free ((char *) debug_str_contents); + debug_str_contents = NULL; + debug_str_size = 0; +} - return last_pointer_size; +static const char * +fetch_indirect_string (unsigned long offset) +{ + if (debug_str_contents == NULL) + return _(""); + + if (offset > debug_str_size) + return _(""); + return debug_str_contents + offset; } -/* Locate and scan the .debug_info section in the file and record the pointer - sizes for the compilation units in it. Usually an executable will have - just one pointer size, but this is not guaranteed, and so we try not to - make any assumptions. Returns zero upon failure, or the number of - compilation units upon success. */ +static const char *debug_loc_contents; +static bfd_vma debug_loc_size; -static unsigned int -get_debug_info (FILE * file) +static void +load_debug_loc (FILE *file) { - Elf_Internal_Shdr * section; - unsigned char * start; - unsigned char * end; - unsigned char * begin; - unsigned long length; - unsigned int num_units; - unsigned int unit; - - /* Reset the last pointer size so that we can issue correct - error messages if we are displaying the contents of more - than one file. */ - last_pointer_size = 0; - warned_about_missing_comp_units = FALSE; + Elf_Internal_Shdr *sec; - /* If we already have the information there is nothing else to do. */ - if (num_debug_info_entries > 0) - return num_debug_info_entries; + /* If it is already loaded, do nothing. */ + if (debug_loc_contents != NULL) + return; - section = find_section (".debug_info"); - if (section == NULL) - return 0; + /* Locate the .debug_loc section. */ + sec = find_section (".debug_loc"); + if (sec == NULL) + return; - length = section->sh_size; - start = get_data (NULL, file, section->sh_offset, section->sh_size, - _("extracting information from .debug_info section")); - if (start == NULL) - return 0; + debug_loc_size = sec->sh_size; - end = start + section->sh_size; - /* First scan the section to get the number of comp units. */ - for (begin = start, num_units = 0; begin < end; num_units++) - { - /* Read the first 4 bytes. For a 32-bit DWARF section, this will - be the length. For a 64-bit DWARF section, it'll be the escape - code 0xffffffff followed by an 8 byte length. */ - length = byte_get (begin, 4); + debug_loc_contents = get_data (NULL, file, sec->sh_offset, sec->sh_size, + _("debug_loc section data")); +} - if (length == 0xffffffff) - { - length = byte_get (begin + 4, 8); - begin += length + 12; - } - else - begin += length + 4; - } +static void +free_debug_loc (void) +{ + if (debug_loc_contents == NULL) + return; - if (num_units == 0) - { - error (_("No comp units in .debug_info section ?")); - free (start); - return 0; - } + free ((char *) debug_loc_contents); + debug_loc_contents = NULL; + debug_loc_size = 0; +} - /* Then allocate an array to hold the information. */ - debug_information = malloc (num_units * sizeof * debug_information); - if (debug_information == NULL) - { - error (_("Not enough memory for a debug info array of %u entries"), - num_units); - free (start); - return 0; - } +static const char * debug_range_contents; +static unsigned long debug_range_size; - /* Populate the array. */ - for (begin = start, unit = 0; begin < end; unit++) - { - length = byte_get (begin, 4); - if (length == 0xffffffff) - { - /* For 64-bit DWARF, the 1-byte address_size field is 22 bytes - from the start of the section. This is computed as follows: +static void +load_debug_range (FILE *file) +{ + Elf_Internal_Shdr *sec; - unit_length: 12 bytes - version: 2 bytes - debug_abbrev_offset: 8 bytes - ----------------------------- - Total: 22 bytes */ + /* If it is already loaded, do nothing. */ + if (debug_range_contents != NULL) + return; - debug_information [unit].pointer_size = byte_get (begin + 22, 1); - length = byte_get (begin + 4, 8); - begin += length + 12; - } - else - { - /* For 32-bit DWARF, the 1-byte address_size field is 10 bytes from - the start of the section: + /* Locate the .debug_str section. */ + sec = find_section (".debug_ranges"); + if (sec == NULL) + return; - unit_length: 4 bytes - version: 2 bytes - debug_abbrev_offset: 4 bytes - ----------------------------- - Total: 10 bytes */ + debug_range_size = sec->sh_size; - debug_information [unit].pointer_size = byte_get (begin + 10, 1); - begin += length + 4; - } - } + debug_range_contents = get_data (NULL, file, sec->sh_offset, sec->sh_size, + _("debug_range section data")); +} - free (start); +static void +free_debug_range (void) +{ + if (debug_range_contents == NULL) + return; - return num_debug_info_entries = num_units; + free ((char *) debug_range_contents); + debug_range_contents = NULL; + debug_range_size = 0; } +/* Apply addends of RELA relocations. */ + static int -display_debug_lines (Elf_Internal_Shdr *section, - unsigned char *start, FILE *file) +debug_apply_rela_addends (FILE *file, + Elf_Internal_Shdr *section, + int reloc_size, + unsigned char *sec_data, + unsigned char *start, + unsigned char *end) { - unsigned char *data = start; - unsigned char *end = start + section->sh_size; - unsigned int comp_unit = 0; - - printf (_("\nDump of debug contents of section %s:\n\n"), - SECTION_NAME (section)); + Elf_Internal_Shdr *relsec; - get_debug_info (file); + if (end - start < reloc_size) + return 1; - while (data < end) + for (relsec = section_headers; + relsec < section_headers + elf_header.e_shnum; + ++relsec) { - DWARF2_Internal_LineInfo info; - unsigned char *standard_opcodes; - unsigned char *end_of_sequence; - unsigned char *hdrptr; - unsigned int pointer_size; - int initial_length_size; - int offset_size; - int i; - - hdrptr = data; + unsigned long nrelas; + Elf_Internal_Rela *rela, *rp; + Elf_Internal_Shdr *symsec; + Elf_Internal_Sym *symtab; + Elf_Internal_Sym *sym; - /* Check the length of the block. */ - info.li_length = byte_get (hdrptr, 4); - hdrptr += 4; + if (relsec->sh_type != SHT_RELA + || SECTION_HEADER (relsec->sh_info) != section + || relsec->sh_size == 0) + continue; - 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 (!slurp_rela_relocs (file, relsec->sh_offset, relsec->sh_size, + &rela, &nrelas)) + return 0; - if (info.li_length + initial_length_size > section->sh_size) - { - warn - (_("The line info appears to be corrupt - the section is too small\n")); - return 0; - } + symsec = SECTION_HEADER (relsec->sh_link); + symtab = GET_ELF_SYMBOLS (file, symsec); - /* Check its version number. */ - info.li_version = byte_get (hdrptr, 2); - hdrptr += 2; - if (info.li_version != 2 && info.li_version != 3) + for (rp = rela; rp < rela + nrelas; ++rp) { - warn (_("Only DWARF version 2 and 3 line info is currently supported.\n")); - return 0; - } + unsigned char *loc; - 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++; + if (rp->r_offset >= (bfd_vma) (start - sec_data) + && rp->r_offset < (bfd_vma) (end - sec_data) - reloc_size) + loc = sec_data + rp->r_offset; + else + continue; - /* Sign extend the line base field. */ - info.li_line_base <<= 24; - info.li_line_base >>= 24; + if (is_32bit_elf) + { + sym = symtab + ELF32_R_SYM (rp->r_info); - /* Get the pointer size from the comp unit associated - with this block of line number information. */ - pointer_size = get_pointer_size_of_comp_unit (comp_unit, - ".debug_lines"); - comp_unit ++; + if (ELF32_R_SYM (rp->r_info) != 0 + && ELF32_ST_TYPE (sym->st_info) != STT_SECTION + /* Relocations against object symbols can happen, + eg when referencing a global array. For an + example of this see the _clz.o binary in libgcc.a. */ + && ELF32_ST_TYPE (sym->st_info) != STT_OBJECT) + { + warn (_("%s: skipping unexpected symbol type %s in relocation in section .rela%s\n"), + get_symbol_type (ELF32_ST_TYPE (sym->st_info)), + SECTION_NAME (section)); + continue; + } + } + else + { + sym = symtab + ELF64_R_SYM (rp->r_info); - printf (_(" Length: %ld\n"), info.li_length); - printf (_(" DWARF Version: %d\n"), info.li_version); - printf (_(" Prologue Length: %d\n"), info.li_prologue_length); - printf (_(" Minimum Instruction Length: %d\n"), info.li_min_insn_length); - printf (_(" Initial value of 'is_stmt': %d\n"), info.li_default_is_stmt); - printf (_(" Line Base: %d\n"), info.li_line_base); - printf (_(" Line Range: %d\n"), info.li_line_range); - printf (_(" Opcode Base: %d\n"), info.li_opcode_base); - printf (_(" (Pointer size: %u)\n"), pointer_size); + if (ELF64_R_SYM (rp->r_info) != 0 + && ELF64_ST_TYPE (sym->st_info) != STT_SECTION + && ELF64_ST_TYPE (sym->st_info) != STT_OBJECT) + { + warn (_("skipping unexpected symbol type %s in relocation in section .rela.%s\n"), + get_symbol_type (ELF64_ST_TYPE (sym->st_info)), + SECTION_NAME (section)); + continue; + } + } - end_of_sequence = data + info.li_length + initial_length_size; + byte_put (loc, rp->r_addend, reloc_size); + } - reset_state_machine (info.li_default_is_stmt); + free (symtab); + free (rela); + break; + } + return 1; +} - /* Display the contents of the Opcodes table. */ - standard_opcodes = hdrptr; +/* FIXME: There are better and more efficient ways to handle + these structures. For now though, I just want something that + is simple to implement. */ +typedef struct abbrev_attr +{ + unsigned long attribute; + unsigned long form; + struct abbrev_attr *next; +} +abbrev_attr; - printf (_("\n Opcodes:\n")); +typedef struct abbrev_entry +{ + unsigned long entry; + unsigned long tag; + int children; + struct abbrev_attr *first_attr; + struct abbrev_attr *last_attr; + struct abbrev_entry *next; +} +abbrev_entry; - for (i = 1; i < info.li_opcode_base; i++) - printf (_(" Opcode %d has %d args\n"), i, standard_opcodes[i - 1]); +static abbrev_entry *first_abbrev = NULL; +static abbrev_entry *last_abbrev = NULL; - /* Display the contents of the Directory table. */ - data = standard_opcodes + info.li_opcode_base - 1; +static void +free_abbrevs (void) +{ + abbrev_entry *abbrev; - if (*data == 0) - printf (_("\n The Directory Table is empty.\n")); - else - { - printf (_("\n The Directory Table:\n")); + for (abbrev = first_abbrev; abbrev;) + { + abbrev_entry *next = abbrev->next; + abbrev_attr *attr; - while (*data != 0) - { - printf (_(" %s\n"), data); + for (attr = abbrev->first_attr; attr;) + { + abbrev_attr *next = attr->next; - data += strlen ((char *) data) + 1; - } + free (attr); + attr = next; } - /* Skip the NUL at the end of the table. */ - data++; + free (abbrev); + abbrev = next; + } - /* Display the contents of the File Name table. */ - if (*data == 0) - printf (_("\n The File Name Table is empty.\n")); - else - { - printf (_("\n The File Name Table:\n")); - printf (_(" Entry\tDir\tTime\tSize\tName\n")); + last_abbrev = first_abbrev = NULL; +} - while (*data != 0) - { - unsigned char *name; - int bytes_read; +static void +add_abbrev (unsigned long number, unsigned long tag, int children) +{ + abbrev_entry *entry; - printf (_(" %d\t"), ++state_machine_regs.last_file_entry); - name = data; + entry = malloc (sizeof (*entry)); - data += strlen ((char *) data) + 1; + if (entry == NULL) + /* ugg */ + return; - printf (_("%lu\t"), read_leb128 (data, & bytes_read, 0)); - data += bytes_read; - printf (_("%lu\t"), read_leb128 (data, & bytes_read, 0)); - data += bytes_read; - printf (_("%lu\t"), read_leb128 (data, & bytes_read, 0)); - data += bytes_read; - printf (_("%s\n"), name); - } - } + entry->entry = number; + entry->tag = tag; + entry->children = children; + entry->first_attr = NULL; + entry->last_attr = NULL; + entry->next = NULL; - /* Skip the NUL at the end of the table. */ - data++; + if (first_abbrev == NULL) + first_abbrev = entry; + else + last_abbrev->next = entry; - /* Now display the statements. */ - printf (_("\n Line Number Statements:\n")); + last_abbrev = entry; +} - while (data < end_of_sequence) - { - unsigned char op_code; - int adv; - int bytes_read; +static void +add_abbrev_attr (unsigned long attribute, unsigned long form) +{ + abbrev_attr *attr; - op_code = *data++; + attr = malloc (sizeof (*attr)); - if (op_code >= info.li_opcode_base) - { - op_code -= info.li_opcode_base; - adv = (op_code / info.li_line_range) * info.li_min_insn_length; - state_machine_regs.address += adv; - printf (_(" Special opcode %d: advance Address by %d to 0x%lx"), - op_code, adv, state_machine_regs.address); - adv = (op_code % info.li_line_range) + info.li_line_base; - state_machine_regs.line += adv; - printf (_(" and Line by %d to %d\n"), - adv, state_machine_regs.line); - } - else switch (op_code) - { - case DW_LNS_extended_op: - data += process_extended_line_op (data, info.li_default_is_stmt, - pointer_size); - break; + if (attr == NULL) + /* ugg */ + return; - case DW_LNS_copy: - printf (_(" Copy\n")); - break; + attr->attribute = attribute; + attr->form = form; + attr->next = NULL; - case DW_LNS_advance_pc: - adv = info.li_min_insn_length * read_leb128 (data, & bytes_read, 0); - data += bytes_read; - state_machine_regs.address += adv; - printf (_(" Advance PC by %d to %lx\n"), adv, - state_machine_regs.address); - break; + if (last_abbrev->first_attr == NULL) + last_abbrev->first_attr = attr; + else + last_abbrev->last_attr->next = attr; - case DW_LNS_advance_line: - adv = read_leb128 (data, & bytes_read, 1); - data += bytes_read; - state_machine_regs.line += adv; - printf (_(" Advance Line by %d to %d\n"), adv, - state_machine_regs.line); - break; + last_abbrev->last_attr = attr; +} - case DW_LNS_set_file: - adv = read_leb128 (data, & bytes_read, 0); - data += bytes_read; - printf (_(" Set File Name to entry %d in the File Name Table\n"), - adv); - state_machine_regs.file = adv; - break; +/* Processes the (partial) contents of a .debug_abbrev section. + Returns NULL if the end of the section was encountered. + Returns the address after the last byte read if the end of + an abbreviation set was found. */ - case DW_LNS_set_column: - adv = read_leb128 (data, & bytes_read, 0); - data += bytes_read; - printf (_(" Set column to %d\n"), adv); - state_machine_regs.column = adv; - break; +static unsigned char * +process_abbrev_section (unsigned char *start, unsigned char *end) +{ + if (first_abbrev != NULL) + return NULL; - case DW_LNS_negate_stmt: - adv = state_machine_regs.is_stmt; - adv = ! adv; - printf (_(" Set is_stmt to %d\n"), adv); - state_machine_regs.is_stmt = adv; - break; + while (start < end) + { + int bytes_read; + unsigned long entry; + unsigned long tag; + unsigned long attribute; + int children; - case DW_LNS_set_basic_block: - printf (_(" Set basic block\n")); - state_machine_regs.basic_block = 1; - break; + entry = read_leb128 (start, & bytes_read, 0); + start += bytes_read; - case DW_LNS_const_add_pc: - adv = (((255 - info.li_opcode_base) / info.li_line_range) - * info.li_min_insn_length); - state_machine_regs.address += adv; - printf (_(" Advance PC by constant %d to 0x%lx\n"), adv, - state_machine_regs.address); - break; - - case DW_LNS_fixed_advance_pc: - adv = byte_get (data, 2); - data += 2; - state_machine_regs.address += adv; - printf (_(" Advance PC by fixed size amount %d to 0x%lx\n"), - adv, state_machine_regs.address); - break; - - case DW_LNS_set_prologue_end: - printf (_(" Set prologue_end to true\n")); - break; - - case DW_LNS_set_epilogue_begin: - printf (_(" Set epilogue_begin to true\n")); - break; - - case DW_LNS_set_isa: - adv = read_leb128 (data, & bytes_read, 0); - data += bytes_read; - printf (_(" Set ISA to %d\n"), adv); - 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; - } - } - putchar ('\n'); - } - - return 1; -} - -static int -display_debug_pubnames (Elf_Internal_Shdr *section, - unsigned char *start, - FILE *file ATTRIBUTE_UNUSED) -{ - DWARF2_Internal_PubNames pubnames; - unsigned char *end; - - end = start + section->sh_size; - - printf (_("Contents of the %s section:\n\n"), SECTION_NAME (section)); - - while (start < end) - { - unsigned char *data; - unsigned long offset; - int offset_size, initial_length_size; - - data = start; + /* A single zero is supposed to end the section according + to the standard. If there's more, then signal that to + the caller. */ + if (entry == 0) + return start == end ? NULL : start; - pubnames.pn_length = byte_get (data, 4); - data += 4; - if (pubnames.pn_length == 0xffffffff) - { - pubnames.pn_length = byte_get (data, 8); - data += 8; - offset_size = 8; - initial_length_size = 12; - } - else - { - offset_size = 4; - initial_length_size = 4; - } + tag = read_leb128 (start, & bytes_read, 0); + start += bytes_read; - pubnames.pn_version = byte_get (data, 2); - data += 2; - pubnames.pn_offset = byte_get (data, offset_size); - data += offset_size; - pubnames.pn_size = byte_get (data, offset_size); - data += offset_size; + children = *start++; - start += pubnames.pn_length + initial_length_size; + add_abbrev (entry, tag, children); - if (pubnames.pn_version != 2 && pubnames.pn_version != 3) + do { - static int warned = 0; - - if (! warned) - { - warn (_("Only DWARF 2 and 3 pubnames are currently supported\n")); - warned = 1; - } - - continue; - } - - printf (_(" Length: %ld\n"), - pubnames.pn_length); - printf (_(" Version: %d\n"), - pubnames.pn_version); - printf (_(" Offset into .debug_info section: %ld\n"), - pubnames.pn_offset); - printf (_(" Size of area in .debug_info section: %ld\n"), - pubnames.pn_size); + unsigned long form; - printf (_("\n Offset\tName\n")); + attribute = read_leb128 (start, & bytes_read, 0); + start += bytes_read; - do - { - offset = byte_get (data, offset_size); + form = read_leb128 (start, & bytes_read, 0); + start += bytes_read; - if (offset != 0) - { - data += offset_size; - printf (" %-6ld\t\t%s\n", offset, data); - data += strlen ((char *) data) + 1; - } + if (attribute != 0) + add_abbrev_attr (attribute, form); } - while (offset != 0); + while (attribute != 0); } - printf ("\n"); - return 1; + return NULL; } static char * @@ -7672,119 +7505,6 @@ get_TAG_name (unsigned long tag) } static char * -get_AT_name (unsigned long attribute) -{ - switch (attribute) - { - case DW_AT_sibling: return "DW_AT_sibling"; - case DW_AT_location: return "DW_AT_location"; - case DW_AT_name: return "DW_AT_name"; - case DW_AT_ordering: return "DW_AT_ordering"; - case DW_AT_subscr_data: return "DW_AT_subscr_data"; - case DW_AT_byte_size: return "DW_AT_byte_size"; - case DW_AT_bit_offset: return "DW_AT_bit_offset"; - case DW_AT_bit_size: return "DW_AT_bit_size"; - case DW_AT_element_list: return "DW_AT_element_list"; - case DW_AT_stmt_list: return "DW_AT_stmt_list"; - case DW_AT_low_pc: return "DW_AT_low_pc"; - case DW_AT_high_pc: return "DW_AT_high_pc"; - case DW_AT_language: return "DW_AT_language"; - case DW_AT_member: return "DW_AT_member"; - case DW_AT_discr: return "DW_AT_discr"; - case DW_AT_discr_value: return "DW_AT_discr_value"; - case DW_AT_visibility: return "DW_AT_visibility"; - case DW_AT_import: return "DW_AT_import"; - case DW_AT_string_length: return "DW_AT_string_length"; - case DW_AT_common_reference: return "DW_AT_common_reference"; - case DW_AT_comp_dir: return "DW_AT_comp_dir"; - case DW_AT_const_value: return "DW_AT_const_value"; - case DW_AT_containing_type: return "DW_AT_containing_type"; - case DW_AT_default_value: return "DW_AT_default_value"; - case DW_AT_inline: return "DW_AT_inline"; - case DW_AT_is_optional: return "DW_AT_is_optional"; - case DW_AT_lower_bound: return "DW_AT_lower_bound"; - case DW_AT_producer: return "DW_AT_producer"; - case DW_AT_prototyped: return "DW_AT_prototyped"; - case DW_AT_return_addr: return "DW_AT_return_addr"; - case DW_AT_start_scope: return "DW_AT_start_scope"; - case DW_AT_stride_size: return "DW_AT_stride_size"; - case DW_AT_upper_bound: return "DW_AT_upper_bound"; - case DW_AT_abstract_origin: return "DW_AT_abstract_origin"; - case DW_AT_accessibility: return "DW_AT_accessibility"; - case DW_AT_address_class: return "DW_AT_address_class"; - case DW_AT_artificial: return "DW_AT_artificial"; - case DW_AT_base_types: return "DW_AT_base_types"; - case DW_AT_calling_convention: return "DW_AT_calling_convention"; - case DW_AT_count: return "DW_AT_count"; - case DW_AT_data_member_location: return "DW_AT_data_member_location"; - case DW_AT_decl_column: return "DW_AT_decl_column"; - case DW_AT_decl_file: return "DW_AT_decl_file"; - case DW_AT_decl_line: return "DW_AT_decl_line"; - case DW_AT_declaration: return "DW_AT_declaration"; - case DW_AT_discr_list: return "DW_AT_discr_list"; - case DW_AT_encoding: return "DW_AT_encoding"; - case DW_AT_external: return "DW_AT_external"; - case DW_AT_frame_base: return "DW_AT_frame_base"; - case DW_AT_friend: return "DW_AT_friend"; - case DW_AT_identifier_case: return "DW_AT_identifier_case"; - case DW_AT_macro_info: return "DW_AT_macro_info"; - case DW_AT_namelist_items: return "DW_AT_namelist_items"; - case DW_AT_priority: return "DW_AT_priority"; - case DW_AT_segment: return "DW_AT_segment"; - case DW_AT_specification: return "DW_AT_specification"; - case DW_AT_static_link: return "DW_AT_static_link"; - case DW_AT_type: return "DW_AT_type"; - case DW_AT_use_location: return "DW_AT_use_location"; - case DW_AT_variable_parameter: return "DW_AT_variable_parameter"; - case DW_AT_virtuality: return "DW_AT_virtuality"; - case DW_AT_vtable_elem_location: return "DW_AT_vtable_elem_location"; - /* DWARF 2.1 values. */ - case DW_AT_allocated: return "DW_AT_allocated"; - case DW_AT_associated: return "DW_AT_associated"; - case DW_AT_data_location: return "DW_AT_data_location"; - case DW_AT_stride: return "DW_AT_stride"; - case DW_AT_entry_pc: return "DW_AT_entry_pc"; - case DW_AT_use_UTF8: return "DW_AT_use_UTF8"; - case DW_AT_extension: return "DW_AT_extension"; - case DW_AT_ranges: return "DW_AT_ranges"; - case DW_AT_trampoline: return "DW_AT_trampoline"; - case DW_AT_call_column: return "DW_AT_call_column"; - case DW_AT_call_file: return "DW_AT_call_file"; - case DW_AT_call_line: return "DW_AT_call_line"; - /* SGI/MIPS extensions. */ - case DW_AT_MIPS_fde: return "DW_AT_MIPS_fde"; - case DW_AT_MIPS_loop_begin: return "DW_AT_MIPS_loop_begin"; - case DW_AT_MIPS_tail_loop_begin: return "DW_AT_MIPS_tail_loop_begin"; - case DW_AT_MIPS_epilog_begin: return "DW_AT_MIPS_epilog_begin"; - case DW_AT_MIPS_loop_unroll_factor: return "DW_AT_MIPS_loop_unroll_factor"; - case DW_AT_MIPS_software_pipeline_depth: - return "DW_AT_MIPS_software_pipeline_depth"; - case DW_AT_MIPS_linkage_name: return "DW_AT_MIPS_linkage_name"; - case DW_AT_MIPS_stride: return "DW_AT_MIPS_stride"; - case DW_AT_MIPS_abstract_name: return "DW_AT_MIPS_abstract_name"; - case DW_AT_MIPS_clone_origin: return "DW_AT_MIPS_clone_origin"; - case DW_AT_MIPS_has_inlines: return "DW_AT_MIPS_has_inlines"; - /* GNU extensions. */ - case DW_AT_sf_names: return "DW_AT_sf_names"; - case DW_AT_src_info: return "DW_AT_src_info"; - case DW_AT_mac_info: return "DW_AT_mac_info"; - case DW_AT_src_coords: return "DW_AT_src_coords"; - case DW_AT_body_begin: return "DW_AT_body_begin"; - case DW_AT_body_end: return "DW_AT_body_end"; - case DW_AT_GNU_vector: return "DW_AT_GNU_vector"; - /* UPC extension. */ - case DW_AT_upc_threads_scaled: return "DW_AT_upc_threads_scaled"; - default: - { - static char buffer[100]; - - sprintf (buffer, _("Unknown AT value: %lx"), attribute); - return buffer; - } - } -} - -static char * get_FORM_name (unsigned long form) { switch (form) @@ -7820,325 +7540,51 @@ get_FORM_name (unsigned long form) } } -/* FIXME: There are better and more efficient ways to handle - these structures. For now though, I just want something that - is simple to implement. */ -typedef struct abbrev_attr +static unsigned char * +display_block (unsigned char *data, unsigned long length) { - unsigned long attribute; - unsigned long form; - struct abbrev_attr *next; -} -abbrev_attr; - -typedef struct abbrev_entry -{ - unsigned long entry; - unsigned long tag; - int children; - struct abbrev_attr *first_attr; - struct abbrev_attr *last_attr; - struct abbrev_entry *next; -} -abbrev_entry; - -static abbrev_entry *first_abbrev = NULL; -static abbrev_entry *last_abbrev = NULL; - -static void -free_abbrevs (void) -{ - abbrev_entry *abbrev; - - for (abbrev = first_abbrev; abbrev;) - { - abbrev_entry *next = abbrev->next; - abbrev_attr *attr; - - for (attr = abbrev->first_attr; attr;) - { - abbrev_attr *next = attr->next; - - free (attr); - attr = next; - } - - free (abbrev); - abbrev = next; - } - - last_abbrev = first_abbrev = NULL; -} - -static void -add_abbrev (unsigned long number, unsigned long tag, int children) -{ - abbrev_entry *entry; - - entry = malloc (sizeof (*entry)); - - if (entry == NULL) - /* ugg */ - return; - - entry->entry = number; - entry->tag = tag; - entry->children = children; - entry->first_attr = NULL; - entry->last_attr = NULL; - entry->next = NULL; + printf (_(" %lu byte block: "), length); - if (first_abbrev == NULL) - first_abbrev = entry; - else - last_abbrev->next = entry; + while (length --) + printf ("%lx ", (unsigned long) byte_get (data++, 1)); - last_abbrev = entry; + return data; } static void -add_abbrev_attr (unsigned long attribute, unsigned long form) -{ - abbrev_attr *attr; - - attr = malloc (sizeof (*attr)); - - if (attr == NULL) - /* ugg */ - return; - - attr->attribute = attribute; - attr->form = form; - attr->next = NULL; - - if (last_abbrev->first_attr == NULL) - last_abbrev->first_attr = attr; - else - last_abbrev->last_attr->next = attr; - - last_abbrev->last_attr = attr; -} - -/* Processes the (partial) contents of a .debug_abbrev section. - Returns NULL if the end of the section was encountered. - Returns the address after the last byte read if the end of - an abbreviation set was found. */ - -static unsigned char * -process_abbrev_section (unsigned char *start, unsigned char *end) -{ - if (first_abbrev != NULL) - return NULL; - - while (start < end) - { - int bytes_read; - unsigned long entry; - unsigned long tag; - unsigned long attribute; - int children; - - entry = read_leb128 (start, & bytes_read, 0); - start += bytes_read; - - /* A single zero is supposed to end the section according - to the standard. If there's more, then signal that to - the caller. */ - if (entry == 0) - return start == end ? NULL : start; - - tag = read_leb128 (start, & bytes_read, 0); - start += bytes_read; - - children = *start++; - - add_abbrev (entry, tag, children); - - do - { - unsigned long form; - - attribute = read_leb128 (start, & bytes_read, 0); - start += bytes_read; - - form = read_leb128 (start, & bytes_read, 0); - start += bytes_read; - - if (attribute != 0) - add_abbrev_attr (attribute, form); - } - while (attribute != 0); - } - - return NULL; -} - - -static int -display_debug_macinfo (Elf_Internal_Shdr *section, - unsigned char *start, - FILE *file ATTRIBUTE_UNUSED) +decode_location_expression (unsigned char * data, + unsigned int pointer_size, + unsigned long length, + unsigned long cu_offset) { - unsigned char *end = start + section->sh_size; - unsigned char *curr = start; - unsigned int bytes_read; - enum dwarf_macinfo_record_type op; - - printf (_("Contents of the %s section:\n\n"), SECTION_NAME (section)); + unsigned op; + int bytes_read; + unsigned long uvalue; + unsigned char *end = data + length; - while (curr < end) + while (data < end) { - unsigned int lineno; - const char *string; - - op = *curr; - curr++; + op = *data++; switch (op) { - case DW_MACINFO_start_file: - { - unsigned int filenum; - - lineno = read_leb128 (curr, & bytes_read, 0); - curr += bytes_read; - filenum = read_leb128 (curr, & bytes_read, 0); - curr += bytes_read; - - printf (_(" DW_MACINFO_start_file - lineno: %d filenum: %d\n"), - lineno, filenum); - } + case DW_OP_addr: + printf ("DW_OP_addr: %lx", + (unsigned long) byte_get (data, pointer_size)); + data += pointer_size; break; - - case DW_MACINFO_end_file: - printf (_(" DW_MACINFO_end_file\n")); + case DW_OP_deref: + printf ("DW_OP_deref"); break; - - case DW_MACINFO_define: - lineno = read_leb128 (curr, & bytes_read, 0); - curr += bytes_read; - string = curr; - curr += strlen (string) + 1; - printf (_(" DW_MACINFO_define - lineno : %d macro : %s\n"), - lineno, string); + case DW_OP_const1u: + printf ("DW_OP_const1u: %lu", (unsigned long) byte_get (data++, 1)); break; - - case DW_MACINFO_undef: - lineno = read_leb128 (curr, & bytes_read, 0); - curr += bytes_read; - string = curr; - curr += strlen (string) + 1; - printf (_(" DW_MACINFO_undef - lineno : %d macro : %s\n"), - lineno, string); + case DW_OP_const1s: + printf ("DW_OP_const1s: %ld", (long) byte_get_signed (data++, 1)); break; - - case DW_MACINFO_vendor_ext: - { - unsigned int constant; - - constant = read_leb128 (curr, & bytes_read, 0); - curr += bytes_read; - string = curr; - curr += strlen (string) + 1; - printf (_(" DW_MACINFO_vendor_ext - constant : %d string : %s\n"), - constant, string); - } - break; - } - } - - return 1; -} - - -static int -display_debug_abbrev (Elf_Internal_Shdr *section, - unsigned char *start, - FILE *file ATTRIBUTE_UNUSED) -{ - abbrev_entry *entry; - unsigned char *end = start + section->sh_size; - - printf (_("Contents of the %s section:\n\n"), SECTION_NAME (section)); - - do - { - start = process_abbrev_section (start, end); - - if (first_abbrev == NULL) - continue; - - printf (_(" Number TAG\n")); - - for (entry = first_abbrev; entry; entry = entry->next) - { - abbrev_attr *attr; - - printf (_(" %ld %s [%s]\n"), - entry->entry, - get_TAG_name (entry->tag), - entry->children ? _("has children") : _("no children")); - - for (attr = entry->first_attr; attr; attr = attr->next) - printf (_(" %-18s %s\n"), - get_AT_name (attr->attribute), - get_FORM_name (attr->form)); - } - - free_abbrevs (); - } - while (start); - - printf ("\n"); - - return 1; -} - - -static unsigned char * -display_block (unsigned char *data, unsigned long length) -{ - printf (_(" %lu byte block: "), length); - - while (length --) - printf ("%lx ", (unsigned long) byte_get (data++, 1)); - - return data; -} - -static void -decode_location_expression (unsigned char * data, - unsigned int pointer_size, - unsigned long length) -{ - unsigned op; - int bytes_read; - unsigned long uvalue; - unsigned char *end = data + length; - - while (data < end) - { - op = *data++; - - switch (op) - { - case DW_OP_addr: - printf ("DW_OP_addr: %lx", - (unsigned long) byte_get (data, pointer_size)); - data += pointer_size; - break; - case DW_OP_deref: - printf ("DW_OP_deref"); - break; - case DW_OP_const1u: - printf ("DW_OP_const1u: %lu", (unsigned long) byte_get (data++, 1)); - break; - case DW_OP_const1s: - printf ("DW_OP_const1s: %ld", (long) byte_get_signed (data++, 1)); - break; - case DW_OP_const2u: - printf ("DW_OP_const2u: %lu", (unsigned long) byte_get (data, 2)); - data += 2; + case DW_OP_const2u: + printf ("DW_OP_const2u: %lu", (unsigned long) byte_get (data, 2)); + data += 2; break; case DW_OP_const2s: printf ("DW_OP_const2s: %ld", (long) byte_get_signed (data, 2)); @@ -8265,1153 +7711,1894 @@ decode_location_expression (unsigned char * data, data += 2; break; - case DW_OP_lit0: - case DW_OP_lit1: - case DW_OP_lit2: - case DW_OP_lit3: - case DW_OP_lit4: - case DW_OP_lit5: - case DW_OP_lit6: - case DW_OP_lit7: - case DW_OP_lit8: - case DW_OP_lit9: - case DW_OP_lit10: - case DW_OP_lit11: - case DW_OP_lit12: - case DW_OP_lit13: - case DW_OP_lit14: - case DW_OP_lit15: - case DW_OP_lit16: - case DW_OP_lit17: - case DW_OP_lit18: - case DW_OP_lit19: - case DW_OP_lit20: - case DW_OP_lit21: - case DW_OP_lit22: - case DW_OP_lit23: - case DW_OP_lit24: - case DW_OP_lit25: - case DW_OP_lit26: - case DW_OP_lit27: - case DW_OP_lit28: - case DW_OP_lit29: - case DW_OP_lit30: - case DW_OP_lit31: - printf ("DW_OP_lit%d", op - DW_OP_lit0); - break; + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + printf ("DW_OP_lit%d", op - DW_OP_lit0); + break; + + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + printf ("DW_OP_reg%d", op - DW_OP_reg0); + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + printf ("DW_OP_breg%d: %ld", op - DW_OP_breg0, + read_leb128 (data, &bytes_read, 1)); + data += bytes_read; + break; + + case DW_OP_regx: + printf ("DW_OP_regx: %lu", read_leb128 (data, &bytes_read, 0)); + data += bytes_read; + break; + case DW_OP_fbreg: + printf ("DW_OP_fbreg: %ld", read_leb128 (data, &bytes_read, 1)); + data += bytes_read; + break; + case DW_OP_bregx: + uvalue = read_leb128 (data, &bytes_read, 0); + data += bytes_read; + printf ("DW_OP_bregx: %lu %ld", uvalue, + read_leb128 (data, &bytes_read, 1)); + data += bytes_read; + break; + case DW_OP_piece: + printf ("DW_OP_piece: %lu", read_leb128 (data, &bytes_read, 0)); + data += bytes_read; + break; + case DW_OP_deref_size: + printf ("DW_OP_deref_size: %ld", (long) byte_get (data++, 1)); + break; + case DW_OP_xderef_size: + printf ("DW_OP_xderef_size: %ld", (long) byte_get (data++, 1)); + break; + case DW_OP_nop: + printf ("DW_OP_nop"); + break; + + /* DWARF 3 extensions. */ + case DW_OP_push_object_address: + printf ("DW_OP_push_object_address"); + break; + case DW_OP_call2: + /* XXX: Strictly speaking for 64-bit DWARF3 files + this ought to be an 8-byte wide computation. */ + printf ("DW_OP_call2: <%lx>", (long) byte_get (data, 2) + cu_offset); + data += 2; + break; + case DW_OP_call4: + /* XXX: Strictly speaking for 64-bit DWARF3 files + this ought to be an 8-byte wide computation. */ + printf ("DW_OP_call4: <%lx>", (long) byte_get (data, 4) + cu_offset); + data += 4; + break; + case DW_OP_call_ref: + printf ("DW_OP_call_ref"); + break; + + /* GNU extensions. */ + case DW_OP_GNU_push_tls_address: + printf ("DW_OP_GNU_push_tls_address"); + break; + + default: + if (op >= DW_OP_lo_user + && op <= DW_OP_hi_user) + printf (_("(User defined location op)")); + else + printf (_("(Unknown location op)")); + /* No way to tell where the next op is, so just bail. */ + return; + } + + /* Separate the ops. */ + if (data < end) + printf ("; "); + } +} + +/* Decode a DW_AT_ranges attribute for 64bit DWARF3 . */ + +static void +decode_64bit_range (unsigned long offset, bfd_vma base_address) +{ + const char * start = debug_range_contents + offset; + const char * end = debug_range_contents + debug_range_size; + + do + { + bfd_vma a; + bfd_vma b; + + a = byte_get ((unsigned char *) start, 8); + b = byte_get ((unsigned char *) start + 8, 8); + + if (a == 0xffffffff) + { + base_address = b; + } + else if (a == 0 && b == 0) + break; + else if (a > b) + printf (_(" [corrupt: start > end]")); + else + { + printf (" "); + print_vma (base_address + a, PREFIX_HEX); + printf (" - "); + print_vma (base_address + b, PREFIX_HEX); + printf (", "); + } + + start += 16; + } + while (start < end); +} + +/* Decode a DW_AT_ranges attribute. */ + +static void +decode_range (unsigned long offset, bfd_vma base_address) +{ + const char * start; + const char * end; + + if (offset >= (debug_range_size - 8)) + { + printf (_("[corrupt: offset is outside the .debug_ranges section]")); + return; + } + + /* Since all entries in the .debug_ranges section are pairs of either + 4-byte integers (32-bit DWARF3) or 8-byte integers (64-bit DWARF3) + the offset should always be a multiple of 8 bytes. */ + if (offset % 8) + { + printf (_("[corrupt: offset is not a multiple of 8]")); + return; + } + + start = debug_range_contents + offset; + + if (offset > 0 + /* Be paranoid - check to see if the previous + two words were and end-of-range marker. */ + && (byte_get ((unsigned char *) start - 4, 4) != 0 + || byte_get ((unsigned char *) start - 8, 4) != 0)) + { + printf (_("[corrupt: offset is not at the start of a range]")); + return; + } + + end = debug_range_contents + debug_range_size; + + printf ("("); + do + { + unsigned long a; + unsigned long b; + + a = byte_get ((unsigned char *) start, 4); + b = byte_get ((unsigned char *) start + 4, 4); + + if (a == 0xffffffff) + { + if (b == 0xffffffff) + { + decode_64bit_range (offset, base_address); + return; + } + + base_address = b; + } + else if (a == 0 && b == 0) + break; + else if (a > b) + printf (_("[corrupt: start > end]")); + else + { + if (start > debug_range_contents + offset) + printf (", "); + + printf (_("0x%lx - 0x%lx"), + (unsigned long) base_address + a, + (unsigned long) base_address + b); + } + + start += 8; + } + while (start < end); + printf (")"); +} + +/* This structure records the information that + we extract from the.debug_info section. */ +typedef struct +{ + unsigned int pointer_size; + unsigned long cu_offset; + /* This is an array of offsets to the location list table. */ + unsigned long *loc_offsets; + unsigned int num_loc_offsets; + unsigned int max_loc_offsets; +} +debug_info; + +static debug_info * debug_information = NULL; +static unsigned int num_debug_info_entries = 0; +static unsigned int last_pointer_size = 0; +static int warned_about_missing_comp_units = FALSE; + +static unsigned char * +read_and_display_attr_value (unsigned long attribute, + unsigned long form, + unsigned char *data, + unsigned long cu_offset, + unsigned long pointer_size, + unsigned long offset_size, + int dwarf_version, + debug_info *debug_info_p, + int do_loc) +{ + static unsigned long saved_DW_AT_low_pc = 0; + unsigned long uvalue = 0; + unsigned char *block_start = NULL; + int bytes_read; + + switch (form) + { + default: + break; + + case DW_FORM_ref_addr: + if (dwarf_version == 2) + { + uvalue = byte_get (data, pointer_size); + data += pointer_size; + } + else if (dwarf_version == 3) + { + uvalue = byte_get (data, offset_size); + data += offset_size; + } + else + { + error (_("Internal error: DWARF version is not 2 or 3.\n")); + } + break; + + case DW_FORM_addr: + uvalue = byte_get (data, pointer_size); + data += pointer_size; + break; + + case DW_FORM_strp: + uvalue = byte_get (data, offset_size); + data += offset_size; + break; + + case DW_FORM_ref1: + case DW_FORM_flag: + case DW_FORM_data1: + uvalue = byte_get (data++, 1); + break; + + case DW_FORM_ref2: + case DW_FORM_data2: + uvalue = byte_get (data, 2); + data += 2; + break; + + case DW_FORM_ref4: + case DW_FORM_data4: + uvalue = byte_get (data, 4); + data += 4; + break; + + case DW_FORM_sdata: + uvalue = read_leb128 (data, & bytes_read, 1); + data += bytes_read; + break; + + case DW_FORM_ref_udata: + case DW_FORM_udata: + uvalue = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + break; + + case DW_FORM_indirect: + form = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + if (!do_loc) + printf (" %s", get_FORM_name (form)); + return read_and_display_attr_value (attribute, form, data, + cu_offset, pointer_size, + offset_size, dwarf_version, + debug_info_p, do_loc); + } + + switch (form) + { + case DW_FORM_ref_addr: + if (!do_loc) + printf (" <#%lx>", uvalue); + break; + + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref_udata: + if (!do_loc) + printf (" <%lx>", uvalue + cu_offset); + break; + + case DW_FORM_data4: + case DW_FORM_addr: + if (!do_loc) + printf (" %#lx", uvalue); + break; + + case DW_FORM_flag: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_sdata: + case DW_FORM_udata: + if (!do_loc) + printf (" %ld", uvalue); + break; + + case DW_FORM_ref8: + case DW_FORM_data8: + if (!do_loc) + { + uvalue = byte_get (data, 4); + printf (" %lx", uvalue); + printf (" %lx", (unsigned long) byte_get (data + 4, 4)); + } + if ((do_loc || do_debug_loc) + && num_debug_info_entries == 0) + { + if (sizeof (uvalue) == 8) + uvalue = byte_get (data, 8); + else + error (_("DW_FORM_data8 is unsupported when sizeof (unsigned long) != 8\n")); + } + data += 8; + break; + + case DW_FORM_string: + if (!do_loc) + printf (" %s", data); + data += strlen ((char *) data) + 1; + break; + + case DW_FORM_block: + uvalue = read_leb128 (data, & bytes_read, 0); + block_start = data + bytes_read; + if (do_loc) + data = block_start + uvalue; + else + data = display_block (block_start, uvalue); + break; + + case DW_FORM_block1: + uvalue = byte_get (data, 1); + block_start = data + 1; + if (do_loc) + data = block_start + uvalue; + else + data = display_block (block_start, uvalue); + break; + + case DW_FORM_block2: + uvalue = byte_get (data, 2); + block_start = data + 2; + if (do_loc) + data = block_start + uvalue; + else + data = display_block (block_start, uvalue); + break; + + case DW_FORM_block4: + uvalue = byte_get (data, 4); + block_start = data + 4; + if (do_loc) + data = block_start + uvalue; + else + data = display_block (block_start, uvalue); + break; + + case DW_FORM_strp: + if (!do_loc) + printf (_(" (indirect string, offset: 0x%lx): %s"), + uvalue, fetch_indirect_string (uvalue)); + break; + + case DW_FORM_indirect: + /* Handled above. */ + break; + + default: + warn (_("Unrecognized form: %d\n"), form); + break; + } + + /* For some attributes we can display further information. */ + if ((do_loc || do_debug_loc) + && num_debug_info_entries == 0) + { + switch (attribute) + { + case DW_AT_frame_base: + case DW_AT_location: + case DW_AT_data_member_location: + case DW_AT_vtable_elem_location: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_stride: + case DW_AT_upper_bound: + case DW_AT_lower_bound: + if (form == DW_FORM_data4 || form == DW_FORM_data8) + { + /* Process location list. */ + unsigned int max = debug_info_p->max_loc_offsets; + unsigned int num = debug_info_p->num_loc_offsets; + + if (max == 0 || num >= max) + { + max += 1024; + debug_info_p->loc_offsets + = xrealloc (debug_info_p->loc_offsets, + max * sizeof (*debug_info_p->loc_offsets)); + debug_info_p->max_loc_offsets = max; + } + debug_info_p->loc_offsets [num] = uvalue; + debug_info_p->num_loc_offsets++; + } + break; + + default: + break; + } + } + + if (do_loc) + return data; + + printf ("\t"); + + switch (attribute) + { + case DW_AT_inline: + switch (uvalue) + { + case DW_INL_not_inlined: + printf (_("(not inlined)")); + break; + case DW_INL_inlined: + printf (_("(inlined)")); + break; + case DW_INL_declared_not_inlined: + printf (_("(declared as inline but ignored)")); + break; + case DW_INL_declared_inlined: + printf (_("(declared as inline and inlined)")); + break; + default: + printf (_(" (Unknown inline attribute value: %lx)"), uvalue); + break; + } + break; + + case DW_AT_language: + switch (uvalue) + { + case DW_LANG_C: printf ("(non-ANSI C)"); break; + case DW_LANG_C89: printf ("(ANSI C)"); break; + case DW_LANG_C_plus_plus: printf ("(C++)"); break; + case DW_LANG_Fortran77: printf ("(FORTRAN 77)"); break; + case DW_LANG_Fortran90: printf ("(Fortran 90)"); break; + case DW_LANG_Modula2: printf ("(Modula 2)"); break; + case DW_LANG_Pascal83: printf ("(ANSI Pascal)"); break; + case DW_LANG_Ada83: printf ("(Ada)"); break; + case DW_LANG_Cobol74: printf ("(Cobol 74)"); break; + case DW_LANG_Cobol85: printf ("(Cobol 85)"); break; + /* DWARF 2.1 values. */ + case DW_LANG_C99: printf ("(ANSI C99)"); break; + case DW_LANG_Ada95: printf ("(ADA 95)"); break; + case DW_LANG_Fortran95: printf ("(Fortran 95)"); break; + /* MIPS extension. */ + case DW_LANG_Mips_Assembler: printf ("(MIPS assembler)"); break; + /* UPC extension. */ + case DW_LANG_Upc: printf ("(Unified Parallel C)"); break; + default: + printf ("(Unknown: %lx)", uvalue); + break; + } + break; + + case DW_AT_encoding: + switch (uvalue) + { + case DW_ATE_void: printf ("(void)"); break; + case DW_ATE_address: printf ("(machine address)"); break; + case DW_ATE_boolean: printf ("(boolean)"); break; + case DW_ATE_complex_float: printf ("(complex float)"); break; + case DW_ATE_float: printf ("(float)"); break; + case DW_ATE_signed: printf ("(signed)"); break; + case DW_ATE_signed_char: printf ("(signed char)"); break; + case DW_ATE_unsigned: printf ("(unsigned)"); break; + case DW_ATE_unsigned_char: printf ("(unsigned char)"); break; + /* DWARF 2.1 value. */ + case DW_ATE_imaginary_float: printf ("(imaginary float)"); break; + default: + if (uvalue >= DW_ATE_lo_user + && uvalue <= DW_ATE_hi_user) + printf ("(user defined type)"); + else + printf ("(unknown type)"); + break; + } + break; + + case DW_AT_accessibility: + switch (uvalue) + { + case DW_ACCESS_public: printf ("(public)"); break; + case DW_ACCESS_protected: printf ("(protected)"); break; + case DW_ACCESS_private: printf ("(private)"); break; + default: + printf ("(unknown accessibility)"); + break; + } + break; + + case DW_AT_visibility: + switch (uvalue) + { + case DW_VIS_local: printf ("(local)"); break; + case DW_VIS_exported: printf ("(exported)"); break; + case DW_VIS_qualified: printf ("(qualified)"); break; + default: printf ("(unknown visibility)"); break; + } + break; + + case DW_AT_virtuality: + switch (uvalue) + { + case DW_VIRTUALITY_none: printf ("(none)"); break; + case DW_VIRTUALITY_virtual: printf ("(virtual)"); break; + case DW_VIRTUALITY_pure_virtual:printf ("(pure_virtual)"); break; + default: printf ("(unknown virtuality)"); break; + } + break; + + case DW_AT_identifier_case: + switch (uvalue) + { + case DW_ID_case_sensitive: printf ("(case_sensitive)"); break; + case DW_ID_up_case: printf ("(up_case)"); break; + case DW_ID_down_case: printf ("(down_case)"); break; + case DW_ID_case_insensitive: printf ("(case_insensitive)"); break; + default: printf ("(unknown case)"); break; + } + break; + + case DW_AT_calling_convention: + switch (uvalue) + { + case DW_CC_normal: printf ("(normal)"); break; + case DW_CC_program: printf ("(program)"); break; + case DW_CC_nocall: printf ("(nocall)"); break; + default: + if (uvalue >= DW_CC_lo_user + && uvalue <= DW_CC_hi_user) + printf ("(user defined)"); + else + printf ("(unknown convention)"); + } + break; + + case DW_AT_ordering: + switch (uvalue) + { + case -1: printf ("(undefined)"); break; + case 0: printf ("(row major)"); break; + case 1: printf ("(column major)"); break; + } + break; + + case DW_AT_frame_base: + case DW_AT_location: + case DW_AT_data_member_location: + case DW_AT_vtable_elem_location: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_stride: + case DW_AT_upper_bound: + case DW_AT_lower_bound: + if (block_start) + { + printf ("("); + decode_location_expression (block_start, pointer_size, uvalue, cu_offset); + printf (")"); + } + else if (form == DW_FORM_data4 || form == DW_FORM_data8) + printf (_("(location list)")); + + break; + + case DW_AT_low_pc: + /* This is a hack. We keep track of the DW_AT_low_pc attributes + and use them when decoding DW_AT_ranges attributes. The + assumption here is that we are decoding the attributes in order + and so the correct base address for the range is the low_pc. */ + saved_DW_AT_low_pc = uvalue; + break; + + case DW_AT_ranges: + decode_range (uvalue, saved_DW_AT_low_pc); + break; + + default: + break; + } + + return data; +} + +static char * +get_AT_name (unsigned long attribute) +{ + switch (attribute) + { + case DW_AT_sibling: return "DW_AT_sibling"; + case DW_AT_location: return "DW_AT_location"; + case DW_AT_name: return "DW_AT_name"; + case DW_AT_ordering: return "DW_AT_ordering"; + case DW_AT_subscr_data: return "DW_AT_subscr_data"; + case DW_AT_byte_size: return "DW_AT_byte_size"; + case DW_AT_bit_offset: return "DW_AT_bit_offset"; + case DW_AT_bit_size: return "DW_AT_bit_size"; + case DW_AT_element_list: return "DW_AT_element_list"; + case DW_AT_stmt_list: return "DW_AT_stmt_list"; + case DW_AT_low_pc: return "DW_AT_low_pc"; + case DW_AT_high_pc: return "DW_AT_high_pc"; + case DW_AT_language: return "DW_AT_language"; + case DW_AT_member: return "DW_AT_member"; + case DW_AT_discr: return "DW_AT_discr"; + case DW_AT_discr_value: return "DW_AT_discr_value"; + case DW_AT_visibility: return "DW_AT_visibility"; + case DW_AT_import: return "DW_AT_import"; + case DW_AT_string_length: return "DW_AT_string_length"; + case DW_AT_common_reference: return "DW_AT_common_reference"; + case DW_AT_comp_dir: return "DW_AT_comp_dir"; + case DW_AT_const_value: return "DW_AT_const_value"; + case DW_AT_containing_type: return "DW_AT_containing_type"; + case DW_AT_default_value: return "DW_AT_default_value"; + case DW_AT_inline: return "DW_AT_inline"; + case DW_AT_is_optional: return "DW_AT_is_optional"; + case DW_AT_lower_bound: return "DW_AT_lower_bound"; + case DW_AT_producer: return "DW_AT_producer"; + case DW_AT_prototyped: return "DW_AT_prototyped"; + case DW_AT_return_addr: return "DW_AT_return_addr"; + case DW_AT_start_scope: return "DW_AT_start_scope"; + case DW_AT_stride_size: return "DW_AT_stride_size"; + case DW_AT_upper_bound: return "DW_AT_upper_bound"; + case DW_AT_abstract_origin: return "DW_AT_abstract_origin"; + case DW_AT_accessibility: return "DW_AT_accessibility"; + case DW_AT_address_class: return "DW_AT_address_class"; + case DW_AT_artificial: return "DW_AT_artificial"; + case DW_AT_base_types: return "DW_AT_base_types"; + case DW_AT_calling_convention: return "DW_AT_calling_convention"; + case DW_AT_count: return "DW_AT_count"; + case DW_AT_data_member_location: return "DW_AT_data_member_location"; + case DW_AT_decl_column: return "DW_AT_decl_column"; + case DW_AT_decl_file: return "DW_AT_decl_file"; + case DW_AT_decl_line: return "DW_AT_decl_line"; + case DW_AT_declaration: return "DW_AT_declaration"; + case DW_AT_discr_list: return "DW_AT_discr_list"; + case DW_AT_encoding: return "DW_AT_encoding"; + case DW_AT_external: return "DW_AT_external"; + case DW_AT_frame_base: return "DW_AT_frame_base"; + case DW_AT_friend: return "DW_AT_friend"; + case DW_AT_identifier_case: return "DW_AT_identifier_case"; + case DW_AT_macro_info: return "DW_AT_macro_info"; + case DW_AT_namelist_items: return "DW_AT_namelist_items"; + case DW_AT_priority: return "DW_AT_priority"; + case DW_AT_segment: return "DW_AT_segment"; + case DW_AT_specification: return "DW_AT_specification"; + case DW_AT_static_link: return "DW_AT_static_link"; + case DW_AT_type: return "DW_AT_type"; + case DW_AT_use_location: return "DW_AT_use_location"; + case DW_AT_variable_parameter: return "DW_AT_variable_parameter"; + case DW_AT_virtuality: return "DW_AT_virtuality"; + case DW_AT_vtable_elem_location: return "DW_AT_vtable_elem_location"; + /* DWARF 2.1 values. */ + case DW_AT_allocated: return "DW_AT_allocated"; + case DW_AT_associated: return "DW_AT_associated"; + case DW_AT_data_location: return "DW_AT_data_location"; + case DW_AT_stride: return "DW_AT_stride"; + case DW_AT_entry_pc: return "DW_AT_entry_pc"; + case DW_AT_use_UTF8: return "DW_AT_use_UTF8"; + case DW_AT_extension: return "DW_AT_extension"; + case DW_AT_ranges: return "DW_AT_ranges"; + case DW_AT_trampoline: return "DW_AT_trampoline"; + case DW_AT_call_column: return "DW_AT_call_column"; + case DW_AT_call_file: return "DW_AT_call_file"; + case DW_AT_call_line: return "DW_AT_call_line"; + /* SGI/MIPS extensions. */ + case DW_AT_MIPS_fde: return "DW_AT_MIPS_fde"; + case DW_AT_MIPS_loop_begin: return "DW_AT_MIPS_loop_begin"; + case DW_AT_MIPS_tail_loop_begin: return "DW_AT_MIPS_tail_loop_begin"; + case DW_AT_MIPS_epilog_begin: return "DW_AT_MIPS_epilog_begin"; + case DW_AT_MIPS_loop_unroll_factor: return "DW_AT_MIPS_loop_unroll_factor"; + case DW_AT_MIPS_software_pipeline_depth: + return "DW_AT_MIPS_software_pipeline_depth"; + case DW_AT_MIPS_linkage_name: return "DW_AT_MIPS_linkage_name"; + case DW_AT_MIPS_stride: return "DW_AT_MIPS_stride"; + case DW_AT_MIPS_abstract_name: return "DW_AT_MIPS_abstract_name"; + case DW_AT_MIPS_clone_origin: return "DW_AT_MIPS_clone_origin"; + case DW_AT_MIPS_has_inlines: return "DW_AT_MIPS_has_inlines"; + /* GNU extensions. */ + case DW_AT_sf_names: return "DW_AT_sf_names"; + case DW_AT_src_info: return "DW_AT_src_info"; + case DW_AT_mac_info: return "DW_AT_mac_info"; + case DW_AT_src_coords: return "DW_AT_src_coords"; + case DW_AT_body_begin: return "DW_AT_body_begin"; + case DW_AT_body_end: return "DW_AT_body_end"; + case DW_AT_GNU_vector: return "DW_AT_GNU_vector"; + /* UPC extension. */ + case DW_AT_upc_threads_scaled: return "DW_AT_upc_threads_scaled"; + default: + { + static char buffer[100]; + + sprintf (buffer, _("Unknown AT value: %lx"), attribute); + return buffer; + } + } +} - case DW_OP_reg0: - case DW_OP_reg1: - case DW_OP_reg2: - case DW_OP_reg3: - case DW_OP_reg4: - case DW_OP_reg5: - case DW_OP_reg6: - case DW_OP_reg7: - case DW_OP_reg8: - case DW_OP_reg9: - case DW_OP_reg10: - case DW_OP_reg11: - case DW_OP_reg12: - case DW_OP_reg13: - case DW_OP_reg14: - case DW_OP_reg15: - case DW_OP_reg16: - case DW_OP_reg17: - case DW_OP_reg18: - case DW_OP_reg19: - case DW_OP_reg20: - case DW_OP_reg21: - case DW_OP_reg22: - case DW_OP_reg23: - case DW_OP_reg24: - case DW_OP_reg25: - case DW_OP_reg26: - case DW_OP_reg27: - case DW_OP_reg28: - case DW_OP_reg29: - case DW_OP_reg30: - case DW_OP_reg31: - printf ("DW_OP_reg%d", op - DW_OP_reg0); - break; +static unsigned char * +read_and_display_attr (unsigned long attribute, + unsigned long form, + unsigned char *data, + unsigned long cu_offset, + unsigned long pointer_size, + unsigned long offset_size, + int dwarf_version, + debug_info *debug_info_p, + int do_loc) +{ + if (!do_loc) + printf (" %-18s:", get_AT_name (attribute)); + data = read_and_display_attr_value (attribute, form, data, cu_offset, + pointer_size, offset_size, + dwarf_version, debug_info_p, + do_loc); + if (!do_loc) + printf ("\n"); + return data; +} - case DW_OP_breg0: - case DW_OP_breg1: - case DW_OP_breg2: - case DW_OP_breg3: - case DW_OP_breg4: - case DW_OP_breg5: - case DW_OP_breg6: - case DW_OP_breg7: - case DW_OP_breg8: - case DW_OP_breg9: - case DW_OP_breg10: - case DW_OP_breg11: - case DW_OP_breg12: - case DW_OP_breg13: - case DW_OP_breg14: - case DW_OP_breg15: - case DW_OP_breg16: - case DW_OP_breg17: - case DW_OP_breg18: - case DW_OP_breg19: - case DW_OP_breg20: - case DW_OP_breg21: - case DW_OP_breg22: - case DW_OP_breg23: - case DW_OP_breg24: - case DW_OP_breg25: - case DW_OP_breg26: - case DW_OP_breg27: - case DW_OP_breg28: - case DW_OP_breg29: - case DW_OP_breg30: - case DW_OP_breg31: - printf ("DW_OP_breg%d: %ld", op - DW_OP_breg0, - read_leb128 (data, &bytes_read, 1)); - data += bytes_read; - break; - case DW_OP_regx: - printf ("DW_OP_regx: %lu", read_leb128 (data, &bytes_read, 0)); - data += bytes_read; - break; - case DW_OP_fbreg: - printf ("DW_OP_fbreg: %ld", read_leb128 (data, &bytes_read, 1)); - data += bytes_read; - break; - case DW_OP_bregx: - uvalue = read_leb128 (data, &bytes_read, 0); - data += bytes_read; - printf ("DW_OP_bregx: %lu %ld", uvalue, - read_leb128 (data, &bytes_read, 1)); - data += bytes_read; - break; - case DW_OP_piece: - printf ("DW_OP_piece: %lu", read_leb128 (data, &bytes_read, 0)); - data += bytes_read; - break; - case DW_OP_deref_size: - printf ("DW_OP_deref_size: %ld", (long) byte_get (data++, 1)); - break; - case DW_OP_xderef_size: - printf ("DW_OP_xderef_size: %ld", (long) byte_get (data++, 1)); - break; - case DW_OP_nop: - printf ("DW_OP_nop"); - break; +/* Process the contents of a .debug_info section. If do_loc is non-zero + then we are scanning for location lists and we do not want to display + anything to the user. */ - /* DWARF 3 extensions. */ - case DW_OP_push_object_address: - printf ("DW_OP_push_object_address"); - break; - case DW_OP_call2: - printf ("DW_OP_call2: <%lx>", (long) byte_get (data, 2)); - data += 2; - break; - case DW_OP_call4: - printf ("DW_OP_call4: <%lx>", (long) byte_get (data, 4)); - data += 4; - break; - case DW_OP_call_ref: - printf ("DW_OP_call_ref"); - break; +static int +process_debug_info (Elf_Internal_Shdr *section, unsigned char *start, + FILE *file, int do_loc) +{ + unsigned char *end = start + section->sh_size; + unsigned char *section_begin; + unsigned int unit; + unsigned int num_units = 0; - /* GNU extensions. */ - case DW_OP_GNU_push_tls_address: - printf ("DW_OP_GNU_push_tls_address"); - break; + if ((do_loc || do_debug_loc) + && num_debug_info_entries == 0) + { + unsigned long length; - default: - if (op >= DW_OP_lo_user - && op <= DW_OP_hi_user) - printf (_("(User defined location op)")); + /* First scan the section to get the number of comp units. */ + for (section_begin = start, num_units = 0; section_begin < end; + num_units ++) + { + /* Read the first 4 bytes. For a 32-bit DWARF section, this + will be the length. For a 64-bit DWARF section, it'll be + the escape code 0xffffffff followed by an 8 byte length. */ + length = byte_get (section_begin, 4); + + if (length == 0xffffffff) + { + length = byte_get (section_begin + 4, 8); + section_begin += length + 12; + } else - printf (_("(Unknown location op)")); - /* No way to tell where the next op is, so just bail. */ - return; + section_begin += length + 4; } - /* Separate the ops. */ - if (data < end) - printf ("; "); + if (num_units == 0) + { + error (_("No comp units in .debug_info section ?")); + return 0; + } + + /* Then allocate an array to hold the information. */ + debug_information = malloc (num_units * + sizeof (* debug_information)); + if (debug_information == NULL) + { + error (_("Not enough memory for a debug info array of %u entries"), + num_units); + return 0; + } } -} -static const char *debug_loc_contents; -static bfd_vma debug_loc_size; + if (!do_loc) + { + printf (_("The section %s contains:\n\n"), + SECTION_NAME (section)); -static void -load_debug_loc (FILE *file) -{ - Elf_Internal_Shdr *sec; + load_debug_str (file); + load_debug_loc (file); + load_debug_range (file); + } - /* If it is already loaded, do nothing. */ - if (debug_loc_contents != NULL) - return; + for (section_begin = start, unit = 0; start < end; unit++) + { + DWARF2_Internal_CompUnit compunit; + unsigned char *hdrptr; + unsigned char *cu_abbrev_offset_ptr; + unsigned char *tags; + int level; + unsigned long cu_offset; + int offset_size; + int initial_length_size; - /* Locate the .debug_loc section. */ - sec = find_section (".debug_loc"); - if (sec == NULL) - return; + hdrptr = start; - debug_loc_size = sec->sh_size; + compunit.cu_length = byte_get (hdrptr, 4); + hdrptr += 4; - debug_loc_contents = get_data (NULL, file, sec->sh_offset, sec->sh_size, - _("debug_loc section data")); -} + if (compunit.cu_length == 0xffffffff) + { + compunit.cu_length = byte_get (hdrptr, 8); + hdrptr += 8; + offset_size = 8; + initial_length_size = 12; + } + else + { + offset_size = 4; + initial_length_size = 4; + } -static void -free_debug_loc (void) -{ - if (debug_loc_contents == NULL) - return; + compunit.cu_version = byte_get (hdrptr, 2); + hdrptr += 2; + + cu_offset = start - section_begin; + start += compunit.cu_length + initial_length_size; - free ((char *) debug_loc_contents); - debug_loc_contents = NULL; - debug_loc_size = 0; -} + if (elf_header.e_type == ET_REL + && !debug_apply_rela_addends (file, section, offset_size, + section_begin, hdrptr, start)) + return 0; + cu_abbrev_offset_ptr = hdrptr; + compunit.cu_abbrev_offset = byte_get (hdrptr, offset_size); + hdrptr += offset_size; -static int -display_debug_loc (Elf_Internal_Shdr *section, - unsigned char *start, FILE *file) -{ - unsigned char *section_end; - unsigned long bytes; - unsigned char *section_begin = start; - bfd_vma addr; - unsigned int comp_unit = 0; + compunit.cu_pointer_size = byte_get (hdrptr, 1); + hdrptr += 1; + if ((do_loc || do_debug_loc) + && num_debug_info_entries == 0) + { + debug_information [unit].cu_offset = cu_offset; + debug_information [unit].pointer_size + = compunit.cu_pointer_size; + debug_information [unit].loc_offsets = NULL; + debug_information [unit].max_loc_offsets = 0; + debug_information [unit].num_loc_offsets = 0; + } - addr = section->sh_addr; - bytes = section->sh_size; - section_end = start + bytes; + tags = hdrptr; - if (bytes == 0) - { - printf (_("\nThe .debug_loc section is empty.\n")); - return 0; - } + if (!do_loc) + { + printf (_(" Compilation Unit @ %lx:\n"), cu_offset); + printf (_(" Length: %ld\n"), compunit.cu_length); + printf (_(" Version: %d\n"), compunit.cu_version); + printf (_(" Abbrev Offset: %ld\n"), compunit.cu_abbrev_offset); + printf (_(" Pointer Size: %d\n"), compunit.cu_pointer_size); + } - get_debug_info (file); + if (compunit.cu_version != 2 && compunit.cu_version != 3) + { + warn (_("Only version 2 and 3 DWARF debug information is currently supported.\n")); + continue; + } - printf (_("Contents of the .debug_loc section:\n\n")); - printf (_("\n Offset Begin End Expression\n")); + free_abbrevs (); - while (start < section_end) - { - unsigned long begin; - unsigned long end; - unsigned short length; - unsigned long offset; - unsigned int pointer_size; + /* Read in the abbrevs used by this compilation unit. */ + { + Elf_Internal_Shdr *sec; + unsigned char *begin; - offset = start - section_begin; + /* Locate the .debug_abbrev section and process it. */ + sec = find_section (".debug_abbrev"); + if (sec == NULL) + { + warn (_("Unable to locate .debug_abbrev section!\n")); + return 0; + } - /* Get the pointer size from the comp unit associated - with this block of location information. */ - pointer_size = get_pointer_size_of_comp_unit (comp_unit, ".debug_loc"); + begin = get_data (NULL, file, sec->sh_offset, sec->sh_size, + _("debug_abbrev section data")); + if (!begin) + return 0; - comp_unit ++; + process_abbrev_section (begin + compunit.cu_abbrev_offset, + begin + sec->sh_size); + + free (begin); + } - while (1) + level = 0; + while (tags < start) { - begin = byte_get (start, pointer_size); - start += pointer_size; - end = byte_get (start, pointer_size); - start += pointer_size; + int bytes_read; + unsigned long abbrev_number; + abbrev_entry *entry; + abbrev_attr *attr; - if (begin == 0 && end == 0) - break; + abbrev_number = read_leb128 (tags, & bytes_read, 0); + tags += bytes_read; - /* For now, skip any base address specifiers. */ - if (begin == 0xffffffff) - continue; + /* A null DIE marks the end of a list of children. */ + if (abbrev_number == 0) + { + --level; + continue; + } - begin += addr; - end += addr; + /* Scan through the abbreviation list until we reach the + correct entry. */ + for (entry = first_abbrev; + entry && entry->entry != abbrev_number; + entry = entry->next) + continue; - length = byte_get (start, 2); - start += 2; + if (entry == NULL) + { + warn (_("Unable to locate entry %lu in the abbreviation table\n"), + abbrev_number); + return 0; + } - printf (" %8.8lx %8.8lx %8.8lx (", offset, begin, end); - decode_location_expression (start, pointer_size, length); - printf (")\n"); + if (!do_loc) + printf (_(" <%d><%lx>: Abbrev Number: %lu (%s)\n"), + level, + (unsigned long) (tags - section_begin + - bytes_read), + abbrev_number, + get_TAG_name (entry->tag)); + + for (attr = entry->first_attr; attr; attr = attr->next) + tags = read_and_display_attr (attr->attribute, + attr->form, + tags, cu_offset, + compunit.cu_pointer_size, + offset_size, + compunit.cu_version, + &debug_information [unit], + do_loc); + + if (entry->children) + ++level; + } + } + + /* Set num_debug_info_entries here so that it can be used + to check if we need to proecess .debug_loc section. */ + if ((do_loc || do_debug_loc) + && num_debug_info_entries == 0) + num_debug_info_entries = num_units; + + if (!do_loc) + { + free_debug_range (); + free_debug_str (); + free_debug_loc (); - start += length; - } printf ("\n"); } + return 1; } -static const char *debug_str_contents; -static bfd_vma debug_str_size; +/* Retrieve the pointer size associated with the given compilation unit. + Optionally the offset of this unit into the .debug_info section is + also retutned. If there is no .debug_info section then an error + message is issued and 0 is returned. If the requested comp unit has + not been defined in the .debug_info section then a warning message + is issued and the last know pointer size is returned. This message + is only issued once per section dumped per file dumped. */ -static void -load_debug_str (FILE *file) +static unsigned int +get_pointer_size_and_offset_of_comp_unit (unsigned int comp_unit, + const char * section_name, + unsigned long * offset_return) { - Elf_Internal_Shdr *sec; + unsigned long offset = 0; - /* If it is already loaded, do nothing. */ - if (debug_str_contents != NULL) - return; + if (num_debug_info_entries == 0) + error (_("%s section needs a populated .debug_info section\n"), + section_name); - /* Locate the .debug_str section. */ - sec = find_section (".debug_str"); - if (sec == NULL) - return; + else if (comp_unit >= num_debug_info_entries) + { + if (!warned_about_missing_comp_units) + { + warn (_("%s section has more comp units than .debug_info section\n"), + section_name); + warn (_("assuming that the pointer size is %d, from the last comp unit in .debug_info\n\n"), + last_pointer_size); + warned_about_missing_comp_units = TRUE; + } + } + else + { + last_pointer_size = debug_information [comp_unit].pointer_size; + offset = debug_information [comp_unit].cu_offset; + } - debug_str_size = sec->sh_size; + if (offset_return != NULL) + * offset_return = offset; - debug_str_contents = get_data (NULL, file, sec->sh_offset, sec->sh_size, - _("debug_str section data")); + return last_pointer_size; } -static void -free_debug_str (void) +/* Locate and scan the .debug_info section in the file and record the pointer + sizes and offsets for the compilation units in it. Usually an executable + will have just one pointer size, but this is not guaranteed, and so we try + not to make any assumptions. Returns zero upon failure, or the number of + compilation units upon success. */ + +static unsigned int +get_debug_info (FILE * file) { - if (debug_str_contents == NULL) - return; + Elf_Internal_Shdr * section; + unsigned char * start; + int ret; - free ((char *) debug_str_contents); - debug_str_contents = NULL; - debug_str_size = 0; -} + /* Reset the last pointer size so that we can issue correct error + messages if we are displaying the contents of more than one section. */ + last_pointer_size = 0; + warned_about_missing_comp_units = FALSE; -static const char * -fetch_indirect_string (unsigned long offset) -{ - if (debug_str_contents == NULL) - return _(""); + /* If we already have the information there is nothing else to do. */ + if (num_debug_info_entries > 0) + return num_debug_info_entries; - if (offset > debug_str_size) - return _(""); + section = find_section (".debug_info"); + if (section == NULL) + return 0; - return debug_str_contents + offset; + start = get_data (NULL, file, section->sh_offset, section->sh_size, + _("extracting information from .debug_info section")); + if (start == NULL) + return 0; + + ret = process_debug_info (section, start, file, 1); + free (start); + + return ret ? num_debug_info_entries : 0; } static int -display_debug_str (Elf_Internal_Shdr *section, - unsigned char *start, - FILE *file ATTRIBUTE_UNUSED) +display_debug_lines (Elf_Internal_Shdr *section, + unsigned char *start, FILE *file) { - unsigned long bytes; - bfd_vma addr; + unsigned char *data = start; + unsigned char *end = start + section->sh_size; + unsigned int comp_unit = 0; - addr = section->sh_addr; - bytes = section->sh_size; + printf (_("\nDump of debug contents of section %s:\n\n"), + SECTION_NAME (section)); - if (bytes == 0) + get_debug_info (file); + + while (data < end) { - printf (_("\nThe .debug_str section is empty.\n")); - return 0; - } + DWARF2_Internal_LineInfo info; + unsigned char *standard_opcodes; + unsigned char *end_of_sequence; + unsigned char *hdrptr; + unsigned int pointer_size; + int initial_length_size; + int offset_size; + int i; + + hdrptr = data; + + /* Check the length of the 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->sh_size) + { + warn + (_("The line info appears to be corrupt - the section is too small\n")); + return 0; + } + + /* Check its 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++; - printf (_("Contents of the .debug_str section:\n\n")); + /* Sign extend the line base field. */ + info.li_line_base <<= 24; + info.li_line_base >>= 24; - while (bytes) - { - int j; - int k; - int lbytes; + /* Get the pointer size from the comp unit associated + with this block of line number information. */ + pointer_size = get_pointer_size_and_offset_of_comp_unit + (comp_unit, ".debug_lines", NULL); + comp_unit ++; - lbytes = (bytes > 16 ? 16 : bytes); + printf (_(" Length: %ld\n"), info.li_length); + printf (_(" DWARF Version: %d\n"), info.li_version); + printf (_(" Prologue Length: %d\n"), info.li_prologue_length); + printf (_(" Minimum Instruction Length: %d\n"), info.li_min_insn_length); + printf (_(" Initial value of 'is_stmt': %d\n"), info.li_default_is_stmt); + printf (_(" Line Base: %d\n"), info.li_line_base); + printf (_(" Line Range: %d\n"), info.li_line_range); + printf (_(" Opcode Base: %d\n"), info.li_opcode_base); + printf (_(" (Pointer size: %u)\n"), pointer_size); - printf (" 0x%8.8lx ", (unsigned long) addr); + end_of_sequence = data + info.li_length + initial_length_size; - for (j = 0; j < 16; j++) - { - if (j < lbytes) - printf ("%2.2x", start[j]); - else - printf (" "); + reset_state_machine (info.li_default_is_stmt); - if ((j & 3) == 3) - printf (" "); - } + /* Display the contents of the Opcodes table. */ + standard_opcodes = hdrptr; - for (j = 0; j < lbytes; j++) - { - k = start[j]; - if (k >= ' ' && k < 0x80) - printf ("%c", k); - else - printf ("."); - } + printf (_("\n Opcodes:\n")); - putchar ('\n'); + for (i = 1; i < info.li_opcode_base; i++) + printf (_(" Opcode %d has %d args\n"), i, standard_opcodes[i - 1]); - start += lbytes; - addr += lbytes; - bytes -= lbytes; - } + /* Display the contents of the Directory table. */ + data = standard_opcodes + info.li_opcode_base - 1; - return 1; -} + if (*data == 0) + printf (_("\n The Directory Table is empty.\n")); + else + { + printf (_("\n The Directory Table:\n")); -static const char * debug_range_contents; -static unsigned long debug_range_size; + while (*data != 0) + { + printf (_(" %s\n"), data); -static void -load_debug_range (FILE *file) -{ - Elf_Internal_Shdr *sec; + data += strlen ((char *) data) + 1; + } + } - /* If it is already loaded, do nothing. */ - if (debug_range_contents != NULL) - return; + /* Skip the NUL at the end of the table. */ + data++; - /* Locate the .debug_str section. */ - sec = find_section (".debug_ranges"); - if (sec == NULL) - return; + /* Display the contents of the File Name table. */ + if (*data == 0) + printf (_("\n The File Name Table is empty.\n")); + else + { + printf (_("\n The File Name Table:\n")); + printf (_(" Entry\tDir\tTime\tSize\tName\n")); - debug_range_size = sec->sh_size; + while (*data != 0) + { + unsigned char *name; + int bytes_read; - debug_range_contents = get_data (NULL, file, sec->sh_offset, sec->sh_size, - _("debug_range section data")); -} + printf (_(" %d\t"), ++state_machine_regs.last_file_entry); + name = data; -static void -free_debug_range (void) -{ - if (debug_range_contents == NULL) - return; + data += strlen ((char *) data) + 1; - free ((char *) debug_range_contents); - debug_range_contents = NULL; - debug_range_size = 0; -} + printf (_("%lu\t"), read_leb128 (data, & bytes_read, 0)); + data += bytes_read; + printf (_("%lu\t"), read_leb128 (data, & bytes_read, 0)); + data += bytes_read; + printf (_("%lu\t"), read_leb128 (data, & bytes_read, 0)); + data += bytes_read; + printf (_("%s\n"), name); + } + } + /* Skip the NUL at the end of the table. */ + data++; -/* Decode a DW_AT_ranges attribute for 64bit DWARF3 . */ + /* Now display the statements. */ + printf (_("\n Line Number Statements:\n")); -static void -decode_64bit_range (unsigned long offset, bfd_vma base_address) -{ - const char * start = debug_range_contents + offset; - const char * end = debug_range_contents + debug_range_size; + while (data < end_of_sequence) + { + unsigned char op_code; + int adv; + int bytes_read; - do - { - bfd_vma a; - bfd_vma b; + op_code = *data++; - a = byte_get ((unsigned char *) start, 8); - b = byte_get ((unsigned char *) start + 8, 8); + if (op_code >= info.li_opcode_base) + { + op_code -= info.li_opcode_base; + adv = (op_code / info.li_line_range) * info.li_min_insn_length; + state_machine_regs.address += adv; + printf (_(" Special opcode %d: advance Address by %d to 0x%lx"), + op_code, adv, state_machine_regs.address); + adv = (op_code % info.li_line_range) + info.li_line_base; + state_machine_regs.line += adv; + printf (_(" and Line by %d to %d\n"), + adv, state_machine_regs.line); + } + else switch (op_code) + { + case DW_LNS_extended_op: + data += process_extended_line_op (data, info.li_default_is_stmt, + pointer_size); + break; - if (a == 0xffffffff) - { - base_address = b; - } - else if (a == 0 && b == 0) - break; - else if (a > b) - printf (_(" [corrupt: start > end]")); - else - { - printf (" "); - print_vma (base_address + a, PREFIX_HEX); - printf (" - "); - print_vma (base_address + b, PREFIX_HEX); - printf (", "); - } + case DW_LNS_copy: + printf (_(" Copy\n")); + break; - start += 16; - } - while (start < end); -} + case DW_LNS_advance_pc: + adv = info.li_min_insn_length * read_leb128 (data, & bytes_read, 0); + data += bytes_read; + state_machine_regs.address += adv; + printf (_(" Advance PC by %d to %lx\n"), adv, + state_machine_regs.address); + break; -/* Decode a DW_AT_ranges attribute. */ + case DW_LNS_advance_line: + adv = read_leb128 (data, & bytes_read, 1); + data += bytes_read; + state_machine_regs.line += adv; + printf (_(" Advance Line by %d to %d\n"), adv, + state_machine_regs.line); + break; -static void -decode_range (unsigned long offset, bfd_vma base_address) -{ - const char * start; - const char * end; + case DW_LNS_set_file: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + printf (_(" Set File Name to entry %d in the File Name Table\n"), + adv); + state_machine_regs.file = adv; + break; - if (offset >= (debug_range_size - 8)) - { - printf (_("[corrupt: offset is outside the .debug_ranges section]")); - return; - } + case DW_LNS_set_column: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + printf (_(" Set column to %d\n"), adv); + state_machine_regs.column = adv; + break; - /* Since all entries in the .debug_ranges section are pairs of either - 4-byte integers (32-bit DWARF3) or 8-byte integers (64-bit DWARF3) - the offset should always be a multiple of 8 bytes. */ - if (offset % 8) - { - printf (_("[corrupt: offset is not a multiple of 8]")); - return; - } + case DW_LNS_negate_stmt: + adv = state_machine_regs.is_stmt; + adv = ! adv; + printf (_(" Set is_stmt to %d\n"), adv); + state_machine_regs.is_stmt = adv; + break; - start = debug_range_contents + offset; + case DW_LNS_set_basic_block: + printf (_(" Set basic block\n")); + state_machine_regs.basic_block = 1; + break; - if (offset > 0 - /* Be paranoid - check to see if the previous - two words were and end-of-range marker. */ - && (byte_get ((unsigned char *) start - 4, 4) != 0 - || byte_get ((unsigned char *) start - 8, 4) != 0)) - { - printf (_("[corrupt: offset is not at the start of a range]")); - return; - } + case DW_LNS_const_add_pc: + adv = (((255 - info.li_opcode_base) / info.li_line_range) + * info.li_min_insn_length); + state_machine_regs.address += adv; + printf (_(" Advance PC by constant %d to 0x%lx\n"), adv, + state_machine_regs.address); + break; - end = debug_range_contents + debug_range_size; + case DW_LNS_fixed_advance_pc: + adv = byte_get (data, 2); + data += 2; + state_machine_regs.address += adv; + printf (_(" Advance PC by fixed size amount %d to 0x%lx\n"), + adv, state_machine_regs.address); + break; - printf ("("); - do - { - unsigned long a; - unsigned long b; + case DW_LNS_set_prologue_end: + printf (_(" Set prologue_end to true\n")); + break; - a = byte_get ((unsigned char *) start, 4); - b = byte_get ((unsigned char *) start + 4, 4); + case DW_LNS_set_epilogue_begin: + printf (_(" Set epilogue_begin to true\n")); + break; - if (a == 0xffffffff) - { - if (b == 0xffffffff) - { - decode_64bit_range (offset, base_address); - return; - } + case DW_LNS_set_isa: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + printf (_(" Set ISA to %d\n"), adv); + break; - base_address = b; - } - else if (a == 0 && b == 0) - break; - else if (a > b) - printf (_("[corrupt: start > end]")); - else - { - if (start > debug_range_contents + offset) - printf (", "); + default: + printf (_(" Unknown opcode %d with operands: "), op_code); - printf (_("0x%lx - 0x%lx"), - (unsigned long) base_address + a, - (unsigned long) base_address + b); + 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; + } } - - start += 8; + putchar ('\n'); } - while (start < end); - printf (")"); -} + return 1; +} -static unsigned char * -read_and_display_attr_value (unsigned long attribute, - unsigned long form, - unsigned char *data, - unsigned long cu_offset, - unsigned long pointer_size, - unsigned long offset_size, - int dwarf_version) +static int +display_debug_pubnames (Elf_Internal_Shdr *section, + unsigned char *start, + FILE *file ATTRIBUTE_UNUSED) { - static unsigned long saved_DW_AT_low_pc = 0; - unsigned long uvalue = 0; - unsigned char *block_start = NULL; - int bytes_read; + DWARF2_Internal_PubNames pubnames; + unsigned char *end; - switch (form) + end = start + section->sh_size; + + printf (_("Contents of the %s section:\n\n"), SECTION_NAME (section)); + + while (start < end) { - default: - break; + unsigned char *data; + unsigned long offset; + int offset_size, initial_length_size; - case DW_FORM_ref_addr: - if (dwarf_version == 2) - { - uvalue = byte_get (data, pointer_size); - data += pointer_size; - } - else if (dwarf_version == 3) + data = start; + + pubnames.pn_length = byte_get (data, 4); + data += 4; + if (pubnames.pn_length == 0xffffffff) { - uvalue = byte_get (data, offset_size); - data += offset_size; + pubnames.pn_length = byte_get (data, 8); + data += 8; + offset_size = 8; + initial_length_size = 12; } else { - error (_("Internal error: DWARF version is not 2 or 3.\n")); + offset_size = 4; + initial_length_size = 4; } - break; - - case DW_FORM_addr: - uvalue = byte_get (data, pointer_size); - data += pointer_size; - break; - - case DW_FORM_strp: - uvalue = byte_get (data, offset_size); - data += offset_size; - break; - - case DW_FORM_ref1: - case DW_FORM_flag: - case DW_FORM_data1: - uvalue = byte_get (data++, 1); - break; - case DW_FORM_ref2: - case DW_FORM_data2: - uvalue = byte_get (data, 2); + pubnames.pn_version = byte_get (data, 2); data += 2; - break; - - case DW_FORM_ref4: - case DW_FORM_data4: - uvalue = byte_get (data, 4); - data += 4; - break; - - case DW_FORM_sdata: - uvalue = read_leb128 (data, & bytes_read, 1); - data += bytes_read; - break; - - case DW_FORM_ref_udata: - case DW_FORM_udata: - uvalue = read_leb128 (data, & bytes_read, 0); - data += bytes_read; - break; - - case DW_FORM_indirect: - form = read_leb128 (data, & bytes_read, 0); - data += bytes_read; - printf (" %s", get_FORM_name (form)); - return read_and_display_attr_value (attribute, form, data, cu_offset, - pointer_size, offset_size, - dwarf_version); - } - - switch (form) - { - case DW_FORM_ref_addr: - printf (" <#%lx>", uvalue); - break; - - case DW_FORM_ref1: - case DW_FORM_ref2: - case DW_FORM_ref4: - case DW_FORM_ref_udata: - printf (" <%lx>", uvalue + cu_offset); - break; - - case DW_FORM_addr: - printf (" %#lx", uvalue); - break; - - case DW_FORM_flag: - case DW_FORM_data1: - case DW_FORM_data2: - case DW_FORM_data4: - case DW_FORM_sdata: - case DW_FORM_udata: - printf (" %ld", uvalue); - break; - - case DW_FORM_ref8: - case DW_FORM_data8: - uvalue = byte_get (data, 4); - printf (" %lx", uvalue); - printf (" %lx", (unsigned long) byte_get (data + 4, 4)); - data += 8; - break; + pubnames.pn_offset = byte_get (data, offset_size); + data += offset_size; + pubnames.pn_size = byte_get (data, offset_size); + data += offset_size; - case DW_FORM_string: - printf (" %s", data); - data += strlen ((char *) data) + 1; - break; + start += pubnames.pn_length + initial_length_size; - case DW_FORM_block: - uvalue = read_leb128 (data, & bytes_read, 0); - block_start = data + bytes_read; - data = display_block (block_start, uvalue); - break; + if (pubnames.pn_version != 2 && pubnames.pn_version != 3) + { + static int warned = 0; - case DW_FORM_block1: - uvalue = byte_get (data, 1); - block_start = data + 1; - data = display_block (block_start, uvalue); - break; + if (! warned) + { + warn (_("Only DWARF 2 and 3 pubnames are currently supported\n")); + warned = 1; + } - case DW_FORM_block2: - uvalue = byte_get (data, 2); - block_start = data + 2; - data = display_block (block_start, uvalue); - break; + continue; + } - case DW_FORM_block4: - uvalue = byte_get (data, 4); - block_start = data + 4; - data = display_block (block_start, uvalue); - break; + printf (_(" Length: %ld\n"), + pubnames.pn_length); + printf (_(" Version: %d\n"), + pubnames.pn_version); + printf (_(" Offset into .debug_info section: %ld\n"), + pubnames.pn_offset); + printf (_(" Size of area in .debug_info section: %ld\n"), + pubnames.pn_size); - case DW_FORM_strp: - printf (_(" (indirect string, offset: 0x%lx): %s"), - uvalue, fetch_indirect_string (uvalue)); - break; + printf (_("\n Offset\tName\n")); - case DW_FORM_indirect: - /* Handled above. */ - break; + do + { + offset = byte_get (data, offset_size); - default: - warn (_("Unrecognized form: %d\n"), form); - break; + if (offset != 0) + { + data += offset_size; + printf (" %-6ld\t\t%s\n", offset, data); + data += strlen ((char *) data) + 1; + } + } + while (offset != 0); } - /* For some attributes we can display further information. */ + printf ("\n"); + return 1; +} + +static int +display_debug_macinfo (Elf_Internal_Shdr *section, + unsigned char *start, + FILE *file ATTRIBUTE_UNUSED) +{ + unsigned char *end = start + section->sh_size; + unsigned char *curr = start; + unsigned int bytes_read; + enum dwarf_macinfo_record_type op; - printf ("\t"); + printf (_("Contents of the %s section:\n\n"), SECTION_NAME (section)); - switch (attribute) + while (curr < end) { - case DW_AT_inline: - switch (uvalue) + unsigned int lineno; + const char *string; + + op = *curr; + curr++; + + switch (op) { - case DW_INL_not_inlined: - printf (_("(not inlined)")); - break; - case DW_INL_inlined: - printf (_("(inlined)")); - break; - case DW_INL_declared_not_inlined: - printf (_("(declared as inline but ignored)")); - break; - case DW_INL_declared_inlined: - printf (_("(declared as inline and inlined)")); + case DW_MACINFO_start_file: + { + unsigned int filenum; + + lineno = read_leb128 (curr, & bytes_read, 0); + curr += bytes_read; + filenum = read_leb128 (curr, & bytes_read, 0); + curr += bytes_read; + + printf (_(" DW_MACINFO_start_file - lineno: %d filenum: %d\n"), + lineno, filenum); + } break; - default: - printf (_(" (Unknown inline attribute value: %lx)"), uvalue); + + case DW_MACINFO_end_file: + printf (_(" DW_MACINFO_end_file\n")); break; - } - break; - case DW_AT_language: - switch (uvalue) - { - case DW_LANG_C: printf ("(non-ANSI C)"); break; - case DW_LANG_C89: printf ("(ANSI C)"); break; - case DW_LANG_C_plus_plus: printf ("(C++)"); break; - case DW_LANG_Fortran77: printf ("(FORTRAN 77)"); break; - case DW_LANG_Fortran90: printf ("(Fortran 90)"); break; - case DW_LANG_Modula2: printf ("(Modula 2)"); break; - case DW_LANG_Pascal83: printf ("(ANSI Pascal)"); break; - case DW_LANG_Ada83: printf ("(Ada)"); break; - case DW_LANG_Cobol74: printf ("(Cobol 74)"); break; - case DW_LANG_Cobol85: printf ("(Cobol 85)"); break; - /* DWARF 2.1 values. */ - case DW_LANG_C99: printf ("(ANSI C99)"); break; - case DW_LANG_Ada95: printf ("(ADA 95)"); break; - case DW_LANG_Fortran95: printf ("(Fortran 95)"); break; - /* MIPS extension. */ - case DW_LANG_Mips_Assembler: printf ("(MIPS assembler)"); break; - /* UPC extension. */ - case DW_LANG_Upc: printf ("(Unified Parallel C)"); break; - default: - printf ("(Unknown: %lx)", uvalue); + case DW_MACINFO_define: + lineno = read_leb128 (curr, & bytes_read, 0); + curr += bytes_read; + string = curr; + curr += strlen (string) + 1; + printf (_(" DW_MACINFO_define - lineno : %d macro : %s\n"), + lineno, string); break; - } - break; - case DW_AT_encoding: - switch (uvalue) - { - case DW_ATE_void: printf ("(void)"); break; - case DW_ATE_address: printf ("(machine address)"); break; - case DW_ATE_boolean: printf ("(boolean)"); break; - case DW_ATE_complex_float: printf ("(complex float)"); break; - case DW_ATE_float: printf ("(float)"); break; - case DW_ATE_signed: printf ("(signed)"); break; - case DW_ATE_signed_char: printf ("(signed char)"); break; - case DW_ATE_unsigned: printf ("(unsigned)"); break; - case DW_ATE_unsigned_char: printf ("(unsigned char)"); break; - /* DWARF 2.1 value. */ - case DW_ATE_imaginary_float: printf ("(imaginary float)"); break; - default: - if (uvalue >= DW_ATE_lo_user - && uvalue <= DW_ATE_hi_user) - printf ("(user defined type)"); - else - printf ("(unknown type)"); + case DW_MACINFO_undef: + lineno = read_leb128 (curr, & bytes_read, 0); + curr += bytes_read; + string = curr; + curr += strlen (string) + 1; + printf (_(" DW_MACINFO_undef - lineno : %d macro : %s\n"), + lineno, string); break; - } - break; - case DW_AT_accessibility: - switch (uvalue) - { - case DW_ACCESS_public: printf ("(public)"); break; - case DW_ACCESS_protected: printf ("(protected)"); break; - case DW_ACCESS_private: printf ("(private)"); break; - default: - printf ("(unknown accessibility)"); + case DW_MACINFO_vendor_ext: + { + unsigned int constant; + + constant = read_leb128 (curr, & bytes_read, 0); + curr += bytes_read; + string = curr; + curr += strlen (string) + 1; + printf (_(" DW_MACINFO_vendor_ext - constant : %d string : %s\n"), + constant, string); + } break; } - break; + } - case DW_AT_visibility: - switch (uvalue) - { - case DW_VIS_local: printf ("(local)"); break; - case DW_VIS_exported: printf ("(exported)"); break; - case DW_VIS_qualified: printf ("(qualified)"); break; - default: printf ("(unknown visibility)"); break; - } - break; + return 1; +} - case DW_AT_virtuality: - switch (uvalue) - { - case DW_VIRTUALITY_none: printf ("(none)"); break; - case DW_VIRTUALITY_virtual: printf ("(virtual)"); break; - case DW_VIRTUALITY_pure_virtual:printf ("(pure_virtual)"); break; - default: printf ("(unknown virtuality)"); break; - } - break; - case DW_AT_identifier_case: - switch (uvalue) - { - case DW_ID_case_sensitive: printf ("(case_sensitive)"); break; - case DW_ID_up_case: printf ("(up_case)"); break; - case DW_ID_down_case: printf ("(down_case)"); break; - case DW_ID_case_insensitive: printf ("(case_insensitive)"); break; - default: printf ("(unknown case)"); break; - } - break; +static int +display_debug_abbrev (Elf_Internal_Shdr *section, + unsigned char *start, + FILE *file ATTRIBUTE_UNUSED) +{ + abbrev_entry *entry; + unsigned char *end = start + section->sh_size; - case DW_AT_calling_convention: - switch (uvalue) - { - case DW_CC_normal: printf ("(normal)"); break; - case DW_CC_program: printf ("(program)"); break; - case DW_CC_nocall: printf ("(nocall)"); break; - default: - if (uvalue >= DW_CC_lo_user - && uvalue <= DW_CC_hi_user) - printf ("(user defined)"); - else - printf ("(unknown convention)"); - } - break; + printf (_("Contents of the %s section:\n\n"), SECTION_NAME (section)); - case DW_AT_ordering: - switch (uvalue) - { - case -1: printf ("(undefined)"); break; - case 0: printf ("(row major)"); break; - case 1: printf ("(column major)"); break; - } - break; + do + { + start = process_abbrev_section (start, end); - case DW_AT_frame_base: - case DW_AT_location: - case DW_AT_data_member_location: - case DW_AT_vtable_elem_location: - case DW_AT_allocated: - case DW_AT_associated: - case DW_AT_data_location: - case DW_AT_stride: - case DW_AT_upper_bound: - case DW_AT_lower_bound: - if (block_start) - { - printf ("("); - decode_location_expression (block_start, pointer_size, uvalue); - printf (")"); - } - else if (form == DW_FORM_data4 || form == DW_FORM_data8) - printf (_("(location list)")); + if (first_abbrev == NULL) + continue; - break; + printf (_(" Number TAG\n")); - case DW_AT_low_pc: - /* This is a hack. We keep track of the DW_AT_low_pc attributes - and use them when decoding DW_AT_ranges attributes. The - assumption here is that we are decoding the attributes in order - and so the correct base address for the range is the low_pc. */ - saved_DW_AT_low_pc = uvalue; - break; + for (entry = first_abbrev; entry; entry = entry->next) + { + abbrev_attr *attr; - case DW_AT_ranges: - decode_range (uvalue, saved_DW_AT_low_pc); - break; + printf (_(" %ld %s [%s]\n"), + entry->entry, + get_TAG_name (entry->tag), + entry->children ? _("has children") : _("no children")); - default: - break; - } + for (attr = entry->first_attr; attr; attr = attr->next) + printf (_(" %-18s %s\n"), + get_AT_name (attr->attribute), + get_FORM_name (attr->form)); + } - return data; -} + free_abbrevs (); + } + while (start); -static unsigned char * -read_and_display_attr (unsigned long attribute, - unsigned long form, - unsigned char *data, - unsigned long cu_offset, - unsigned long pointer_size, - unsigned long offset_size, - int dwarf_version) -{ - printf (" %-18s:", get_AT_name (attribute)); - data = read_and_display_attr_value (attribute, form, data, cu_offset, - pointer_size, offset_size, dwarf_version); printf ("\n"); - return data; -} -/* Apply addends of RELA relocations. */ + return 1; +} static int -debug_apply_rela_addends (FILE *file, - Elf_Internal_Shdr *section, - int reloc_size, - unsigned char *sec_data, - unsigned char *start, - unsigned char *end) +display_debug_loc (Elf_Internal_Shdr *section, + unsigned char *start, FILE *file) { - Elf_Internal_Shdr *relsec; + unsigned char *section_end; + unsigned long bytes; + unsigned char *section_begin = start; + bfd_vma addr; + unsigned int num_loc_list = 0; + unsigned long last_offset = 0; + unsigned int first = 0; + unsigned int i; + unsigned int j; + int seen_first_offset = 0; + int use_debug_info = 1; + unsigned char *next; - if (end - start < reloc_size) - return 1; + addr = section->sh_addr; + bytes = section->sh_size; + section_end = start + bytes; - for (relsec = section_headers; - relsec < section_headers + elf_header.e_shnum; - ++relsec) + if (bytes == 0) { - unsigned long nrelas; - Elf_Internal_Rela *rela, *rp; - Elf_Internal_Shdr *symsec; - Elf_Internal_Sym *symtab; - Elf_Internal_Sym *sym; + printf (_("\nThe .debug_loc section is empty.\n")); + return 0; + } - if (relsec->sh_type != SHT_RELA - || SECTION_HEADER (relsec->sh_info) != section - || relsec->sh_size == 0) - continue; + get_debug_info (file); - if (!slurp_rela_relocs (file, relsec->sh_offset, relsec->sh_size, - &rela, &nrelas)) - return 0; + /* Check the order of location list in .debug_info section. If + offsets of location lists are in the ascending order, we can + use `debug_information' directly. */ + for (i = 0; i < num_debug_info_entries; i++) + { + unsigned int num; - symsec = SECTION_HEADER (relsec->sh_link); - symtab = GET_ELF_SYMBOLS (file, symsec); + num = debug_information [i].num_loc_offsets; + num_loc_list += num; - for (rp = rela; rp < rela + nrelas; ++rp) + /* Check if we can use `debug_information' directly. */ + if (use_debug_info && num != 0) { - unsigned char *loc; - - if (rp->r_offset >= (bfd_vma) (start - sec_data) - && rp->r_offset < (bfd_vma) (end - sec_data) - reloc_size) - loc = sec_data + rp->r_offset; - else - continue; - - if (is_32bit_elf) + if (!seen_first_offset) { - sym = symtab + ELF32_R_SYM (rp->r_info); - - if (ELF32_R_SYM (rp->r_info) != 0 - && ELF32_ST_TYPE (sym->st_info) != STT_SECTION - /* Relocations against object symbols can happen, - eg when referencing a global array. For an - example of this see the _clz.o binary in libgcc.a. */ - && ELF32_ST_TYPE (sym->st_info) != STT_OBJECT) - { - warn (_("%s: skipping unexpected symbol type %s in relocation in section .rela%s\n"), - get_symbol_type (ELF32_ST_TYPE (sym->st_info)), - SECTION_NAME (section)); - continue; - } + /* This is the first location list. */ + last_offset = debug_information [i].loc_offsets [0]; + first = i; + seen_first_offset = 1; + j = 1; } else - { - sym = symtab + ELF64_R_SYM (rp->r_info); + j = 0; - if (ELF64_R_SYM (rp->r_info) != 0 - && ELF64_ST_TYPE (sym->st_info) != STT_SECTION - && ELF64_ST_TYPE (sym->st_info) != STT_OBJECT) + for (; j < num; j++) + { + if (last_offset > + debug_information [i].loc_offsets [j]) { - warn (_("skipping unexpected symbol type %s in relocation in section .rela.%s\n"), - get_symbol_type (ELF64_ST_TYPE (sym->st_info)), - SECTION_NAME (section)); - continue; + use_debug_info = 0; + break; } + last_offset = debug_information [i].loc_offsets [j]; } - - byte_put (loc, rp->r_addend, reloc_size); } - - free (symtab); - free (rela); - break; } - return 1; -} -static int -display_debug_info (Elf_Internal_Shdr *section, - unsigned char *start, - FILE *file) -{ - unsigned char *end = start + section->sh_size; - unsigned char *section_begin = start; + if (!use_debug_info) + /* FIXME: Should we handle this case? */ + error (_("Location lists in .debug_info section aren't in ascending order!\n")); - printf (_("The section %s contains:\n\n"), SECTION_NAME (section)); + if (!seen_first_offset) + error (_("No location lists in .debug_info section!\n")); - load_debug_str (file); - load_debug_loc (file); - load_debug_range (file); + if (debug_information [first].loc_offsets [0] != 0) + warn (_("Location lists in .debug_loc section start at 0x%lx\n"), + debug_information [first].loc_offsets [0]); - while (start < end) + printf (_("Contents of the .debug_loc section:\n\n")); + printf (_(" Offset Begin End Expression\n")); + + seen_first_offset = 0; + for (i = first; i < num_debug_info_entries; i++) { - DWARF2_Internal_CompUnit compunit; - unsigned char *hdrptr; - unsigned char *cu_abbrev_offset_ptr; - unsigned char *tags; - int level; + unsigned long begin; + unsigned long end; + unsigned short length; + unsigned long offset; + unsigned int pointer_size; unsigned long cu_offset; - int offset_size; - int initial_length_size; - - hdrptr = start; - compunit.cu_length = byte_get (hdrptr, 4); - hdrptr += 4; + pointer_size = debug_information [i].pointer_size; + cu_offset = debug_information [i].cu_offset; - if (compunit.cu_length == 0xffffffff) + for (j = 0; j < debug_information [i].num_loc_offsets; j++) { - compunit.cu_length = byte_get (hdrptr, 8); - hdrptr += 8; - offset_size = 8; - initial_length_size = 12; - } - else - { - offset_size = 4; - initial_length_size = 4; - } + offset = debug_information [i].loc_offsets [j]; + next = section_begin + offset; - compunit.cu_version = byte_get (hdrptr, 2); - hdrptr += 2; + if (!seen_first_offset) + seen_first_offset = 1; + else + { + if (start < next) + warn (_("There is a hole [0x%lx - 0x%lx] in .debug_loc section.\n"), + start, next); + else if (start > next) + warn (_("There is an overlap [0x%lx - 0x%lx] in .debug_loc section.\n"), + start, next); + } + start = next; - cu_offset = start - section_begin; - start += compunit.cu_length + initial_length_size; + while (1) + { + begin = byte_get (start, pointer_size); + start += pointer_size; + end = byte_get (start, pointer_size); + start += pointer_size; - if (elf_header.e_type == ET_REL - && !debug_apply_rela_addends (file, section, offset_size, - section_begin, hdrptr, start)) - return 0; + if (begin == 0 && end == 0) + break; - cu_abbrev_offset_ptr = hdrptr; - compunit.cu_abbrev_offset = byte_get (hdrptr, offset_size); - hdrptr += offset_size; + /* For now, skip any base address specifiers. */ + if (begin == 0xffffffff) + continue; - compunit.cu_pointer_size = byte_get (hdrptr, 1); - hdrptr += 1; + begin += addr; + end += addr; - tags = hdrptr; + length = byte_get (start, 2); + start += 2; - printf (_(" Compilation Unit @ %lx:\n"), cu_offset); - printf (_(" Length: %ld\n"), compunit.cu_length); - printf (_(" Version: %d\n"), compunit.cu_version); - printf (_(" Abbrev Offset: %ld\n"), compunit.cu_abbrev_offset); - printf (_(" Pointer Size: %d\n"), compunit.cu_pointer_size); + printf (" %8.8lx %8.8lx %8.8lx (", + offset, begin, end); + decode_location_expression (start, pointer_size, length, + cu_offset); + printf (")\n"); - if (compunit.cu_version != 2 && compunit.cu_version != 3) - { - warn (_("Only version 2 and 3 DWARF debug information is currently supported.\n")); - continue; - } + if (begin >= end) + warn ("Bad location list at %8.8lx from %8.8lx to %8.8lx\n", + offset, begin, end); - free_abbrevs (); + start += length; + } - /* Read in the abbrevs used by this compilation unit. */ - { - Elf_Internal_Shdr *sec; - unsigned char *begin; + if (j < debug_information [i].num_loc_offsets -1) + printf (_(" ")); + printf ("\n"); + } + } + return 1; +} - /* Locate the .debug_abbrev section and process it. */ - sec = find_section (".debug_abbrev"); - if (sec == NULL) - { - warn (_("Unable to locate .debug_abbrev section!\n")); - return 0; - } +static int +display_debug_str (Elf_Internal_Shdr *section, + unsigned char *start, + FILE *file ATTRIBUTE_UNUSED) +{ + unsigned long bytes; + bfd_vma addr; - begin = get_data (NULL, file, sec->sh_offset, sec->sh_size, - _("debug_abbrev section data")); - if (!begin) - return 0; + addr = section->sh_addr; + bytes = section->sh_size; - process_abbrev_section (begin + compunit.cu_abbrev_offset, - begin + sec->sh_size); + if (bytes == 0) + { + printf (_("\nThe .debug_str section is empty.\n")); + return 0; + } - free (begin); - } + printf (_("Contents of the .debug_str section:\n\n")); - level = 0; - while (tags < start) - { - int bytes_read; - unsigned long abbrev_number; - abbrev_entry *entry; - abbrev_attr *attr; + while (bytes) + { + int j; + int k; + int lbytes; - abbrev_number = read_leb128 (tags, & bytes_read, 0); - tags += bytes_read; + lbytes = (bytes > 16 ? 16 : bytes); - /* A null DIE marks the end of a list of children. */ - if (abbrev_number == 0) - { - --level; - continue; - } + printf (" 0x%8.8lx ", (unsigned long) addr); - /* Scan through the abbreviation list until we reach the - correct entry. */ - for (entry = first_abbrev; - entry && entry->entry != abbrev_number; - entry = entry->next) - continue; + for (j = 0; j < 16; j++) + { + if (j < lbytes) + printf ("%2.2x", start[j]); + else + printf (" "); - if (entry == NULL) - { - warn (_("Unable to locate entry %lu in the abbreviation table\n"), - abbrev_number); - return 0; - } + if ((j & 3) == 3) + printf (" "); + } - printf (_(" <%d><%lx>: Abbrev Number: %lu (%s)\n"), - level, - (unsigned long) (tags - section_begin - bytes_read), - abbrev_number, - get_TAG_name (entry->tag)); + for (j = 0; j < lbytes; j++) + { + k = start[j]; + if (k >= ' ' && k < 0x80) + printf ("%c", k); + else + printf ("."); + } - for (attr = entry->first_attr; attr; attr = attr->next) - tags = read_and_display_attr (attr->attribute, - attr->form, - tags, cu_offset, - compunit.cu_pointer_size, - offset_size, - compunit.cu_version); + putchar ('\n'); - if (entry->children) - ++level; - } + start += lbytes; + addr += lbytes; + bytes -= lbytes; } - free_debug_range (); - free_debug_str (); - free_debug_loc (); + return 1; +} - printf ("\n"); - return 1; +static int +display_debug_info (Elf_Internal_Shdr * section, + unsigned char * start, FILE * file) +{ + return process_debug_info (section, start, file, 0); } + static int display_debug_aranges (Elf_Internal_Shdr *section, unsigned char *start, @@ -9984,6 +10171,7 @@ display_debug_frames (Elf_Internal_Shdr *section, if (! do_debug_frames_interp && augmentation_data_len) { unsigned long i; + printf (" Augmentation data: "); for (i = 0; i < augmentation_data_len; ++i) printf (" %02x", augmentation_data[i]); @@ -10314,7 +10502,7 @@ display_debug_frames (Elf_Internal_Shdr *section, if (! do_debug_frames_interp) { printf (" DW_CFA_def_cfa_expression ("); - decode_location_expression (start, addr_size, ul); + decode_location_expression (start, addr_size, ul, 0); printf (")\n"); } fc->cfa_exp = 1; @@ -10327,7 +10515,7 @@ display_debug_frames (Elf_Internal_Shdr *section, if (! do_debug_frames_interp) { printf (" DW_CFA_expression: r%ld (", reg); - decode_location_expression (start, addr_size, ul); + decode_location_expression (start, addr_size, ul, 0); printf (")\n"); } fc->col_type[reg] = DW_CFA_expression; @@ -11559,6 +11747,9 @@ process_object (char *file_name, FILE *file) if (debug_information) { + for (i = 0; i < num_debug_info_entries; i++) + if (debug_information [i].loc_offsets != NULL) + free (debug_information [i].loc_offsets); free (debug_information); debug_information = NULL; num_debug_info_entries = 0; -- 2.7.4