X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=ld%2Fldcref.c;h=19c9062f4c36307cbc6a7f237dcbf951abad1c48;hb=8eb67463b6edfdb06bb647c53ca02f691b456417;hp=2aceb1fde33f5e3b3ecd4461ca32c34123981127;hpb=2c310d1af682e0f63d3f0b745f5291f7b6926449;p=platform%2Fupstream%2Fbinutils.git diff --git a/ld/ldcref.c b/ld/ldcref.c index 2aceb1f..19c9062 100644 --- a/ld/ldcref.c +++ b/ld/ldcref.c @@ -1,33 +1,41 @@ /* ldcref.c -- output a cross reference table - Copyright (C) 1996 Free Software Foundation, Inc. + Copyright (C) 1996-2014 Free Software Foundation, Inc. Written by Ian Lance Taylor -This file is part of GLD, the Gnu Linker. + This file is part of the GNU Binutils. -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 your option) any later version. + 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 3 of the License, or + (at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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 -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + 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. */ -/* This file holds routines that manage the cross reference table. */ -#include "bfd.h" +/* This file holds routines that manage the cross reference table. + The table is used to generate cross reference reports. It is also + used to implement the NOCROSSREFS command in the linker script. */ + #include "sysdep.h" +#include "bfd.h" #include "bfdlink.h" #include "libiberty.h" +#include "demangle.h" +#include "objalloc.h" #include "ld.h" #include "ldmain.h" #include "ldmisc.h" +#include "ldexp.h" +#include "ldlang.h" /* We keep an instance of this structure for each reference to a symbol from a given object. */ @@ -52,7 +60,7 @@ struct cref_hash_entry { struct bfd_hash_entry root; /* The demangled name. */ - char *demangled; + const char *demangled; /* References to and definitions of this symbol. */ struct cref_ref *refs; }; @@ -64,13 +72,14 @@ struct cref_hash_table struct bfd_hash_table root; }; -/* Local functions. */ +/* Forward declarations. */ -static struct bfd_hash_entry *cref_hash_newfunc - PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); -static boolean cref_fill_array PARAMS ((struct cref_hash_entry *, PTR)); -static int cref_sort_array PARAMS ((const PTR, const PTR)); -static void output_one_cref PARAMS ((FILE *, struct cref_hash_entry *)); +static void output_one_cref (FILE *, struct cref_hash_entry *); +static void check_local_sym_xref (lang_input_statement_type *); +static bfd_boolean check_nocrossref (struct cref_hash_entry *, void *); +static void check_refs (const char *, bfd_boolean, asection *, bfd *, + struct lang_nocrossrefs *); +static void check_reloc_refs (bfd *, asection *, void *); /* Look up an entry in the cref hash table. */ @@ -83,7 +92,7 @@ static void output_one_cref PARAMS ((FILE *, struct cref_hash_entry *)); #define cref_hash_traverse(table, func, info) \ (bfd_hash_traverse \ (&(table)->root, \ - (boolean (*) PARAMS ((struct bfd_hash_entry *, PTR))) (func), \ + (bfd_boolean (*) (struct bfd_hash_entry *, void *)) (func), \ (info))) /* The cref hash table. */ @@ -92,19 +101,28 @@ static struct cref_hash_table cref_table; /* Whether the cref hash table has been initialized. */ -static boolean cref_initialized; +static bfd_boolean cref_initialized; /* The number of symbols seen so far. */ static size_t cref_symcount; +/* Used to take a snapshot of the cref hash table when starting to + add syms from an as-needed library. */ +static struct bfd_hash_entry **old_table; +static unsigned int old_size; +static unsigned int old_count; +static void * old_tab; +static void * alloc_mark; +static size_t tabsize, entsize, refsize; +static size_t old_symcount; + /* Create an entry in a cref hash table. */ static struct bfd_hash_entry * -cref_hash_newfunc (entry, table, string) - struct bfd_hash_entry *entry; - struct bfd_hash_table *table; - const char *string; +cref_hash_newfunc (struct bfd_hash_entry *entry, + struct bfd_hash_table *table, + const char *string) { struct cref_hash_entry *ret = (struct cref_hash_entry *) entry; @@ -114,7 +132,7 @@ cref_hash_newfunc (entry, table, string) ret = ((struct cref_hash_entry *) bfd_hash_allocate (table, sizeof (struct cref_hash_entry))); if (ret == NULL) - return (struct bfd_hash_entry *) ret; + return NULL; /* Call the allocation method of the superclass. */ ret = ((struct cref_hash_entry *) @@ -126,37 +144,36 @@ cref_hash_newfunc (entry, table, string) ret->refs = NULL; /* Keep a count of the number of entries created in the hash - table. */ + table. */ ++cref_symcount; } - return (struct bfd_hash_entry *) ret; + return &ret->root; } /* Add a symbol to the cref hash table. This is called for every - symbol that is seen during the link. */ + global symbol that is seen during the link. */ -/*ARGSUSED*/ void -add_cref (name, abfd, section, value) - const char *name; - bfd *abfd; - asection *section; - bfd_vma value; +add_cref (const char *name, + bfd *abfd, + asection *section, + bfd_vma value ATTRIBUTE_UNUSED) { struct cref_hash_entry *h; struct cref_ref *r; if (! cref_initialized) { - if (! bfd_hash_table_init (&cref_table.root, cref_hash_newfunc)) - einfo ("%X%P: bfd_hash_table_init of cref table failed: %E\n"); - cref_initialized = true; + if (!bfd_hash_table_init (&cref_table.root, cref_hash_newfunc, + sizeof (struct cref_hash_entry))) + einfo (_("%X%P: bfd_hash_table_init of cref table failed: %E\n")); + cref_initialized = TRUE; } - h = cref_hash_lookup (&cref_table, name, true, false); + h = cref_hash_lookup (&cref_table, name, TRUE, FALSE); if (h == NULL) - einfo ("%X%P: cref_hash_lookup failed: %E\n"); + einfo (_("%X%P: cref_hash_lookup failed: %E\n")); for (r = h->refs; r != NULL; r = r->next) if (r->abfd == abfd) @@ -164,55 +181,180 @@ add_cref (name, abfd, section, value) if (r == NULL) { - r = (struct cref_ref *) xmalloc (sizeof *r); + r = (struct cref_ref *) bfd_hash_allocate (&cref_table.root, sizeof *r); + if (r == NULL) + einfo (_("%X%P: cref alloc failed: %E\n")); r->next = h->refs; h->refs = r; r->abfd = abfd; - r->def = false; - r->common = false; - r->undef = false; + r->def = FALSE; + r->common = FALSE; + r->undef = FALSE; } if (bfd_is_und_section (section)) - r->undef = true; + r->undef = TRUE; else if (bfd_is_com_section (section)) - r->common = true; + r->common = TRUE; else - r->def = true; + r->def = TRUE; +} + +/* Called before loading an as-needed library to take a snapshot of + the cref hash table, and after we have loaded or found that the + library was not needed. */ + +bfd_boolean +handle_asneeded_cref (bfd *abfd ATTRIBUTE_UNUSED, + enum notice_asneeded_action act) +{ + unsigned int i; + + if (!cref_initialized) + return TRUE; + + if (act == notice_as_needed) + { + char *old_ent, *old_ref; + + for (i = 0; i < cref_table.root.size; i++) + { + struct bfd_hash_entry *p; + struct cref_hash_entry *c; + struct cref_ref *r; + + for (p = cref_table.root.table[i]; p != NULL; p = p->next) + { + entsize += cref_table.root.entsize; + c = (struct cref_hash_entry *) p; + for (r = c->refs; r != NULL; r = r->next) + refsize += sizeof (struct cref_ref); + } + } + + tabsize = cref_table.root.size * sizeof (struct bfd_hash_entry *); + old_tab = xmalloc (tabsize + entsize + refsize); + + alloc_mark = bfd_hash_allocate (&cref_table.root, 1); + if (alloc_mark == NULL) + return FALSE; + + memcpy (old_tab, cref_table.root.table, tabsize); + old_ent = (char *) old_tab + tabsize; + old_ref = (char *) old_ent + entsize; + old_table = cref_table.root.table; + old_size = cref_table.root.size; + old_count = cref_table.root.count; + old_symcount = cref_symcount; + + for (i = 0; i < cref_table.root.size; i++) + { + struct bfd_hash_entry *p; + struct cref_hash_entry *c; + struct cref_ref *r; + + for (p = cref_table.root.table[i]; p != NULL; p = p->next) + { + memcpy (old_ent, p, cref_table.root.entsize); + old_ent = (char *) old_ent + cref_table.root.entsize; + c = (struct cref_hash_entry *) p; + for (r = c->refs; r != NULL; r = r->next) + { + memcpy (old_ref, r, sizeof (struct cref_ref)); + old_ref = (char *) old_ref + sizeof (struct cref_ref); + } + } + } + return TRUE; + } + + if (act == notice_not_needed) + { + char *old_ent, *old_ref; + + if (old_tab == NULL) + { + /* The only way old_tab can be NULL is if the cref hash table + had not been initialised when notice_as_needed. */ + bfd_hash_table_free (&cref_table.root); + cref_initialized = FALSE; + return TRUE; + } + + old_ent = (char *) old_tab + tabsize; + old_ref = (char *) old_ent + entsize; + cref_table.root.table = old_table; + cref_table.root.size = old_size; + cref_table.root.count = old_count; + memcpy (cref_table.root.table, old_tab, tabsize); + cref_symcount = old_symcount; + + for (i = 0; i < cref_table.root.size; i++) + { + struct bfd_hash_entry *p; + struct cref_hash_entry *c; + struct cref_ref *r; + + for (p = cref_table.root.table[i]; p != NULL; p = p->next) + { + memcpy (p, old_ent, cref_table.root.entsize); + old_ent = (char *) old_ent + cref_table.root.entsize; + c = (struct cref_hash_entry *) p; + for (r = c->refs; r != NULL; r = r->next) + { + memcpy (r, old_ref, sizeof (struct cref_ref)); + old_ref = (char *) old_ref + sizeof (struct cref_ref); + } + } + } + + objalloc_free_block ((struct objalloc *) cref_table.root.memory, + alloc_mark); + } + else if (act != notice_needed) + return FALSE; + + free (old_tab); + old_tab = NULL; + return TRUE; } /* Copy the addresses of the hash table entries into an array. This is called via cref_hash_traverse. We also fill in the demangled name. */ -static boolean -cref_fill_array (h, data) - struct cref_hash_entry *h; - PTR data; +static bfd_boolean +cref_fill_array (struct cref_hash_entry *h, void *data) { struct cref_hash_entry ***pph = (struct cref_hash_entry ***) data; ASSERT (h->demangled == NULL); - h->demangled = demangle (h->root.string); + h->demangled = bfd_demangle (link_info.output_bfd, h->root.string, + DMGL_ANSI | DMGL_PARAMS); + if (h->demangled == NULL) + h->demangled = h->root.string; **pph = h; ++*pph; - return true; + return TRUE; } /* Sort an array of cref hash table entries by name. */ static int -cref_sort_array (a1, a2) - const PTR a1; - const PTR a2; +cref_sort_array (const void *a1, const void *a2) { - const struct cref_hash_entry **p1 = (const struct cref_hash_entry **) a1; - const struct cref_hash_entry **p2 = (const struct cref_hash_entry **) a2; + const struct cref_hash_entry * const *p1 = + (const struct cref_hash_entry * const *) a1; + const struct cref_hash_entry * const *p2 = + (const struct cref_hash_entry * const *) a2; - return strcmp ((*p1)->demangled, (*p2)->demangled); + if (demangling) + return strcmp ((*p1)->demangled, (*p2)->demangled); + else + return strcmp ((*p1)->root.string, (*p2)->root.string); } /* Write out the cref table. */ @@ -220,34 +362,34 @@ cref_sort_array (a1, a2) #define FILECOL (50) void -output_cref (fp) - FILE *fp; +output_cref (FILE *fp) { int len; struct cref_hash_entry **csyms, **csym_fill, **csym, **csym_end; + const char *msg; - fprintf (fp, "\nCross Reference Table\n\n"); - fprintf (fp, "Symbol"); - len = sizeof "Symbol" - 1; + fprintf (fp, _("\nCross Reference Table\n\n")); + msg = _("Symbol"); + fprintf (fp, "%s", msg); + len = strlen (msg); while (len < FILECOL) { - putc (' ' , fp); + putc (' ', fp); ++len; } - fprintf (fp, "File\n"); + fprintf (fp, _("File\n")); if (! cref_initialized) { - fprintf (fp, "No symbols\n"); + fprintf (fp, _("No symbols\n")); return; } - csyms = ((struct cref_hash_entry **) - xmalloc (cref_symcount * sizeof (*csyms))); + csyms = (struct cref_hash_entry **) xmalloc (cref_symcount * sizeof (*csyms)); csym_fill = csyms; cref_hash_traverse (&cref_table, cref_fill_array, &csym_fill); - ASSERT (csym_fill - csyms == cref_symcount); + ASSERT ((size_t) (csym_fill - csyms) == cref_symcount); qsort (csyms, cref_symcount, sizeof (*csyms), cref_sort_array); @@ -259,16 +401,14 @@ output_cref (fp) /* Output one entry in the cross reference table. */ static void -output_one_cref (fp, h) - FILE *fp; - struct cref_hash_entry *h; +output_one_cref (FILE *fp, struct cref_hash_entry *h) { int len; struct bfd_link_hash_entry *hl; struct cref_ref *r; - hl = bfd_link_hash_lookup (link_info.hash, h->root.string, false, - false, true); + hl = bfd_link_hash_lookup (link_info.hash, h->root.string, FALSE, + FALSE, TRUE); if (hl == NULL) einfo ("%P: symbol `%T' missing from main hash table\n", h->root.string); @@ -292,8 +432,16 @@ output_one_cref (fp, h) } } - fprintf (fp, "%s ", h->demangled); - len = strlen (h->demangled) + 1; + if (demangling) + { + fprintf (fp, "%s ", h->demangled); + len = strlen (h->demangled) + 1; + } + else + { + fprintf (fp, "%s ", h->root.string); + len = strlen (h->root.string) + 1; + } for (r = h->refs; r != NULL; r = r->next) { @@ -304,24 +452,267 @@ output_one_cref (fp, h) putc (' ', fp); ++len; } - finfo (fp, "%B\n", r->abfd); + lfinfo (fp, "%B\n", r->abfd); len = 0; } } for (r = h->refs; r != NULL; r = r->next) { - if (! r->def) + if (r->common) { while (len < FILECOL) { putc (' ', fp); ++len; } - finfo (fp, "%B\n", r->abfd); + lfinfo (fp, "%B\n", r->abfd); + len = 0; + } + } + + for (r = h->refs; r != NULL; r = r->next) + { + if (! r->def && ! r->common) + { + while (len < FILECOL) + { + putc (' ', fp); + ++len; + } + lfinfo (fp, "%B\n", r->abfd); len = 0; } } ASSERT (len == 0); } + +/* Check for prohibited cross references. */ + +void +check_nocrossrefs (void) +{ + if (! cref_initialized) + return; + + cref_hash_traverse (&cref_table, check_nocrossref, NULL); + + lang_for_each_file (check_local_sym_xref); +} + +/* Check for prohibited cross references to local and section symbols. */ + +static void +check_local_sym_xref (lang_input_statement_type *statement) +{ + bfd *abfd; + asymbol **syms; + + abfd = statement->the_bfd; + if (abfd == NULL) + return; + + if (!bfd_generic_link_read_symbols (abfd)) + einfo (_("%B%F: could not read symbols: %E\n"), abfd); + + for (syms = bfd_get_outsymbols (abfd); *syms; ++syms) + { + asymbol *sym = *syms; + if (sym->flags & (BSF_GLOBAL | BSF_WARNING | BSF_INDIRECT | BSF_FILE)) + continue; + if ((sym->flags & (BSF_LOCAL | BSF_SECTION_SYM)) != 0 + && sym->section->output_section != NULL) + { + const char *outsecname, *symname; + struct lang_nocrossrefs *ncrs; + struct lang_nocrossref *ncr; + + outsecname = sym->section->output_section->name; + symname = NULL; + if ((sym->flags & BSF_SECTION_SYM) == 0) + symname = sym->name; + for (ncrs = nocrossref_list; ncrs != NULL; ncrs = ncrs->next) + for (ncr = ncrs->list; ncr != NULL; ncr = ncr->next) + if (strcmp (ncr->name, outsecname) == 0) + check_refs (symname, FALSE, sym->section, abfd, ncrs); + } + } +} + +/* Check one symbol to see if it is a prohibited cross reference. */ + +static bfd_boolean +check_nocrossref (struct cref_hash_entry *h, void *ignore ATTRIBUTE_UNUSED) +{ + struct bfd_link_hash_entry *hl; + asection *defsec; + const char *defsecname; + struct lang_nocrossrefs *ncrs; + struct lang_nocrossref *ncr; + struct cref_ref *ref; + + hl = bfd_link_hash_lookup (link_info.hash, h->root.string, FALSE, + FALSE, TRUE); + if (hl == NULL) + { + einfo (_("%P: symbol `%T' missing from main hash table\n"), + h->root.string); + return TRUE; + } + + if (hl->type != bfd_link_hash_defined + && hl->type != bfd_link_hash_defweak) + return TRUE; + + defsec = hl->u.def.section->output_section; + if (defsec == NULL) + return TRUE; + defsecname = bfd_get_section_name (defsec->owner, defsec); + + for (ncrs = nocrossref_list; ncrs != NULL; ncrs = ncrs->next) + for (ncr = ncrs->list; ncr != NULL; ncr = ncr->next) + if (strcmp (ncr->name, defsecname) == 0) + for (ref = h->refs; ref != NULL; ref = ref->next) + check_refs (hl->root.string, TRUE, hl->u.def.section, + ref->abfd, ncrs); + + return TRUE; +} + +/* The struct is used to pass information from check_refs to + check_reloc_refs through bfd_map_over_sections. */ + +struct check_refs_info { + const char *sym_name; + asection *defsec; + struct lang_nocrossrefs *ncrs; + asymbol **asymbols; + bfd_boolean global; +}; + +/* This function is called for each symbol defined in a section which + prohibits cross references. We need to look through all references + to this symbol, and ensure that the references are not from + prohibited sections. */ + +static void +check_refs (const char *name, + bfd_boolean global, + asection *sec, + bfd *abfd, + struct lang_nocrossrefs *ncrs) +{ + struct check_refs_info info; + + /* We need to look through the relocations for this BFD, to see + if any of the relocations which refer to this symbol are from + a prohibited section. Note that we need to do this even for + the BFD in which the symbol is defined, since even a single + BFD might contain a prohibited cross reference. */ + + if (!bfd_generic_link_read_symbols (abfd)) + einfo (_("%B%F: could not read symbols: %E\n"), abfd); + + info.sym_name = name; + info.global = global; + info.defsec = sec; + info.ncrs = ncrs; + info.asymbols = bfd_get_outsymbols (abfd); + bfd_map_over_sections (abfd, check_reloc_refs, &info); +} + +/* This is called via bfd_map_over_sections. INFO->SYM_NAME is a symbol + defined in INFO->DEFSECNAME. If this section maps into any of the + sections listed in INFO->NCRS, other than INFO->DEFSECNAME, then we + look through the relocations. If any of the relocations are to + INFO->SYM_NAME, then we report a prohibited cross reference error. */ + +static void +check_reloc_refs (bfd *abfd, asection *sec, void *iarg) +{ + struct check_refs_info *info = (struct check_refs_info *) iarg; + asection *outsec; + const char *outsecname; + asection *outdefsec; + const char *outdefsecname; + struct lang_nocrossref *ncr; + const char *symname; + bfd_boolean global; + long relsize; + arelent **relpp; + long relcount; + arelent **p, **pend; + + outsec = sec->output_section; + outsecname = bfd_get_section_name (outsec->owner, outsec); + + outdefsec = info->defsec->output_section; + outdefsecname = bfd_get_section_name (outdefsec->owner, outdefsec); + + /* The section where the symbol is defined is permitted. */ + if (strcmp (outsecname, outdefsecname) == 0) + return; + + for (ncr = info->ncrs->list; ncr != NULL; ncr = ncr->next) + if (strcmp (outsecname, ncr->name) == 0) + break; + + if (ncr == NULL) + return; + + /* This section is one for which cross references are prohibited. + Look through the relocations, and see if any of them are to + INFO->SYM_NAME. If INFO->SYMNAME is NULL, check for relocations + against the section symbol. If INFO->GLOBAL is TRUE, the + definition is global, check for relocations against the global + symbols. Otherwise check for relocations against the local and + section symbols. */ + + symname = info->sym_name; + global = info->global; + + relsize = bfd_get_reloc_upper_bound (abfd, sec); + if (relsize < 0) + einfo (_("%B%F: could not read relocs: %E\n"), abfd); + if (relsize == 0) + return; + + relpp = (arelent **) xmalloc (relsize); + relcount = bfd_canonicalize_reloc (abfd, sec, relpp, info->asymbols); + if (relcount < 0) + einfo (_("%B%F: could not read relocs: %E\n"), abfd); + + p = relpp; + pend = p + relcount; + for (; p < pend && *p != NULL; p++) + { + arelent *q = *p; + + if (q->sym_ptr_ptr != NULL + && *q->sym_ptr_ptr != NULL + && ((global + && (bfd_is_und_section (bfd_get_section (*q->sym_ptr_ptr)) + || bfd_is_com_section (bfd_get_section (*q->sym_ptr_ptr)) + || ((*q->sym_ptr_ptr)->flags & (BSF_GLOBAL + | BSF_WEAK)) != 0)) + || (!global + && ((*q->sym_ptr_ptr)->flags & (BSF_LOCAL + | BSF_SECTION_SYM)) != 0 + && bfd_get_section (*q->sym_ptr_ptr) == info->defsec)) + && (symname != NULL + ? strcmp (bfd_asymbol_name (*q->sym_ptr_ptr), symname) == 0 + : ((*q->sym_ptr_ptr)->flags & BSF_SECTION_SYM) != 0)) + { + /* We found a reloc for the symbol. The symbol is defined + in OUTSECNAME. This reloc is from a section which is + mapped into a section from which references to OUTSECNAME + are prohibited. We must report an error. */ + einfo (_("%X%C: prohibited cross reference from %s to `%T' in %s\n"), + abfd, sec, q->address, outsecname, + bfd_asymbol_name (*q->sym_ptr_ptr), outdefsecname); + } + } + + free (relpp); +}