This commit was generated by cvs2svn to track changes on a CVS vendor
[binutils-gdb.git] / bfd / syms.c
index 2234c80f6b22dd286a23bbba5e2551116519455f..30fb74c352a1ab10a342e129cbf965eb82c2812b 100644 (file)
@@ -1,5 +1,6 @@
 /* Generic symbol-table support for the BFD library.
 /* Generic symbol-table support for the BFD library.
-   Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+   Copyright (C) 1990, 91, 92, 93, 94, 95, 96, 97, 1998
+   Free Software Foundation, Inc.
    Written by Cygnus Support.
 
 This file is part of BFD, the Binary File Descriptor library.
    Written by Cygnus Support.
 
 This file is part of BFD, the Binary File Descriptor library.
@@ -16,7 +17,7 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 
 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 /*
 SECTION
 
 /*
 SECTION
@@ -48,6 +49,7 @@ SECTION
 @menu
 @* Reading Symbols::
 @* Writing Symbols::
 @menu
 @* Reading Symbols::
 @* Writing Symbols::
+@* Mini Symbols::
 @* typedef asymbol::
 @* symbol handling functions::
 @end menu
 @* typedef asymbol::
 @* symbol handling functions::
 @end menu
@@ -86,12 +88,12 @@ SUBSECTION
 |           process_symbol (symbol_table[i]);
 |        }
 
 |           process_symbol (symbol_table[i]);
 |        }
 
-       All storage for the symbols themselves is in an obstack
+       All storage for the symbols themselves is in an objalloc
        connected to the BFD; it is freed when the BFD is closed.
 
 
 INODE
        connected to the BFD; it is freed when the BFD is closed.
 
 
 INODE
-Writing Symbols, typedef asymbol, Reading Symbols, Symbols
+Writing Symbols, Mini Symbols, Reading Symbols, Symbols
 SUBSECTION
        Writing symbols
 
 SUBSECTION
        Writing symbols
 
@@ -137,6 +139,28 @@ SUBSECTION
        which is not one  of <<.text>>, <<.data>> or <<.bss>> cannot
        be described.
 
        which is not one  of <<.text>>, <<.data>> or <<.bss>> cannot
        be described.
 
+INODE
+Mini Symbols, typedef asymbol, Writing Symbols, Symbols
+SUBSECTION
+       Mini Symbols
+
+       Mini symbols provide read-only access to the symbol table.
+       They use less memory space, but require more time to access.
+       They can be useful for tools like nm or objdump, which may
+       have to handle symbol tables of extremely large executables.
+
+       The <<bfd_read_minisymbols>> function will read the symbols
+       into memory in an internal form.  It will return a <<void *>>
+       pointer to a block of memory, a symbol count, and the size of
+       each symbol.  The pointer is allocated using <<malloc>>, and
+       should be freed by the caller when it is no longer needed.
+
+       The function <<bfd_minisymbol_to_symbol>> will take a pointer
+       to a minisymbol, and a pointer to a structure returned by
+       <<bfd_make_empty_symbol>>, and return a <<asymbol>> structure.
+       The return value may or may not be the same as the value from
+       <<bfd_make_empty_symbol>> which was passed in.
+
 */
 
 
 */
 
 
@@ -144,7 +168,7 @@ SUBSECTION
 /*
 DOCDD
 INODE
 /*
 DOCDD
 INODE
-typedef asymbol, symbol handling functions, Writing Symbols, Symbols
+typedef asymbol, symbol handling functions, Mini Symbols, Symbols
 
 */
 /*
 
 */
 /*
@@ -240,15 +264,14 @@ CODE_FRAGMENT
 .      {* Signal that the symbol is the label of constructor section. *}
 .#define BSF_CONSTRUCTOR   0x800
 .
 .      {* Signal that the symbol is the label of constructor section. *}
 .#define BSF_CONSTRUCTOR   0x800
 .
-.      {* Signal that the symbol is a warning symbol. If the symbol
-.         is a warning symbol, then the value field (I know this is
-.         tacky) will point to the asymbol which when referenced will
-.         cause the warning. *}
+.      {* Signal that the symbol is a warning symbol.  The name is a
+.         warning.  The name of the next symbol is the one to warn about;
+.         if a reference is made to a symbol with the same name as the next
+.         symbol, a warning is issued by the linker. *}
 .#define BSF_WARNING       0x1000
 .
 .#define BSF_WARNING       0x1000
 .
-.      {* Signal that the symbol is indirect. The value of the symbol
-.         is a pointer to an undefined asymbol which contains the
-.         name to use instead. *}
+.      {* Signal that the symbol is indirect.  This symbol is an indirect
+.         pointer to the symbol with the same name as the next symbol. *}
 .#define BSF_INDIRECT      0x2000
 .
 .      {* BSF_FILE marks symbols that contain a file name.  This is used
 .#define BSF_INDIRECT      0x2000
 .
 .      {* BSF_FILE marks symbols that contain a file name.  This is used
@@ -258,6 +281,10 @@ CODE_FRAGMENT
 .      {* Symbol is from dynamic linking information.  *}
 .#define BSF_DYNAMIC      0x8000
 .
 .      {* Symbol is from dynamic linking information.  *}
 .#define BSF_DYNAMIC      0x8000
 .
+.       {* The symbol denotes a data object.  Used in ELF, and perhaps
+.          others someday.  *}
+.#define BSF_OBJECT       0x10000
+.
 .  flagword flags;
 .
 .      {* A pointer to the section to which this symbol is
 .  flagword flags;
 .
 .      {* A pointer to the section to which this symbol is
@@ -277,10 +304,12 @@ CODE_FRAGMENT
 
 #include "bfd.h"
 #include "sysdep.h"
 
 #include "bfd.h"
 #include "sysdep.h"
-
 #include "libbfd.h"
 #include "libbfd.h"
+#include "bfdlink.h"
 #include "aout/stab_gnu.h"
 
 #include "aout/stab_gnu.h"
 
+static char coff_section_type PARAMS ((const char *));
+
 /*
 DOCDD
 INODE
 /*
 DOCDD
 INODE
@@ -314,8 +343,37 @@ SYNOPSIS
 DESCRIPTION
        Return true if the given symbol @var{sym} in the BFD @var{abfd} is
        a compiler generated local label, else return false.
 DESCRIPTION
        Return true if the given symbol @var{sym} in the BFD @var{abfd} is
        a compiler generated local label, else return false.
-.#define bfd_is_local_label(abfd, sym) \
-.     BFD_SEND (abfd, _bfd_is_local_label,(abfd, sym))
+*/
+
+boolean
+bfd_is_local_label (abfd, sym)
+     bfd *abfd;
+     asymbol *sym;
+{
+  if ((sym->flags & (BSF_GLOBAL | BSF_WEAK)) != 0)
+    return false;
+  if (sym->name == NULL)
+    return false;
+  if (sym->flags & BSF_DEBUGGING)
+    return true;
+  return bfd_is_local_label_name (abfd, sym->name);
+}
+
+/*
+FUNCTION
+       bfd_is_local_label_name
+
+SYNOPSIS
+        boolean bfd_is_local_label_name(bfd *abfd, const char *name);
+
+DESCRIPTION
+       Return true if a symbol with the name @var{name} in the BFD
+       @var{abfd} is a compiler generated local label, else return
+       false.  This just checks whether the name has the form of a
+       local label.
+
+.#define bfd_is_local_label_name(abfd, name) \
+.     BFD_SEND (abfd, _bfd_is_local_label_name, (abfd, name))
 */
 
 /*
 */
 
 /*
@@ -395,7 +453,8 @@ bfd_print_symbol_vandf (arg, symbol)
     }
 
   /* This presumes that a symbol can not be both BSF_DEBUGGING and
     }
 
   /* This presumes that a symbol can not be both BSF_DEBUGGING and
-     BSF_DYNAMIC, nor both BSF_FUNCTION and BSF_FILE.  */
+     BSF_DYNAMIC, nor more than one of BSF_FUNCTION, BSF_FILE, and
+     BSF_OBJECT.  */
   fprintf (file, " %c%c%c%c%c%c%c",
           ((type & BSF_LOCAL)
            ? (type & BSF_GLOBAL) ? '!' : 'l'
   fprintf (file, " %c%c%c%c%c%c%c",
           ((type & BSF_LOCAL)
            ? (type & BSF_GLOBAL) ? '!' : 'l'
@@ -405,7 +464,11 @@ bfd_print_symbol_vandf (arg, symbol)
           (type & BSF_WARNING) ? 'W' : ' ',
           (type & BSF_INDIRECT) ? 'I' : ' ',
           (type & BSF_DEBUGGING) ? 'd' : (type & BSF_DYNAMIC) ? 'D' : ' ',
           (type & BSF_WARNING) ? 'W' : ' ',
           (type & BSF_INDIRECT) ? 'I' : ' ',
           (type & BSF_DEBUGGING) ? 'd' : (type & BSF_DYNAMIC) ? 'D' : ' ',
-          (type & BSF_FUNCTION) ? 'F' : (type & BSF_FILE) ? 'f' : ' ');
+          ((type & BSF_FUNCTION)
+           ? 'F'
+           : ((type & BSF_FILE)
+              ? 'f'
+              : ((type & BSF_OBJECT) ? 'O' : ' '))));
 }
 
 
 }
 
 
@@ -452,13 +515,16 @@ static CONST struct section_to_type stt[] =
 {
   {"*DEBUG*", 'N'},
   {".bss", 'b'},
 {
   {"*DEBUG*", 'N'},
   {".bss", 'b'},
+  {"zerovars", 'b'},           /* MRI .bss */
   {".data", 'd'},
   {".data", 'd'},
+  {"vars", 'd'},               /* MRI .data */
   {".rdata", 'r'},             /* Read only data.  */
   {".rodata", 'r'},            /* Read only data.  */
   {".sbss", 's'},              /* Small BSS (uninitialized data).  */
   {".scommon", 'c'},           /* Small common.  */
   {".sdata", 'g'},             /* Small initialized data.  */
   {".text", 't'},
   {".rdata", 'r'},             /* Read only data.  */
   {".rodata", 'r'},            /* Read only data.  */
   {".sbss", 's'},              /* Small BSS (uninitialized data).  */
   {".scommon", 'c'},           /* Small common.  */
   {".sdata", 'g'},             /* Small initialized data.  */
   {".text", 't'},
+  {"code", 't'},               /* MRI .text */
   {0, 0}
 };
 
   {0, 0}
 };
 
@@ -470,7 +536,7 @@ static CONST struct section_to_type stt[] =
 
 static char
 coff_section_type (s)
 
 static char
 coff_section_type (s)
-     char *s;
+     const char *s;
 {
   CONST struct section_to_type *t;
 
 {
   CONST struct section_to_type *t;
 
@@ -562,12 +628,6 @@ bfd_symbol_info (symbol, ret)
   ret->name = symbol->name;
 }
 
   ret->name = symbol->name;
 }
 
-void
-bfd_symbol_is_absolute ()
-{
-  abort ();
-}
-
 /*
 FUNCTION
        bfd_copy_private_symbol_data
 /*
 FUNCTION
        bfd_copy_private_symbol_data
@@ -585,7 +645,557 @@ DESCRIPTION
        Not enough memory exists to create private data for @var{osec}.
 
 .#define bfd_copy_private_symbol_data(ibfd, isymbol, obfd, osymbol) \
        Not enough memory exists to create private data for @var{osec}.
 
 .#define bfd_copy_private_symbol_data(ibfd, isymbol, obfd, osymbol) \
-.     BFD_SEND (ibfd, _bfd_copy_private_symbol_data, \
+.     BFD_SEND (obfd, _bfd_copy_private_symbol_data, \
 .              (ibfd, isymbol, obfd, osymbol))
 
 */
 .              (ibfd, isymbol, obfd, osymbol))
 
 */
+
+/* The generic version of the function which returns mini symbols.
+   This is used when the backend does not provide a more efficient
+   version.  It just uses BFD asymbol structures as mini symbols.  */
+
+long
+_bfd_generic_read_minisymbols (abfd, dynamic, minisymsp, sizep)
+     bfd *abfd;
+     boolean dynamic;
+     PTR *minisymsp;
+     unsigned int *sizep;
+{
+  long storage;
+  asymbol **syms = NULL;
+  long symcount;
+
+  if (dynamic)
+    storage = bfd_get_dynamic_symtab_upper_bound (abfd);
+  else
+    storage = bfd_get_symtab_upper_bound (abfd);
+  if (storage < 0)
+    goto error_return;
+
+  syms = (asymbol **) bfd_malloc ((size_t) storage);
+  if (syms == NULL)
+    goto error_return;
+
+  if (dynamic)
+    symcount = bfd_canonicalize_dynamic_symtab (abfd, syms);
+  else
+    symcount = bfd_canonicalize_symtab (abfd, syms);
+  if (symcount < 0)
+    goto error_return;
+
+  *minisymsp = (PTR) syms;
+  *sizep = sizeof (asymbol *);
+  return symcount;
+
+ error_return:
+  if (syms != NULL)
+    free (syms);
+  return -1;
+}
+
+/* The generic version of the function which converts a minisymbol to
+   an asymbol.  We don't worry about the sym argument we are passed;
+   we just return the asymbol the minisymbol points to.  */
+
+/*ARGSUSED*/
+asymbol *
+_bfd_generic_minisymbol_to_symbol (abfd, dynamic, minisym, sym)
+     bfd *abfd;
+     boolean dynamic;
+     const PTR minisym;
+     asymbol *sym;
+{
+  return *(asymbol **) minisym;
+}
+
+/* Look through stabs debugging information in .stab and .stabstr
+   sections to find the source file and line closest to a desired
+   location.  This is used by COFF and ELF targets.  It sets *pfound
+   to true if it finds some information.  The *pinfo field is used to
+   pass cached information in and out of this routine; this first time
+   the routine is called for a BFD, *pinfo should be NULL.  The value
+   placed in *pinfo should be saved with the BFD, and passed back each
+   time this function is called.  */
+
+/* We use a cache by default.  */
+
+#define ENABLE_CACHING
+
+/* We keep an array of indexentry structures to record where in the
+   stabs section we should look to find line number information for a
+   particular address.  */
+
+struct indexentry
+{
+  bfd_vma val;
+  bfd_byte *stab;
+  bfd_byte *str;
+  char *directory_name;
+  char *file_name;
+  char *function_name;
+};
+
+/* Compare two indexentry structures.  This is called via qsort.  */
+
+static int
+cmpindexentry (a, b)
+     const PTR *a;
+     const PTR *b;
+{
+  const struct indexentry *contestantA = (const struct indexentry *) a;
+  const struct indexentry *contestantB = (const struct indexentry *) b;
+
+  if (contestantA->val < contestantB->val)
+    return -1;
+  else if (contestantA->val > contestantB->val)
+    return 1;
+  else
+    return 0;
+}
+
+/* A pointer to this structure is stored in *pinfo.  */
+
+struct stab_find_info
+{
+  /* The .stab section.  */
+  asection *stabsec;
+  /* The .stabstr section.  */
+  asection *strsec;
+  /* The contents of the .stab section.  */
+  bfd_byte *stabs;
+  /* The contents of the .stabstr section.  */
+  bfd_byte *strs;
+
+  /* A table that indexes stabs by memory address.  */
+  struct indexentry *indextable;
+  /* The number of entries in indextable.  */
+  int indextablesize;
+
+#ifdef ENABLE_CACHING
+  /* Cached values to restart quickly.  */
+  struct indexentry *cached_indexentry;
+  bfd_vma cached_offset;
+  bfd_byte *cached_stab;
+  char *cached_file_name;
+#endif
+
+  /* Saved ptr to malloc'ed filename.  */
+  char *filename;
+};
+
+boolean
+_bfd_stab_section_find_nearest_line (abfd, symbols, section, offset, pfound,
+                                    pfilename, pfnname, pline, pinfo)
+     bfd *abfd;
+     asymbol **symbols;
+     asection *section;
+     bfd_vma offset;
+     boolean *pfound;
+     const char **pfilename;
+     const char **pfnname;
+     unsigned int *pline;
+     PTR *pinfo;
+{
+  struct stab_find_info *info;
+  bfd_size_type stabsize, strsize;
+  bfd_byte *stab, *str;
+  bfd_size_type stroff;
+  struct indexentry *indexentry;
+  char *directory_name, *file_name;
+
+  *pfound = false;
+  *pfilename = bfd_get_filename (abfd);
+  *pfnname = NULL;
+  *pline = 0;
+
+  /* Stabs entries use a 12 byte format:
+       4 byte string table index
+       1 byte stab type
+       1 byte stab other field
+       2 byte stab desc field
+       4 byte stab value
+     FIXME: This will have to change for a 64 bit object format.
+
+     The stabs symbols are divided into compilation units.  For the
+     first entry in each unit, the type of 0, the value is the length
+     of the string table for this unit, and the desc field is the
+     number of stabs symbols for this unit.  */
+
+#define STRDXOFF (0)
+#define TYPEOFF (4)
+#define OTHEROFF (5)
+#define DESCOFF (6)
+#define VALOFF (8)
+#define STABSIZE (12)
+
+  info = (struct stab_find_info *) *pinfo;
+  if (info != NULL)
+    {
+      if (info->stabsec == NULL || info->strsec == NULL)
+       {
+         /* No stabs debugging information.  */
+         return true;
+       }
+
+      stabsize = info->stabsec->_raw_size;
+      strsize = info->strsec->_raw_size;
+    }
+  else
+    {
+      long reloc_size, reloc_count;
+      arelent **reloc_vector;
+      bfd_vma val;
+      int i;
+      char *name;
+      char *file_name;
+      char *directory_name;
+      char *function_name;
+
+      info = (struct stab_find_info *) bfd_zalloc (abfd, sizeof *info);
+      if (info == NULL)
+       return false;
+
+      /* FIXME: When using the linker --split-by-file or
+        --split-by-reloc options, it is possible for the .stab and
+        .stabstr sections to be split.  We should handle that.  */
+
+      info->stabsec = bfd_get_section_by_name (abfd, ".stab");
+      info->strsec = bfd_get_section_by_name (abfd, ".stabstr");
+
+      if (info->stabsec == NULL || info->strsec == NULL)
+       {
+         /* No stabs debugging information.  Set *pinfo so that we
+             can return quickly in the info != NULL case above.  */
+         *pinfo = (PTR) info;
+         return true;
+       }
+
+      stabsize = info->stabsec->_raw_size;
+      strsize = info->strsec->_raw_size;
+
+      info->stabs = (bfd_byte *) bfd_alloc (abfd, stabsize);
+      info->strs = (bfd_byte *) bfd_alloc (abfd, strsize);
+      if (info->stabs == NULL || info->strs == NULL)
+       return false;
+
+      if (! bfd_get_section_contents (abfd, info->stabsec, info->stabs, 0,
+                                     stabsize)
+         || ! bfd_get_section_contents (abfd, info->strsec, info->strs, 0,
+                                        strsize))
+       return false;
+
+      /* If this is a relocateable object file, we have to relocate
+        the entries in .stab.  This should always be simple 32 bit
+        relocations against symbols defined in this object file, so
+        this should be no big deal.  */
+      reloc_size = bfd_get_reloc_upper_bound (abfd, info->stabsec);
+      if (reloc_size < 0)
+       return false;
+      reloc_vector = (arelent **) bfd_malloc (reloc_size);
+      if (reloc_vector == NULL && reloc_size != 0)
+       return false;
+      reloc_count = bfd_canonicalize_reloc (abfd, info->stabsec, reloc_vector,
+                                           symbols);
+      if (reloc_count < 0)
+       {
+         if (reloc_vector != NULL)
+           free (reloc_vector);
+         return false;
+       }
+      if (reloc_count > 0)
+       {
+         arelent **pr;
+
+         for (pr = reloc_vector; *pr != NULL; pr++)
+           {
+             arelent *r;
+             unsigned long val;
+             asymbol *sym;
+
+             r = *pr;
+             if (r->howto->rightshift != 0
+                 || r->howto->size != 2
+                 || r->howto->bitsize != 32
+                 || r->howto->pc_relative
+                 || r->howto->bitpos != 0
+                 || r->howto->dst_mask != 0xffffffff)
+               {
+                 (*_bfd_error_handler)
+                   (_("Unsupported .stab relocation"));
+                 bfd_set_error (bfd_error_invalid_operation);
+                 if (reloc_vector != NULL)
+                   free (reloc_vector);
+                 return false;
+               }
+
+             val = bfd_get_32 (abfd, info->stabs + r->address);
+             val &= r->howto->src_mask;
+             sym = *r->sym_ptr_ptr;
+             val += sym->value + sym->section->vma + r->addend;
+             bfd_put_32 (abfd, val, info->stabs + r->address);
+           }
+       }
+
+      if (reloc_vector != NULL)
+       free (reloc_vector);
+
+      /* First time through this function, build a table matching
+        function VM addresses to stabs, then sort based on starting
+        VM address.  Do this in two passes: once to count how many
+        table entries we'll need, and a second to actually build the
+        table.  */
+
+      info->indextablesize = 0;
+      for (stab = info->stabs; stab < info->stabs + stabsize; stab += STABSIZE)
+       {
+         if (stab[TYPEOFF] == N_FUN)
+           ++info->indextablesize;
+       }
+
+      if (info->indextablesize == 0)
+       return true;
+      ++info->indextablesize;
+
+      info->indextable = ((struct indexentry *)
+                         bfd_alloc (abfd,
+                                    (sizeof (struct indexentry)
+                                     * info->indextablesize)));
+      if (info->indextable == NULL)
+       return false;
+
+      file_name = NULL;
+      directory_name = NULL;
+
+      for (i = 0, stroff = 0, stab = info->stabs, str = info->strs;
+          i < info->indextablesize && stab < info->stabs + stabsize;
+          stab += STABSIZE)
+       {
+         switch (stab[TYPEOFF])
+           {
+           case 0:
+             /* This is the first entry in a compilation unit.  */
+             if ((bfd_size_type) ((info->strs + strsize) - str) < stroff)
+               break;
+             str += stroff;
+             stroff = bfd_get_32 (abfd, stab + VALOFF);
+             break;
+
+           case N_SO:
+             /* The main file name.  */
+
+             file_name = (char *) str + bfd_get_32 (abfd, stab + STRDXOFF);
+
+             if (*file_name == '\0')
+               {
+                 directory_name = NULL;
+                 file_name = NULL;
+               }
+             else if (stab + STABSIZE >= info->stabs + stabsize
+                      || *(stab + STABSIZE + TYPEOFF) != N_SO)
+               {
+                 directory_name = NULL;
+               }
+             else
+               {
+                 /* Two consecutive N_SOs are a directory and a file
+                     name.  */
+                 stab += STABSIZE;
+                 directory_name = file_name;
+                 file_name = ((char *) str
+                              + bfd_get_32 (abfd, stab + STRDXOFF));
+               }
+             break;
+
+           case N_SOL:
+             /* The name of an include file.  */
+             file_name = (char *) str + bfd_get_32 (abfd, stab + STRDXOFF);
+             break;
+
+           case N_FUN:
+             /* A function name.  */
+
+             name = (char *) str + bfd_get_32 (abfd, stab + STRDXOFF);
+
+             if (*name == '\0')
+               name = NULL;
+
+             function_name = name;
+
+             if (name == NULL)
+               continue;
+
+             val = bfd_get_32 (abfd, stab + VALOFF);
+
+             info->indextable[i].val = val;
+             info->indextable[i].stab = stab;
+             info->indextable[i].str = str;
+             info->indextable[i].directory_name = directory_name;
+             info->indextable[i].file_name = file_name;
+             info->indextable[i].function_name = function_name;
+
+             ++i;
+             break;
+           }
+       }
+
+      info->indextable[i].val = (bfd_vma) -1;
+      info->indextable[i].stab = info->stabs + stabsize;
+      info->indextable[i].str = str;
+      info->indextable[i].directory_name = NULL;
+      info->indextable[i].file_name = NULL;
+      info->indextable[i].function_name = NULL;
+      ++i;
+
+      info->indextablesize = i;
+
+      qsort (info->indextable, i, sizeof (struct indexentry), cmpindexentry);
+
+      *pinfo = (PTR) info;
+    }
+
+  /* We are passed a section relative offset.  The offsets in the
+     stabs information are absolute.  */
+  offset += bfd_get_section_vma (abfd, section);
+
+#ifdef ENABLE_CACHING
+  if (info->cached_indexentry != NULL
+      && offset >= info->cached_offset
+      && offset < (info->cached_indexentry + 1)->val)
+    {
+      stab = info->cached_stab;
+      indexentry = info->cached_indexentry;
+      file_name = info->cached_file_name;
+    }
+  else
+#endif
+    {
+      /* Cache non-existant or invalid.  Do binary search on
+         indextable.  */
+
+      long low, high;
+      long mid = -1;
+
+      indexentry = NULL;
+
+      low = 0;
+      high = info->indextablesize - 1;
+      while (low != high)
+       {
+         mid = (high + low) / 2;
+         if (offset >= info->indextable[mid].val
+             && offset < info->indextable[mid + 1].val)
+           {
+             indexentry = &info->indextable[mid];
+             break;
+           }
+
+         if (info->indextable[mid].val > offset)
+           high = mid;
+         else
+           low = mid + 1;
+       }
+
+      if (indexentry == NULL)
+       return true;
+
+      stab = indexentry->stab + STABSIZE;
+      file_name = indexentry->file_name;
+    }
+
+  directory_name = indexentry->directory_name;
+  str = indexentry->str;
+
+  for (; stab < (indexentry+1)->stab; stab += STABSIZE)
+    {
+      boolean done;
+      bfd_vma val;
+
+      done = false;
+
+      switch (stab[TYPEOFF])
+       {
+       case N_SOL:
+         /* The name of an include file.  */
+         val = bfd_get_32 (abfd, stab + VALOFF);
+         if (val <= offset)
+           {
+             file_name = (char *) str + bfd_get_32 (abfd, stab + STRDXOFF);
+             *pline = 0;
+           }
+         break;
+
+       case N_SLINE:
+       case N_DSLINE:
+       case N_BSLINE:
+         /* A line number.  The value is relative to the start of the
+             current function.  */
+         val = indexentry->val + bfd_get_32 (abfd, stab + VALOFF);
+         if (val <= offset)
+           {
+             *pline = bfd_get_16 (abfd, stab + DESCOFF);
+
+#ifdef ENABLE_CACHING
+             info->cached_stab = stab;
+             info->cached_offset = val;
+             info->cached_file_name = file_name;
+             info->cached_indexentry = indexentry;
+#endif
+           }
+         if (val > offset)
+           done = true;
+         break;
+
+       case N_FUN:
+       case N_SO:
+         done = true;
+         break;
+       }
+
+      if (done)
+       break;
+    }
+
+  *pfound = true;
+
+  if (file_name[0] == '/' || directory_name == NULL)
+    *pfilename = file_name;
+  else
+    {
+      size_t dirlen;
+
+      dirlen = strlen (directory_name);
+      if (info->filename == NULL
+         || strncmp (info->filename, directory_name, dirlen) != 0
+         || strcmp (info->filename + dirlen, file_name) != 0)
+       {
+         if (info->filename != NULL)
+           free (info->filename);
+         info->filename = (char *) bfd_malloc (dirlen +
+                                               strlen (file_name)
+                                               + 1);
+         if (info->filename == NULL)
+           return false;
+         strcpy (info->filename, directory_name);
+         strcpy (info->filename + dirlen, file_name);
+       }
+
+      *pfilename = info->filename;
+    }
+
+  if (indexentry->function_name != NULL)
+    {
+      char *s;
+
+      /* This will typically be something like main:F(0,1), so we want
+         to clobber the colon.  It's OK to change the name, since the
+         string is in our own local storage anyhow.  */
+
+      s = strchr (indexentry->function_name, ':');
+      if (s != NULL)
+       *s = '\0';
+
+      *pfnname = indexentry->function_name;
+    }
+
+  return true;
+}