This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
RFC: PR 990: "nm -l" doesn't work well on relocatable files
- From: "H. J. Lu" <hjl at lucon dot org>
- To: binutils at sources dot redhat dot com
- Date: Sun, 5 Jun 2005 11:35:51 -0700
- Subject: RFC: PR 990: "nm -l" doesn't work well on relocatable files
_bfd_dwarf2_find_nearest_line only works on functions in executable. It
doesn't work on symbols nor relocatable files where multiple functons
can have the same address in different sections. This patch introduces
_bfd_dwarf2_find_line for finding location for symbols. It may not
handle all cases, e.g., local symbols with the same name in different
files may have the same address in a relocatable file generated by
"ld -r".
H.J.
----
bfd/
2005-06-05 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 and line.
(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.
binutils/
2005-06-05 H.J. Lu <hongjiu.lu@intel.com>
PR 990
* nm.c (print_symbol): Call bfd_find_line before
bfd_find_nearest_line.
--- binutils/bfd/bfd.c.line 2005-06-05 11:12:28.000000000 -0700
+++ binutils/bfd/bfd.c 2005-06-05 11:12:29.000000000 -0700
@@ -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))
--- binutils/bfd/dwarf2.c.line 2005-06-05 11:12:29.000000000 -0700
+++ binutils/bfd/dwarf2.c 2005-06-05 11:33:31.000000000 -0700
@@ -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,12 +700,31 @@ 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 */
+ /* Source location file name */
+ char *file;
+ /* Source location line number */
+ int line;
int tag;
int nesting_level;
char *name;
struct arange arange;
};
+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;
+ unsigned int global: 1;
+ /* Is this a stack variable. */
+ unsigned int stack: 1;
+};
+
/* Adds a new entry to the line_info list in the line_info_table, ensuring
that the list is sorted. Note that the line_info list is sorted from
highest to lowest VMA (with possible duplicates); that is,
@@ -1424,6 +1446,78 @@ lookup_address_in_function_table (struct
}
}
+/* 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);
+
+ for (each_func = unit->function_table;
+ each_func;
+ each_func = each_func->prev_func)
+ {
+ for (arange = &each_func->arange;
+ arange;
+ arange = arange->next)
+ {
+ if (addr >= arange->low
+ && addr < arange->high
+ && strcmp (name, each_func->name) == 0)
+ {
+ if (!best_fit ||
+ ((arange->high - arange->low) < (best_fit->arange.high - best_fit->arange.low)))
+ best_fit = each_func;
+ }
+ }
+ }
+
+ if (best_fit)
+ {
+ *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);
+ struct varinfo* each;
+
+ for (each = unit->variable_table; each; each = each->prev_var)
+ if (each->stack == 0 && strcmp (name, each->name) == 0)
+ break;
+
+ if (each)
+ {
+ *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 +1607,10 @@ read_rangelist (struct comp_unit *unit,
/* 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 +1622,7 @@ scan_unit_for_functions (struct comp_uni
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 +1644,9 @@ scan_unit_for_functions (struct comp_uni
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 +1657,18 @@ scan_unit_for_functions (struct comp_uni
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 +1712,63 @@ scan_unit_for_functions (struct comp_uni
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->global = 1;
+ var->stack = 0;
+ }
+ break;
+
+ case DW_AT_location:
+ if (!var->global && 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 +2009,7 @@ comp_unit_find_nearest_line (struct comp
}
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 +2027,58 @@ comp_unit_find_nearest_line (struct comp
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 +2324,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,
--- binutils/bfd/elf-bfd.h.line 2005-06-05 11:12:28.000000000 -0700
+++ binutils/bfd/elf-bfd.h 2005-06-05 11:12:29.000000000 -0700
@@ -1500,6 +1500,9 @@ extern bfd_boolean _bfd_elf_set_arch_mac
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
--- binutils/bfd/elf.c.line 2005-06-05 11:12:28.000000000 -0700
+++ binutils/bfd/elf.c 2005-06-05 11:12:29.000000000 -0700
@@ -6675,6 +6675,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
--- binutils/bfd/libbfd-in.h.line 2005-05-24 07:13:20.000000000 -0700
+++ binutils/bfd/libbfd-in.h 2005-06-05 11:12:29.000000000 -0700
@@ -428,6 +428,14 @@ extern bfd_boolean _bfd_dwarf2_find_near
(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 **);
--- binutils/bfd/libbfd.c.line 2005-05-05 09:07:02.000000000 -0700
+++ binutils/bfd/libbfd.c 2005-06-05 11:12:29.000000000 -0700
@@ -918,3 +918,13 @@ read_signed_leb128 (bfd *abfd ATTRIBUTE_
*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;
+}
--- binutils/bfd/targets.c.line 2005-05-24 07:13:21.000000000 -0700
+++ binutils/bfd/targets.c 2005-06-05 11:12:29.000000000 -0700
@@ -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
--- binutils/binutils/nm.c.line 2005-05-09 06:18:24.000000000 -0700
+++ binutils/binutils/nm.c 2005-06-05 11:12:29.000000000 -0700
@@ -907,9 +907,10 @@ print_symbol (bfd *abfd, asymbol *sym, b
}
else if (bfd_get_section (sym)->owner == abfd)
{
- if (bfd_find_nearest_line (abfd, bfd_get_section (sym), syms,
- sym->value, &filename, &functionname,
- &lineno)
+ if ((bfd_find_line (abfd, syms, sym, &filename, &lineno)
+ || bfd_find_nearest_line (abfd, bfd_get_section (sym),
+ syms, sym->value, &filename,
+ &functionname, &lineno))
&& filename != NULL
&& lineno != 0)
printf ("\t%s:%u", filename, lineno);