2007-07-27 Michael Snyder <msnyder@access-company.com>
[binutils-gdb.git] / bfd / dwarf2.c
index 2ba3152d5215d3874607bfcc5aeafdae04c67c58..37d68f9b9e67dca9cf9801339f28615a47911b7a 100644 (file)
@@ -17,7 +17,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or (at
+   the Free Software Foundation; either version 3 of the License, or (at
    your option) any later version.
 
    This program is distributed in the hope that it will be useful, but
@@ -27,7 +27,8 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
 #include "sysdep.h"
 #include "bfd.h"
@@ -85,6 +86,9 @@ struct dwarf2_debug
   /* 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;
+
   /* 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.  */
@@ -138,6 +142,27 @@ struct dwarf2_debug
 
   /* 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
 };
 
 struct arange
@@ -155,6 +180,10 @@ struct comp_unit
   /* 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;
 
@@ -211,6 +240,9 @@ struct comp_unit
   /* 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.  */
@@ -237,6 +269,126 @@ struct attr_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.  */
@@ -1709,6 +1861,7 @@ scan_unit_for_symbols (struct comp_unit *unit)
          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--)
@@ -1730,6 +1883,7 @@ scan_unit_for_symbols (struct comp_unit *unit)
              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.  */
@@ -2124,21 +2278,13 @@ 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;
+/* 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;
@@ -2167,14 +2313,131 @@ comp_unit_find_line (struct comp_unit *unit,
        }
     }
 
+  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
@@ -2301,264 +2564,253 @@ place_sections (bfd *abfd, struct dwarf2_debug *stash)
   return TRUE;
 }
 
-/* The DWARF2 version of find_nearest_line.  Return TRUE if the line
-   is found without error.  ADDR_SIZE is the number of bytes in the
-   initial .debug_info length field and in the abbreviation offset.
-   You may use zero to indicate that the default value should be
-   used.  */
+/* 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.
 
-bfd_boolean
-_bfd_dwarf2_find_nearest_line (bfd *abfd,
-                              asection *section,
-                              asymbol **symbols,
-                              bfd_vma offset,
-                              const char **filename_ptr,
-                              const char **functionname_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;
-  bfd_vma found = FALSE;
+   This function returns TRUE if a funcinfo that matches the given symbol
+   and address is found with any error; otherwise it returns FALSE.  */
 
-  stash = *pinfo;
+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);
 
-  if (! stash)
+  for (node = lookup_info_hash_table (hash_table, name);
+       node;
+       node = node->next)
     {
-      bfd_size_type amt = sizeof (struct dwarf2_debug);
-
-      stash = bfd_zalloc (abfd, amt);
-      if (! stash)
-       return FALSE;
+      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;
+       }
     }
 
-  /* In a relocatable file, 2 functions may have the same address.
-     We change the section vma so that they won't overlap.  */
-  if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
+  if (best_fit)
     {
-      if (! place_sections (abfd, stash))
-       return FALSE;
+      best_fit->sec = sec;
+      *filename_ptr = best_fit->file;
+      *linenumber_ptr = best_fit->line;
+      return TRUE;
     }
 
-  addr = offset;
-  if (section->output_section)
-    addr += section->output_section->vma + section->output_offset;
-  else
-    addr += section->vma;
-  *filename_ptr = NULL;
-  *functionname_ptr = NULL;
-  *linenumber_ptr = 0;
+  return FALSE;
+}
 
-  /* 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);
+/* 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.
 
-  if (! *pinfo)
-    {
-      bfd *debug_bfd;
-      bfd_size_type total_size;
-      asection *msec;
+   This function returns TRUE if a varinfo that matches the given symbol
+   and address is found with any error; otherwise it returns FALSE.  */
 
-      *pinfo = stash;
+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;
 
-      msec = find_debug_info (abfd, NULL);
-      if (msec == NULL)
+  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))
        {
-         char * debug_filename = bfd_follow_gnu_debuglink (abfd, DEBUGDIR);
-
-         if (debug_filename == NULL)
-           /* No dwarf2 info, and no gnu_debuglink to follow.
-              Note that at this point the stash has been allocated, but
-              contains zeros.  This lets future calls to this function
-              fail more quickly.  */
-           goto done;
-
-         if ((debug_bfd = bfd_openr (debug_filename, NULL)) == NULL
-             || ! bfd_check_format (debug_bfd, bfd_object)
-             || (msec = find_debug_info (debug_bfd, NULL)) == NULL)
-           {
-             if (debug_bfd)
-               bfd_close (debug_bfd);
-             /* FIXME: Should we report our failure to follow the debuglink ?  */
-             free (debug_filename);
-             goto done;
-           }
+         each->sec = sec;
+         *filename_ptr = each->file;
+         *linenumber_ptr = each->line;
+         return TRUE;
        }
-      else
-       debug_bfd = abfd;
-
-      /* 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 (debug_bfd, msec))
-       total_size += msec->size;
-
-      stash->info_ptr = bfd_alloc (debug_bfd, total_size);
-      if (stash->info_ptr == NULL)
-       goto done;
+    }
 
-      stash->info_ptr_end = stash->info_ptr;
+  return FALSE;
+}
 
-      for (msec = find_debug_info (debug_bfd, NULL);
-          msec;
-          msec = find_debug_info (debug_bfd, msec))
-       {
-         bfd_size_type size;
-         bfd_size_type start;
+/* 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.  */
 
-         size = msec->size;
-         if (size == 0)
-           continue;
+static bfd_boolean
+stash_maybe_update_info_hash_tables (struct dwarf2_debug *stash)
+{
+  struct comp_unit *each;
 
-         start = stash->info_ptr_end - stash->info_ptr;
+  /* Exit if hash tables are up-to-date.  */
+  if (stash->all_comp_units == stash->hash_units_head)
+    return TRUE;
 
-         if ((bfd_simple_get_relocated_section_contents
-              (debug_bfd, msec, stash->info_ptr + start, symbols)) == NULL)
-           continue;
+  if (stash->hash_units_head)
+    each = stash->hash_units_head->prev_unit;
+  else
+    each = stash->last_comp_unit;
 
-         stash->info_ptr_end = stash->info_ptr + start + size;
+  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;
        }
-
-      BFD_ASSERT (stash->info_ptr_end == stash->info_ptr + total_size);
-
-      stash->sec = find_debug_info (debug_bfd, NULL);
-      stash->sec_info_ptr = stash->info_ptr;
-      stash->syms = symbols;
-      stash->bfd = debug_bfd;
+      each = each->prev_unit;
     }
 
-  /* 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)
-    goto done;
-
-  stash->inliner_chain = NULL;
+  stash->hash_units_head = stash->all_comp_units;
+  return TRUE;
+}
 
-  /* Check the previously read comp. units first.  */
-  for (each = stash->all_comp_units; each; each = each->next_unit)
-    if (comp_unit_contains_address (each, addr)
-       && comp_unit_find_nearest_line (each, addr, filename_ptr,
-                                       functionname_ptr,
-                                       linenumber_ptr, stash))
-      {
-       found = TRUE;
-       goto done;
-      }
+/* Check consistency of info hash tables.  This is for debugging only. */
 
-  /* Read each remaining comp. units checking each as they are read.  */
-  while (stash->info_ptr < stash->info_ptr_end)
+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)
     {
-      bfd_vma length;
-      unsigned int offset_size = addr_size;
-      bfd_byte *info_ptr_unit = stash->info_ptr;
-
-      length = read_4_bytes (stash->bfd, 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)
+      for (each_func = each_unit->function_table;
+          each_func;
+          each_func = each_func->prev_func)
        {
-         offset_size = 8;
-         length = read_8_bytes (stash->bfd, 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 (stash->bfd, stash->info_ptr + 4);
-         stash->info_ptr += 8;
-       }
-      /* In the absence of the hints above, we assume 32-bit DWARF2
-        offsets even for targets with 64-bit addresses, because:
-          a) most of the time these targets will not have generated
-             more than 2Gb of debug info and so will not need 64-bit
-             offsets,
-        and
-          b) if they do use 64-bit offsets but they are not using
-             the size hints that are tested for above then they are
-             not conforming to the DWARF3 standard anyway.  */
-      else if (addr_size == 8)
-       {
-         offset_size = 4;
-          stash->info_ptr += 4;
+         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);
        }
-      else
-       stash->info_ptr += 4;
 
-      if (length > 0)
+      for (each_var = each_unit->variable_table;
+          each_var;
+          each_var = each_var->prev_var)
        {
-         each = parse_comp_unit (stash, length, info_ptr_unit,
-                                 offset_size);
-         stash->info_ptr += length;
-
-         if ((bfd_vma) (stash->info_ptr - stash->sec_info_ptr)
-             == stash->sec->size)
+         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)
            {
-             stash->sec = find_debug_info (stash->bfd, stash->sec);
-             stash->sec_info_ptr = stash->info_ptr;
+             found = node->info == each_var;
+             node = node->next;
            }
+         BFD_ASSERT (found);
+       }
+    }
+}
 
-         if (each)
-           {
-             each->next_unit = stash->all_comp_units;
-             stash->all_comp_units = each;
+/* 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.  */
 
-             /* 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.  */
-             if ((each->arange.high == 0
-                  || comp_unit_contains_address (each, addr))
-                 && comp_unit_find_nearest_line (each, addr,
-                                                 filename_ptr,
-                                                 functionname_ptr,
-                                                 linenumber_ptr,
-                                                 stash))
-               {
-                 found = TRUE;
-                 goto done;
-               }
-           }
-       }
+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;
+}
 
-done:
-  if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
-    unset_sections (stash);
+/* 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.  */
 
-  return found;
+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);
 }
 
-/* The DWARF2 version of find_line.  Return TRUE if the line is found
-   without error.  */
+/* 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.  */
 
-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)
+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)
 {
   /* Read each compilation unit from the section .debug_info, and check
      to see if it contains the address we are searching for.  If yes,
@@ -2572,10 +2824,8 @@ _bfd_dwarf2_find_line (bfd *abfd,
   /* What address are we looking for?  */
   bfd_vma addr;
   struct comp_unit* each;
-  asection *section;
-  bfd_boolean found = FALSE;
-
-  section = bfd_get_section (symbol);
+  bfd_vma found = FALSE;
+  bfd_boolean do_line;
 
   stash = *pinfo;
 
@@ -2590,20 +2840,35 @@ _bfd_dwarf2_find_line (bfd *abfd,
 
   /* In a relocatable file, 2 functions may have the same address.
      We change the section vma so that they won't overlap.  */
-  if (!stash && (abfd->flags & (EXEC_P | DYNAMIC)) == 0)
+  if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
     {
       if (! place_sections (abfd, stash))
        return FALSE;
     }
 
-  addr = symbol->value;
+  do_line = (section == NULL
+            && offset == 0
+            && functionname_ptr == NULL
+            && symbol != NULL);
+  if (do_line)
+    {
+      addr = symbol->value;
+      section = bfd_get_section (symbol);
+    }
+  else if (section != NULL
+          && functionname_ptr != NULL
+          && symbol == NULL)
+    addr = offset;
+  else
+    abort ();
+
   if (section->output_section)
     addr += section->output_section->vma + section->output_offset;
   else
     addr += section->vma;
-
-  *filename_ptr = NULL;
   *filename_ptr = NULL;
+  if (! do_line)
+    *functionname_ptr = NULL;
   *linenumber_ptr = 0;
 
   if (! *pinfo)
@@ -2690,15 +2955,54 @@ _bfd_dwarf2_find_line (bfd *abfd,
   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)
-         goto done;
-      }
+  if (do_line)
+    {
+      /* 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
+       {
+         /* 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
+    {
+      for (each = stash->all_comp_units; each; each = each->next_unit)
+       {
+         found = (comp_unit_contains_address (each, addr)
+                  && comp_unit_find_nearest_line (each, addr,
+                                                  filename_ptr,
+                                                  functionname_ptr,
+                                                  linenumber_ptr,
+                                                  stash));
+         if (found)
+           goto done;
+       }
+    }
 
   /* The DWARF2 spec says that the initial length field, and the
      offset of the abbreviation table, should both be 4-byte values.
@@ -2715,8 +3019,8 @@ _bfd_dwarf2_find_line (bfd *abfd,
       bfd_byte *info_ptr_unit = stash->info_ptr;
 
       length = read_4_bytes (stash->bfd, stash->info_ptr);
-      /* A 0xffffff length is the DWARF3 way of indicating we use
-        64-bit offsets, instead of 32-bit offsets.  */
+      /* 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;
@@ -2764,6 +3068,11 @@ _bfd_dwarf2_find_line (bfd *abfd,
 
          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;
 
@@ -2772,13 +3081,22 @@ _bfd_dwarf2_find_line (bfd *abfd,
                 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 (do_line)
+               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));
+             else
+               found = ((each->arange.high == 0
+                         || comp_unit_contains_address (each, addr))
+                        && comp_unit_find_nearest_line (each, addr,
+                                                        filename_ptr,
+                                                        functionname_ptr,
+                                                        linenumber_ptr,
+                                                        stash));
              if (found)
                goto done;
            }
@@ -2792,6 +3110,42 @@ done:
   return found;
 }
 
+/* The DWARF2 version of find_nearest_line.
+   Return TRUE if the line is found without error.  */
+
+bfd_boolean
+_bfd_dwarf2_find_nearest_line (bfd *abfd,
+                              asection *section,
+                              asymbol **symbols,
+                              bfd_vma offset,
+                              const char **filename_ptr,
+                              const char **functionname_ptr,
+                              unsigned int *linenumber_ptr,
+                              unsigned int addr_size,
+                              void **pinfo)
+{
+  return find_line (abfd, section, offset, NULL, symbols, filename_ptr,
+                   functionname_ptr, linenumber_ptr, addr_size,
+                   pinfo);
+}
+
+/* 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)
+{
+  return find_line (abfd, NULL, 0, symbol, symbols, filename_ptr,
+                   NULL, linenumber_ptr, addr_size,
+                   pinfo);
+}
+
 bfd_boolean
 _bfd_dwarf2_find_inliner_info (bfd *abfd ATTRIBUTE_UNUSED,
                               const char **filename_ptr,
@@ -2805,17 +3159,18 @@ _bfd_dwarf2_find_inliner_info (bfd *abfd ATTRIBUTE_UNUSED,
   if (stash)
     {
       struct funcinfo *func = stash->inliner_chain;
+
       if (func && func->caller_func)
        {
          *filename_ptr = func->caller_file;
          *functionname_ptr = func->caller_func->name;
          *linenumber_ptr = func->caller_line;
          stash->inliner_chain = func->caller_func;
-         return (TRUE);
+         return TRUE;
        }
     }
 
-  return (FALSE);
+  return FALSE;
 }
 
 void