* ldcref.c (output_one_cref): Don't crash if a symbol is defined
authorIan Lance Taylor <ian@airs.com>
Tue, 30 Jul 1996 18:48:25 +0000 (18:48 +0000)
committerIan Lance Taylor <ian@airs.com>
Tue, 30 Jul 1996 18:48:25 +0000 (18:48 +0000)
in a section without an owner.

ld/ldcref.c [new file with mode: 0644]

diff --git a/ld/ldcref.c b/ld/ldcref.c
new file mode 100644 (file)
index 0000000..2aceb1f
--- /dev/null
@@ -0,0 +1,327 @@
+/* ldcref.c -- output a cross reference table
+   Copyright (C) 1996 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor <ian@cygnus.com>
+
+This file is part of GLD, the Gnu Linker.
+
+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 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.  */
+
+/* This file holds routines that manage the cross reference table.  */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "bfdlink.h"
+#include "libiberty.h"
+
+#include "ld.h"
+#include "ldmain.h"
+#include "ldmisc.h"
+
+/* We keep an instance of this structure for each reference to a
+   symbol from a given object.  */
+
+struct cref_ref
+{
+  /* The next reference.  */
+  struct cref_ref *next;
+  /* The object.  */
+  bfd *abfd;
+  /* True if the symbol is defined.  */
+  unsigned int def : 1;
+  /* True if the symbol is common.  */
+  unsigned int common : 1;
+  /* True if the symbol is undefined.  */
+  unsigned int undef : 1;
+};
+
+/* We keep a hash table of symbols.  Each entry looks like this.  */
+
+struct cref_hash_entry
+{
+  struct bfd_hash_entry root;
+  /* The demangled name.  */
+  char *demangled;
+  /* References to and definitions of this symbol.  */
+  struct cref_ref *refs;
+};
+
+/* This is what the hash table looks like.  */
+
+struct cref_hash_table
+{
+  struct bfd_hash_table root;
+};
+
+/* Local functions.  */
+
+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 *));
+
+/* Look up an entry in the cref hash table.  */
+
+#define cref_hash_lookup(table, string, create, copy)          \
+  ((struct cref_hash_entry *)                                  \
+   bfd_hash_lookup (&(table)->root, (string), (create), (copy)))
+
+/* Traverse the cref hash table.  */
+
+#define cref_hash_traverse(table, func, info)                          \
+  (bfd_hash_traverse                                                   \
+   (&(table)->root,                                                    \
+    (boolean (*) PARAMS ((struct bfd_hash_entry *, PTR))) (func),      \
+    (info)))
+
+/* The cref hash table.  */
+
+static struct cref_hash_table cref_table;
+
+/* Whether the cref hash table has been initialized.  */
+
+static boolean cref_initialized;
+
+/* The number of symbols seen so far.  */
+
+static size_t cref_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;
+{
+  struct cref_hash_entry *ret = (struct cref_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == NULL)
+    ret = ((struct cref_hash_entry *)
+          bfd_hash_allocate (table, sizeof (struct cref_hash_entry)));
+  if (ret == NULL)
+    return (struct bfd_hash_entry *) ret;
+
+  /* Call the allocation method of the superclass.  */
+  ret = ((struct cref_hash_entry *)
+        bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
+  if (ret != NULL)
+    {
+      /* Set local fields.  */
+      ret->demangled = NULL;
+      ret->refs = NULL;
+
+      /* Keep a count of the number of entries created in the hash
+         table.  */
+      ++cref_symcount;
+    }
+
+  return (struct bfd_hash_entry *) ret;
+}
+
+/* Add a symbol to the cref hash table.  This is called for every
+   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;
+{
+  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;
+    }
+
+  h = cref_hash_lookup (&cref_table, name, true, false);
+  if (h == NULL)
+    einfo ("%X%P: cref_hash_lookup failed: %E\n");
+
+  for (r = h->refs; r != NULL; r = r->next)
+    if (r->abfd == abfd)
+      break;
+
+  if (r == NULL)
+    {
+      r = (struct cref_ref *) xmalloc (sizeof *r);
+      r->next = h->refs;
+      h->refs = r;
+      r->abfd = abfd;
+      r->def = false;
+      r->common = false;
+      r->undef = false;
+    }
+
+  if (bfd_is_und_section (section))
+    r->undef = true;
+  else if (bfd_is_com_section (section))
+    r->common = true;
+  else
+    r->def = 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;
+{
+  struct cref_hash_entry ***pph = (struct cref_hash_entry ***) data;
+
+  ASSERT (h->demangled == NULL);
+  h->demangled = demangle (h->root.string);
+
+  **pph = h;
+
+  ++*pph;
+
+  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;
+{
+  const struct cref_hash_entry **p1 = (const struct cref_hash_entry **) a1;
+  const struct cref_hash_entry **p2 = (const struct cref_hash_entry **) a2;
+
+  return strcmp ((*p1)->demangled, (*p2)->demangled);
+}
+
+/* Write out the cref table.  */
+
+#define FILECOL (50)
+
+void
+output_cref (fp)
+     FILE *fp;
+{
+  int len;
+  struct cref_hash_entry **csyms, **csym_fill, **csym, **csym_end;
+
+  fprintf (fp, "\nCross Reference Table\n\n");
+  fprintf (fp, "Symbol");
+  len = sizeof "Symbol" - 1;
+  while (len < FILECOL)
+    {
+      putc (' ' , fp);
+      ++len;
+    }
+  fprintf (fp, "File\n");
+
+  if (! cref_initialized)
+    {
+      fprintf (fp, "No symbols\n");
+      return;
+    }
+
+  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);
+
+  qsort (csyms, cref_symcount, sizeof (*csyms), cref_sort_array);
+
+  csym_end = csyms + cref_symcount;
+  for (csym = csyms; csym < csym_end; csym++)
+    output_one_cref (fp, *csym);
+}
+
+/* Output one entry in the cross reference table.  */
+
+static void
+output_one_cref (fp, h)
+     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);
+  if (hl == NULL)
+    einfo ("%P: symbol `%T' missing from main hash table\n",
+          h->root.string);
+  else
+    {
+      /* If this symbol is defined in a dynamic object but never
+        referenced by a normal object, then don't print it.  */
+      if (hl->type == bfd_link_hash_defined)
+       {
+         if (hl->u.def.section->output_section == NULL)
+           return;
+         if (hl->u.def.section->owner != NULL
+             && (hl->u.def.section->owner->flags & DYNAMIC) != 0)
+           {
+             for (r = h->refs; r != NULL; r = r->next)
+               if ((r->abfd->flags & DYNAMIC) == 0)
+                 break;
+             if (r == NULL)
+               return;
+           }
+       }
+    }
+
+  fprintf (fp, "%s ", h->demangled);
+  len = strlen (h->demangled) + 1;
+
+  for (r = h->refs; r != NULL; r = r->next)
+    {
+      if (r->def)
+       {
+         while (len < FILECOL)
+           {
+             putc (' ', fp);
+             ++len;
+           }
+         finfo (fp, "%B\n", r->abfd);
+         len = 0;
+       }
+    }
+
+  for (r = h->refs; r != NULL; r = r->next)
+    {
+      if (! r->def)
+       {
+         while (len < FILECOL)
+           {
+             putc (' ', fp);
+             ++len;
+           }
+         finfo (fp, "%B\n", r->abfd);
+         len = 0;
+       }
+    }
+
+  ASSERT (len == 0);
+}