diff options
-rw-r--r-- | bfd/ChangeLog | 34 | ||||
-rw-r--r-- | bfd/bfd-in2.h | 8 | ||||
-rw-r--r-- | bfd/bfd.c | 4 | ||||
-rw-r--r-- | bfd/dwarf2.c | 432 | ||||
-rw-r--r-- | bfd/elf-bfd.h | 3 | ||||
-rw-r--r-- | bfd/elf.c | 11 | ||||
-rw-r--r-- | bfd/libbfd-in.h | 8 | ||||
-rw-r--r-- | bfd/libbfd.c | 10 | ||||
-rw-r--r-- | bfd/libbfd.h | 8 | ||||
-rw-r--r-- | bfd/targets.c | 4 |
10 files changed, 518 insertions, 4 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 2aef89f0b7b..39626c1a108 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,39 @@ 2005-06-06 H.J. Lu <hongjiu.lu@intel.com> + PR 990 + * bfd.c (bfd_find_line): New. + + * dwarf2.c (comp_unit): Add variable_table. + (funcinfo): Add file, line, and sec. + (varinfo): New. + (lookup_symbol_in_function_table): New. + (lookup_symbol_in_variable_table): New. + (scan_unit_for_functions): Renamed to ... + (scan_unit_for_symbols): This. Handle DW_TAG_entry_point and + DW_TAG_variable. + (comp_unit_find_nearest_line): Updated. + (comp_unit_find_line): New. + (_bfd_dwarf2_find_line): New. + + * elf-bfd.h (_bfd_elf_find_line): New. + (_bfd_generic_find_line): New. Defined. + + * elf.c (_bfd_elf_find_line): New. + + * libbfd-in.h (_bfd_dwarf2_find_line): New. + (_bfd_generic_find_line): New. + + * bfd-in2.h: Regenerated. + * libbfd.h: Likewise. + + * libbfd.c (_bfd_generic_find_line): New. + + * targets.c (BFD_JUMP_TABLE_SYMBOLS): Initialize _bfd_find_line + with _bfd_generic_find_line. + (bfd_target): Add _bfd_find_line. + +2005-06-06 H.J. Lu <hongjiu.lu@intel.com> + * dwarf2.c (decode_line_info): Properly set low_pc. 2005-06-06 Alan Modra <amodra@bigpond.net.au> diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index f4b82b8bb22..93d86688cca 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -4307,6 +4307,10 @@ bfd_boolean bfd_set_private_flags (bfd *abfd, flagword flags); BFD_SEND (abfd, _bfd_find_nearest_line, \ (abfd, sec, syms, off, file, func, line)) +#define bfd_find_line(abfd, syms, sym, file, line) \ + BFD_SEND (abfd, _bfd_find_line, \ + (abfd, syms, sym, file, line)) + #define bfd_find_inliner_info(abfd, file, func, line) \ BFD_SEND (abfd, _bfd_find_inliner_info, \ (abfd, file, func, line)) @@ -4647,6 +4651,7 @@ typedef struct bfd_target NAME##_bfd_is_target_special_symbol, \ NAME##_get_lineno, \ NAME##_find_nearest_line, \ + _bfd_generic_find_line, \ NAME##_find_inliner_info, \ NAME##_bfd_make_debug_symbol, \ NAME##_read_minisymbols, \ @@ -4669,6 +4674,9 @@ typedef struct bfd_target bfd_boolean (*_bfd_find_nearest_line) (bfd *, struct bfd_section *, struct bfd_symbol **, bfd_vma, const char **, const char **, unsigned int *); + bfd_boolean (*_bfd_find_line) + (bfd *, struct bfd_symbol **, struct bfd_symbol *, + const char **, unsigned int *); bfd_boolean (*_bfd_find_inliner_info) (bfd *, const char **, const char **, unsigned int *); /* Back-door to allow format-aware applications to create debug symbols diff --git a/bfd/bfd.c b/bfd/bfd.c index f62411b7f27..908d79471f6 100644 --- a/bfd/bfd.c +++ b/bfd/bfd.c @@ -1161,6 +1161,10 @@ DESCRIPTION . BFD_SEND (abfd, _bfd_find_nearest_line, \ . (abfd, sec, syms, off, file, func, line)) . +.#define bfd_find_line(abfd, syms, sym, file, line) \ +. BFD_SEND (abfd, _bfd_find_line, \ +. (abfd, syms, sym, file, line)) +. .#define bfd_find_inliner_info(abfd, file, func, line) \ . BFD_SEND (abfd, _bfd_find_inliner_info, \ . (abfd, file, func, line)) diff --git a/bfd/dwarf2.c b/bfd/dwarf2.c index 1b08f283916..410a51e84b0 100644 --- a/bfd/dwarf2.c +++ b/bfd/dwarf2.c @@ -182,6 +182,9 @@ struct comp_unit /* A list of the functions found in this comp. unit. */ struct funcinfo *function_table; + /* A list of the variables found in this comp. unit. */ + struct varinfo *variable_table; + /* Pointer to dwarf2_debug structure. */ struct dwarf2_debug *stash; @@ -697,10 +700,29 @@ struct funcinfo struct funcinfo *caller_func; /* Pointer to function one scope higher */ char *caller_file; /* Source location file name where caller_func inlines this func */ int caller_line; /* Source location line number where caller_func inlines this func */ + char *file; /* Source location file name */ + int line; /* Source location line number */ int tag; int nesting_level; char *name; struct arange arange; + asection *sec; /* Where the symbol is defined */ +}; + +struct varinfo +{ + /* Pointer to previous variable in list of all variables */ + struct varinfo *prev_var; + /* Source location file name */ + char *file; + /* Source location line number */ + int line; + int tag; + char *name; + /* Where the symbol is defined */ + asection *sec; + /* Is this a stack variable? */ + unsigned int stack: 1; }; /* Adds a new entry to the line_info list in the line_info_table, ensuring @@ -1424,6 +1446,84 @@ lookup_address_in_function_table (struct comp_unit *unit, } } +/* If SYM at ADDR is within function table of UNIT, set FILENAME_PTR + and LINENUMBER_PTR, and return TRUE. */ + +static bfd_boolean +lookup_symbol_in_function_table (struct comp_unit *unit, + asymbol *sym, + bfd_vma addr, + const char **filename_ptr, + unsigned int *linenumber_ptr) +{ + struct funcinfo* each_func; + struct funcinfo* best_fit = NULL; + struct arange *arange; + const char *name = bfd_asymbol_name (sym); + asection *sec = bfd_get_section (sym); + + for (each_func = unit->function_table; + each_func; + each_func = each_func->prev_func) + { + for (arange = &each_func->arange; + arange; + arange = arange->next) + { + if ((!each_func->sec || each_func->sec == sec) + && addr >= arange->low + && addr < arange->high + && strcmp (name, each_func->name) == 0 + && (!best_fit + || ((arange->high - arange->low) + < (best_fit->arange.high - best_fit->arange.low)))) + best_fit = each_func; + } + } + + if (best_fit) + { + best_fit->sec = sec; + *filename_ptr = best_fit->file; + *linenumber_ptr = best_fit->line; + return TRUE; + } + else + return FALSE; +} + +/* Variable table functions. */ + +/* If SYM is within variable table of UNIT, set FILENAME_PTR and + LINENUMBER_PTR, and return TRUE. */ + +static bfd_boolean +lookup_symbol_in_variable_table (struct comp_unit *unit, + asymbol *sym, + const char **filename_ptr, + unsigned int *linenumber_ptr) +{ + const char *name = bfd_asymbol_name (sym); + asection *sec = bfd_get_section (sym); + struct varinfo* each; + + for (each = unit->variable_table; each; each = each->prev_var) + if (each->stack == 0 + && (!each->sec || each->sec == sec) + && strcmp (name, each->name) == 0) + break; + + if (each) + { + each->sec = sec; + *filename_ptr = each->file; + *linenumber_ptr = each->line; + return TRUE; + } + else + return FALSE; +} + static char * find_abstract_instance_name (struct comp_unit *unit, bfd_uint64_t die_ref) { @@ -1513,10 +1613,10 @@ read_rangelist (struct comp_unit *unit, struct arange *arange, bfd_uint64_t offs /* DWARF2 Compilation unit functions. */ /* Scan over each die in a comp. unit looking for functions to add - to the function table. */ + to the function table and variables to the variable table. */ static bfd_boolean -scan_unit_for_functions (struct comp_unit *unit) +scan_unit_for_symbols (struct comp_unit *unit) { bfd *abfd = unit->abfd; bfd_byte *info_ptr = unit->first_child_die_ptr; @@ -1528,6 +1628,7 @@ scan_unit_for_functions (struct comp_unit *unit) struct abbrev_info *abbrev; struct attribute attr; struct funcinfo *func; + struct varinfo *var; bfd_vma low_pc = 0; bfd_vma high_pc = 0; @@ -1549,7 +1650,9 @@ scan_unit_for_functions (struct comp_unit *unit) return FALSE; } + var = NULL; if (abbrev->tag == DW_TAG_subprogram + || abbrev->tag == DW_TAG_entry_point || abbrev->tag == DW_TAG_inlined_subroutine) { bfd_size_type amt = sizeof (struct funcinfo); @@ -1560,7 +1663,18 @@ scan_unit_for_functions (struct comp_unit *unit) unit->function_table = func; } else - func = NULL; + { + func = NULL; + if (abbrev->tag == DW_TAG_variable) + { + bfd_size_type amt = sizeof (struct varinfo); + var = bfd_zalloc (abfd, amt); + var->tag = abbrev->tag; + var->stack = 1; + var->prev_var = unit->variable_table; + unit->variable_table = var; + } + } for (i = 0; i < abbrev->num_attrs; ++i) { @@ -1604,6 +1718,60 @@ scan_unit_for_functions (struct comp_unit *unit) read_rangelist (unit, &func->arange, attr.u.val); break; + case DW_AT_decl_file: + func->file = concat_filename (unit->line_table, + attr.u.val); + break; + + case DW_AT_decl_line: + func->line = attr.u.val; + break; + + default: + break; + } + } + else if (var) + { + switch (attr.name) + { + case DW_AT_name: + var->name = attr.u.str; + break; + + case DW_AT_decl_file: + var->file = concat_filename (unit->line_table, + attr.u.val); + break; + + case DW_AT_decl_line: + var->line = attr.u.val; + break; + + case DW_AT_external: + if (attr.u.val != 0) + var->stack = 0; + break; + + case DW_AT_location: + if (var->stack) + { + switch (attr.form) + { + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + if (*attr.u.blk->data == DW_OP_addr) + var->stack = 0; + break; + + default: + break; + } + } + break; + default: break; } @@ -1844,7 +2012,7 @@ comp_unit_find_nearest_line (struct comp_unit *unit, } if (unit->first_child_die_ptr < unit->end_ptr - && ! scan_unit_for_functions (unit)) + && ! scan_unit_for_symbols (unit)) { unit->error = 1; return FALSE; @@ -1862,6 +2030,58 @@ comp_unit_find_nearest_line (struct comp_unit *unit, return line_p || func_p; } +/* If UNIT contains SYM at ADDR, set the output parameters to the + values for the line containing SYM. The output parameters, + FILENAME_PTR, and LINENUMBER_PTR, are pointers to the objects to be + filled in. + + Return TRUE if UNIT contains SYM, and no errors were encountered; + FALSE otherwise. */ + +static bfd_boolean +comp_unit_find_line (struct comp_unit *unit, + asymbol *sym, + bfd_vma addr, + const char **filename_ptr, + unsigned int *linenumber_ptr, + struct dwarf2_debug *stash) +{ + if (unit->error) + return FALSE; + + if (! unit->line_table) + { + if (! unit->stmtlist) + { + unit->error = 1; + return FALSE; + } + + unit->line_table = decode_line_info (unit, stash); + + if (! unit->line_table) + { + unit->error = 1; + return FALSE; + } + + if (unit->first_child_die_ptr < unit->end_ptr + && ! scan_unit_for_symbols (unit)) + { + unit->error = 1; + return FALSE; + } + } + + if (sym->flags & BSF_FUNCTION) + return lookup_symbol_in_function_table (unit, sym, addr, + filename_ptr, + linenumber_ptr); + else + return lookup_symbol_in_variable_table (unit, sym, filename_ptr, + linenumber_ptr); +} + /* Locate a section in a BFD containing debugging info. The search starts from the section after AFTER_SEC, or from the first section in the BFD if AFTER_SEC is NULL. The search works by examining the names of the @@ -2107,6 +2327,210 @@ _bfd_dwarf2_find_nearest_line (bfd *abfd, return FALSE; } +/* The DWARF2 version of find_line. Return TRUE if the line is found + without error. */ + +bfd_boolean +_bfd_dwarf2_find_line (bfd *abfd, + asymbol **symbols, + asymbol *symbol, + const char **filename_ptr, + unsigned int *linenumber_ptr, + unsigned int addr_size, + void **pinfo) +{ + /* Read each compilation unit from the section .debug_info, and check + to see if it contains the address we are searching for. If yes, + lookup the address, and return the line number info. If no, go + on to the next compilation unit. + + We keep a list of all the previously read compilation units, and + a pointer to the next un-read compilation unit. Check the + previously read units before reading more. */ + struct dwarf2_debug *stash; + + /* What address are we looking for? */ + bfd_vma addr; + + struct comp_unit* each; + + asection *section; + + bfd_boolean found; + + section = bfd_get_section (symbol); + + addr = symbol->value; + if (section->output_section) + addr += section->output_section->vma + section->output_offset; + else + addr += section->vma; + + *filename_ptr = NULL; + stash = *pinfo; + *filename_ptr = NULL; + *linenumber_ptr = 0; + + if (! stash) + { + bfd_size_type total_size; + asection *msec; + bfd_size_type amt = sizeof (struct dwarf2_debug); + + stash = bfd_zalloc (abfd, amt); + if (! stash) + return FALSE; + + *pinfo = stash; + + msec = find_debug_info (abfd, NULL); + if (! msec) + /* No dwarf2 info. Note that at this point the stash + has been allocated, but contains zeros, this lets + future calls to this function fail quicker. */ + return FALSE; + + /* There can be more than one DWARF2 info section in a BFD these days. + Read them all in and produce one large stash. We do this in two + passes - in the first pass we just accumulate the section sizes. + In the second pass we read in the section's contents. The allows + us to avoid reallocing the data as we add sections to the stash. */ + for (total_size = 0; msec; msec = find_debug_info (abfd, msec)) + total_size += msec->size; + + stash->info_ptr = bfd_alloc (abfd, total_size); + if (stash->info_ptr == NULL) + return FALSE; + + stash->info_ptr_end = stash->info_ptr; + + for (msec = find_debug_info (abfd, NULL); + msec; + msec = find_debug_info (abfd, msec)) + { + bfd_size_type size; + bfd_size_type start; + + size = msec->size; + if (size == 0) + continue; + + start = stash->info_ptr_end - stash->info_ptr; + + if ((bfd_simple_get_relocated_section_contents + (abfd, msec, stash->info_ptr + start, symbols)) == NULL) + continue; + + stash->info_ptr_end = stash->info_ptr + start + size; + } + + BFD_ASSERT (stash->info_ptr_end == stash->info_ptr + total_size); + + stash->sec = find_debug_info (abfd, NULL); + stash->sec_info_ptr = stash->info_ptr; + stash->syms = symbols; + } + + /* A null info_ptr indicates that there is no dwarf2 info + (or that an error occured while setting up the stash). */ + if (! stash->info_ptr) + return FALSE; + + stash->inliner_chain = NULL; + + /* Check the previously read comp. units first. */ + for (each = stash->all_comp_units; each; each = each->next_unit) + if ((symbol->flags & BSF_FUNCTION) == 0 + || comp_unit_contains_address (each, addr)) + { + found = comp_unit_find_line (each, symbol, addr, filename_ptr, + linenumber_ptr, stash); + if (found) + return found; + } + + /* The DWARF2 spec says that the initial length field, and the + offset of the abbreviation table, should both be 4-byte values. + However, some compilers do things differently. */ + if (addr_size == 0) + addr_size = 4; + BFD_ASSERT (addr_size == 4 || addr_size == 8); + + /* Read each remaining comp. units checking each as they are read. */ + while (stash->info_ptr < stash->info_ptr_end) + { + bfd_vma length; + unsigned int offset_size = addr_size; + bfd_byte *info_ptr_unit = stash->info_ptr; + + length = read_4_bytes (abfd, stash->info_ptr); + /* A 0xffffff length is the DWARF3 way of indicating we use + 64-bit offsets, instead of 32-bit offsets. */ + if (length == 0xffffffff) + { + offset_size = 8; + length = read_8_bytes (abfd, stash->info_ptr + 4); + stash->info_ptr += 12; + } + /* A zero length is the IRIX way of indicating 64-bit offsets, + mostly because the 64-bit length will generally fit in 32 + bits, and the endianness helps. */ + else if (length == 0) + { + offset_size = 8; + length = read_4_bytes (abfd, stash->info_ptr + 4); + stash->info_ptr += 8; + } + /* In the absence of the hints above, we assume addr_size-sized + offsets, for backward-compatibility with pre-DWARF3 64-bit + platforms. */ + else if (addr_size == 8) + { + length = read_8_bytes (abfd, stash->info_ptr); + stash->info_ptr += 8; + } + else + stash->info_ptr += 4; + + if (length > 0) + { + each = parse_comp_unit (abfd, stash, length, info_ptr_unit, + offset_size); + stash->info_ptr += length; + + if ((bfd_vma) (stash->info_ptr - stash->sec_info_ptr) + == stash->sec->size) + { + stash->sec = find_debug_info (abfd, stash->sec); + stash->sec_info_ptr = stash->info_ptr; + } + + if (each) + { + each->next_unit = stash->all_comp_units; + stash->all_comp_units = each; + + /* DW_AT_low_pc and DW_AT_high_pc are optional for + compilation units. If we don't have them (i.e., + unit->high == 0), we need to consult the line info + table to see if a compilation unit contains the given + address. */ + found = (((symbol->flags & BSF_FUNCTION) == 0 + || each->arange.high <= 0 + || comp_unit_contains_address (each, addr)) + && comp_unit_find_line (each, symbol, addr, + filename_ptr, + linenumber_ptr, + stash)); + if (found) + return TRUE; + } + } + } + + return FALSE; +} + bfd_boolean _bfd_dwarf2_find_inliner_info (bfd *abfd ATTRIBUTE_UNUSED, const char **filename_ptr, diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 53f2845be9e..7c758910f42 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -1488,6 +1488,9 @@ extern bfd_boolean _bfd_elf_set_arch_mach extern bfd_boolean _bfd_elf_find_nearest_line (bfd *, asection *, asymbol **, bfd_vma, const char **, const char **, unsigned int *); +extern bfd_boolean _bfd_elf_find_line + (bfd *, asymbol **, asymbol *, const char **, unsigned int *); +#define _bfd_generic_find_line _bfd_elf_find_line extern bfd_boolean _bfd_elf_find_inliner_info (bfd *, const char **, const char **, unsigned int *); #define _bfd_elf_read_minisymbols _bfd_generic_read_minisymbols diff --git a/bfd/elf.c b/bfd/elf.c index 4ff1d256ce1..189e099fed6 100644 --- a/bfd/elf.c +++ b/bfd/elf.c @@ -6672,6 +6672,17 @@ _bfd_elf_find_nearest_line (bfd *abfd, return TRUE; } +/* Find the line for a symbol. */ + +bfd_boolean +_bfd_elf_find_line (bfd *abfd, asymbol **symbols, asymbol *symbol, + const char **filename_ptr, unsigned int *line_ptr) +{ + return _bfd_dwarf2_find_line (abfd, symbols, symbol, + filename_ptr, line_ptr, 0, + &elf_tdata (abfd)->dwarf2_find_line_info); +} + /* After a call to bfd_find_nearest_line, successive calls to bfd_find_inliner_info can be used to get source information about each level of function inlining that terminated at the address diff --git a/bfd/libbfd-in.h b/bfd/libbfd-in.h index 84236f9d570..2c690a4bcc3 100644 --- a/bfd/libbfd-in.h +++ b/bfd/libbfd-in.h @@ -428,6 +428,14 @@ extern bfd_boolean _bfd_dwarf2_find_nearest_line (bfd *, asection *, asymbol **, bfd_vma, const char **, const char **, unsigned int *, unsigned int, void **); +/* Find the line using DWARF 2 debugging information. */ +extern bfd_boolean _bfd_dwarf2_find_line + (bfd *, asymbol **, asymbol *, const char **, + unsigned int *, unsigned int, void **); + +bfd_boolean _bfd_generic_find_line + (bfd *, asymbol **, asymbol *, const char **, unsigned int *); + /* Find inliner info after calling bfd_find_nearest_line. */ extern bfd_boolean _bfd_dwarf2_find_inliner_info (bfd *, const char **, const char **, unsigned int *, void **); diff --git a/bfd/libbfd.c b/bfd/libbfd.c index c66a570e2fc..92f0089898c 100644 --- a/bfd/libbfd.c +++ b/bfd/libbfd.c @@ -918,3 +918,13 @@ read_signed_leb128 (bfd *abfd ATTRIBUTE_UNUSED, *bytes_read_ptr = num_read; return result; } + +bfd_boolean +_bfd_generic_find_line (bfd *abfd ATTRIBUTE_UNUSED, + asymbol **symbols ATTRIBUTE_UNUSED, + asymbol *symbol ATTRIBUTE_UNUSED, + const char **filename_ptr ATTRIBUTE_UNUSED, + unsigned int *linenumber_ptr ATTRIBUTE_UNUSED) +{ + return FALSE; +} diff --git a/bfd/libbfd.h b/bfd/libbfd.h index f2d51efb9e1..a318744fc8e 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -433,6 +433,14 @@ extern bfd_boolean _bfd_dwarf2_find_nearest_line (bfd *, asection *, asymbol **, bfd_vma, const char **, const char **, unsigned int *, unsigned int, void **); +/* Find the line using DWARF 2 debugging information. */ +extern bfd_boolean _bfd_dwarf2_find_line + (bfd *, asymbol **, asymbol *, const char **, + unsigned int *, unsigned int, void **); + +bfd_boolean _bfd_generic_find_line + (bfd *, asymbol **, asymbol *, const char **, unsigned int *); + /* Find inliner info after calling bfd_find_nearest_line. */ extern bfd_boolean _bfd_dwarf2_find_inliner_info (bfd *, const char **, const char **, unsigned int *, void **); diff --git a/bfd/targets.c b/bfd/targets.c index 50b3eeb5003..3bfde1b02fd 100644 --- a/bfd/targets.c +++ b/bfd/targets.c @@ -349,6 +349,7 @@ BFD_JUMP_TABLE macros. . NAME##_bfd_is_target_special_symbol, \ . NAME##_get_lineno, \ . NAME##_find_nearest_line, \ +. _bfd_generic_find_line, \ . NAME##_find_inliner_info, \ . NAME##_bfd_make_debug_symbol, \ . NAME##_read_minisymbols, \ @@ -371,6 +372,9 @@ BFD_JUMP_TABLE macros. . bfd_boolean (*_bfd_find_nearest_line) . (bfd *, struct bfd_section *, struct bfd_symbol **, bfd_vma, . const char **, const char **, unsigned int *); +. bfd_boolean (*_bfd_find_line) +. (bfd *, struct bfd_symbol **, struct bfd_symbol *, +. const char **, unsigned int *); . bfd_boolean (*_bfd_find_inliner_info) . (bfd *, const char **, const char **, unsigned int *); . {* Back-door to allow format-aware applications to create debug symbols |