xref: /netbsd-src/external/gpl3/binutils/dist/ld/ldcref.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
12a6b7db3Sskrll /* ldcref.c -- output a cross reference table
2*cb63e24eSchristos    Copyright (C) 1996-2024 Free Software Foundation, Inc.
32a6b7db3Sskrll    Written by Ian Lance Taylor <ian@cygnus.com>
42a6b7db3Sskrll 
52a6b7db3Sskrll    This file is part of the GNU Binutils.
62a6b7db3Sskrll 
72a6b7db3Sskrll    This program is free software; you can redistribute it and/or modify
82a6b7db3Sskrll    it under the terms of the GNU General Public License as published by
92a6b7db3Sskrll    the Free Software Foundation; either version 3 of the License, or
102a6b7db3Sskrll    (at your option) any later version.
112a6b7db3Sskrll 
122a6b7db3Sskrll    This program is distributed in the hope that it will be useful,
132a6b7db3Sskrll    but WITHOUT ANY WARRANTY; without even the implied warranty of
142a6b7db3Sskrll    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
152a6b7db3Sskrll    GNU General Public License for more details.
162a6b7db3Sskrll 
172a6b7db3Sskrll    You should have received a copy of the GNU General Public License
182a6b7db3Sskrll    along with this program; if not, write to the Free Software
192a6b7db3Sskrll    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
202a6b7db3Sskrll    MA 02110-1301, USA.  */
212a6b7db3Sskrll 
222a6b7db3Sskrll 
232a6b7db3Sskrll /* This file holds routines that manage the cross reference table.
242a6b7db3Sskrll    The table is used to generate cross reference reports.  It is also
252a6b7db3Sskrll    used to implement the NOCROSSREFS command in the linker script.  */
262a6b7db3Sskrll 
272a6b7db3Sskrll #include "sysdep.h"
282a6b7db3Sskrll #include "bfd.h"
292a6b7db3Sskrll #include "bfdlink.h"
306f4ced0bSchristos #include "ctf-api.h"
312a6b7db3Sskrll #include "libiberty.h"
322a6b7db3Sskrll #include "demangle.h"
332a6b7db3Sskrll #include "objalloc.h"
342a6b7db3Sskrll 
352a6b7db3Sskrll #include "ld.h"
362a6b7db3Sskrll #include "ldmain.h"
372a6b7db3Sskrll #include "ldmisc.h"
382a6b7db3Sskrll #include "ldexp.h"
392a6b7db3Sskrll #include "ldlang.h"
402a6b7db3Sskrll 
412a6b7db3Sskrll /* We keep an instance of this structure for each reference to a
422a6b7db3Sskrll    symbol from a given object.  */
432a6b7db3Sskrll 
449573673dSchristos struct cref_ref
459573673dSchristos {
462a6b7db3Sskrll   /* The next reference.  */
472a6b7db3Sskrll   struct cref_ref *next;
482a6b7db3Sskrll   /* The object.  */
492a6b7db3Sskrll   bfd *abfd;
502a6b7db3Sskrll   /* True if the symbol is defined.  */
512a6b7db3Sskrll   unsigned int def : 1;
522a6b7db3Sskrll   /* True if the symbol is common.  */
532a6b7db3Sskrll   unsigned int common : 1;
542a6b7db3Sskrll   /* True if the symbol is undefined.  */
552a6b7db3Sskrll   unsigned int undef : 1;
562a6b7db3Sskrll };
572a6b7db3Sskrll 
582a6b7db3Sskrll /* We keep a hash table of symbols.  Each entry looks like this.  */
592a6b7db3Sskrll 
609573673dSchristos struct cref_hash_entry
619573673dSchristos {
622a6b7db3Sskrll   struct bfd_hash_entry root;
632a6b7db3Sskrll   /* The demangled name.  */
642a6b7db3Sskrll   const char *demangled;
652a6b7db3Sskrll   /* References to and definitions of this symbol.  */
662a6b7db3Sskrll   struct cref_ref *refs;
672a6b7db3Sskrll };
682a6b7db3Sskrll 
692a6b7db3Sskrll /* This is what the hash table looks like.  */
702a6b7db3Sskrll 
719573673dSchristos struct cref_hash_table
729573673dSchristos {
732a6b7db3Sskrll   struct bfd_hash_table root;
742a6b7db3Sskrll };
752a6b7db3Sskrll 
762a6b7db3Sskrll /* Forward declarations.  */
772a6b7db3Sskrll 
782a6b7db3Sskrll static void output_one_cref (FILE *, struct cref_hash_entry *);
792a6b7db3Sskrll static void check_local_sym_xref (lang_input_statement_type *);
804f645668Schristos static bool check_nocrossref (struct cref_hash_entry *, void *);
814f645668Schristos static void check_refs (const char *, bool, asection *, bfd *,
822a6b7db3Sskrll 			struct lang_nocrossrefs *);
832a6b7db3Sskrll static void check_reloc_refs (bfd *, asection *, void *);
842a6b7db3Sskrll 
852a6b7db3Sskrll /* Look up an entry in the cref hash table.  */
862a6b7db3Sskrll 
872a6b7db3Sskrll #define cref_hash_lookup(table, string, create, copy)		\
882a6b7db3Sskrll   ((struct cref_hash_entry *)					\
892a6b7db3Sskrll    bfd_hash_lookup (&(table)->root, (string), (create), (copy)))
902a6b7db3Sskrll 
912a6b7db3Sskrll /* Traverse the cref hash table.  */
922a6b7db3Sskrll 
932a6b7db3Sskrll #define cref_hash_traverse(table, func, info)				\
942a6b7db3Sskrll   (bfd_hash_traverse							\
952a6b7db3Sskrll    (&(table)->root,							\
964f645668Schristos     (bool (*) (struct bfd_hash_entry *, void *)) (func), (info)))
972a6b7db3Sskrll 
982a6b7db3Sskrll /* The cref hash table.  */
992a6b7db3Sskrll 
1002a6b7db3Sskrll static struct cref_hash_table cref_table;
1012a6b7db3Sskrll 
1022a6b7db3Sskrll /* Whether the cref hash table has been initialized.  */
1032a6b7db3Sskrll 
1044f645668Schristos static bool cref_initialized;
1052a6b7db3Sskrll 
1062a6b7db3Sskrll /* The number of symbols seen so far.  */
1072a6b7db3Sskrll 
1082a6b7db3Sskrll static size_t cref_symcount;
1092a6b7db3Sskrll 
1102a6b7db3Sskrll /* Used to take a snapshot of the cref hash table when starting to
1112a6b7db3Sskrll    add syms from an as-needed library.  */
1122a6b7db3Sskrll static struct bfd_hash_entry **old_table;
1132a6b7db3Sskrll static unsigned int old_size;
1142a6b7db3Sskrll static unsigned int old_count;
1152a6b7db3Sskrll static void *old_tab;
1162a6b7db3Sskrll static void *alloc_mark;
1172a6b7db3Sskrll static size_t tabsize, entsize, refsize;
1182a6b7db3Sskrll static size_t old_symcount;
1192a6b7db3Sskrll 
1202a6b7db3Sskrll /* Create an entry in a cref hash table.  */
1212a6b7db3Sskrll 
1222a6b7db3Sskrll static struct bfd_hash_entry *
cref_hash_newfunc(struct bfd_hash_entry * entry,struct bfd_hash_table * table,const char * string)1232a6b7db3Sskrll cref_hash_newfunc (struct bfd_hash_entry *entry,
1242a6b7db3Sskrll 		   struct bfd_hash_table *table,
1252a6b7db3Sskrll 		   const char *string)
1262a6b7db3Sskrll {
1272a6b7db3Sskrll   struct cref_hash_entry *ret = (struct cref_hash_entry *) entry;
1282a6b7db3Sskrll 
1292a6b7db3Sskrll   /* Allocate the structure if it has not already been allocated by a
1302a6b7db3Sskrll      subclass.  */
1312a6b7db3Sskrll   if (ret == NULL)
1322a6b7db3Sskrll     ret = ((struct cref_hash_entry *)
1332a6b7db3Sskrll 	   bfd_hash_allocate (table, sizeof (struct cref_hash_entry)));
1342a6b7db3Sskrll   if (ret == NULL)
1352a6b7db3Sskrll     return NULL;
1362a6b7db3Sskrll 
1372a6b7db3Sskrll   /* Call the allocation method of the superclass.  */
1382a6b7db3Sskrll   ret = ((struct cref_hash_entry *)
1392a6b7db3Sskrll 	 bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
1402a6b7db3Sskrll   if (ret != NULL)
1412a6b7db3Sskrll     {
1422a6b7db3Sskrll       /* Set local fields.  */
1432a6b7db3Sskrll       ret->demangled = NULL;
1442a6b7db3Sskrll       ret->refs = NULL;
1452a6b7db3Sskrll 
1462a6b7db3Sskrll       /* Keep a count of the number of entries created in the hash
1472a6b7db3Sskrll 	 table.  */
1482a6b7db3Sskrll       ++cref_symcount;
1492a6b7db3Sskrll     }
1502a6b7db3Sskrll 
1512a6b7db3Sskrll   return &ret->root;
1522a6b7db3Sskrll }
1532a6b7db3Sskrll 
1542a6b7db3Sskrll /* Add a symbol to the cref hash table.  This is called for every
1552a6b7db3Sskrll    global symbol that is seen during the link.  */
1562a6b7db3Sskrll 
1572a6b7db3Sskrll void
add_cref(const char * name,bfd * abfd,asection * section,bfd_vma value ATTRIBUTE_UNUSED)1582a6b7db3Sskrll add_cref (const char *name,
1592a6b7db3Sskrll 	  bfd *abfd,
1602a6b7db3Sskrll 	  asection *section,
1612a6b7db3Sskrll 	  bfd_vma value ATTRIBUTE_UNUSED)
1622a6b7db3Sskrll {
1632a6b7db3Sskrll   struct cref_hash_entry *h;
1642a6b7db3Sskrll   struct cref_ref *r;
1652a6b7db3Sskrll 
1662a6b7db3Sskrll   if (!cref_initialized)
1672a6b7db3Sskrll     {
1682a6b7db3Sskrll       if (!bfd_hash_table_init (&cref_table.root, cref_hash_newfunc,
1692a6b7db3Sskrll 				sizeof (struct cref_hash_entry)))
1702a6b7db3Sskrll 	einfo (_("%X%P: bfd_hash_table_init of cref table failed: %E\n"));
1714f645668Schristos       cref_initialized = true;
1722a6b7db3Sskrll     }
1732a6b7db3Sskrll 
1744f645668Schristos   h = cref_hash_lookup (&cref_table, name, true, false);
1752a6b7db3Sskrll   if (h == NULL)
1762a6b7db3Sskrll     einfo (_("%X%P: cref_hash_lookup failed: %E\n"));
1772a6b7db3Sskrll 
1782a6b7db3Sskrll   for (r = h->refs; r != NULL; r = r->next)
1792a6b7db3Sskrll     if (r->abfd == abfd)
1802a6b7db3Sskrll       break;
1812a6b7db3Sskrll 
1822a6b7db3Sskrll   if (r == NULL)
1832a6b7db3Sskrll     {
184be9ac0eaSchristos       r = (struct cref_ref *) bfd_hash_allocate (&cref_table.root, sizeof *r);
1852a6b7db3Sskrll       if (r == NULL)
1862a6b7db3Sskrll 	einfo (_("%X%P: cref alloc failed: %E\n"));
1872a6b7db3Sskrll       r->next = h->refs;
1882a6b7db3Sskrll       h->refs = r;
1892a6b7db3Sskrll       r->abfd = abfd;
1904f645668Schristos       r->def = false;
1914f645668Schristos       r->common = false;
1924f645668Schristos       r->undef = false;
1932a6b7db3Sskrll     }
1942a6b7db3Sskrll 
1952a6b7db3Sskrll   if (bfd_is_und_section (section))
1964f645668Schristos     r->undef = true;
1972a6b7db3Sskrll   else if (bfd_is_com_section (section))
1984f645668Schristos     r->common = true;
1992a6b7db3Sskrll   else
2004f645668Schristos     r->def = true;
2012a6b7db3Sskrll }
2022a6b7db3Sskrll 
2032a6b7db3Sskrll /* Called before loading an as-needed library to take a snapshot of
2042a6b7db3Sskrll    the cref hash table, and after we have loaded or found that the
2052a6b7db3Sskrll    library was not needed.  */
2062a6b7db3Sskrll 
2074f645668Schristos bool
handle_asneeded_cref(bfd * abfd ATTRIBUTE_UNUSED,enum notice_asneeded_action act)2082a6b7db3Sskrll handle_asneeded_cref (bfd *abfd ATTRIBUTE_UNUSED,
2092a6b7db3Sskrll 		      enum notice_asneeded_action act)
2102a6b7db3Sskrll {
2112a6b7db3Sskrll   unsigned int i;
2122a6b7db3Sskrll 
2132a6b7db3Sskrll   if (!cref_initialized)
2144f645668Schristos     return true;
2152a6b7db3Sskrll 
2162a6b7db3Sskrll   if (act == notice_as_needed)
2172a6b7db3Sskrll     {
2182a6b7db3Sskrll       char *old_ent, *old_ref;
2192a6b7db3Sskrll 
2202a6b7db3Sskrll       for (i = 0; i < cref_table.root.size; i++)
2212a6b7db3Sskrll 	{
2222a6b7db3Sskrll 	  struct bfd_hash_entry *p;
2232a6b7db3Sskrll 	  struct cref_hash_entry *c;
2242a6b7db3Sskrll 	  struct cref_ref *r;
2252a6b7db3Sskrll 
2262a6b7db3Sskrll 	  for (p = cref_table.root.table[i]; p != NULL; p = p->next)
2272a6b7db3Sskrll 	    {
2282a6b7db3Sskrll 	      entsize += cref_table.root.entsize;
2292a6b7db3Sskrll 	      c = (struct cref_hash_entry *) p;
2302a6b7db3Sskrll 	      for (r = c->refs; r != NULL; r = r->next)
231be9ac0eaSchristos 		refsize += sizeof (struct cref_ref);
2322a6b7db3Sskrll 	    }
2332a6b7db3Sskrll 	}
2342a6b7db3Sskrll 
2352a6b7db3Sskrll       tabsize = cref_table.root.size * sizeof (struct bfd_hash_entry *);
2362a6b7db3Sskrll       old_tab = xmalloc (tabsize + entsize + refsize);
2372a6b7db3Sskrll 
2382a6b7db3Sskrll       alloc_mark = bfd_hash_allocate (&cref_table.root, 1);
2392a6b7db3Sskrll       if (alloc_mark == NULL)
2404f645668Schristos 	return false;
2412a6b7db3Sskrll 
2422a6b7db3Sskrll       memcpy (old_tab, cref_table.root.table, tabsize);
2432a6b7db3Sskrll       old_ent = (char *) old_tab + tabsize;
2442a6b7db3Sskrll       old_ref = (char *) old_ent + entsize;
2452a6b7db3Sskrll       old_table = cref_table.root.table;
2462a6b7db3Sskrll       old_size = cref_table.root.size;
2472a6b7db3Sskrll       old_count = cref_table.root.count;
2482a6b7db3Sskrll       old_symcount = cref_symcount;
2492a6b7db3Sskrll 
2502a6b7db3Sskrll       for (i = 0; i < cref_table.root.size; i++)
2512a6b7db3Sskrll 	{
2522a6b7db3Sskrll 	  struct bfd_hash_entry *p;
2532a6b7db3Sskrll 	  struct cref_hash_entry *c;
2542a6b7db3Sskrll 	  struct cref_ref *r;
2552a6b7db3Sskrll 
2562a6b7db3Sskrll 	  for (p = cref_table.root.table[i]; p != NULL; p = p->next)
2572a6b7db3Sskrll 	    {
2582a6b7db3Sskrll 	      memcpy (old_ent, p, cref_table.root.entsize);
2592a6b7db3Sskrll 	      old_ent = (char *) old_ent + cref_table.root.entsize;
2602a6b7db3Sskrll 	      c = (struct cref_hash_entry *) p;
2612a6b7db3Sskrll 	      for (r = c->refs; r != NULL; r = r->next)
2622a6b7db3Sskrll 		{
263be9ac0eaSchristos 		  memcpy (old_ref, r, sizeof (struct cref_ref));
264be9ac0eaSchristos 		  old_ref = (char *) old_ref + sizeof (struct cref_ref);
2652a6b7db3Sskrll 		}
2662a6b7db3Sskrll 	    }
2672a6b7db3Sskrll 	}
2684f645668Schristos       return true;
2692a6b7db3Sskrll     }
2702a6b7db3Sskrll 
2712a6b7db3Sskrll   if (act == notice_not_needed)
2722a6b7db3Sskrll     {
2732a6b7db3Sskrll       char *old_ent, *old_ref;
2742a6b7db3Sskrll 
2752a6b7db3Sskrll       if (old_tab == NULL)
2762a6b7db3Sskrll 	{
2772a6b7db3Sskrll 	  /* The only way old_tab can be NULL is if the cref hash table
2782a6b7db3Sskrll 	     had not been initialised when notice_as_needed.  */
2792a6b7db3Sskrll 	  bfd_hash_table_free (&cref_table.root);
2804f645668Schristos 	  cref_initialized = false;
2814f645668Schristos 	  return true;
2822a6b7db3Sskrll 	}
2832a6b7db3Sskrll 
2842a6b7db3Sskrll       old_ent = (char *) old_tab + tabsize;
2852a6b7db3Sskrll       old_ref = (char *) old_ent + entsize;
2862a6b7db3Sskrll       cref_table.root.table = old_table;
2872a6b7db3Sskrll       cref_table.root.size = old_size;
2882a6b7db3Sskrll       cref_table.root.count = old_count;
2892a6b7db3Sskrll       memcpy (cref_table.root.table, old_tab, tabsize);
2902a6b7db3Sskrll       cref_symcount = old_symcount;
2912a6b7db3Sskrll 
2922a6b7db3Sskrll       for (i = 0; i < cref_table.root.size; i++)
2932a6b7db3Sskrll 	{
2942a6b7db3Sskrll 	  struct bfd_hash_entry *p;
2952a6b7db3Sskrll 	  struct cref_hash_entry *c;
2962a6b7db3Sskrll 	  struct cref_ref *r;
2972a6b7db3Sskrll 
2982a6b7db3Sskrll 	  for (p = cref_table.root.table[i]; p != NULL; p = p->next)
2992a6b7db3Sskrll 	    {
3002a6b7db3Sskrll 	      memcpy (p, old_ent, cref_table.root.entsize);
3012a6b7db3Sskrll 	      old_ent = (char *) old_ent + cref_table.root.entsize;
3022a6b7db3Sskrll 	      c = (struct cref_hash_entry *) p;
3032a6b7db3Sskrll 	      for (r = c->refs; r != NULL; r = r->next)
3042a6b7db3Sskrll 		{
305be9ac0eaSchristos 		  memcpy (r, old_ref, sizeof (struct cref_ref));
306be9ac0eaSchristos 		  old_ref = (char *) old_ref + sizeof (struct cref_ref);
3072a6b7db3Sskrll 		}
3082a6b7db3Sskrll 	    }
3092a6b7db3Sskrll 	}
3102a6b7db3Sskrll 
3112a6b7db3Sskrll       objalloc_free_block ((struct objalloc *) cref_table.root.memory,
3122a6b7db3Sskrll 			   alloc_mark);
3132a6b7db3Sskrll     }
3142a6b7db3Sskrll   else if (act != notice_needed)
3154f645668Schristos     return false;
3162a6b7db3Sskrll 
3172a6b7db3Sskrll   free (old_tab);
3182a6b7db3Sskrll   old_tab = NULL;
3194f645668Schristos   return true;
3202a6b7db3Sskrll }
3212a6b7db3Sskrll 
3222a6b7db3Sskrll /* Copy the addresses of the hash table entries into an array.  This
3232a6b7db3Sskrll    is called via cref_hash_traverse.  We also fill in the demangled
3242a6b7db3Sskrll    name.  */
3252a6b7db3Sskrll 
3264f645668Schristos static bool
cref_fill_array(struct cref_hash_entry * h,void * data)3272a6b7db3Sskrll cref_fill_array (struct cref_hash_entry *h, void *data)
3282a6b7db3Sskrll {
329be9ac0eaSchristos   struct cref_hash_entry ***pph = (struct cref_hash_entry ***) data;
3302a6b7db3Sskrll 
3312a6b7db3Sskrll   ASSERT (h->demangled == NULL);
3322a6b7db3Sskrll   h->demangled = bfd_demangle (link_info.output_bfd, h->root.string,
3332a6b7db3Sskrll 			       DMGL_ANSI | DMGL_PARAMS);
3342a6b7db3Sskrll   if (h->demangled == NULL)
3352a6b7db3Sskrll     h->demangled = h->root.string;
3362a6b7db3Sskrll 
3372a6b7db3Sskrll   **pph = h;
3382a6b7db3Sskrll 
3392a6b7db3Sskrll   ++*pph;
3402a6b7db3Sskrll 
3414f645668Schristos   return true;
3422a6b7db3Sskrll }
3432a6b7db3Sskrll 
3442a6b7db3Sskrll /* Sort an array of cref hash table entries by name.  */
3452a6b7db3Sskrll 
3462a6b7db3Sskrll static int
cref_sort_array(const void * a1,const void * a2)3472a6b7db3Sskrll cref_sort_array (const void *a1, const void *a2)
3482a6b7db3Sskrll {
3498cbf5cb7Schristos   const struct cref_hash_entry *const *p1
3508cbf5cb7Schristos     = (const struct cref_hash_entry *const *) a1;
3518cbf5cb7Schristos   const struct cref_hash_entry *const *p2
3528cbf5cb7Schristos     = (const struct cref_hash_entry *const *) a2;
3532a6b7db3Sskrll 
3549573673dSchristos   if (demangling)
3552a6b7db3Sskrll     return strcmp ((*p1)->demangled, (*p2)->demangled);
3569573673dSchristos   else
3579573673dSchristos     return strcmp ((*p1)->root.string, (*p2)->root.string);
3582a6b7db3Sskrll }
3592a6b7db3Sskrll 
3602a6b7db3Sskrll /* Write out the cref table.  */
3612a6b7db3Sskrll 
3622a6b7db3Sskrll #define FILECOL (50)
3632a6b7db3Sskrll 
3642a6b7db3Sskrll void
output_cref(FILE * fp)3652a6b7db3Sskrll output_cref (FILE *fp)
3662a6b7db3Sskrll {
3672a6b7db3Sskrll   int len;
3682a6b7db3Sskrll   struct cref_hash_entry **csyms, **csym_fill, **csym, **csym_end;
3692a6b7db3Sskrll   const char *msg;
3702a6b7db3Sskrll 
3712a6b7db3Sskrll   fprintf (fp, _("\nCross Reference Table\n\n"));
3722a6b7db3Sskrll   msg = _("Symbol");
3732a6b7db3Sskrll   fprintf (fp, "%s", msg);
3742a6b7db3Sskrll   len = strlen (msg);
3752a6b7db3Sskrll   while (len < FILECOL)
3762a6b7db3Sskrll     {
3772a6b7db3Sskrll       putc (' ', fp);
3782a6b7db3Sskrll       ++len;
3792a6b7db3Sskrll     }
3802a6b7db3Sskrll   fprintf (fp, _("File\n"));
3812a6b7db3Sskrll 
3822a6b7db3Sskrll   if (!cref_initialized)
3832a6b7db3Sskrll     {
3842a6b7db3Sskrll       fprintf (fp, _("No symbols\n"));
3852a6b7db3Sskrll       return;
3862a6b7db3Sskrll     }
3872a6b7db3Sskrll 
388be9ac0eaSchristos   csyms = (struct cref_hash_entry **) xmalloc (cref_symcount * sizeof (*csyms));
3892a6b7db3Sskrll 
3902a6b7db3Sskrll   csym_fill = csyms;
3912a6b7db3Sskrll   cref_hash_traverse (&cref_table, cref_fill_array, &csym_fill);
3922a6b7db3Sskrll   ASSERT ((size_t) (csym_fill - csyms) == cref_symcount);
3932a6b7db3Sskrll 
3942a6b7db3Sskrll   qsort (csyms, cref_symcount, sizeof (*csyms), cref_sort_array);
3952a6b7db3Sskrll 
3962a6b7db3Sskrll   csym_end = csyms + cref_symcount;
3972a6b7db3Sskrll   for (csym = csyms; csym < csym_end; csym++)
3982a6b7db3Sskrll     output_one_cref (fp, *csym);
3992a6b7db3Sskrll }
4002a6b7db3Sskrll 
4012a6b7db3Sskrll /* Output one entry in the cross reference table.  */
4022a6b7db3Sskrll 
4032a6b7db3Sskrll static void
output_one_cref(FILE * fp,struct cref_hash_entry * h)4042a6b7db3Sskrll output_one_cref (FILE *fp, struct cref_hash_entry *h)
4052a6b7db3Sskrll {
4062a6b7db3Sskrll   int len;
4072a6b7db3Sskrll   struct bfd_link_hash_entry *hl;
4082a6b7db3Sskrll   struct cref_ref *r;
4092a6b7db3Sskrll 
4104f645668Schristos   hl = bfd_link_hash_lookup (link_info.hash, h->root.string, false,
4114f645668Schristos 			     false, true);
4122a6b7db3Sskrll   if (hl == NULL)
413c1a20988Schristos     einfo (_("%P: symbol `%pT' missing from main hash table\n"),
4142a6b7db3Sskrll 	   h->root.string);
4152a6b7db3Sskrll   else
4162a6b7db3Sskrll     {
4172a6b7db3Sskrll       /* If this symbol is defined in a dynamic object but never
4182a6b7db3Sskrll 	 referenced by a normal object, then don't print it.  */
4192a6b7db3Sskrll       if (hl->type == bfd_link_hash_defined)
4202a6b7db3Sskrll 	{
4212a6b7db3Sskrll 	  if (hl->u.def.section->output_section == NULL)
4222a6b7db3Sskrll 	    return;
4232a6b7db3Sskrll 	  if (hl->u.def.section->owner != NULL
4242a6b7db3Sskrll 	      && (hl->u.def.section->owner->flags & DYNAMIC) != 0)
4252a6b7db3Sskrll 	    {
4262a6b7db3Sskrll 	      for (r = h->refs; r != NULL; r = r->next)
4272a6b7db3Sskrll 		if ((r->abfd->flags & DYNAMIC) == 0)
4282a6b7db3Sskrll 		  break;
4292a6b7db3Sskrll 	      if (r == NULL)
4302a6b7db3Sskrll 		return;
4312a6b7db3Sskrll 	    }
4322a6b7db3Sskrll 	}
4332a6b7db3Sskrll     }
4342a6b7db3Sskrll 
4359573673dSchristos   if (demangling)
4369573673dSchristos     {
4372a6b7db3Sskrll       fprintf (fp, "%s ", h->demangled);
4382a6b7db3Sskrll       len = strlen (h->demangled) + 1;
4399573673dSchristos     }
4409573673dSchristos   else
4419573673dSchristos     {
4429573673dSchristos       fprintf (fp, "%s ", h->root.string);
4439573673dSchristos       len = strlen (h->root.string) + 1;
4449573673dSchristos     }
4452a6b7db3Sskrll 
4462a6b7db3Sskrll   for (r = h->refs; r != NULL; r = r->next)
4472a6b7db3Sskrll     {
4482a6b7db3Sskrll       if (r->def)
4492a6b7db3Sskrll 	{
4502a6b7db3Sskrll 	  while (len < FILECOL)
4512a6b7db3Sskrll 	    {
4522a6b7db3Sskrll 	      putc (' ', fp);
4532a6b7db3Sskrll 	      ++len;
4542a6b7db3Sskrll 	    }
455c1a20988Schristos 	  lfinfo (fp, "%pB\n", r->abfd);
4562a6b7db3Sskrll 	  len = 0;
4572a6b7db3Sskrll 	}
4582a6b7db3Sskrll     }
4592a6b7db3Sskrll 
4602a6b7db3Sskrll   for (r = h->refs; r != NULL; r = r->next)
4612a6b7db3Sskrll     {
4629573673dSchristos       if (r->common)
4639573673dSchristos 	{
4649573673dSchristos 	  while (len < FILECOL)
4659573673dSchristos 	    {
4669573673dSchristos 	      putc (' ', fp);
4679573673dSchristos 	      ++len;
4689573673dSchristos 	    }
469c1a20988Schristos 	  lfinfo (fp, "%pB\n", r->abfd);
4709573673dSchristos 	  len = 0;
4719573673dSchristos 	}
4729573673dSchristos     }
4739573673dSchristos 
4749573673dSchristos   for (r = h->refs; r != NULL; r = r->next)
4759573673dSchristos     {
4769573673dSchristos       if (!r->def && !r->common)
4772a6b7db3Sskrll 	{
4782a6b7db3Sskrll 	  while (len < FILECOL)
4792a6b7db3Sskrll 	    {
4802a6b7db3Sskrll 	      putc (' ', fp);
4812a6b7db3Sskrll 	      ++len;
4822a6b7db3Sskrll 	    }
483c1a20988Schristos 	  lfinfo (fp, "%pB\n", r->abfd);
4842a6b7db3Sskrll 	  len = 0;
4852a6b7db3Sskrll 	}
4862a6b7db3Sskrll     }
4872a6b7db3Sskrll 
4882a6b7db3Sskrll   ASSERT (len == 0);
4892a6b7db3Sskrll }
4902a6b7db3Sskrll 
4912a6b7db3Sskrll /* Check for prohibited cross references.  */
4922a6b7db3Sskrll 
4932a6b7db3Sskrll void
check_nocrossrefs(void)4942a6b7db3Sskrll check_nocrossrefs (void)
4952a6b7db3Sskrll {
4962a6b7db3Sskrll   if (!cref_initialized)
4972a6b7db3Sskrll     return;
4982a6b7db3Sskrll 
4992a6b7db3Sskrll   cref_hash_traverse (&cref_table, check_nocrossref, NULL);
5002a6b7db3Sskrll 
5012a6b7db3Sskrll   lang_for_each_file (check_local_sym_xref);
5022a6b7db3Sskrll }
5032a6b7db3Sskrll 
5042a6b7db3Sskrll /* Check for prohibited cross references to local and section symbols.  */
5052a6b7db3Sskrll 
5062a6b7db3Sskrll static void
check_local_sym_xref(lang_input_statement_type * statement)5072a6b7db3Sskrll check_local_sym_xref (lang_input_statement_type *statement)
5082a6b7db3Sskrll {
5092a6b7db3Sskrll   bfd *abfd;
5102a6b7db3Sskrll   asymbol **syms;
5112a6b7db3Sskrll 
5122a6b7db3Sskrll   abfd = statement->the_bfd;
5132a6b7db3Sskrll   if (abfd == NULL)
5142a6b7db3Sskrll     return;
5152a6b7db3Sskrll 
5162a6b7db3Sskrll   if (!bfd_generic_link_read_symbols (abfd))
517c1a20988Schristos     einfo (_("%F%P: %pB: could not read symbols: %E\n"), abfd);
5182a6b7db3Sskrll 
5192a6b7db3Sskrll   for (syms = bfd_get_outsymbols (abfd); *syms; ++syms)
5202a6b7db3Sskrll     {
5212a6b7db3Sskrll       asymbol *sym = *syms;
5222a6b7db3Sskrll       if (sym->flags & (BSF_GLOBAL | BSF_WARNING | BSF_INDIRECT | BSF_FILE))
5232a6b7db3Sskrll 	continue;
5242a6b7db3Sskrll       if ((sym->flags & (BSF_LOCAL | BSF_SECTION_SYM)) != 0
5252a6b7db3Sskrll 	  && sym->section->output_section != NULL)
5262a6b7db3Sskrll 	{
5272a6b7db3Sskrll 	  const char *outsecname, *symname;
5282a6b7db3Sskrll 	  struct lang_nocrossrefs *ncrs;
5292a6b7db3Sskrll 	  struct lang_nocrossref *ncr;
5302a6b7db3Sskrll 
5312a6b7db3Sskrll 	  outsecname = sym->section->output_section->name;
5322a6b7db3Sskrll 	  symname = NULL;
5332a6b7db3Sskrll 	  if ((sym->flags & BSF_SECTION_SYM) == 0)
5342a6b7db3Sskrll 	    symname = sym->name;
5352a6b7db3Sskrll 	  for (ncrs = nocrossref_list; ncrs != NULL; ncrs = ncrs->next)
5362a6b7db3Sskrll 	    for (ncr = ncrs->list; ncr != NULL; ncr = ncr->next)
5378cbf5cb7Schristos 	      {
5382a6b7db3Sskrll 		if (strcmp (ncr->name, outsecname) == 0)
5394f645668Schristos 		  check_refs (symname, false, sym->section, abfd, ncrs);
5408cbf5cb7Schristos 		/* The NOCROSSREFS_TO command only checks symbols defined in
5418cbf5cb7Schristos 		   the first section in the list.  */
5428cbf5cb7Schristos 		if (ncrs->onlyfirst)
5438cbf5cb7Schristos 		  break;
5448cbf5cb7Schristos 	      }
5452a6b7db3Sskrll 	}
5462a6b7db3Sskrll     }
5472a6b7db3Sskrll }
5482a6b7db3Sskrll 
5492a6b7db3Sskrll /* Check one symbol to see if it is a prohibited cross reference.  */
5502a6b7db3Sskrll 
5514f645668Schristos static bool
check_nocrossref(struct cref_hash_entry * h,void * ignore ATTRIBUTE_UNUSED)5522a6b7db3Sskrll check_nocrossref (struct cref_hash_entry *h, void *ignore ATTRIBUTE_UNUSED)
5532a6b7db3Sskrll {
5542a6b7db3Sskrll   struct bfd_link_hash_entry *hl;
5552a6b7db3Sskrll   asection *defsec;
5562a6b7db3Sskrll   const char *defsecname;
5572a6b7db3Sskrll   struct lang_nocrossrefs *ncrs;
5582a6b7db3Sskrll   struct lang_nocrossref *ncr;
5592a6b7db3Sskrll   struct cref_ref *ref;
5602a6b7db3Sskrll 
5614f645668Schristos   hl = bfd_link_hash_lookup (link_info.hash, h->root.string, false,
5624f645668Schristos 			     false, true);
5632a6b7db3Sskrll   if (hl == NULL)
5642a6b7db3Sskrll     {
565c1a20988Schristos       einfo (_("%P: symbol `%pT' missing from main hash table\n"),
5662a6b7db3Sskrll 	     h->root.string);
5674f645668Schristos       return true;
5682a6b7db3Sskrll     }
5692a6b7db3Sskrll 
5702a6b7db3Sskrll   if (hl->type != bfd_link_hash_defined
5712a6b7db3Sskrll       && hl->type != bfd_link_hash_defweak)
5724f645668Schristos     return true;
5732a6b7db3Sskrll 
5742a6b7db3Sskrll   defsec = hl->u.def.section->output_section;
5752a6b7db3Sskrll   if (defsec == NULL)
5764f645668Schristos     return true;
5776f4ced0bSchristos   defsecname = bfd_section_name (defsec);
5782a6b7db3Sskrll 
5792a6b7db3Sskrll   for (ncrs = nocrossref_list; ncrs != NULL; ncrs = ncrs->next)
5802a6b7db3Sskrll     for (ncr = ncrs->list; ncr != NULL; ncr = ncr->next)
5818cbf5cb7Schristos       {
5822a6b7db3Sskrll 	if (strcmp (ncr->name, defsecname) == 0)
5832a6b7db3Sskrll 	  for (ref = h->refs; ref != NULL; ref = ref->next)
5844f645668Schristos 	    check_refs (hl->root.string, true, hl->u.def.section,
5852a6b7db3Sskrll 			ref->abfd, ncrs);
5868cbf5cb7Schristos 	/* The NOCROSSREFS_TO command only checks symbols defined in the first
5878cbf5cb7Schristos 	   section in the list.  */
5888cbf5cb7Schristos 	if (ncrs->onlyfirst)
5898cbf5cb7Schristos 	  break;
5908cbf5cb7Schristos       }
5912a6b7db3Sskrll 
5924f645668Schristos   return true;
5932a6b7db3Sskrll }
5942a6b7db3Sskrll 
5952a6b7db3Sskrll /* The struct is used to pass information from check_refs to
5962a6b7db3Sskrll    check_reloc_refs through bfd_map_over_sections.  */
5972a6b7db3Sskrll 
5988cbf5cb7Schristos struct check_refs_info
5998cbf5cb7Schristos {
6002a6b7db3Sskrll   const char *sym_name;
6012a6b7db3Sskrll   asection *defsec;
6022a6b7db3Sskrll   struct lang_nocrossrefs *ncrs;
6032a6b7db3Sskrll   asymbol **asymbols;
6044f645668Schristos   bool global;
6052a6b7db3Sskrll };
6062a6b7db3Sskrll 
6072a6b7db3Sskrll /* This function is called for each symbol defined in a section which
6082a6b7db3Sskrll    prohibits cross references.  We need to look through all references
6092a6b7db3Sskrll    to this symbol, and ensure that the references are not from
6102a6b7db3Sskrll    prohibited sections.  */
6112a6b7db3Sskrll 
6122a6b7db3Sskrll static void
check_refs(const char * name,bool global,asection * sec,bfd * abfd,struct lang_nocrossrefs * ncrs)6132a6b7db3Sskrll check_refs (const char *name,
6144f645668Schristos 	    bool global,
6152a6b7db3Sskrll 	    asection *sec,
6162a6b7db3Sskrll 	    bfd *abfd,
6172a6b7db3Sskrll 	    struct lang_nocrossrefs *ncrs)
6182a6b7db3Sskrll {
6192a6b7db3Sskrll   struct check_refs_info info;
6202a6b7db3Sskrll 
6212a6b7db3Sskrll   /* We need to look through the relocations for this BFD, to see
6222a6b7db3Sskrll      if any of the relocations which refer to this symbol are from
6232a6b7db3Sskrll      a prohibited section.  Note that we need to do this even for
6242a6b7db3Sskrll      the BFD in which the symbol is defined, since even a single
6252a6b7db3Sskrll      BFD might contain a prohibited cross reference.  */
6262a6b7db3Sskrll 
6272a6b7db3Sskrll   if (!bfd_generic_link_read_symbols (abfd))
628c1a20988Schristos     einfo (_("%F%P: %pB: could not read symbols: %E\n"), abfd);
6292a6b7db3Sskrll 
6302a6b7db3Sskrll   info.sym_name = name;
6312a6b7db3Sskrll   info.global = global;
6322a6b7db3Sskrll   info.defsec = sec;
6332a6b7db3Sskrll   info.ncrs = ncrs;
6342a6b7db3Sskrll   info.asymbols = bfd_get_outsymbols (abfd);
6352a6b7db3Sskrll   bfd_map_over_sections (abfd, check_reloc_refs, &info);
6362a6b7db3Sskrll }
6372a6b7db3Sskrll 
6382a6b7db3Sskrll /* This is called via bfd_map_over_sections.  INFO->SYM_NAME is a symbol
6392a6b7db3Sskrll    defined in INFO->DEFSECNAME.  If this section maps into any of the
6402a6b7db3Sskrll    sections listed in INFO->NCRS, other than INFO->DEFSECNAME, then we
6412a6b7db3Sskrll    look through the relocations.  If any of the relocations are to
6422a6b7db3Sskrll    INFO->SYM_NAME, then we report a prohibited cross reference error.  */
6432a6b7db3Sskrll 
6442a6b7db3Sskrll static void
check_reloc_refs(bfd * abfd,asection * sec,void * iarg)6452a6b7db3Sskrll check_reloc_refs (bfd *abfd, asection *sec, void *iarg)
6462a6b7db3Sskrll {
647be9ac0eaSchristos   struct check_refs_info *info = (struct check_refs_info *) iarg;
6482a6b7db3Sskrll   asection *outsec;
6492a6b7db3Sskrll   const char *outsecname;
6502a6b7db3Sskrll   asection *outdefsec;
6512a6b7db3Sskrll   const char *outdefsecname;
6522a6b7db3Sskrll   struct lang_nocrossref *ncr;
6532a6b7db3Sskrll   const char *symname;
6544f645668Schristos   bool global;
6552a6b7db3Sskrll   long relsize;
6562a6b7db3Sskrll   arelent **relpp;
6572a6b7db3Sskrll   long relcount;
6582a6b7db3Sskrll   arelent **p, **pend;
6592a6b7db3Sskrll 
6602a6b7db3Sskrll   outsec = sec->output_section;
6616f4ced0bSchristos   outsecname = bfd_section_name (outsec);
6622a6b7db3Sskrll 
6632a6b7db3Sskrll   outdefsec = info->defsec->output_section;
6646f4ced0bSchristos   outdefsecname = bfd_section_name (outdefsec);
6652a6b7db3Sskrll 
6662a6b7db3Sskrll   /* The section where the symbol is defined is permitted.  */
6672a6b7db3Sskrll   if (strcmp (outsecname, outdefsecname) == 0)
6682a6b7db3Sskrll     return;
6692a6b7db3Sskrll 
6702a6b7db3Sskrll   for (ncr = info->ncrs->list; ncr != NULL; ncr = ncr->next)
6712a6b7db3Sskrll     if (strcmp (outsecname, ncr->name) == 0)
6722a6b7db3Sskrll       break;
6732a6b7db3Sskrll 
6742a6b7db3Sskrll   if (ncr == NULL)
6752a6b7db3Sskrll     return;
6762a6b7db3Sskrll 
6772a6b7db3Sskrll   /* This section is one for which cross references are prohibited.
6782a6b7db3Sskrll      Look through the relocations, and see if any of them are to
6792a6b7db3Sskrll      INFO->SYM_NAME.  If INFO->SYMNAME is NULL, check for relocations
6802a6b7db3Sskrll      against the section symbol.  If INFO->GLOBAL is TRUE, the
6812a6b7db3Sskrll      definition is global, check for relocations against the global
6822a6b7db3Sskrll      symbols.  Otherwise check for relocations against the local and
6832a6b7db3Sskrll      section symbols.  */
6842a6b7db3Sskrll 
6852a6b7db3Sskrll   symname = info->sym_name;
6862a6b7db3Sskrll   global = info->global;
6872a6b7db3Sskrll 
6882a6b7db3Sskrll   relsize = bfd_get_reloc_upper_bound (abfd, sec);
6892a6b7db3Sskrll   if (relsize < 0)
690c1a20988Schristos     einfo (_("%F%P: %pB: could not read relocs: %E\n"), abfd);
6912a6b7db3Sskrll   if (relsize == 0)
6922a6b7db3Sskrll     return;
6932a6b7db3Sskrll 
694be9ac0eaSchristos   relpp = (arelent **) xmalloc (relsize);
6952a6b7db3Sskrll   relcount = bfd_canonicalize_reloc (abfd, sec, relpp, info->asymbols);
6962a6b7db3Sskrll   if (relcount < 0)
697c1a20988Schristos     einfo (_("%F%P: %pB: could not read relocs: %E\n"), abfd);
6982a6b7db3Sskrll 
6992a6b7db3Sskrll   p = relpp;
7002a6b7db3Sskrll   pend = p + relcount;
7012a6b7db3Sskrll   for (; p < pend && *p != NULL; p++)
7022a6b7db3Sskrll     {
7032a6b7db3Sskrll       arelent *q = *p;
7042a6b7db3Sskrll 
7052a6b7db3Sskrll       if (q->sym_ptr_ptr != NULL
7062a6b7db3Sskrll 	  && *q->sym_ptr_ptr != NULL
7072a6b7db3Sskrll 	  && ((global
7086f4ced0bSchristos 	       && (bfd_is_und_section (bfd_asymbol_section (*q->sym_ptr_ptr))
7096f4ced0bSchristos 		   || bfd_is_com_section (bfd_asymbol_section (*q->sym_ptr_ptr))
7102a6b7db3Sskrll 		   || ((*q->sym_ptr_ptr)->flags & (BSF_GLOBAL
7112a6b7db3Sskrll 						   | BSF_WEAK)) != 0))
7122a6b7db3Sskrll 	      || (!global
7132a6b7db3Sskrll 		  && ((*q->sym_ptr_ptr)->flags & (BSF_LOCAL
7142a6b7db3Sskrll 						  | BSF_SECTION_SYM)) != 0
7156f4ced0bSchristos 		  && bfd_asymbol_section (*q->sym_ptr_ptr) == info->defsec))
7162a6b7db3Sskrll 	  && (symname != NULL
7172a6b7db3Sskrll 	      ? strcmp (bfd_asymbol_name (*q->sym_ptr_ptr), symname) == 0
7182a6b7db3Sskrll 	      : ((*q->sym_ptr_ptr)->flags & BSF_SECTION_SYM) != 0))
7192a6b7db3Sskrll 	{
7202a6b7db3Sskrll 	  /* We found a reloc for the symbol.  The symbol is defined
7212a6b7db3Sskrll 	     in OUTSECNAME.  This reloc is from a section which is
7222a6b7db3Sskrll 	     mapped into a section from which references to OUTSECNAME
7232a6b7db3Sskrll 	     are prohibited.  We must report an error.  */
724*cb63e24eSchristos 	  einfo (_("%X%P: %H: prohibited cross reference from %s to `%pT' in %s\n"),
7252a6b7db3Sskrll 		 abfd, sec, q->address, outsecname,
7262a6b7db3Sskrll 		 bfd_asymbol_name (*q->sym_ptr_ptr), outdefsecname);
7272a6b7db3Sskrll 	}
7282a6b7db3Sskrll     }
7292a6b7db3Sskrll 
7302a6b7db3Sskrll   free (relpp);
7312a6b7db3Sskrll }
732