/* 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, 1996 Free Software Foundation, Inc.
Written by Cygnus Support.
This file is part of BFD, the Binary File Descriptor library.
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
@menu
@* Reading Symbols::
@* Writing Symbols::
+@* Mini Symbols::
@* typedef asymbol::
@* symbol handling functions::
@end menu
INODE
-Writing Symbols, typedef asymbol, Reading Symbols, Symbols
+Writing Symbols, Mini Symbols, Reading Symbols, Symbols
SUBSECTION
Writing symbols
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.
+
*/
/*
DOCDD
INODE
-typedef asymbol, symbol handling functions, Writing Symbols, Symbols
+typedef asymbol, symbol handling functions, Mini Symbols, Symbols
*/
/*
. {* 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
. {* 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
. relative. This will always be non NULL, there are special
-. sections for undefined and absolute symbols *}
+. sections for undefined and absolute symbols. *}
. struct sec *section;
.
-. {* Back end special data. This is being phased out in favour
-. of making this a union. *}
-. PTR udata;
+. {* Back end special data. *}
+. union
+. {
+. PTR p;
+. bfd_vma i;
+. } udata;
.
.} asymbol;
*/
#include "bfd.h"
#include "sysdep.h"
-
#include "libbfd.h"
+#include "bfdlink.h"
#include "aout/stab_gnu.h"
+static char coff_section_type PARAMS ((const char *));
+
/*
DOCDD
INODE
}
/* This presumes that a symbol can not be both BSF_DEBUGGING and
- BSF_DYNAMIC. */
+ 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) ? 'l' : ' ',
- (type & BSF_GLOBAL) ? 'g' : ' ',
+ ((type & BSF_LOCAL)
+ ? (type & BSF_GLOBAL) ? '!' : 'l'
+ : (type & BSF_GLOBAL) ? 'g' : ' '),
(type & BSF_WEAK) ? 'w' : ' ',
(type & BSF_CONSTRUCTOR) ? 'C' : ' ',
(type & BSF_WARNING) ? 'W' : ' ',
(type & BSF_INDIRECT) ? 'I' : ' ',
- (type & BSF_DEBUGGING) ? 'd'
- : (type & BSF_DYNAMIC) ? 'D' : ' ');
+ (type & BSF_DEBUGGING) ? 'd' : (type & BSF_DYNAMIC) ? 'D' : ' ',
+ ((type & BSF_FUNCTION)
+ ? 'F'
+ : ((type & BSF_FILE)
+ ? 'f'
+ : ((type & BSF_OBJECT) ? 'O' : ' '))));
}
{
{"*DEBUG*", 'N'},
{".bss", 'b'},
+ {"zerovars", 'b'}, /* MRI .bss */
{".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'},
+ {"code", 't'}, /* MRI .text */
{0, 0}
};
/* Return the single-character symbol type corresponding to
- section S, or '?' for an unknown COFF section. */
+ section S, or '?' for an unknown COFF section.
+
+ Check for any leading string which matches, so .text5 returns
+ 't' as well as .text */
static char
coff_section_type (s)
- char *s;
+ const char *s;
{
CONST struct section_to_type *t;
- for (t = &stt[0]; t->section; t++)
- if (!strcmp (s, t->section))
+ for (t = &stt[0]; t->section; t++)
+ if (!strncmp (s, t->section, strlen (t->section)))
return t->type;
+
return '?';
}
return 'U';
if (bfd_is_ind_section (symbol->section))
return 'I';
+ if (symbol->flags & BSF_WEAK)
+ return 'W';
if (!(symbol->flags & (BSF_GLOBAL | BSF_LOCAL)))
return '?';
{
abort ();
}
+
+/*
+FUNCTION
+ bfd_copy_private_symbol_data
+
+SYNOPSIS
+ boolean bfd_copy_private_symbol_data(bfd *ibfd, asymbol *isym, bfd *obfd, asymbol *osym);
+
+DESCRIPTION
+ Copy private symbol information from @var{isym} in the BFD
+ @var{ibfd} to the symbol @var{osym} in the BFD @var{obfd}.
+ Return <<true>> on success, <<false>> on error. Possible error
+ returns are:
+
+ o <<bfd_error_no_memory>> -
+ 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, \
+. (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. */
+
+/* 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 = (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);
+
+ *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);
+
+ /* 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 = (char *) 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 = ((char *) 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 = ((char *) 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 = (char *) 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;
+}