#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/dwarf2.h"
+#include "arange-set.h"
/* The data in the .debug_line statement prologue looks like this. */
/* A list of all previously read comp_units. */
struct comp_unit *all_comp_units;
+ /* Last comp unit in list above. */
+ struct comp_unit *last_comp_unit;
+
+ /* Number of comp units. */
+ int comp_unit_count;
+
/* The next unread compilation unit within the .debug_info section.
Zero indicates that the .debug_info section has not been loaded
into a buffer yet. */
/* Array of loadable sections. */
struct loadable_section *loadable_sections;
+
+ /* Number of times find_line is called. This is used in
+ the heuristic for enabling the info hash tables. */
+ int info_hash_count;
+
+#define STASH_INFO_HASH_TRIGGER 100
+
+ /* Hash table mapping symbol names to function infos. */
+ struct info_hash_table *funcinfo_hash_table;
+
+ /* Hash table mapping symbol names to variable infos. */
+ struct info_hash_table *varinfo_hash_table;
+
+ /* Head of comp_unit list in the last hash table update. */
+ struct comp_unit *hash_units_head;
+
+ /* Status of info hash. */
+ int info_hash_status;
+#define STASH_INFO_HASH_OFF 0
+#define STASH_INFO_HASH_ON 1
+#define STASH_INFO_HASH_DISABLED 2
+
+ /* Arange-set for fast lookup. The aranges in this set have pointers
+ to compilation units containing them. In the unlikely case that there
+ are multiple compilation units associated with an arange, the arange-set
+ is a NULL pointer and we need to fall back to sequential search. */
+ arange_set comp_unit_arange_set;
+
+ /* Status of global arange set. */
+ int arange_set_status;
+#define STASH_ARANGE_SET_OFF 0
+#define STASH_ARANGE_SET_ON 1
+#define STASH_ARANGE_SET_DISABLED 2
+
+ /* Build a whole binary arange-set for compilation unit look-up
+ if there are at least this many compilation units. */
+#define STASH_ARANGE_SET_TRIGGER 500
};
+/* Simple singly linked list for aranges. We now use a more scalable
+ arange-set for aranges in compilation units. For functions, we still
+ use this since it is more efficient for simple cases. */
+
struct arange
{
struct arange *next;
+ /* The lowest and highest addresses contained a compilation
+ unit as specified in the compilation unit's header. */
bfd_vma low;
bfd_vma high;
};
/* Chain the previously read compilation units. */
struct comp_unit *next_unit;
+ /* Likewise, chain the compilation unit read after this one.
+ The comp units are stored in reversed reading order. */
+ struct comp_unit *prev_unit;
+
/* Keep the bfd convenient (for memory allocation). */
bfd *abfd;
- /* The lowest and highest addresses contained in this compilation
- unit as specified in the compilation unit header. */
- struct arange arange;
+ /* The set of aranges in a compilation unit. */
+ arange_set arange_set;
/* The DW_AT_name attribute (for error messages). */
char *name;
/* Base address for this unit - from DW_AT_low_pc attribute of
DW_TAG_compile_unit DIE */
bfd_vma base_address;
+
+ /* TRUE if symbols are cached in hash table for faster lookup by name. */
+ bfd_boolean cached;
};
/* This data structure holds the information of an abbrev. */
#define ATTR_ALLOC_CHUNK 4
#endif
+/* Variable and function hash tables. This is used to speed up look-up
+ in lookup_symbol_in_var_table() and lookup_symbol_in_function_table().
+ In order to share code between variable and function infos, we use
+ a list of untyped pointer for all variable/function info associated with
+ a symbol. We waste a bit of memory for list with one node but that
+ simplifies the code. */
+
+struct info_list_node
+{
+ struct info_list_node *next;
+ void *info;
+};
+
+/* Info hash entry. */
+struct info_hash_entry
+{
+ struct bfd_hash_entry root;
+ struct info_list_node *head;
+};
+
+struct info_hash_table
+{
+ struct bfd_hash_table base;
+};
+
+/* Function to create a new entry in info hash table. */
+
+static struct bfd_hash_entry *
+info_hash_table_newfunc (struct bfd_hash_entry *entry,
+ struct bfd_hash_table *table,
+ const char *string)
+{
+ struct info_hash_entry *ret = (struct info_hash_entry *) entry;
+
+ /* Allocate the structure if it has not already been allocated by a
+ derived class. */
+ if (ret == NULL)
+ {
+ ret = bfd_hash_allocate (table, sizeof (* ret));
+ if (ret == NULL)
+ return NULL;
+ }
+
+ /* Call the allocation method of the base class. */
+ ret = ((struct info_hash_entry *)
+ bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
+
+ /* Initialize the local fields here. */
+ if (ret)
+ ret->head = NULL;
+
+ return (struct bfd_hash_entry *) ret;
+}
+
+/* Function to create a new info hash table. It returns a pointer to the
+ newly created table or NULL if there is any error. We need abfd
+ solely for memory allocation. */
+
+static struct info_hash_table *
+create_info_hash_table (bfd *abfd)
+{
+ struct info_hash_table *hash_table;
+
+ hash_table = bfd_alloc (abfd, sizeof (struct info_hash_table));
+ if (!hash_table)
+ return hash_table;
+
+ if (!bfd_hash_table_init (&hash_table->base, info_hash_table_newfunc,
+ sizeof (struct info_hash_entry)))
+ {
+ bfd_release (abfd, hash_table);
+ return NULL;
+ }
+
+ return hash_table;
+}
+
+/* Insert an info entry into an info hash table. We do not check of
+ duplicate entries. Also, the caller need to guarantee that the
+ right type of info in inserted as info is passed as a void* pointer.
+ This function returns true if there is no error. */
+
+static bfd_boolean
+insert_info_hash_table (struct info_hash_table *hash_table,
+ const char *key,
+ void *info,
+ bfd_boolean copy_p)
+{
+ struct info_hash_entry *entry;
+ struct info_list_node *node;
+
+ entry = (struct info_hash_entry*) bfd_hash_lookup (&hash_table->base,
+ key, TRUE, copy_p);
+ if (!entry)
+ return FALSE;
+
+ node = bfd_hash_allocate (&hash_table->base, sizeof (*node));
+ if (!node)
+ return FALSE;
+
+ node->info = info;
+ node->next = entry->head;
+ entry->head = node;
+
+ return TRUE;
+}
+
+/* Look up an info entry list from an info hash table. Return NULL
+ if there is none. */
+
+static struct info_list_node *
+lookup_info_hash_table (struct info_hash_table *hash_table, const char *key)
+{
+ struct info_hash_entry *entry;
+
+ entry = (struct info_hash_entry*) bfd_hash_lookup (&hash_table->base, key,
+ FALSE, FALSE);
+ return entry ? entry->head : NULL;
+}
+
/* VERBATIM
The following function up to the END VERBATIM mark are
copied directly from dwarf2read.c. */
char *comp_dir;
char **dirs;
struct fileinfo* files;
- struct line_info* last_line; /* largest VMA */
- struct line_info* lcl_head; /* local head; used in 'add_line_info' */
+ struct line_info* last_line; /* Largest VMA. */
+ struct line_info* lcl_head; /* Local head; used in 'add_line_info'. */
};
/* Remember some information about each function. If the function is
struct funcinfo
{
- struct funcinfo *prev_func; /* Pointer to previous function in list of all functions */
- 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 */
+ struct funcinfo *prev_func; /* Pointer to previous function in list of all functions. */
+ 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;
char *name;
struct arange arange;
- asection *sec; /* Where the symbol is defined */
+ asection *sec; /* Where the symbol is defined. */
};
struct varinfo
{
- /* Pointer to previous variable in list of all variables */
+ /* Pointer to previous variable in list of all variables. */
struct varinfo *prev_var;
- /* Source location file name */
+ /* Source location file name. */
char *file;
- /* Source location line number */
+ /* Source location line number. */
int line;
int tag;
char *name;
bfd_vma addr;
- /* Where the symbol is defined */
+ /* Where the symbol is defined. */
asection *sec;
- /* Is this a stack variable? */
+ /* Is this a stack variable? */
unsigned int stack: 1;
};
+/* Arange-sets:
+
+ To handle extremely large binaries, we want to use a more efficient data
+ structure than a singly-linked list to represent aranges. So instead we
+ use an arange-set, which supports efficient insertions and queries. We
+ use a simple arange-set with no values attached to represent the aranges
+ in a compilation unit and we also use a global arange-set to store all
+ the aranges in all the compilation units. The global arange-set stores
+ values which are pointers to the compilation units.
+
+ Normally aranges in the global set do not overlap, but this can happen.
+ To simplify things and to prevent excessive memory usage, an arange in
+ the global set can only point to at most one compilation unit. In case
+ of an overlap, the pointer is set to NULL, meaning that there are more
+ than one compilation units containing that arange. Code that looks up
+ the global set should fall back to searching all compilation units if
+ that happens. */
+
+/* Allocate memory for an arange set. */
+
+static void *
+dwarf2_arange_set_allocate (int size, void *data)
+{
+ return bfd_alloc ((bfd *) data, size);
+}
+
+/* Deallocate memory of an arange set. */
+
+static void
+dwarf2_arange_set_deallocate (void *object ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED)
+{
+ /* Do nothing. Let BFD clean up when it's done. */
+}
+
+/* Combine two comp unit pointers. If they are the same,
+ return either one, otherwise return NULL. */
+
+static arange_value_type
+dwarf2_combine_arange_value (arange_value_type value1,
+ arange_value_type value2,
+ void *data ATTRIBUTE_UNUSED)
+{
+ return ((value1 == value2) ? value1 : 0);
+}
+
+/* Create a simple arange set that does not store values. */
+
+static arange_set
+dwarf2_arange_set_new (bfd *abfd)
+{
+ return arange_set_new (dwarf2_arange_set_allocate,
+ dwarf2_arange_set_deallocate,
+ FALSE, NULL, NULL, NULL, NULL, (void *) abfd);
+}
+
+/* Create an arange set that stores pointers to compilation units. */
+
+static arange_set
+dwarf2_arange_set_with_value_new (bfd *abfd)
+{
+ return arange_set_new (dwarf2_arange_set_allocate,
+ dwarf2_arange_set_deallocate,
+ TRUE, NULL, NULL, dwarf2_combine_arange_value,
+ NULL, (void *) abfd);
+}
+
+/* Add an arange to a compilation unit. Add the arange to both the
+ unit's valueless arange set and the global arange set. */
+
+static void
+dwarf2_comp_unit_arange_add (struct comp_unit *unit,
+ bfd_vma low,
+ bfd_vma high)
+{
+ /* Add arange to unit's local arange set. */
+ arange_set_insert (unit->arange_set, low, high - 1, 0);
+
+ if (unit->stash->arange_set_status == STASH_ARANGE_SET_ON)
+ {
+ BFD_ASSERT (unit->stash->comp_unit_arange_set);
+ arange_set_insert (unit->stash->comp_unit_arange_set, low, high - 1,
+ (arange_value_type) unit);
+ }
+}
+
/* Return TRUE if NEW_LINE should sort after LINE. */
static inline bfd_boolean
int end_sequence)
{
bfd_size_type amt = sizeof (struct line_info);
- struct line_info* info = bfd_alloc (table->abfd, amt);
+ struct line_info * info = bfd_alloc (table->abfd, amt);
/* Set member data of 'info'. */
info->address = address;
Note: we may receive duplicate entries from 'decode_line_info'. */
- if (!table->last_line
- || new_line_sorts_after (info, table->last_line))
+ if (table->last_line
+ && table->last_line->address == address
+ && table->last_line->end_sequence == end_sequence)
+ {
+ /* We only keep the last entry with the same address and end
+ sequence. See PR ld/4986. */
+ if (table->lcl_head == table->last_line)
+ table->lcl_head = info;
+ info->prev_line = table->last_line->prev_line;
+ table->last_line = info;
+ }
+ else if (!table->last_line
+ || new_line_sorts_after (info, table->last_line))
{
- /* Normal case: add 'info' to the beginning of the list */
+ /* Normal case: add 'info' to the beginning of the list. */
info->prev_line = table->last_line;
table->last_line = info;
{
/* Abnormal and hard: Neither 'last_line' nor 'lcl_head' are valid
heads for 'info'. Reset 'lcl_head'. */
- struct line_info* li2 = table->last_line; /* always non-NULL */
+ struct line_info* li2 = table->last_line; /* Always non-NULL. */
struct line_info* li1 = li2->prev_line;
while (li1)
&& new_line_sorts_after (info, li1))
break;
- li2 = li1; /* always non-NULL */
+ li2 = li1; /* Always non-NULL. */
li1 = li1->prev_line;
}
table->lcl_head = li2;
}
static void
-arange_add (bfd *abfd, struct arange *first_arange, bfd_vma low_pc, bfd_vma high_pc)
+arange_add (bfd *abfd, struct arange *first_arange, bfd_vma low_pc,
+ bfd_vma high_pc)
{
struct arange *arange;
- /* If the first arange is empty, use it. */
+ /* If the first arange is empty, use it. */
if (first_arange->high == 0)
{
first_arange->low = low_pc;
while (arange);
/* Need to allocate a new arange and insert it into the arange list.
- Order isn't significant, so just insert after the first arange. */
+ Order isn't significant, so just insert after the first arange. */
arange = bfd_zalloc (abfd, sizeof (*arange));
arange->low = low_pc;
arange->high = high_pc;
low_pc = address;
if (address > high_pc)
high_pc = address;
- arange_add (unit->abfd, &unit->arange, low_pc, high_pc);
+ dwarf2_comp_unit_arange_add (unit, low_pc, high_pc);
break;
case DW_LNE_set_address:
address = read_address (unit, line_ptr);
}
}
}
- return (name);
+ return name;
}
+/* Type of callback function used in read_rangelist below. */
+
+typedef void (*read_rangelist_callback_t)(struct comp_unit*, bfd_vma,
+ bfd_vma, void*);
+
+/* Call back to add an arange to the old-style arange list. */
+
static void
-read_rangelist (struct comp_unit *unit, struct arange *arange, bfd_uint64_t offset)
+read_rangelist_insert_arange_list (struct comp_unit *unit,
+ bfd_vma low,
+ bfd_vma high,
+ void *data)
+{
+ arange_add (unit->abfd, (struct arange*) data, low, high);
+}
+
+/* Callback to add an arange in the arange set of a compilation unit. */
+
+static void
+read_rangelist_comp_unit_arange_add (struct comp_unit *unit,
+ bfd_vma low,
+ bfd_vma high,
+ void *data ATTRIBUTE_UNUSED)
+{
+ dwarf2_comp_unit_arange_add (unit, low, high);
+}
+
+/* Read ARANGE list of a compilation unit. For each read arange,
+ call the supplied callback function for further processing. */
+
+static void
+read_rangelist (struct comp_unit *unit,
+ bfd_uint64_t offset,
+ read_rangelist_callback_t callback,
+ void *callback_data)
{
bfd_byte *ranges_ptr;
bfd_vma base_address = unit->base_address;
if (low_pc == -1UL && high_pc != -1UL)
base_address = high_pc;
else
- arange_add (unit->abfd, arange, base_address + low_pc, base_address + high_pc);
+ /* Call callback to process new arange. */
+ (callback) (unit, base_address + low_pc, base_address + high_pc,
+ callback_data);
}
}
func->tag = abbrev->tag;
func->prev_func = unit->function_table;
unit->function_table = func;
+ BFD_ASSERT (!unit->cached);
if (func->tag == DW_TAG_inlined_subroutine)
for (i = nesting_level - 1; i >= 1; i--)
var->stack = 1;
var->prev_var = unit->variable_table;
unit->variable_table = var;
+ BFD_ASSERT (!unit->cached);
}
/* No inline function in scope at this nesting level. */
break;
case DW_AT_ranges:
- read_rangelist (unit, &func->arange, attr.u.val);
+ read_rangelist (unit, attr.u.val,
+ read_rangelist_insert_arange_list,
+ & func->arange);
break;
case DW_AT_decl_file:
unit->end_ptr = end_ptr;
unit->stash = stash;
unit->info_ptr_unit = info_ptr_unit;
+ unit->arange_set = dwarf2_arange_set_new (abfd);
for (i = 0; i < abbrev->num_attrs; ++i)
{
break;
case DW_AT_ranges:
- read_rangelist (unit, &unit->arange, attr.u.val);
+ read_rangelist (unit, attr.u.val,
+ read_rangelist_comp_unit_arange_add, NULL);
break;
case DW_AT_comp_dir:
{
char *comp_dir = attr.u.str;
+
if (comp_dir)
{
/* Irix 6.2 native cc prepends <machine>.: to the compilation
break;
}
}
+
if (high_pc != 0)
- {
- arange_add (unit->abfd, &unit->arange, low_pc, high_pc);
- }
+ dwarf2_comp_unit_arange_add (unit, low_pc, high_pc);
unit->first_child_die_ptr = info_ptr;
return unit;
static bfd_boolean
comp_unit_contains_address (struct comp_unit *unit, bfd_vma addr)
{
- struct arange *arange;
-
- if (unit->error)
- return FALSE;
-
- arange = &unit->arange;
- do
- {
- if (addr >= arange->low && addr < arange->high)
- return TRUE;
- arange = arange->next;
- }
- while (arange);
-
- return FALSE;
+ return arange_set_lookup_address (unit->arange_set, addr, NULL, NULL, NULL);
}
/* If UNIT contains ADDR, set the output parameters to the values for
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;
+/* Check to see if line info is already decoded in a comp_unit.
+ If not, decode it. Returns TRUE if 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)
+comp_unit_maybe_decode_line_info (struct comp_unit *unit,
+ struct dwarf2_debug *stash)
{
if (unit->error)
return FALSE;
}
}
+ return TRUE;
+}
+
+/* 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 (!comp_unit_maybe_decode_line_info (unit, stash))
+ 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, addr,
- filename_ptr,
- linenumber_ptr);
+
+ return lookup_symbol_in_variable_table (unit, sym, addr,
+ filename_ptr,
+ linenumber_ptr);
+}
+
+static struct funcinfo *
+reverse_funcinfo_list (struct funcinfo *head)
+{
+ struct funcinfo *rhead;
+ struct funcinfo *temp;
+
+ for (rhead = NULL; head; head = temp)
+ {
+ temp = head->prev_func;
+ head->prev_func = rhead;
+ rhead = head;
+ }
+ return rhead;
+}
+
+static struct varinfo *
+reverse_varinfo_list (struct varinfo *head)
+{
+ struct varinfo *rhead;
+ struct varinfo *temp;
+
+ for (rhead = NULL; head; head = temp)
+ {
+ temp = head->prev_var;
+ head->prev_var = rhead;
+ rhead = head;
+ }
+ return rhead;
+}
+
+/* Extract all interesting funcinfos and varinfos of a compilation
+ unit into hash tables for faster lookup. Returns TRUE if no
+ errors were enountered; FALSE otherwise. */
+
+static bfd_boolean
+comp_unit_hash_info (struct dwarf2_debug *stash,
+ struct comp_unit *unit,
+ struct info_hash_table *funcinfo_hash_table,
+ struct info_hash_table *varinfo_hash_table)
+{
+ struct funcinfo* each_func;
+ struct varinfo* each_var;
+ bfd_boolean okay = TRUE;
+
+ BFD_ASSERT (stash->info_hash_status != STASH_INFO_HASH_DISABLED);
+
+ if (!comp_unit_maybe_decode_line_info (unit, stash))
+ return FALSE;
+
+ BFD_ASSERT (!unit->cached);
+
+ /* To preserve the original search order, we went to visit the function
+ infos in the reversed order of the list. However, making the list
+ bi-directional use quite a bit of extra memory. So we reverse
+ the list first, traverse the list in the now reversed order and
+ finally reverse the list again to get back the original order. */
+ unit->function_table = reverse_funcinfo_list (unit->function_table);
+ for (each_func = unit->function_table;
+ each_func && okay;
+ each_func = each_func->prev_func)
+ {
+ /* Skip nameless functions. */
+ if (each_func->name)
+ /* There is no need to copy name string into hash table as
+ name string is either in the dwarf string buffer or
+ info in the stash. */
+ okay = insert_info_hash_table (funcinfo_hash_table, each_func->name,
+ (void*) each_func, FALSE);
+ }
+ unit->function_table = reverse_funcinfo_list (unit->function_table);
+ if (!okay)
+ return FALSE;
+
+ /* We do the same for variable infos. */
+ unit->variable_table = reverse_varinfo_list (unit->variable_table);
+ for (each_var = unit->variable_table;
+ each_var && okay;
+ each_var = each_var->prev_var)
+ {
+ /* Skip stack vars and vars with no files or names. */
+ if (each_var->stack == 0
+ && each_var->file != NULL
+ && each_var->name != NULL)
+ /* There is no need to copy name string into hash table as
+ name string is either in the dwarf string buffer or
+ info in the stash. */
+ okay = insert_info_hash_table (varinfo_hash_table, each_var->name,
+ (void*) each_var, FALSE);
+ }
+
+ unit->variable_table = reverse_varinfo_list (unit->variable_table);
+ unit->cached = TRUE;
+ return okay;
}
/* Locate a section in a BFD containing debugging info. The search starts
return TRUE;
}
+/* Look up a funcinfo by name using the given info hash table. If found,
+ also update the locations pointed to by filename_ptr and linenumber_ptr.
+
+ This function returns TRUE if a funcinfo that matches the given symbol
+ and address is found with any error; otherwise it returns FALSE. */
+
+static bfd_boolean
+info_hash_lookup_funcinfo (struct info_hash_table *hash_table,
+ asymbol *sym,
+ bfd_vma addr,
+ const char **filename_ptr,
+ unsigned int *linenumber_ptr)
+{
+ struct funcinfo* each_func;
+ struct funcinfo* best_fit = NULL;
+ struct info_list_node *node;
+ struct arange *arange;
+ const char *name = bfd_asymbol_name (sym);
+ asection *sec = bfd_get_section (sym);
+
+ for (node = lookup_info_hash_table (hash_table, name);
+ node;
+ node = node->next)
+ {
+ each_func = node->info;
+ for (arange = &each_func->arange;
+ arange;
+ arange = arange->next)
+ {
+ if ((!each_func->sec || each_func->sec == sec)
+ && addr >= arange->low
+ && addr < arange->high
+ && (!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;
+ }
+
+ return FALSE;
+}
+
+/* Look up a varinfo by name using the given info hash table. If found,
+ also update the locations pointed to by filename_ptr and linenumber_ptr.
+
+ This function returns TRUE if a varinfo that matches the given symbol
+ and address is found with any error; otherwise it returns FALSE. */
+
+static bfd_boolean
+info_hash_lookup_varinfo (struct info_hash_table *hash_table,
+ asymbol *sym,
+ bfd_vma addr,
+ const char **filename_ptr,
+ unsigned int *linenumber_ptr)
+{
+ const char *name = bfd_asymbol_name (sym);
+ asection *sec = bfd_get_section (sym);
+ struct varinfo* each;
+ struct info_list_node *node;
+
+ for (node = lookup_info_hash_table (hash_table, name);
+ node;
+ node = node->next)
+ {
+ each = node->info;
+ if (each->addr == addr
+ && (!each->sec || each->sec == sec))
+ {
+ each->sec = sec;
+ *filename_ptr = each->file;
+ *linenumber_ptr = each->line;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Update the funcinfo and varinfo info hash tables if they are
+ not up to date. Returns TRUE if there is no error; otherwise
+ returns FALSE and disable the info hash tables. */
+
+static bfd_boolean
+stash_maybe_update_info_hash_tables (struct dwarf2_debug *stash)
+{
+ struct comp_unit *each;
+
+ /* Exit if hash tables are up-to-date. */
+ if (stash->all_comp_units == stash->hash_units_head)
+ return TRUE;
+
+ if (stash->hash_units_head)
+ each = stash->hash_units_head->prev_unit;
+ else
+ each = stash->last_comp_unit;
+
+ while (each)
+ {
+ if (!comp_unit_hash_info (stash, each, stash->funcinfo_hash_table,
+ stash->varinfo_hash_table))
+ {
+ stash->info_hash_status = STASH_INFO_HASH_DISABLED;
+ return FALSE;
+ }
+ each = each->prev_unit;
+ }
+
+ stash->hash_units_head = stash->all_comp_units;
+ return TRUE;
+}
+
+/* Check consistency of info hash tables. This is for debugging only. */
+
+static void ATTRIBUTE_UNUSED
+stash_verify_info_hash_table (struct dwarf2_debug *stash)
+{
+ struct comp_unit *each_unit;
+ struct funcinfo *each_func;
+ struct varinfo *each_var;
+ struct info_list_node *node;
+ bfd_boolean found;
+
+ for (each_unit = stash->all_comp_units;
+ each_unit;
+ each_unit = each_unit->next_unit)
+ {
+ for (each_func = each_unit->function_table;
+ each_func;
+ each_func = each_func->prev_func)
+ {
+ if (!each_func->name)
+ continue;
+ node = lookup_info_hash_table (stash->funcinfo_hash_table,
+ each_func->name);
+ BFD_ASSERT (node);
+ found = FALSE;
+ while (node && !found)
+ {
+ found = node->info == each_func;
+ node = node->next;
+ }
+ BFD_ASSERT (found);
+ }
+
+ for (each_var = each_unit->variable_table;
+ each_var;
+ each_var = each_var->prev_var)
+ {
+ if (!each_var->name || !each_var->file || each_var->stack)
+ continue;
+ node = lookup_info_hash_table (stash->varinfo_hash_table,
+ each_var->name);
+ BFD_ASSERT (node);
+ found = FALSE;
+ while (node && !found)
+ {
+ found = node->info == each_var;
+ node = node->next;
+ }
+ BFD_ASSERT (found);
+ }
+ }
+}
+
+/* Check to see if we want to enable the info hash tables, which consume
+ quite a bit of memory. Currently we only check the number times
+ bfd_dwarf2_find_line is called. In the future, we may also want to
+ take the number of symbols into account. */
+
+static void
+stash_maybe_enable_info_hash_tables (bfd *abfd, struct dwarf2_debug *stash)
+{
+ BFD_ASSERT (stash->info_hash_status == STASH_INFO_HASH_OFF);
+
+ if (stash->info_hash_count++ < STASH_INFO_HASH_TRIGGER)
+ return;
+
+ /* FIXME: Maybe we should check the reduce_memory_overheads
+ and optimize fields in the bfd_link_info structure ? */
+
+ /* Create hash tables. */
+ stash->funcinfo_hash_table = create_info_hash_table (abfd);
+ stash->varinfo_hash_table = create_info_hash_table (abfd);
+ if (!stash->funcinfo_hash_table || !stash->varinfo_hash_table)
+ {
+ /* Turn off info hashes if any allocation above fails. */
+ stash->info_hash_status = STASH_INFO_HASH_DISABLED;
+ return;
+ }
+ /* We need a forced update so that the info hash tables will
+ be created even though there is no compilation unit. That
+ happens if STASH_INFO_HASH_TRIGGER is 0. */
+ stash_maybe_update_info_hash_tables (stash);
+ stash->info_hash_status = STASH_INFO_HASH_ON;
+}
+
+/* Find the file and line associated with a symbol and address using the
+ info hash tables of a stash. If there is a match, the function returns
+ TRUE and update the locations pointed to by filename_ptr and linenumber_ptr;
+ otherwise it returns FALSE. */
+
+static bfd_boolean
+stash_find_line_fast (struct dwarf2_debug *stash,
+ asymbol *sym,
+ bfd_vma addr,
+ const char **filename_ptr,
+ unsigned int *linenumber_ptr)
+{
+ BFD_ASSERT (stash->info_hash_status == STASH_INFO_HASH_ON);
+
+ if (sym->flags & BSF_FUNCTION)
+ return info_hash_lookup_funcinfo (stash->funcinfo_hash_table, sym, addr,
+ filename_ptr, linenumber_ptr);
+ return info_hash_lookup_varinfo (stash->varinfo_hash_table, sym, addr,
+ filename_ptr, linenumber_ptr);
+}
+
+typedef struct
+{
+ struct dwarf2_debug * stash;
+ arange_set set;
+ struct comp_unit * unit;
+} stash_copy_local_aranges_data_t;
+
+static int
+stash_copy_local_aranges (bfd_vma low,
+ bfd_vma high,
+ arange_value_type data ATTRIBUTE_UNUSED,
+ void *info)
+{
+ bfd_boolean status;
+
+ stash_copy_local_aranges_data_t *copy_data = info;
+ status = arange_set_insert (copy_data->set, low, high,
+ (arange_value_type) copy_data->unit);
+
+ return status ? 0 : 1;
+}
+
+static bfd_boolean
+stash_maybe_enable_arange_set (bfd *abfd, struct dwarf2_debug *stash)
+{
+ struct comp_unit *unit;
+ stash_copy_local_aranges_data_t copy_data;
+
+ if (stash->arange_set_status != STASH_ARANGE_SET_OFF)
+ return TRUE;
+
+ if (stash->comp_unit_count < STASH_ARANGE_SET_TRIGGER)
+ return TRUE;
+
+ if (stash->comp_unit_arange_set == NULL)
+ {
+ stash->comp_unit_arange_set =
+ dwarf2_arange_set_with_value_new (abfd);
+ if (!stash->comp_unit_arange_set)
+ {
+ stash->arange_set_status = STASH_ARANGE_SET_DISABLED;
+ return FALSE;
+ }
+ }
+
+ copy_data.stash = stash;
+ copy_data.set = stash->comp_unit_arange_set;
+ for (unit = stash->all_comp_units; unit; unit = unit->next_unit)
+ {
+ copy_data.unit = unit;
+ if (arange_set_foreach (unit->arange_set, stash_copy_local_aranges,
+ & copy_data))
+ {
+ stash->arange_set_status = STASH_ARANGE_SET_DISABLED;
+ return FALSE;
+ }
+ }
+ stash->arange_set_status = STASH_ARANGE_SET_ON;
+ return TRUE;
+}
+
+/* Find the nearest line to a given address and record filename,
+ function name and line number if found. Return TRUE if a line is
+ found or FALSE otherwise. */
+
+static bfd_boolean ATTRIBUTE_UNUSED
+stash_find_nearest_line_fast (struct dwarf2_debug *stash,
+ bfd_vma addr,
+ const char **filename_ptr,
+ const char **functionname_ptr,
+ unsigned int *linenumber_ptr)
+{
+ arange_value_type value;
+ struct comp_unit *unit;
+
+ /* Try looking up global arange set first. */
+ if (stash->arange_set_status == STASH_ARANGE_SET_ON
+ && arange_set_lookup_address (stash->comp_unit_arange_set, addr, NULL,
+ NULL, &value))
+ {
+ if ((unit = (struct comp_unit *) value) != NULL)
+ /* There is only one compilation unit containing this address. */
+ return comp_unit_find_nearest_line (unit, addr, filename_ptr,
+ functionname_ptr, linenumber_ptr,
+ stash);
+ }
+
+ /* The arange set is not available or there are multiple compilation
+ units containing this address. Search all compilation units. */
+ for (unit = stash->all_comp_units; unit; unit = unit->next_unit)
+ {
+ if (comp_unit_contains_address (unit, addr)
+ && comp_unit_find_nearest_line (unit, addr, filename_ptr,
+ functionname_ptr,
+ linenumber_ptr, stash))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/* Find the source code location of SYMBOL. If SYMBOL is NULL
then find the nearest source code location corresponding to
the address SECTION + OFFSET.
else
addr += section->vma;
*filename_ptr = NULL;
- *functionname_ptr = NULL;
+ if (! do_line)
+ *functionname_ptr = NULL;
*linenumber_ptr = 0;
if (! *pinfo)
stash->inliner_chain = NULL;
/* Check the previously read comp. units first. */
- for (each = stash->all_comp_units; each; each = each->next_unit)
+ if (do_line)
{
- if (do_line)
- found = (((symbol->flags & BSF_FUNCTION) == 0
- || comp_unit_contains_address (each, addr))
- && comp_unit_find_line (each, symbol, addr,
- filename_ptr, linenumber_ptr,
- stash));
+ /* The info hash tables use quite a bit of memory. We may not want to
+ always use them. We use some heuristics to decide if and when to
+ turn it on. */
+ if (stash->info_hash_status == STASH_INFO_HASH_OFF)
+ stash_maybe_enable_info_hash_tables (abfd, stash);
+
+ /* Keep info hash table up to date if they are available. Note that we
+ may disable the hash tables if there is any error duing update. */
+ if (stash->info_hash_status == STASH_INFO_HASH_ON)
+ stash_maybe_update_info_hash_tables (stash);
+
+ if (stash->info_hash_status == STASH_INFO_HASH_ON)
+ {
+ found = stash_find_line_fast (stash, symbol, addr, filename_ptr,
+ linenumber_ptr);
+ if (found)
+ goto done;
+ }
else
- found = (comp_unit_contains_address (each, addr)
- && comp_unit_find_nearest_line (each, addr,
- filename_ptr,
- functionname_ptr,
- linenumber_ptr,
- stash));
+ {
+ /* 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)
+ goto done;
+ }
+ }
+ }
+ else
+ {
+ if (stash->arange_set_status == STASH_ARANGE_SET_OFF)
+ stash_maybe_enable_arange_set (abfd, stash);
+
+ found = stash_find_nearest_line_fast (stash, addr, filename_ptr,
+ functionname_ptr, linenumber_ptr);
if (found)
goto done;
}
if (each)
{
+ if (stash->all_comp_units)
+ stash->all_comp_units->prev_unit = each;
+ else
+ stash->last_comp_unit = each;
+
each->next_unit = stash->all_comp_units;
stash->all_comp_units = each;
+ stash->comp_unit_count++;
/* 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. */
+ compilation units. If we don't have them, we need to
+ consult the line info table to see if a compilation unit
+ contains the given address. */
if (do_line)
found = (((symbol->flags & BSF_FUNCTION) == 0
- || each->arange.high == 0
+ || arange_set_empty_p (each->arange_set)
|| comp_unit_contains_address (each, addr))
&& comp_unit_find_line (each, symbol, addr,
filename_ptr,
linenumber_ptr,
stash));
else
- found = ((each->arange.high == 0
+ found = ((arange_set_empty_p (each->arange_set)
|| comp_unit_contains_address (each, addr))
&& comp_unit_find_nearest_line (each, addr,
filename_ptr,