* syms.c: Include "bfdlink.h".
authorIan Lance Taylor <ian@airs.com>
Fri, 26 Jan 1996 23:42:58 +0000 (23:42 +0000)
committerIan Lance Taylor <ian@airs.com>
Fri, 26 Jan 1996 23:42:58 +0000 (23:42 +0000)
(struct stab_find_info): Define.
(_bfd_stab_section_find_nearest_line): New function.
* libbfd-in.h (_bfd_stab_section_find_nearest_line): Declare.
* libbfd.h: Rebuild.
* elf-bfd.h (struct elf_obj_tdata): Add line_info field.
* elf.c (_bfd_elf_find_nearest_line): Try calling
_bfd_stab_section_find_nearest_line before searching the ELF
symbol table.  Find the closest STT_FUNC symbol, not the last one.
* libcoff-in.h (coff_data_type): Add line_info field.
* libcoff.h: Rebuild.
* coffgen.c (coff_find_nearest_line): Try calling
_bfd_stab_section_find_nearest_line before searching the COFF
symbol table.
* Makefile.in: Rebuild dependencies.

bfd/ChangeLog
bfd/Makefile.in
bfd/elf-bfd.h
bfd/elf.c
bfd/libcoff-in.h
bfd/libcoff.h
bfd/syms.c

index 494f705585f8673607ec63647d1dc81eb3a81754..b076c001fd8ceeca3e321eeca32399dbb9833be6 100644 (file)
@@ -1,3 +1,27 @@
+Fri Jan 26 18:33:35 1996  Ian Lance Taylor  <ian@cygnus.com>
+
+       * syms.c: Include "bfdlink.h".
+       (struct stab_find_info): Define.
+       (_bfd_stab_section_find_nearest_line): New function.
+       * libbfd-in.h (_bfd_stab_section_find_nearest_line): Declare.
+       * libbfd.h: Rebuild.
+       * elf-bfd.h (struct elf_obj_tdata): Add line_info field.
+       * elf.c (_bfd_elf_find_nearest_line): Try calling
+       _bfd_stab_section_find_nearest_line before searching the ELF
+       symbol table.  Find the closest STT_FUNC symbol, not the last one.
+       * libcoff-in.h (coff_data_type): Add line_info field.
+       * libcoff.h: Rebuild.
+       * coffgen.c (coff_find_nearest_line): Try calling
+       _bfd_stab_section_find_nearest_line before searching the COFF
+       symbol table.
+       * Makefile.in: Rebuild dependencies.
+
+Fri Jan 26 16:11:19 1996  Michael Meissner  <meissner@tiktok.cygnus.com>
+
+       * elf32-ppc.c (R_PPC_EMB_SDA21 relocations): Make relocation size
+       4 bytes, so we get the correct value when updating the register
+       field in little endian mode.
+
 Thu Jan 25 12:14:16 1996  Ian Lance Taylor  <ian@cygnus.com>
 
        * libcoff-in.h (struct xcoff_tdata): Remove toc_section and
index 46a70958fe3de06effcb14628e2c987cf85d85c2..6af061ceed5d3368110f3651a2b1e354bcd35ff1 100644 (file)
@@ -1,5 +1,6 @@
 #    Makefile template for Configure for the BFD library.
-#    Copyright (C) 1990, 91, 92, 93, 94, 1995 Free Software Foundation, Inc.
+#    Copyright (C) 1990, 91, 92, 93, 94, 95, 1996
+#    Free Software Foundation, Inc.
 #    Written by Cygnus Support.
 # 
 # This file is part of BFD, the Binary File Descriptor library.
@@ -713,7 +714,8 @@ libbfd.o: libbfd.c
 opncls.o: opncls.c
 reloc.o: reloc.c $(INCDIR)/bfdlink.h
 section.o: section.c
-syms.o: syms.c $(INCDIR)/aout/stab_gnu.h $(INCDIR)/aout/stab.def
+syms.o: syms.c $(INCDIR)/bfdlink.h $(INCDIR)/aout/stab_gnu.h \
+  $(INCDIR)/aout/stab.def
 targets.o: targets.c
 hash.o: hash.c
 linker.o: linker.c $(INCDIR)/bfdlink.h genlink.h
@@ -852,7 +854,7 @@ elf32-ppc.o: elf32-ppc.c $(INCDIR)/bfdlink.h elf-bfd.h \
   $(INCDIR)/elf/ppc.h elf32-target.h
 elf32-sparc.o: elf32-sparc.c $(INCDIR)/bfdlink.h elf-bfd.h \
   $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
-  elf32-target.h
+  $(INCDIR)/elf/sparc.h elf32-target.h
 elf32.o: elf32.c elfcode.h $(INCDIR)/bfdlink.h elf-bfd.h \
   $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
   elfcore.h elflink.h
@@ -962,8 +964,8 @@ aout64.o: aout64.c aoutx.h $(INCDIR)/bfdlink.h libaout.h \
   $(INCDIR)/aout/ar.h
 coff-alpha.o: coff-alpha.c $(INCDIR)/bfdlink.h $(INCDIR)/coff/internal.h \
   $(INCDIR)/coff/sym.h $(INCDIR)/coff/symconst.h $(INCDIR)/coff/ecoff.h \
-  $(INCDIR)/coff/alpha.h libcoff.h libecoff.h coffswap.h \
-  ecoffswap.h
+  $(INCDIR)/coff/alpha.h $(INCDIR)/aout/ar.h libcoff.h \
+  libecoff.h coffswap.h ecoffswap.h
 demo64.o: demo64.c aoutf1.h $(INCDIR)/aout/sun4.h libaout.h \
   $(INCDIR)/bfdlink.h $(INCDIR)/aout/aout64.h $(INCDIR)/aout/stab_gnu.h \
   $(INCDIR)/aout/stab.def $(INCDIR)/aout/ar.h aout-target.h
@@ -987,8 +989,7 @@ hpux-core.o: hpux-core.c
 irix-core.o: irix-core.c
 lynx-core.o: lynx-core.c
 osf-core.o: osf-core.c
-trad-core.o: trad-core.c libaout.h $(INCDIR)/bfdlink.h \
-  hosts/i386bsd.h
+trad-core.o: trad-core.c libaout.h $(INCDIR)/bfdlink.h
 cisco-core.o: cisco-core.c
 i386dynix.o: i386dynix.c $(INCDIR)/aout/dynix3.h aoutx.h \
   $(INCDIR)/bfdlink.h libaout.h $(INCDIR)/aout/aout64.h \
index 10c30c6e0ad0e2dee7b8dcbe5d20f0604e12c337..b6060634eb5998418e0544743651c8039230d2be 100644 (file)
@@ -102,6 +102,11 @@ struct elf_link_hash_entry
      require an entry in the procedure linkage table.  */
   bfd_vma plt_offset;
 
+  /* If this symbol is used in the linker created sections, the processor
+     specific backend uses this field to map the field into the offset
+     from the beginning of the section.  */
+  struct elf_linker_section_pointers *linker_section_pointer;
+
   /* Symbol type (STT_NOTYPE, STT_OBJECT, etc.).  */
   char type;
 
@@ -482,6 +487,52 @@ struct bfd_elf_section_data
 #define get_elf_backend_data(abfd) \
   ((struct elf_backend_data *) (abfd)->xvec->backend_data)
 
+/* Enumeration to specify the special section.  */
+typedef enum elf_linker_section_enum
+{
+  LINKER_SECTION_UNKNOWN,              /* not used */
+  LINKER_SECTION_GOT,                  /* .got section for global offset pointers */
+  LINKER_SECTION_PLT,                  /* .plt section for generated procedure stubs */
+  LINKER_SECTION_SDATA,                        /* .sdata/.sbss section for PowerPC */
+  LINKER_SECTION_SDATA2,               /* .sdata2/.sbss2 section for PowerPC */
+  LINKER_SECTION_MAX                   /* # of linker sections */
+} elf_linker_section_enum_t;
+
+/* Sections created by the linker.  */
+
+typedef struct elf_linker_section
+{
+  char *name;                          /* name of the section */
+  char *rel_name;                      /* name of the associated .rel{,a}. section */
+  char *bss_name;                      /* name of a related .bss section */
+  char *sym_name;                      /* name of symbol to reference this section */
+  asection *section;                   /* pointer to the section */
+  asection *bss_section;               /* pointer to the bss section associated with this */
+  asection *rel_section;               /* pointer to the relocations needed for this section */
+  struct elf_link_hash_entry *sym_hash;        /* pointer to the created symbol hash value */
+  bfd_vma initial_size;                        /* initial size before any linker generated allocations */
+  bfd_vma sym_offset;                  /* offset of symbol from beginning of section */
+  bfd_vma hole_size;                   /* size of reserved address hole in allocation */
+  bfd_vma hole_offset;                 /* current offset for the hole */
+  bfd_vma max_hole_offset;             /* maximum offset for the hole */
+  elf_linker_section_enum_t which;     /* which section this is */
+  boolean hole_written_p;              /* whether the hole has been initialized */
+  int alignment;                       /* alignment for the section */
+  flagword flags;                      /* flags to use to create the section */
+} elf_linker_section_t;
+
+/* Linked list of allocated pointer entries.  This hangs off of the symbol lists, and
+   provides allows us to return different pointers, based on different addend's.  */
+
+typedef struct elf_linker_section_pointers
+{
+  struct elf_linker_section_pointers *next;    /* next allocated pointer for this symbol */
+  bfd_vma offset;                              /* offset of pointer from beginning of section */
+  bfd_signed_vma addend;                       /* addend used */
+  elf_linker_section_enum_t which;             /* which linker section this is */
+  boolean written_address_p;                   /* whether address was written yet */
+} elf_linker_section_pointers_t;
+
 /* Some private data is stashed away for future use using the tdata pointer
    in the bfd structure.  */
 
@@ -494,7 +545,7 @@ struct elf_obj_tdata
   struct bfd_strtab_hash *strtab_ptr;
   int num_locals;
   int num_globals;
-  asymbol **section_syms;      /* STT_SECTION symbols for each section */
+  asymbol **section_syms;              /* STT_SECTION symbols for each section */
   Elf_Internal_Shdr symtab_hdr;
   Elf_Internal_Shdr shstrtab_hdr;
   Elf_Internal_Shdr strtab_hdr;
@@ -503,10 +554,10 @@ struct elf_obj_tdata
   unsigned int symtab_section, shstrtab_section;
   unsigned int strtab_section, dynsymtab_section;
   file_ptr next_file_pos;
-  void *prstatus;              /* The raw /proc prstatus structure */
-  void *prpsinfo;              /* The raw /proc prpsinfo structure */
-  bfd_vma gp;                  /* The gp value (MIPS only, for now) */
-  unsigned int gp_size;                /* The gp size (MIPS only, for now) */
+  void *prstatus;                      /* The raw /proc prstatus structure */
+  void *prpsinfo;                      /* The raw /proc prpsinfo structure */
+  bfd_vma gp;                          /* The gp value (MIPS only, for now) */
+  unsigned int gp_size;                        /* The gp size (MIPS only, for now) */
 
   /* This is set to true if the object was created by the backend
      linker.  */
@@ -521,6 +572,10 @@ struct elf_obj_tdata
      table, used when linking.  This is indexed by the symbol index.  */
   bfd_vma *local_got_offsets;
 
+  /* A mapping from local symbols to offsets into the various linker
+     sections added.  This is index by the symbol index.  */
+  elf_linker_section_pointers_t **linker_section_pointers;
+
   /* The linker ELF emulation code needs to let the backend ELF linker
      know what filename should be used for a dynamic object if the
      dynamic object is found using a search.  This field is used to
@@ -537,6 +592,9 @@ struct elf_obj_tdata
   /* Records the result of `get_program_header_size'.  */
   bfd_size_type program_header_size;
 
+  /* Used by find_nearest_line entry point.  */
+  PTR line_info;
+
   /* Used by MIPS ELF find_nearest_line entry point.  The structure
      could be included directly in this one, but there's no point to
      wasting the memory just for the infrequently called
@@ -545,6 +603,9 @@ struct elf_obj_tdata
 
   /* Used to determine if the e_flags field has been initialized */
   boolean flags_init;
+
+  /* Linker sections that we are interested in.  */
+  struct elf_linker_section *linker_section[ (int)LINKER_SECTION_MAX ];
 };
 
 #define elf_tdata(bfd)         ((bfd) -> tdata.elf_obj_data)
@@ -562,9 +623,11 @@ struct elf_obj_tdata
 #define elf_gp_size(bfd)       (elf_tdata(bfd) -> gp_size)
 #define elf_sym_hashes(bfd)    (elf_tdata(bfd) -> sym_hashes)
 #define elf_local_got_offsets(bfd) (elf_tdata(bfd) -> local_got_offsets)
+#define elf_local_ptr_offsets(bfd) (elf_tdata(bfd) -> linker_section_pointers)
 #define elf_dt_needed_name(bfd)        (elf_tdata(bfd) -> dt_needed_name)
 #define elf_bad_symtab(bfd)    (elf_tdata(bfd) -> bad_symtab)
 #define elf_flags_init(bfd)    (elf_tdata(bfd) -> flags_init)
+#define elf_linker_section(bfd,n) (elf_tdata(bfd) -> linker_section[(int)n])
 \f
 extern int _bfd_elf_section_from_bfd_section PARAMS ((bfd *, asection *));
 extern char *bfd_elf_string_from_elf_section
@@ -661,6 +724,56 @@ boolean _bfd_elf_create_dynamic_sections PARAMS ((bfd *,
 boolean _bfd_elf_create_got_section PARAMS ((bfd *,
                                             struct bfd_link_info *));
 
+elf_linker_section_t *_bfd_elf_create_linker_section
+  PARAMS ((bfd *abfd,
+          struct bfd_link_info *info,
+          enum elf_linker_section_enum,
+          elf_linker_section_t *defaults));
+
+elf_linker_section_pointers_t *_bfd_elf_find_pointer_linker_section
+  PARAMS ((elf_linker_section_pointers_t *linker_pointers,
+          bfd_signed_vma addend,
+          elf_linker_section_enum_t which));
+
+boolean bfd_elf32_create_pointer_linker_section
+  PARAMS ((bfd *abfd,
+          struct bfd_link_info *info,
+          elf_linker_section_t *lsect,
+          struct elf_link_hash_entry *h,
+          const Elf32_Internal_Rela *rel));
+
+bfd_vma bfd_elf32_finish_pointer_linker_section
+  PARAMS ((bfd *output_abfd,
+          bfd *input_bfd,
+          struct bfd_link_info *info,
+          elf_linker_section_t *lsect,
+          struct elf_link_hash_entry *h,
+          bfd_vma relocation,
+          const Elf32_Internal_Rela *rel,
+          int relative_reloc));
+
+boolean bfd_elf64_create_pointer_linker_section
+  PARAMS ((bfd *abfd,
+          struct bfd_link_info *info,
+          elf_linker_section_t *lsect,
+          struct elf_link_hash_entry *h,
+          const Elf64_Internal_Rela *rel));
+
+bfd_vma bfd_elf64_finish_pointer_linker_section
+  PARAMS ((bfd *output_abfd,
+          bfd *input_bfd,
+          struct bfd_link_info *info,
+          elf_linker_section_t *lsect,
+          struct elf_link_hash_entry *h,
+          bfd_vma relocation,
+          const Elf64_Internal_Rela *rel,
+          int relative_reloc));
+
+boolean _bfd_elf_make_linker_section_rela
+  PARAMS ((bfd *dynobj,
+          elf_linker_section_t *lsect,
+          int alignment));
+
 extern const bfd_target *bfd_elf32_object_p PARAMS ((bfd *));
 extern const bfd_target *bfd_elf32_core_file_p PARAMS ((bfd *));
 extern char *bfd_elf32_core_file_failing_command PARAMS ((bfd *));
index e12a885c5ee8cbffddd9ad0f7984f1669fa66c54..95181c6f57ca0b6351cdec8b7514734ba39f1b89 100644 (file)
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -554,6 +554,7 @@ _bfd_elf_link_hash_newfunc (entry, table, string)
       ret->weakdef = NULL;
       ret->got_offset = (bfd_vma) -1;
       ret->plt_offset = (bfd_vma) -1;
+      ret->linker_section_pointer = (elf_linker_section_pointers_t *)0;
       ret->type = STT_NOTYPE;
       ret->elf_link_hash_flags = 0;
     }
@@ -3142,15 +3143,26 @@ _bfd_elf_find_nearest_line (abfd,
      CONST char **functionname_ptr;
      unsigned int *line_ptr;
 {
+  boolean found;
   const char *filename;
   asymbol *func;
+  bfd_vma low_func;
   asymbol **p;
 
+  if (! _bfd_stab_section_find_nearest_line (abfd, symbols, section, offset,
+                                            &found, filename_ptr,
+                                            functionname_ptr, line_ptr,
+                                            &elf_tdata (abfd)->line_info))
+    return false;
+  if (found)
+    return true;
+
   if (symbols == NULL)
     return false;
 
   filename = NULL;
   func = NULL;
+  low_func = 0;
 
   for (p = symbols; *p != NULL; p++)
     {
@@ -3169,9 +3181,13 @@ _bfd_elf_find_nearest_line (abfd,
          filename = bfd_asymbol_name (&q->symbol);
          break;
        case STT_FUNC:
-         if (func == NULL
-             || q->symbol.value <= offset)
-           func = (asymbol *) q;
+         if (q->symbol.section == section
+             && q->symbol.value >= low_func
+             && q->symbol.value <= offset)
+           {
+             func = (asymbol *) q;
+             low_func = q->symbol.value;
+           }
          break;
        }
     }
index 820a75d58a27301a29a49ab315784b5632b49bb5..ce993b2745944b087fdf7d37af4fc4fa0859e300 100644 (file)
@@ -89,6 +89,9 @@ typedef struct coff_tdata
   int *local_toc_sym_map;
 
   struct bfd_link_info *link_info;
+
+  /* Used by coff_find_nearest_line.  */
+  PTR line_info;
 } coff_data_type;
 
 /* Tdata for pe image files. */
@@ -117,11 +120,11 @@ struct xcoff_tdata
   /* TOC value.  */
   bfd_vma toc;
 
-  /* Section holding TOC.  */
-  asection *toc_section;
+  /* Index of section holding TOC.  */
+  int sntoc;
 
-  /* Section holding entry point.  */
-  asection *entry_section;
+  /* Index of section holding entry point.  */
+  int snentry;
 
   /* .text alignment from optional header.  */
   int text_align_power;
@@ -223,11 +226,6 @@ struct coff_link_hash_entry
 
   /* Pointer to array of auxiliary entries, if any.  */
   union internal_auxent *aux;
-
-  /* If this symbol requires an entry in the table of contents, the
-     processor specific backend uses this field to hold the offset
-     into the .toc section.  */
-  bfd_vma toc_offset;
 };
 
 /* COFF linker hash table.  */
index b9fe260153a88a05fa97ef174154aa0a0e1ba220..6cb948ad0d704742b6ff2c4eca18a0bc536e12e4 100644 (file)
@@ -89,6 +89,9 @@ typedef struct coff_tdata
   int *local_toc_sym_map;
 
   struct bfd_link_info *link_info;
+
+  /* Used by coff_find_nearest_line.  */
+  PTR line_info;
 } coff_data_type;
 
 /* Tdata for pe image files. */
@@ -117,11 +120,11 @@ struct xcoff_tdata
   /* TOC value.  */
   bfd_vma toc;
 
-  /* Section holding TOC.  */
-  asection *toc_section;
+  /* Index of section holding TOC.  */
+  int sntoc;
 
-  /* Section holding entry point.  */
-  asection *entry_section;
+  /* Index of section holding entry point.  */
+  int snentry;
 
   /* .text alignment from optional header.  */
   int text_align_power;
@@ -223,11 +226,6 @@ struct coff_link_hash_entry
 
   /* Pointer to array of auxiliary entries, if any.  */
   union internal_auxent *aux;
-
-  /* If this symbol requires an entry in the table of contents, the
-     processor specific backend uses this field to hold the offset
-     into the .toc section.  */
-  bfd_vma toc_offset;
 };
 
 /* COFF linker hash table.  */
index 2365c24e2e276d199ccc1c7673c1c15885581bf2..8024a2bc5fcfdf6163fbab95731760efe862cacd 100644 (file)
@@ -48,6 +48,7 @@ SECTION
 @menu
 @* Reading Symbols::
 @* Writing Symbols::
+@* Mini Symbols::
 @* typedef asymbol::
 @* symbol handling functions::
 @end menu
@@ -91,7 +92,7 @@ SUBSECTION
 
 
 INODE
-Writing Symbols, Mini symbols, Reading Symbols, Symbols
+Writing Symbols, Mini Symbols, Reading Symbols, Symbols
 SUBSECTION
        Writing symbols
 
@@ -138,9 +139,9 @@ SUBSECTION
        be described.
 
 INODE
-Mini symbols, typedef asymbol, Writing Symbols, Symbols
+Mini Symbols, typedef asymbol, Writing Symbols, Symbols
 SUBSECTION
-       Mini symbols
+       Mini Symbols
 
        Mini symbols provide read-only access to the symbol table.
        They use less memory space, but require more time to access.
@@ -166,7 +167,7 @@ SUBSECTION
 /*
 DOCDD
 INODE
-typedef asymbol, symbol handling functions, Mini symbols, Symbols
+typedef asymbol, symbol handling functions, Mini Symbols, Symbols
 
 */
 /*
@@ -262,15 +263,14 @@ CODE_FRAGMENT
 .      {* 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
 .
-.      {* 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
@@ -299,8 +299,8 @@ CODE_FRAGMENT
 
 #include "bfd.h"
 #include "sysdep.h"
-
 #include "libbfd.h"
+#include "bfdlink.h"
 #include "aout/stab_gnu.h"
 
 /*
@@ -634,12 +634,9 @@ _bfd_generic_read_minisymbols (abfd, dynamic, minisymsp, sizep)
   if (storage < 0)
     goto error_return;
 
-  syms = (asymbol **) malloc ((size_t) storage);
+  syms = (asymbol **) bfd_malloc ((size_t) storage);
   if (syms == NULL)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      goto error_return;
-    }
+    goto error_return;
 
   if (dynamic)
     symcount = bfd_canonicalize_dynamic_symtab (abfd, syms);
@@ -672,3 +669,402 @@ _bfd_generic_minisymbol_to_symbol (abfd, dynamic, minisym, 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.  */
+
+/* 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;
+  /* An malloc buffer to hold the file name.  */
+  char *filename;
+  /* Cached values to restart quickly.  */
+  bfd_vma cached_offset;
+  bfd_byte *cached_stab;
+  bfd_byte *cached_str;
+  bfd_size_type cached_stroff;
+};
+
+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, *stabend, *str;
+  bfd_size_type stroff;
+  bfd_vma fnaddr;
+  char *directory_name, *main_file_name, *current_file_name, *line_file_name;
+  char *fnname;
+  bfd_vma low_func_vma, low_line_vma;
+
+  *pfound = false;
+  *pfilename = bfd_get_filename (abfd);
+  *pfnname = NULL;
+  *pline = 0;
+
+  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;
+
+      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 = 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);
+
+      *pinfo = info;
+    }
+
+  /* We are passed a section relative offset.  The offsets in the
+     stabs information are absolute.  */
+  offset += bfd_get_section_vma (abfd, section);
+
+  /* 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)
+
+  /* It would be nice if we could skip ahead to the stabs symbols for
+     the next compilation unit to quickly scan through the compilation
+     units.  Unfortunately, since each line number gets a separate
+     stabs entry, it is entirely plausible that a large source file
+     will overflow the 16 bit count of stabs entries.  */
+  fnaddr = 0;
+  directory_name = NULL;
+  main_file_name = NULL;
+  current_file_name = NULL;
+  line_file_name = NULL;
+  fnname = NULL;
+  low_func_vma = 0;
+  low_line_vma = 0;
+
+  stabend = info->stabs + stabsize;
+
+  if (info->cached_stab == NULL || offset < info->cached_offset)
+    {
+      stab = info->stabs;
+      str = info->strs;
+      stroff = 0;
+    }
+  else
+    {
+      stab = info->cached_stab;
+      str = info->cached_str;
+      stroff = info->cached_stroff;
+    }
+
+  info->cached_offset = offset;
+
+  for (; stab < stabend; stab += STABSIZE)
+    {
+      boolean done;
+      bfd_vma val;
+      char *name;
+
+      done = false;
+
+      switch (stab[TYPEOFF])
+       {
+       case 0:
+         /* This is the first entry in a compilation unit.  */
+         if ((bfd_size_type) ((info->strs + strsize) - str) < stroff)
+           {
+             done = true;
+             break;
+           }
+         str += stroff;
+         stroff = bfd_get_32 (abfd, stab + VALOFF);
+         break;
+
+       case N_SO:
+         /* The main file name.  */
+
+         val = bfd_get_32 (abfd, stab + VALOFF);
+         if (val > offset)
+           {
+             done = true;
+             break;
+           }
+
+         name = str + bfd_get_32 (abfd, stab + STRDXOFF);
+
+         /* An empty string indicates the end of the compilation
+             unit.  */
+         if (*name == '\0')
+           {
+             /* If there are functions in different sections, they
+                 may have addresses larger than val, but we don't want
+                 to forget the file name.  When there are functions in
+                 different cases, there is supposed to be an N_FUN at
+                 the end of the function indicating where it ends.  */
+             if (low_func_vma < val || fnname == NULL)
+               main_file_name = NULL;
+             break;
+           }
+
+         /* We know that we have to get to at least this point in the
+             stabs entries for this offset.  */
+         info->cached_stab = stab;
+         info->cached_str = str;
+         info->cached_stroff = stroff;
+
+         current_file_name = name;
+
+         /* Look ahead to the next symbol.  Two consecutive N_SO
+             symbols are a directory and a file name.  */
+         if (stab + STABSIZE >= stabend
+             || *(stab + STABSIZE + TYPEOFF) != N_SO)
+           directory_name = NULL;
+         else
+           {
+             stab += STABSIZE;
+             directory_name = current_file_name;
+             current_file_name = str + bfd_get_32 (abfd, stab + STRDXOFF);
+           }
+
+         main_file_name = current_file_name;
+
+         break;
+
+       case N_SOL:
+         /* The name of an include file.  */
+         current_file_name = str + bfd_get_32 (abfd, stab + STRDXOFF);
+         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 = fnaddr + bfd_get_32 (abfd, stab + VALOFF);
+         if (val >= low_line_vma && val <= offset)
+           {
+             *pline = bfd_get_16 (abfd, stab + DESCOFF);
+             low_line_vma = val;
+             line_file_name = current_file_name;
+           }
+         break;
+
+       case N_FUN:
+         /* A function name.  */
+         val = bfd_get_32 (abfd, stab + VALOFF);
+         name = str + bfd_get_32 (abfd, stab + STRDXOFF);
+
+         /* An empty string here indicates the end of a function, and
+            the value is relative to fnaddr.  */
+
+         if (*name == '\0')
+           {
+             val += fnaddr;
+             if (val >= low_func_vma && val < offset)
+               fnname = NULL;
+           }
+         else
+           {
+             if (val >= low_func_vma && val <= offset)
+               {
+                 fnname = name;
+                 low_func_vma = val;
+               }
+
+              fnaddr = val;
+            }
+
+         break;
+       }
+
+      if (done)
+       break;
+    }
+
+  if (main_file_name == NULL)
+    {
+      /* No information found.  */
+      return true;
+    }
+
+  *pfound = true;
+
+  if (*pline != 0)
+    main_file_name = line_file_name;
+
+  if (main_file_name != NULL)
+    {
+      if (main_file_name[0] == '/' || directory_name == NULL)
+       *pfilename = main_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, main_file_name) != 0)
+           {
+             if (info->filename != NULL)
+               free (info->filename);
+             info->filename = (char *) bfd_malloc (dirlen +
+                                                   strlen (main_file_name)
+                                                   + 1);
+             if (info->filename == NULL)
+               return false;
+             strcpy (info->filename, directory_name);
+             strcpy (info->filename + dirlen, main_file_name);
+           }
+
+         *pfilename = info->filename;
+       }
+    }
+
+  if (fnname != 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 (fnname, ':');
+      if (s != NULL)
+       *s = '\0';
+
+      *pfnname = fnname;
+    }
+
+  return true;
+}