+static void
+unset_sections (struct dwarf2_debug *stash)
+{
+ unsigned int i;
+ struct adjusted_section *p;
+
+ i = stash->adjusted_section_count;
+ p = stash->adjusted_sections;
+ for (; i > 0; i--, p++)
+ p->section->vma = 0;
+}
+
+/* Set unique VMAs for loadable and DWARF sections in ABFD and save
+ VMAs in STASH for unset_sections. */
+
+static bfd_boolean
+place_sections (bfd *abfd, struct dwarf2_debug *stash)
+{
+ struct adjusted_section *p;
+ unsigned int i;
+
+ if (stash->adjusted_section_count != 0)
+ {
+ i = stash->adjusted_section_count;
+ p = stash->adjusted_sections;
+ for (; i > 0; i--, p++)
+ p->section->vma = p->adj_vma;
+ }
+ else
+ {
+ asection *sect;
+ bfd_vma last_vma = 0, last_dwarf = 0;
+ bfd_size_type amt;
+
+ i = 0;
+ for (sect = abfd->sections; sect != NULL; sect = sect->next)
+ {
+ bfd_size_type sz;
+ int is_debug_info;
+
+ if (sect->vma != 0)
+ continue;
+
+ /* We need to adjust the VMAs of any .debug_info sections.
+ Skip compressed ones, since no relocations could target
+ them - they should not appear in object files anyway. */
+ if (strcmp (sect->name, DWARF2_DEBUG_INFO) == 0)
+ is_debug_info = 1;
+ else if (CONST_STRNEQ (sect->name, GNU_LINKONCE_INFO))
+ is_debug_info = 1;
+ else
+ is_debug_info = 0;
+
+ if (!is_debug_info && (sect->flags & SEC_LOAD) == 0)
+ continue;
+
+ sz = sect->rawsize ? sect->rawsize : sect->size;
+ if (sz == 0)
+ continue;
+
+ i++;
+ }
+
+ amt = i * sizeof (struct adjusted_section);
+ p = (struct adjusted_section *) bfd_zalloc (abfd, amt);
+ if (! p)
+ return FALSE;
+
+ stash->adjusted_sections = p;
+ stash->adjusted_section_count = i;
+
+ for (sect = abfd->sections; sect != NULL; sect = sect->next)
+ {
+ bfd_size_type sz;
+ int is_debug_info;
+
+ if (sect->vma != 0)
+ continue;
+
+ /* We need to adjust the VMAs of any .debug_info sections.
+ Skip compressed ones, since no relocations could target
+ them - they should not appear in object files anyway. */
+ if (strcmp (sect->name, DWARF2_DEBUG_INFO) == 0)
+ is_debug_info = 1;
+ else if (CONST_STRNEQ (sect->name, GNU_LINKONCE_INFO))
+ is_debug_info = 1;
+ else
+ is_debug_info = 0;
+
+ if (!is_debug_info && (sect->flags & SEC_LOAD) == 0)
+ continue;
+
+ sz = sect->rawsize ? sect->rawsize : sect->size;
+ if (sz == 0)
+ continue;
+
+ p->section = sect;
+ if (is_debug_info)
+ {
+ BFD_ASSERT (sect->alignment_power == 0);
+ sect->vma = last_dwarf;
+ last_dwarf += sz;
+ }
+ else if (last_vma != 0)
+ {
+ /* Align the new address to the current section
+ alignment. */
+ last_vma = ((last_vma
+ + ~((bfd_vma) -1 << sect->alignment_power))
+ & ((bfd_vma) -1 << sect->alignment_power));
+ sect->vma = last_vma;
+ last_vma += sect->vma + sz;
+ }
+ else
+ last_vma += sect->vma + sz;
+
+ p->adj_vma = sect->vma;
+
+ p++;
+ }
+ }
+
+ 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 = (struct funcinfo *) 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 = (struct varinfo *) 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);
+}
+
+/* Find the source code location of SYMBOL. If SYMBOL is NULL
+ then find the nearest source code location corresponding to
+ the address SECTION + OFFSET.
+ Returns TRUE if the line is found without error and fills in
+ FILENAME_PTR and LINENUMBER_PTR. In the case where SYMBOL was
+ NULL the FUNCTIONNAME_PTR is also filled in.
+ SYMBOLS contains the symbol table for ABFD.
+ ADDR_SIZE is the number of bytes in the initial .debug_info length
+ field and in the abbreviation offset, or zero to indicate that the
+ default value should be used. */
+
+static bfd_boolean
+find_line (bfd *abfd,
+ asection *section,
+ bfd_vma offset,
+ asymbol *symbol,
+ asymbol **symbols,
+ const char **filename_ptr,
+ const char **functionname_ptr,
+ unsigned int *linenumber_ptr,
+ unsigned int addr_size,
+ void **pinfo)