bfd/
authorAlan Modra <amodra@gmail.com>
Wed, 19 Jul 2006 01:50:23 +0000 (01:50 +0000)
committerAlan Modra <amodra@gmail.com>
Wed, 19 Jul 2006 01:50:23 +0000 (01:50 +0000)
* bfd-in.h (enum notice_asneeded_action): Define.
* bfd-in2.h: Regenerate.
* elflink.c (elf_link_add_object_symbols): Call linker "notice"
function with NULL name for as-needed handling.
ld/
* ld.h (handle_asneeded_cref): Declare.
* ldcref.c: Include objalloc.h.
(old_table, old_count, old_tab, alloc_mark): New variables.
(tabsize, entsize, refsize, old_symcount): Likewise.
(add_cref): Use bfd_hash_allocate for refs.
(handle_asneeded_cref): New function.
* ldmain.c (notice): Call handle_asneeded_cref for NULL name.

bfd/ChangeLog
bfd/bfd-in.h
bfd/bfd-in2.h
bfd/elflink.c
ld/ChangeLog
ld/ld.h
ld/ldcref.c
ld/ldmain.c

index 4ce004e..55e6b54 100644 (file)
@@ -1,3 +1,10 @@
+2006-07-19  Alan Modra  <amodra@bigpond.net.au>
+
+       * bfd-in.h (enum notice_asneeded_action): Define.
+       * bfd-in2.h: Regenerate.
+       * elflink.c (elf_link_add_object_symbols): Call linker "notice"
+       function with NULL name for as-needed handling.
+
 2006-07-18  Paul Brook  <paul@codesourcery.com>
 
        * bfd-in2.h: Regenerate.
index ff9dcd6..6ceb0db 100644 (file)
@@ -638,6 +638,12 @@ enum dynamic_lib_link_class {
   DYN_NO_NEEDED = 8
 };
 
+enum notice_asneeded_action {
+  notice_as_needed,
+  notice_not_needed,
+  notice_needed
+};
+
 extern bfd_boolean bfd_elf_record_link_assignment
   (bfd *, struct bfd_link_info *, const char *, bfd_boolean,
    bfd_boolean);
index 381ae55..b75c8e2 100644 (file)
@@ -645,6 +645,12 @@ enum dynamic_lib_link_class {
   DYN_NO_NEEDED = 8
 };
 
+enum notice_asneeded_action {
+  notice_as_needed,
+  notice_not_needed,
+  notice_needed
+};
+
 extern bfd_boolean bfd_elf_record_link_assignment
   (bfd *, struct bfd_link_info *, const char *, bfd_boolean,
    bfd_boolean);
index c52b42e..99f0acc 100644 (file)
@@ -3551,6 +3551,13 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
       if (alloc_mark == NULL)
        goto error_free_vers;
 
+      /* Make a special call to the linker "notice" function to
+        tell it that we are about to handle an as-needed lib.  */
+      if (!(*info->callbacks->notice) (info, NULL, abfd, NULL,
+                                      notice_as_needed))
+       return FALSE;
+
+
       /* Clone the symbol table and sym hashes.  Remember some
         pointers into the symbol table, and dynamic symbol count.  */
       old_hash = (char *) old_tab + tabsize;
@@ -4241,6 +4248,12 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
            }
        }
 
+      /* Make a special call to the linker "notice" function to
+        tell it that symbols added for crefs may need to be removed.  */
+      if (!(*info->callbacks->notice) (info, NULL, abfd, NULL,
+                                      notice_not_needed))
+       return FALSE;
+
       free (old_tab);
       objalloc_free_block ((struct objalloc *) htab->root.table.memory,
                           alloc_mark);
@@ -4251,6 +4264,9 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 
   if (old_tab != NULL)
     {
+      if (!(*info->callbacks->notice) (info, NULL, abfd, NULL,
+                                      notice_needed))
+       return FALSE;
       free (old_tab);
       old_tab = NULL;
     }
index bb51a4a..14c5a2a 100644 (file)
@@ -1,3 +1,13 @@
+2006-07-19  Alan Modra  <amodra@bigpond.net.au>
+
+       * ld.h (handle_asneeded_cref): Declare.
+       * ldcref.c: Include objalloc.h.
+       (old_table, old_count, old_tab, alloc_mark): New variables.
+       (tabsize, entsize, refsize, old_symcount): Likewise.
+       (add_cref): Use bfd_hash_allocate for refs.
+       (handle_asneeded_cref): New function.
+       * ldmain.c (notice): Call handle_asneeded_cref for NULL name.
+
 2006-07-14  Michael Wetherell  <mike.wetherell@ntlworld.com>
 
        * configure.tgt (i[3-7]86-*-solaris2*, i[3-7]86-*-solaris*): Correct
diff --git a/ld/ld.h b/ld/ld.h
index 174e5b9..0b574d5 100644 (file)
--- a/ld/ld.h
+++ b/ld/ld.h
@@ -1,6 +1,6 @@
 /* ld.h -- general linker header file
    Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-   2001, 2002, 2003, 2004, 2005
+   2001, 2002, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
    This file is part of GLD, the Gnu Linker.
@@ -288,6 +288,7 @@ extern int parsing_defsym;
 
 extern int yyparse (void);
 extern void add_cref (const char *, bfd *, asection *, bfd_vma);
+extern bfd_boolean handle_asneeded_cref (bfd *, enum notice_asneeded_action);
 extern void output_cref (FILE *);
 extern void check_nocrossrefs (void);
 extern void ld_abort (const char *, int, const char *) ATTRIBUTE_NORETURN;
index 7445aa8..fb1d3c9 100644 (file)
@@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
 #include "sysdep.h"
 #include "bfdlink.h"
 #include "libiberty.h"
+#include "objalloc.h"
 
 #include "ld.h"
 #include "ldmain.h"
@@ -101,6 +102,16 @@ static bfd_boolean cref_initialized;
 
 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 *
@@ -165,7 +176,9 @@ add_cref (const char *name,
 
   if (r == NULL)
     {
-      r = xmalloc (sizeof *r);
+      r = 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;
@@ -182,6 +195,125 @@ add_cref (const char *name,
     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_hash_entry);
+           }
+       }
+
+      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_hash_entry));
+                 old_ref = (char *) old_ref + sizeof (struct cref_hash_entry);
+               }
+           }
+       }
+      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_hash_entry));
+                 old_ref = (char *) old_ref + sizeof (struct cref_hash_entry);
+               }
+           }
+       }
+
+      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.  */
index ec31310..cb56a8d 100644 (file)
@@ -1523,6 +1523,13 @@ notice (struct bfd_link_info *info,
        asection *section,
        bfd_vma value)
 {
+  if (name == NULL)
+    {
+      if (command_line.cref || nocrossref_list != NULL)
+       return handle_asneeded_cref (abfd, value);
+      return TRUE;
+    }
+
   if (! info->notice_all
       || (info->notice_hash != NULL
          && bfd_hash_lookup (info->notice_hash, name, FALSE, FALSE) != NULL))