133965Sjdp/* ldcref.c -- output a cross reference table
2218822Sdim   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006,
3218822Sdim   2007 Free Software Foundation, Inc.
433965Sjdp   Written by Ian Lance Taylor <ian@cygnus.com>
533965Sjdp
633965SjdpThis file is part of GLD, the Gnu Linker.
733965Sjdp
833965SjdpThis program is free software; you can redistribute it and/or modify
933965Sjdpit under the terms of the GNU General Public License as published by
1033965Sjdpthe Free Software Foundation; either version 2 of the License, or
1133965Sjdp(at your option) any later version.
1233965Sjdp
1333965SjdpThis program is distributed in the hope that it will be useful,
1433965Sjdpbut WITHOUT ANY WARRANTY; without even the implied warranty of
1533965SjdpMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1633965SjdpGNU General Public License for more details.
1733965Sjdp
1833965SjdpYou should have received a copy of the GNU General Public License
1933965Sjdpalong with this program; if not, write to the Free Software
20218822SdimFoundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
2133965Sjdp
2233965Sjdp/* This file holds routines that manage the cross reference table.
2333965Sjdp   The table is used to generate cross reference reports.  It is also
2433965Sjdp   used to implement the NOCROSSREFS command in the linker script.  */
2533965Sjdp
26218822Sdim#include "sysdep.h"
2733965Sjdp#include "bfd.h"
2833965Sjdp#include "bfdlink.h"
2933965Sjdp#include "libiberty.h"
30218822Sdim#include "demangle.h"
31218822Sdim#include "objalloc.h"
3233965Sjdp
3333965Sjdp#include "ld.h"
3433965Sjdp#include "ldmain.h"
3533965Sjdp#include "ldmisc.h"
3633965Sjdp#include "ldexp.h"
3733965Sjdp#include "ldlang.h"
3833965Sjdp
3933965Sjdp/* We keep an instance of this structure for each reference to a
4033965Sjdp   symbol from a given object.  */
4133965Sjdp
4277298Sobrienstruct cref_ref {
4333965Sjdp  /* The next reference.  */
4433965Sjdp  struct cref_ref *next;
4533965Sjdp  /* The object.  */
4633965Sjdp  bfd *abfd;
4733965Sjdp  /* True if the symbol is defined.  */
4833965Sjdp  unsigned int def : 1;
4933965Sjdp  /* True if the symbol is common.  */
5033965Sjdp  unsigned int common : 1;
5133965Sjdp  /* True if the symbol is undefined.  */
5233965Sjdp  unsigned int undef : 1;
5333965Sjdp};
5433965Sjdp
5533965Sjdp/* We keep a hash table of symbols.  Each entry looks like this.  */
5633965Sjdp
5777298Sobrienstruct cref_hash_entry {
5833965Sjdp  struct bfd_hash_entry root;
5933965Sjdp  /* The demangled name.  */
60218822Sdim  const char *demangled;
6133965Sjdp  /* References to and definitions of this symbol.  */
6233965Sjdp  struct cref_ref *refs;
6333965Sjdp};
6433965Sjdp
6533965Sjdp/* This is what the hash table looks like.  */
6633965Sjdp
6777298Sobrienstruct cref_hash_table {
6833965Sjdp  struct bfd_hash_table root;
6933965Sjdp};
7033965Sjdp
71130561Sobrien/* Forward declarations.  */
7233965Sjdp
73130561Sobrienstatic void output_one_cref (FILE *, struct cref_hash_entry *);
74218822Sdimstatic void check_local_sym_xref (lang_input_statement_type *);
75130561Sobrienstatic bfd_boolean check_nocrossref (struct cref_hash_entry *, void *);
76218822Sdimstatic void check_refs (const char *, bfd_boolean, asection *, bfd *,
77130561Sobrien			struct lang_nocrossrefs *);
78130561Sobrienstatic void check_reloc_refs (bfd *, asection *, void *);
7933965Sjdp
8033965Sjdp/* Look up an entry in the cref hash table.  */
8133965Sjdp
8233965Sjdp#define cref_hash_lookup(table, string, create, copy)		\
8333965Sjdp  ((struct cref_hash_entry *)					\
8433965Sjdp   bfd_hash_lookup (&(table)->root, (string), (create), (copy)))
8533965Sjdp
8633965Sjdp/* Traverse the cref hash table.  */
8733965Sjdp
8833965Sjdp#define cref_hash_traverse(table, func, info)				\
8933965Sjdp  (bfd_hash_traverse							\
9033965Sjdp   (&(table)->root,							\
91130561Sobrien    (bfd_boolean (*) (struct bfd_hash_entry *, void *)) (func),		\
9233965Sjdp    (info)))
9333965Sjdp
9433965Sjdp/* The cref hash table.  */
9533965Sjdp
9633965Sjdpstatic struct cref_hash_table cref_table;
9733965Sjdp
9833965Sjdp/* Whether the cref hash table has been initialized.  */
9933965Sjdp
100130561Sobrienstatic bfd_boolean cref_initialized;
10133965Sjdp
10233965Sjdp/* The number of symbols seen so far.  */
10333965Sjdp
10433965Sjdpstatic size_t cref_symcount;
10533965Sjdp
106218822Sdim/* Used to take a snapshot of the cref hash table when starting to
107218822Sdim   add syms from an as-needed library.  */
108218822Sdimstatic struct bfd_hash_entry **old_table;
109218822Sdimstatic unsigned int old_size;
110218822Sdimstatic unsigned int old_count;
111218822Sdimstatic void *old_tab;
112218822Sdimstatic void *alloc_mark;
113218822Sdimstatic size_t tabsize, entsize, refsize;
114218822Sdimstatic size_t old_symcount;
115218822Sdim
11633965Sjdp/* Create an entry in a cref hash table.  */
11733965Sjdp
11833965Sjdpstatic struct bfd_hash_entry *
119130561Sobriencref_hash_newfunc (struct bfd_hash_entry *entry,
120130561Sobrien		   struct bfd_hash_table *table,
121130561Sobrien		   const char *string)
12233965Sjdp{
12333965Sjdp  struct cref_hash_entry *ret = (struct cref_hash_entry *) entry;
12433965Sjdp
12533965Sjdp  /* Allocate the structure if it has not already been allocated by a
12633965Sjdp     subclass.  */
12733965Sjdp  if (ret == NULL)
12833965Sjdp    ret = ((struct cref_hash_entry *)
12933965Sjdp	   bfd_hash_allocate (table, sizeof (struct cref_hash_entry)));
13033965Sjdp  if (ret == NULL)
131130561Sobrien    return NULL;
13233965Sjdp
13333965Sjdp  /* Call the allocation method of the superclass.  */
13433965Sjdp  ret = ((struct cref_hash_entry *)
13533965Sjdp	 bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
13633965Sjdp  if (ret != NULL)
13733965Sjdp    {
13833965Sjdp      /* Set local fields.  */
13933965Sjdp      ret->demangled = NULL;
14033965Sjdp      ret->refs = NULL;
14133965Sjdp
14233965Sjdp      /* Keep a count of the number of entries created in the hash
143130561Sobrien	 table.  */
14433965Sjdp      ++cref_symcount;
14533965Sjdp    }
14633965Sjdp
147130561Sobrien  return &ret->root;
14833965Sjdp}
14933965Sjdp
15033965Sjdp/* Add a symbol to the cref hash table.  This is called for every
151218822Sdim   global symbol that is seen during the link.  */
15233965Sjdp
15333965Sjdpvoid
154130561Sobrienadd_cref (const char *name,
155130561Sobrien	  bfd *abfd,
156130561Sobrien	  asection *section,
157130561Sobrien	  bfd_vma value ATTRIBUTE_UNUSED)
15833965Sjdp{
15933965Sjdp  struct cref_hash_entry *h;
16033965Sjdp  struct cref_ref *r;
16133965Sjdp
16233965Sjdp  if (! cref_initialized)
16333965Sjdp    {
164218822Sdim      if (!bfd_hash_table_init (&cref_table.root, cref_hash_newfunc,
165218822Sdim				sizeof (struct cref_hash_entry)))
16660484Sobrien	einfo (_("%X%P: bfd_hash_table_init of cref table failed: %E\n"));
167130561Sobrien      cref_initialized = TRUE;
16833965Sjdp    }
16933965Sjdp
170130561Sobrien  h = cref_hash_lookup (&cref_table, name, TRUE, FALSE);
17133965Sjdp  if (h == NULL)
17260484Sobrien    einfo (_("%X%P: cref_hash_lookup failed: %E\n"));
17333965Sjdp
17433965Sjdp  for (r = h->refs; r != NULL; r = r->next)
17533965Sjdp    if (r->abfd == abfd)
17633965Sjdp      break;
17733965Sjdp
17833965Sjdp  if (r == NULL)
17933965Sjdp    {
180218822Sdim      r = bfd_hash_allocate (&cref_table.root, sizeof *r);
181218822Sdim      if (r == NULL)
182218822Sdim	einfo (_("%X%P: cref alloc failed: %E\n"));
18333965Sjdp      r->next = h->refs;
18433965Sjdp      h->refs = r;
18533965Sjdp      r->abfd = abfd;
186130561Sobrien      r->def = FALSE;
187130561Sobrien      r->common = FALSE;
188130561Sobrien      r->undef = FALSE;
18933965Sjdp    }
19033965Sjdp
19133965Sjdp  if (bfd_is_und_section (section))
192130561Sobrien    r->undef = TRUE;
19333965Sjdp  else if (bfd_is_com_section (section))
194130561Sobrien    r->common = TRUE;
19533965Sjdp  else
196130561Sobrien    r->def = TRUE;
19733965Sjdp}
19833965Sjdp
199218822Sdim/* Called before loading an as-needed library to take a snapshot of
200218822Sdim   the cref hash table, and after we have loaded or found that the
201218822Sdim   library was not needed.  */
202218822Sdim
203218822Sdimbfd_boolean
204218822Sdimhandle_asneeded_cref (bfd *abfd ATTRIBUTE_UNUSED,
205218822Sdim		      enum notice_asneeded_action act)
206218822Sdim{
207218822Sdim  unsigned int i;
208218822Sdim
209218822Sdim  if (!cref_initialized)
210218822Sdim    return TRUE;
211218822Sdim
212218822Sdim  if (act == notice_as_needed)
213218822Sdim    {
214218822Sdim      char *old_ent, *old_ref;
215218822Sdim
216218822Sdim      for (i = 0; i < cref_table.root.size; i++)
217218822Sdim	{
218218822Sdim	  struct bfd_hash_entry *p;
219218822Sdim	  struct cref_hash_entry *c;
220218822Sdim	  struct cref_ref *r;
221218822Sdim
222218822Sdim	  for (p = cref_table.root.table[i]; p != NULL; p = p->next)
223218822Sdim	    {
224218822Sdim	      entsize += cref_table.root.entsize;
225218822Sdim	      c = (struct cref_hash_entry *) p;
226218822Sdim	      for (r = c->refs; r != NULL; r = r->next)
227218822Sdim		refsize += sizeof (struct cref_hash_entry);
228218822Sdim	    }
229218822Sdim	}
230218822Sdim
231218822Sdim      tabsize = cref_table.root.size * sizeof (struct bfd_hash_entry *);
232218822Sdim      old_tab = xmalloc (tabsize + entsize + refsize);
233218822Sdim
234218822Sdim      alloc_mark = bfd_hash_allocate (&cref_table.root, 1);
235218822Sdim      if (alloc_mark == NULL)
236218822Sdim	return FALSE;
237218822Sdim
238218822Sdim      memcpy (old_tab, cref_table.root.table, tabsize);
239218822Sdim      old_ent = (char *) old_tab + tabsize;
240218822Sdim      old_ref = (char *) old_ent + entsize;
241218822Sdim      old_table = cref_table.root.table;
242218822Sdim      old_size = cref_table.root.size;
243218822Sdim      old_count = cref_table.root.count;
244218822Sdim      old_symcount = cref_symcount;
245218822Sdim
246218822Sdim      for (i = 0; i < cref_table.root.size; i++)
247218822Sdim	{
248218822Sdim	  struct bfd_hash_entry *p;
249218822Sdim	  struct cref_hash_entry *c;
250218822Sdim	  struct cref_ref *r;
251218822Sdim
252218822Sdim	  for (p = cref_table.root.table[i]; p != NULL; p = p->next)
253218822Sdim	    {
254218822Sdim	      memcpy (old_ent, p, cref_table.root.entsize);
255218822Sdim	      old_ent = (char *) old_ent + cref_table.root.entsize;
256218822Sdim	      c = (struct cref_hash_entry *) p;
257218822Sdim	      for (r = c->refs; r != NULL; r = r->next)
258218822Sdim		{
259218822Sdim		  memcpy (old_ref, r, sizeof (struct cref_hash_entry));
260218822Sdim		  old_ref = (char *) old_ref + sizeof (struct cref_hash_entry);
261218822Sdim		}
262218822Sdim	    }
263218822Sdim	}
264218822Sdim      return TRUE;
265218822Sdim    }
266218822Sdim
267218822Sdim  if (act == notice_not_needed)
268218822Sdim    {
269218822Sdim      char *old_ent, *old_ref;
270218822Sdim
271218822Sdim      if (old_tab == NULL)
272218822Sdim	{
273218822Sdim	  /* The only way old_tab can be NULL is if the cref hash table
274218822Sdim	     had not been initialised when notice_as_needed.  */
275218822Sdim	  bfd_hash_table_free (&cref_table.root);
276218822Sdim	  cref_initialized = FALSE;
277218822Sdim	  return TRUE;
278218822Sdim	}
279218822Sdim
280218822Sdim      old_ent = (char *) old_tab + tabsize;
281218822Sdim      old_ref = (char *) old_ent + entsize;
282218822Sdim      cref_table.root.table = old_table;
283218822Sdim      cref_table.root.size = old_size;
284218822Sdim      cref_table.root.count = old_count;
285218822Sdim      memcpy (cref_table.root.table, old_tab, tabsize);
286218822Sdim      cref_symcount = old_symcount;
287218822Sdim
288218822Sdim      for (i = 0; i < cref_table.root.size; i++)
289218822Sdim	{
290218822Sdim	  struct bfd_hash_entry *p;
291218822Sdim	  struct cref_hash_entry *c;
292218822Sdim	  struct cref_ref *r;
293218822Sdim
294218822Sdim	  for (p = cref_table.root.table[i]; p != NULL; p = p->next)
295218822Sdim	    {
296218822Sdim	      memcpy (p, old_ent, cref_table.root.entsize);
297218822Sdim	      old_ent = (char *) old_ent + cref_table.root.entsize;
298218822Sdim	      c = (struct cref_hash_entry *) p;
299218822Sdim	      for (r = c->refs; r != NULL; r = r->next)
300218822Sdim		{
301218822Sdim		  memcpy (r, old_ref, sizeof (struct cref_hash_entry));
302218822Sdim		  old_ref = (char *) old_ref + sizeof (struct cref_hash_entry);
303218822Sdim		}
304218822Sdim	    }
305218822Sdim	}
306218822Sdim
307218822Sdim      objalloc_free_block ((struct objalloc *) cref_table.root.memory,
308218822Sdim			   alloc_mark);
309218822Sdim    }
310218822Sdim  else if (act != notice_needed)
311218822Sdim    return FALSE;
312218822Sdim
313218822Sdim  free (old_tab);
314218822Sdim  old_tab = NULL;
315218822Sdim  return TRUE;
316218822Sdim}
317218822Sdim
31833965Sjdp/* Copy the addresses of the hash table entries into an array.  This
31933965Sjdp   is called via cref_hash_traverse.  We also fill in the demangled
32033965Sjdp   name.  */
32133965Sjdp
322130561Sobrienstatic bfd_boolean
323130561Sobriencref_fill_array (struct cref_hash_entry *h, void *data)
32433965Sjdp{
325130561Sobrien  struct cref_hash_entry ***pph = data;
32633965Sjdp
32733965Sjdp  ASSERT (h->demangled == NULL);
328218822Sdim  h->demangled = bfd_demangle (output_bfd, h->root.string,
329218822Sdim			       DMGL_ANSI | DMGL_PARAMS);
330218822Sdim  if (h->demangled == NULL)
331218822Sdim    h->demangled = h->root.string;
33233965Sjdp
33333965Sjdp  **pph = h;
33433965Sjdp
33533965Sjdp  ++*pph;
33633965Sjdp
337130561Sobrien  return TRUE;
33833965Sjdp}
33933965Sjdp
34033965Sjdp/* Sort an array of cref hash table entries by name.  */
34133965Sjdp
34233965Sjdpstatic int
343130561Sobriencref_sort_array (const void *a1, const void *a2)
34433965Sjdp{
345130561Sobrien  const struct cref_hash_entry * const *p1 = a1;
346130561Sobrien  const struct cref_hash_entry * const *p2 = a2;
34733965Sjdp
34833965Sjdp  return strcmp ((*p1)->demangled, (*p2)->demangled);
34933965Sjdp}
35033965Sjdp
35133965Sjdp/* Write out the cref table.  */
35233965Sjdp
35333965Sjdp#define FILECOL (50)
35433965Sjdp
35533965Sjdpvoid
356130561Sobrienoutput_cref (FILE *fp)
35733965Sjdp{
35833965Sjdp  int len;
35933965Sjdp  struct cref_hash_entry **csyms, **csym_fill, **csym, **csym_end;
36060484Sobrien  const char *msg;
36133965Sjdp
36260484Sobrien  fprintf (fp, _("\nCross Reference Table\n\n"));
36360484Sobrien  msg = _("Symbol");
36460484Sobrien  fprintf (fp, "%s", msg);
36560484Sobrien  len = strlen (msg);
36633965Sjdp  while (len < FILECOL)
36733965Sjdp    {
36877298Sobrien      putc (' ', fp);
36933965Sjdp      ++len;
37033965Sjdp    }
37160484Sobrien  fprintf (fp, _("File\n"));
37233965Sjdp
37333965Sjdp  if (! cref_initialized)
37433965Sjdp    {
37560484Sobrien      fprintf (fp, _("No symbols\n"));
37633965Sjdp      return;
37733965Sjdp    }
37833965Sjdp
379130561Sobrien  csyms = xmalloc (cref_symcount * sizeof (*csyms));
38033965Sjdp
38133965Sjdp  csym_fill = csyms;
38233965Sjdp  cref_hash_traverse (&cref_table, cref_fill_array, &csym_fill);
38338889Sjdp  ASSERT ((size_t) (csym_fill - csyms) == cref_symcount);
38433965Sjdp
38533965Sjdp  qsort (csyms, cref_symcount, sizeof (*csyms), cref_sort_array);
38633965Sjdp
38733965Sjdp  csym_end = csyms + cref_symcount;
38833965Sjdp  for (csym = csyms; csym < csym_end; csym++)
38933965Sjdp    output_one_cref (fp, *csym);
39033965Sjdp}
39133965Sjdp
39233965Sjdp/* Output one entry in the cross reference table.  */
39333965Sjdp
39433965Sjdpstatic void
395130561Sobrienoutput_one_cref (FILE *fp, struct cref_hash_entry *h)
39633965Sjdp{
39733965Sjdp  int len;
39833965Sjdp  struct bfd_link_hash_entry *hl;
39933965Sjdp  struct cref_ref *r;
40033965Sjdp
401130561Sobrien  hl = bfd_link_hash_lookup (link_info.hash, h->root.string, FALSE,
402130561Sobrien			     FALSE, TRUE);
40333965Sjdp  if (hl == NULL)
40433965Sjdp    einfo ("%P: symbol `%T' missing from main hash table\n",
40533965Sjdp	   h->root.string);
40633965Sjdp  else
40733965Sjdp    {
40833965Sjdp      /* If this symbol is defined in a dynamic object but never
40933965Sjdp	 referenced by a normal object, then don't print it.  */
41033965Sjdp      if (hl->type == bfd_link_hash_defined)
41133965Sjdp	{
41233965Sjdp	  if (hl->u.def.section->output_section == NULL)
41333965Sjdp	    return;
41433965Sjdp	  if (hl->u.def.section->owner != NULL
41533965Sjdp	      && (hl->u.def.section->owner->flags & DYNAMIC) != 0)
41633965Sjdp	    {
41733965Sjdp	      for (r = h->refs; r != NULL; r = r->next)
41833965Sjdp		if ((r->abfd->flags & DYNAMIC) == 0)
41933965Sjdp		  break;
42033965Sjdp	      if (r == NULL)
42133965Sjdp		return;
42233965Sjdp	    }
42333965Sjdp	}
42433965Sjdp    }
42533965Sjdp
42633965Sjdp  fprintf (fp, "%s ", h->demangled);
42733965Sjdp  len = strlen (h->demangled) + 1;
42833965Sjdp
42933965Sjdp  for (r = h->refs; r != NULL; r = r->next)
43033965Sjdp    {
43133965Sjdp      if (r->def)
43233965Sjdp	{
43333965Sjdp	  while (len < FILECOL)
43433965Sjdp	    {
43533965Sjdp	      putc (' ', fp);
43633965Sjdp	      ++len;
43733965Sjdp	    }
43838889Sjdp	  lfinfo (fp, "%B\n", r->abfd);
43933965Sjdp	  len = 0;
44033965Sjdp	}
44133965Sjdp    }
44233965Sjdp
44333965Sjdp  for (r = h->refs; r != NULL; r = r->next)
44433965Sjdp    {
44533965Sjdp      if (! r->def)
44633965Sjdp	{
44733965Sjdp	  while (len < FILECOL)
44833965Sjdp	    {
44933965Sjdp	      putc (' ', fp);
45033965Sjdp	      ++len;
45133965Sjdp	    }
45238889Sjdp	  lfinfo (fp, "%B\n", r->abfd);
45333965Sjdp	  len = 0;
45433965Sjdp	}
45533965Sjdp    }
45633965Sjdp
45733965Sjdp  ASSERT (len == 0);
45833965Sjdp}
45933965Sjdp
46033965Sjdp/* Check for prohibited cross references.  */
46133965Sjdp
46233965Sjdpvoid
463130561Sobriencheck_nocrossrefs (void)
46433965Sjdp{
46533965Sjdp  if (! cref_initialized)
46633965Sjdp    return;
46733965Sjdp
468130561Sobrien  cref_hash_traverse (&cref_table, check_nocrossref, NULL);
46989857Sobrien
470218822Sdim  lang_for_each_file (check_local_sym_xref);
47133965Sjdp}
47233965Sjdp
473218822Sdim/* Check for prohibited cross references to local and section symbols.  */
47489857Sobrien
47589857Sobrienstatic void
476218822Sdimcheck_local_sym_xref (lang_input_statement_type *statement)
47789857Sobrien{
47889857Sobrien  bfd *abfd;
479218822Sdim  lang_input_statement_type *li;
480218822Sdim  asymbol **asymbols, **syms;
48189857Sobrien
48289857Sobrien  abfd = statement->the_bfd;
48389857Sobrien  if (abfd == NULL)
48489857Sobrien    return;
48589857Sobrien
486218822Sdim  li = abfd->usrdata;
487218822Sdim  if (li != NULL && li->asymbols != NULL)
488218822Sdim    asymbols = li->asymbols;
489218822Sdim  else
49089857Sobrien    {
491218822Sdim      long symsize;
492218822Sdim      long symbol_count;
49389857Sobrien
494218822Sdim      symsize = bfd_get_symtab_upper_bound (abfd);
495218822Sdim      if (symsize < 0)
496218822Sdim	einfo (_("%B%F: could not read symbols; %E\n"), abfd);
497218822Sdim      asymbols = xmalloc (symsize);
498218822Sdim      symbol_count = bfd_canonicalize_symtab (abfd, asymbols);
499218822Sdim      if (symbol_count < 0)
500218822Sdim	einfo (_("%B%F: could not read symbols: %E\n"), abfd);
501218822Sdim      if (li != NULL)
50289857Sobrien	{
503218822Sdim	  li->asymbols = asymbols;
504218822Sdim	  li->symbol_count = symbol_count;
505218822Sdim	}
506218822Sdim    }
507218822Sdim
508218822Sdim  for (syms = asymbols; *syms; ++syms)
509218822Sdim    {
510218822Sdim      asymbol *sym = *syms;
511218822Sdim      if (sym->flags & (BSF_GLOBAL | BSF_WARNING | BSF_INDIRECT | BSF_FILE))
512218822Sdim	continue;
513218822Sdim      if ((sym->flags & (BSF_LOCAL | BSF_SECTION_SYM)) != 0
514218822Sdim	  && sym->section->output_section != NULL)
515218822Sdim	{
516218822Sdim	  const char *outsecname, *symname;
51789857Sobrien	  struct lang_nocrossrefs *ncrs;
51889857Sobrien	  struct lang_nocrossref *ncr;
51989857Sobrien
520218822Sdim	  outsecname = sym->section->output_section->name;
521218822Sdim	  symname = NULL;
522218822Sdim	  if ((sym->flags & BSF_SECTION_SYM) == 0)
523218822Sdim	    symname = sym->name;
52489857Sobrien	  for (ncrs = nocrossref_list; ncrs != NULL; ncrs = ncrs->next)
52589857Sobrien	    for (ncr = ncrs->list; ncr != NULL; ncr = ncr->next)
52689857Sobrien	      if (strcmp (ncr->name, outsecname) == 0)
527218822Sdim		check_refs (symname, FALSE, sym->section, abfd, ncrs);
52889857Sobrien	}
52989857Sobrien    }
530218822Sdim
531218822Sdim  if (li == NULL)
532218822Sdim    free (asymbols);
53389857Sobrien}
53489857Sobrien
53533965Sjdp/* Check one symbol to see if it is a prohibited cross reference.  */
53633965Sjdp
537130561Sobrienstatic bfd_boolean
538130561Sobriencheck_nocrossref (struct cref_hash_entry *h, void *ignore ATTRIBUTE_UNUSED)
53933965Sjdp{
54033965Sjdp  struct bfd_link_hash_entry *hl;
54133965Sjdp  asection *defsec;
54233965Sjdp  const char *defsecname;
54333965Sjdp  struct lang_nocrossrefs *ncrs;
54433965Sjdp  struct lang_nocrossref *ncr;
54589857Sobrien  struct cref_ref *ref;
54633965Sjdp
547130561Sobrien  hl = bfd_link_hash_lookup (link_info.hash, h->root.string, FALSE,
548130561Sobrien			     FALSE, TRUE);
54933965Sjdp  if (hl == NULL)
55033965Sjdp    {
55160484Sobrien      einfo (_("%P: symbol `%T' missing from main hash table\n"),
55233965Sjdp	     h->root.string);
553130561Sobrien      return TRUE;
55433965Sjdp    }
55533965Sjdp
55633965Sjdp  if (hl->type != bfd_link_hash_defined
55733965Sjdp      && hl->type != bfd_link_hash_defweak)
558130561Sobrien    return TRUE;
55933965Sjdp
56033965Sjdp  defsec = hl->u.def.section->output_section;
56133965Sjdp  if (defsec == NULL)
562130561Sobrien    return TRUE;
56333965Sjdp  defsecname = bfd_get_section_name (defsec->owner, defsec);
56433965Sjdp
56533965Sjdp  for (ncrs = nocrossref_list; ncrs != NULL; ncrs = ncrs->next)
56633965Sjdp    for (ncr = ncrs->list; ncr != NULL; ncr = ncr->next)
56733965Sjdp      if (strcmp (ncr->name, defsecname) == 0)
56889857Sobrien	for (ref = h->refs; ref != NULL; ref = ref->next)
569218822Sdim	  check_refs (hl->root.string, TRUE, hl->u.def.section,
570218822Sdim		      ref->abfd, ncrs);
57133965Sjdp
572130561Sobrien  return TRUE;
57333965Sjdp}
57433965Sjdp
57533965Sjdp/* The struct is used to pass information from check_refs to
57633965Sjdp   check_reloc_refs through bfd_map_over_sections.  */
57733965Sjdp
57877298Sobrienstruct check_refs_info {
57989857Sobrien  const char *sym_name;
58033965Sjdp  asection *defsec;
58133965Sjdp  struct lang_nocrossrefs *ncrs;
58233965Sjdp  asymbol **asymbols;
583218822Sdim  bfd_boolean global;
58433965Sjdp};
58533965Sjdp
58633965Sjdp/* This function is called for each symbol defined in a section which
58733965Sjdp   prohibits cross references.  We need to look through all references
58833965Sjdp   to this symbol, and ensure that the references are not from
58933965Sjdp   prohibited sections.  */
59033965Sjdp
59133965Sjdpstatic void
592130561Sobriencheck_refs (const char *name,
593218822Sdim	    bfd_boolean global,
594130561Sobrien	    asection *sec,
595130561Sobrien	    bfd *abfd,
596130561Sobrien	    struct lang_nocrossrefs *ncrs)
59733965Sjdp{
59889857Sobrien  lang_input_statement_type *li;
59989857Sobrien  asymbol **asymbols;
60089857Sobrien  struct check_refs_info info;
60133965Sjdp
60289857Sobrien  /* We need to look through the relocations for this BFD, to see
60389857Sobrien     if any of the relocations which refer to this symbol are from
60489857Sobrien     a prohibited section.  Note that we need to do this even for
60589857Sobrien     the BFD in which the symbol is defined, since even a single
60689857Sobrien     BFD might contain a prohibited cross reference.  */
60789857Sobrien
608130561Sobrien  li = abfd->usrdata;
60989857Sobrien  if (li != NULL && li->asymbols != NULL)
61089857Sobrien    asymbols = li->asymbols;
61189857Sobrien  else
61233965Sjdp    {
61389857Sobrien      long symsize;
61489857Sobrien      long symbol_count;
61533965Sjdp
61689857Sobrien      symsize = bfd_get_symtab_upper_bound (abfd);
61789857Sobrien      if (symsize < 0)
61889857Sobrien	einfo (_("%B%F: could not read symbols; %E\n"), abfd);
619130561Sobrien      asymbols = xmalloc (symsize);
62089857Sobrien      symbol_count = bfd_canonicalize_symtab (abfd, asymbols);
62189857Sobrien      if (symbol_count < 0)
62289857Sobrien	einfo (_("%B%F: could not read symbols: %E\n"), abfd);
62389857Sobrien      if (li != NULL)
62433965Sjdp	{
62589857Sobrien	  li->asymbols = asymbols;
62689857Sobrien	  li->symbol_count = symbol_count;
62733965Sjdp	}
62889857Sobrien    }
62933965Sjdp
63089857Sobrien  info.sym_name = name;
631218822Sdim  info.global = global;
63289857Sobrien  info.defsec = sec;
63389857Sobrien  info.ncrs = ncrs;
63489857Sobrien  info.asymbols = asymbols;
635130561Sobrien  bfd_map_over_sections (abfd, check_reloc_refs, &info);
63633965Sjdp
63789857Sobrien  if (li == NULL)
63889857Sobrien    free (asymbols);
63933965Sjdp}
64033965Sjdp
64189857Sobrien/* This is called via bfd_map_over_sections.  INFO->SYM_NAME is a symbol
64233965Sjdp   defined in INFO->DEFSECNAME.  If this section maps into any of the
64333965Sjdp   sections listed in INFO->NCRS, other than INFO->DEFSECNAME, then we
64433965Sjdp   look through the relocations.  If any of the relocations are to
64589857Sobrien   INFO->SYM_NAME, then we report a prohibited cross reference error.  */
64633965Sjdp
64733965Sjdpstatic void
648130561Sobriencheck_reloc_refs (bfd *abfd, asection *sec, void *iarg)
64933965Sjdp{
650130561Sobrien  struct check_refs_info *info = iarg;
65133965Sjdp  asection *outsec;
65233965Sjdp  const char *outsecname;
65333965Sjdp  asection *outdefsec;
65433965Sjdp  const char *outdefsecname;
65533965Sjdp  struct lang_nocrossref *ncr;
65633965Sjdp  const char *symname;
657218822Sdim  bfd_boolean global;
65833965Sjdp  long relsize;
65933965Sjdp  arelent **relpp;
66033965Sjdp  long relcount;
66133965Sjdp  arelent **p, **pend;
66233965Sjdp
66333965Sjdp  outsec = sec->output_section;
66433965Sjdp  outsecname = bfd_get_section_name (outsec->owner, outsec);
66533965Sjdp
66633965Sjdp  outdefsec = info->defsec->output_section;
66733965Sjdp  outdefsecname = bfd_get_section_name (outdefsec->owner, outdefsec);
66833965Sjdp
66933965Sjdp  /* The section where the symbol is defined is permitted.  */
67033965Sjdp  if (strcmp (outsecname, outdefsecname) == 0)
67133965Sjdp    return;
67233965Sjdp
67333965Sjdp  for (ncr = info->ncrs->list; ncr != NULL; ncr = ncr->next)
67433965Sjdp    if (strcmp (outsecname, ncr->name) == 0)
67533965Sjdp      break;
67633965Sjdp
67733965Sjdp  if (ncr == NULL)
67833965Sjdp    return;
67933965Sjdp
68033965Sjdp  /* This section is one for which cross references are prohibited.
68133965Sjdp     Look through the relocations, and see if any of them are to
68289857Sobrien     INFO->SYM_NAME.  If INFO->SYMNAME is NULL, check for relocations
683218822Sdim     against the section symbol.  If INFO->GLOBAL is TRUE, the
684218822Sdim     definition is global, check for relocations against the global
685218822Sdim     symbols.  Otherwise check for relocations against the local and
686218822Sdim     section symbols.  */
68733965Sjdp
68889857Sobrien  symname = info->sym_name;
689218822Sdim  global = info->global;
69033965Sjdp
69133965Sjdp  relsize = bfd_get_reloc_upper_bound (abfd, sec);
69233965Sjdp  if (relsize < 0)
69360484Sobrien    einfo (_("%B%F: could not read relocs: %E\n"), abfd);
69433965Sjdp  if (relsize == 0)
69533965Sjdp    return;
69633965Sjdp
697130561Sobrien  relpp = xmalloc (relsize);
69833965Sjdp  relcount = bfd_canonicalize_reloc (abfd, sec, relpp, info->asymbols);
69933965Sjdp  if (relcount < 0)
70060484Sobrien    einfo (_("%B%F: could not read relocs: %E\n"), abfd);
70133965Sjdp
70233965Sjdp  p = relpp;
70333965Sjdp  pend = p + relcount;
70433965Sjdp  for (; p < pend && *p != NULL; p++)
70533965Sjdp    {
70633965Sjdp      arelent *q = *p;
70733965Sjdp
70833965Sjdp      if (q->sym_ptr_ptr != NULL
70933965Sjdp	  && *q->sym_ptr_ptr != NULL
710218822Sdim	  && ((global
711218822Sdim	       && (bfd_is_und_section (bfd_get_section (*q->sym_ptr_ptr))
712218822Sdim		   || bfd_is_com_section (bfd_get_section (*q->sym_ptr_ptr))
713218822Sdim		   || ((*q->sym_ptr_ptr)->flags & (BSF_GLOBAL
714218822Sdim						   | BSF_WEAK)) != 0))
715218822Sdim	      || (!global
716218822Sdim		  && ((*q->sym_ptr_ptr)->flags & (BSF_LOCAL
717218822Sdim						  | BSF_SECTION_SYM)) != 0
718218822Sdim		  && bfd_get_section (*q->sym_ptr_ptr) == info->defsec))
71989857Sobrien	  && (symname != NULL
72089857Sobrien	      ? strcmp (bfd_asymbol_name (*q->sym_ptr_ptr), symname) == 0
721218822Sdim	      : ((*q->sym_ptr_ptr)->flags & BSF_SECTION_SYM) != 0))
72233965Sjdp	{
72333965Sjdp	  /* We found a reloc for the symbol.  The symbol is defined
724130561Sobrien	     in OUTSECNAME.  This reloc is from a section which is
725130561Sobrien	     mapped into a section from which references to OUTSECNAME
726130561Sobrien	     are prohibited.  We must report an error.  */
72760484Sobrien	  einfo (_("%X%C: prohibited cross reference from %s to `%T' in %s\n"),
72833965Sjdp		 abfd, sec, q->address, outsecname,
72933965Sjdp		 bfd_asymbol_name (*q->sym_ptr_ptr), outdefsecname);
73033965Sjdp	}
73133965Sjdp    }
73233965Sjdp
73333965Sjdp  free (relpp);
73433965Sjdp}
735