Automatic date update in version.in
[platform/upstream/binutils.git] / ld / plugin.c
index 07f3afe..f02a97f 100644 (file)
@@ -1,5 +1,5 @@
 /* Plugin control for the GNU linker.
-   Copyright 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 2010-2014 Free Software Foundation, Inc.
 
    This file is part of the GNU Binutils.
 
@@ -90,17 +90,8 @@ static plugin_t *called_plugin = NULL;
 /* Last plugin to cause an error, if any.  */
 static const char *error_plugin = NULL;
 
-/* A hash table that records symbols referenced by non-IR files.  Used
-   at get_symbols time to determine whether any prevailing defs from
-   IR files are referenced only from other IR files, so tthat we can
-   we can distinguish the LDPR_PREVAILING_DEF and LDPR_PREVAILING_DEF_IRONLY
-   cases when establishing symbol resolutions.  */
-static struct bfd_hash_table *non_ironly_hash = NULL;
-
-/* State of linker "notice" and "multiple_definition" interfaces
-   before we poked at them.  */
+/* State of linker "notice" interface before we poked at it.  */
 static bfd_boolean orig_notice_all;
-static bfd_boolean orig_allow_multiple_defs;
 
 /* Original linker callbacks, and the plugin version.  */
 static const struct bfd_link_callbacks *orig_callbacks;
@@ -125,6 +116,7 @@ static const enum ld_plugin_tag tv_header_tags[] =
   LDPT_GET_INPUT_FILE,
   LDPT_RELEASE_INPUT_FILE,
   LDPT_GET_SYMBOLS,
+  LDPT_GET_SYMBOLS_V2,
   LDPT_ADD_INPUT_FILE,
   LDPT_ADD_INPUT_LIBRARY,
   LDPT_SET_EXTRA_LIBRARY_PATH
@@ -134,15 +126,10 @@ static const enum ld_plugin_tag tv_header_tags[] =
 static const size_t tv_header_size = ARRAY_SIZE (tv_header_tags);
 
 /* Forward references.  */
-static bfd_boolean plugin_notice (struct bfd_link_info *info,
-                                 const char *name, bfd *abfd,
-                                 asection *section, bfd_vma value);
-static bfd_boolean plugin_multiple_definition (struct bfd_link_info *info,
-                                              const char *name,
-                                              bfd *obfd, asection *osec,
-                                              bfd_vma oval, bfd *nbfd,
-                                              asection *nsec,
-                                              bfd_vma nval);
+static bfd_boolean plugin_notice (struct bfd_link_info *,
+                                 struct bfd_link_hash_entry *,
+                                 struct bfd_link_hash_entry *,
+                                 bfd *, asection *, bfd_vma, flagword);
 
 #if !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H)
 
@@ -169,6 +156,14 @@ dlclose (void *handle)
 
 #endif /* !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H)  */
 
+#ifndef HAVE_DLFCN_H
+static const char *
+dlerror (void)
+{
+  return "";
+}
+#endif
+
 /* Helper function for exiting with error status.  */
 static int
 set_plugin_error (const char *plugin)
@@ -192,7 +187,7 @@ plugin_error_plugin (void)
 }
 
 /* Handle -plugin arg: find and load plugin, or return error.  */
-int
+void
 plugin_opt_plugin (const char *plugin)
 {
   plugin_t *newplug;
@@ -202,7 +197,7 @@ plugin_opt_plugin (const char *plugin)
   newplug->name = plugin;
   newplug->dlhandle = dlopen (plugin, RTLD_NOW);
   if (!newplug->dlhandle)
-    return set_plugin_error (plugin);
+    einfo (_("%P%F: %s: error loading plugin: %s\n"), plugin, dlerror ());
 
   /* Chain on end, so when we run list it is in command-line order.  */
   *plugins_tail_chain_ptr = newplug;
@@ -211,7 +206,6 @@ plugin_opt_plugin (const char *plugin)
   /* Record it as current plugin for receiving args.  */
   last_plugin = newplug;
   last_plugin_args_tail_chain_ptr = &newplug->args;
-  return 0;
 }
 
 /* Accumulate option arguments for last-loaded plugin, or return
@@ -224,6 +218,17 @@ plugin_opt_plugin_arg (const char *arg)
   if (!last_plugin)
     return set_plugin_error (_("<no plugin>"));
 
+  /* Ignore -pass-through= from GCC driver.  */
+  if (*arg == '-')
+    {
+      const char *p = arg + 1;
+
+      if (*p == '-')
+       ++p;
+      if (strncmp (p, "pass-through=", 13) == 0)
+       return 0;
+    }
+
   newarg = xmalloc (sizeof *newarg);
   newarg->arg = arg;
   newarg->next = NULL;
@@ -254,7 +259,7 @@ plugin_get_ir_dummy_bfd (const char *name, bfd *srctemplate)
        {
          flagword flags;
 
-         /* Create sections to own the symbols.  */
+         /* Create section to own the symbols.  */
          flags = (SEC_CODE | SEC_HAS_CONTENTS | SEC_READONLY
                   | SEC_ALLOC | SEC_LOAD | SEC_KEEP | SEC_EXCLUDE);
          if (bfd_make_section_anyway_with_flags (abfd, ".text", flags))
@@ -266,17 +271,12 @@ plugin_get_ir_dummy_bfd (const char *name, bfd *srctemplate)
 }
 
 /* Check if the BFD passed in is an IR dummy object file.  */
-static bfd_boolean
+static inline bfd_boolean
 is_ir_dummy_bfd (const bfd *abfd)
 {
   /* ABFD can sometimes legitimately be NULL, e.g. when called from one
-     of the linker callbacks for a symbol in the *ABS* or *UND* sections.
-     Likewise, the usrdata field may be NULL if ABFD was added by the
-     backend without a corresponding input statement, as happens e.g.
-     when processing DT_NEEDED dependencies.  */
-  return (abfd
-         && abfd->usrdata
-         && ((lang_input_statement_type *)(abfd->usrdata))->claimed);
+     of the linker callbacks for a symbol in the *ABS* or *UND* sections.  */
+  return abfd != NULL && (abfd->flags & BFD_PLUGIN) != 0;
 }
 
 /* Helpers to convert between BFD and GOLD symbol formats.  */
@@ -299,7 +299,27 @@ asymbol_from_plugin_symbol (bfd *abfd, asymbol *asym,
       /* FALLTHRU */
     case LDPK_DEF:
       flags |= BSF_GLOBAL;
-      section = bfd_get_section_by_name (abfd, ".text");
+      if (ldsym->comdat_key)
+       {
+         char *name = concat (".gnu.linkonce.t.", ldsym->comdat_key,
+                              (const char *) NULL);
+         section = bfd_get_section_by_name (abfd, name);
+         if (section != NULL)
+           free (name);
+         else
+           {
+             flagword sflags;
+
+             sflags = (SEC_CODE | SEC_HAS_CONTENTS | SEC_READONLY
+                       | SEC_ALLOC | SEC_LOAD | SEC_KEEP | SEC_EXCLUDE
+                       | SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD);
+             section = bfd_make_section_anyway_with_flags (abfd, name, sflags);
+             if (section == NULL)
+               return LDPS_ERR;
+           }
+       }
+      else
+       section = bfd_get_section_by_name (abfd, ".text");
       break;
 
     case LDPK_WEAKUNDEF:
@@ -315,7 +335,10 @@ asymbol_from_plugin_symbol (bfd *abfd, asymbol *asym,
       asym->value = ldsym->size;
       /* For ELF targets, set alignment of common symbol to 1.  */
       if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
-       ((elf_symbol_type *) asym)->internal_elf_sym.st_value = 1;
+       {
+         ((elf_symbol_type *) asym)->internal_elf_sym.st_shndx = SHN_COMMON;
+         ((elf_symbol_type *) asym)->internal_elf_sym.st_value = 1;
+       }
       break;
 
     default:
@@ -392,12 +415,15 @@ add_symbols (void *handle, int nsyms, const struct ld_plugin_symbol *syms)
   asymbol **symptrs;
   bfd *abfd = handle;
   int n;
+
   ASSERT (called_plugin);
   symptrs = xmalloc (nsyms * sizeof *symptrs);
   for (n = 0; n < nsyms; n++)
     {
       enum ld_plugin_status rv;
-      asymbol *bfdsym = bfd_make_empty_symbol (abfd);
+      asymbol *bfdsym;
+
+      bfdsym = bfd_make_empty_symbol (abfd);
       symptrs[n] = bfdsym;
       rv = asymbol_from_plugin_symbol (abfd, bfdsym, syms + n);
       if (rv != LDPS_OK)
@@ -410,37 +436,37 @@ add_symbols (void *handle, int nsyms, const struct ld_plugin_symbol *syms)
 /* Get the input file information with an open (possibly re-opened)
    file descriptor.  */
 static enum ld_plugin_status
-get_input_file (const void *handle, struct ld_plugin_input_file *file)
+get_input_file (const void *handle ATTRIBUTE_UNUSED,
+                struct ld_plugin_input_file *file ATTRIBUTE_UNUSED)
 {
   ASSERT (called_plugin);
-  handle = handle;
-  file = file;
   return LDPS_ERR;
 }
 
 /* Release the input file.  */
 static enum ld_plugin_status
-release_input_file (const void *handle)
+release_input_file (const void *handle ATTRIBUTE_UNUSED)
 {
   ASSERT (called_plugin);
-  handle = handle;
   return LDPS_ERR;
 }
 
 /* Return TRUE if a defined symbol might be reachable from outside the
    universe of claimed objects.  */
 static inline bfd_boolean
-is_visible_from_outside (struct ld_plugin_symbol *lsym, asection *section,
+is_visible_from_outside (struct ld_plugin_symbol *lsym,
                         struct bfd_link_hash_entry *blhe)
 {
-  /* Section's owner may be NULL if it is the absolute
-     section, fortunately is_ir_dummy_bfd handles that.  */
-  if (!is_ir_dummy_bfd (section->owner))
-    return TRUE;
+  struct bfd_sym_chain *sym;
+
   if (link_info.relocatable)
     return TRUE;
-  if (link_info.export_dynamic || link_info.shared)
+  if (link_info.export_dynamic || !link_info.executable)
     {
+      /* Check if symbol is hidden by version script.  */
+      if (bfd_hide_sym_by_version (link_info.version_info,
+                                  blhe->root.string))
+       return FALSE;
       /* Only ELF symbols really have visibility.  */
       if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour)
        {
@@ -461,21 +487,30 @@ is_visible_from_outside (struct ld_plugin_symbol *lsym, asection *section,
       return (lsym->visibility == LDPV_DEFAULT
              || lsym->visibility == LDPV_PROTECTED);
     }
+
+  for (sym = &entry_symbol; sym != NULL; sym = sym->next)
+    if (sym->name
+       && strcmp (sym->name, blhe->root.string) == 0)
+      return TRUE;
+
   return FALSE;
 }
 
 /* Get the symbol resolution info for a plugin-claimed input file.  */
 static enum ld_plugin_status
-get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
+get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms,
+            int def_ironly_exp)
 {
   const bfd *abfd = handle;
   int n;
+
   ASSERT (called_plugin);
   for (n = 0; n < nsyms; n++)
     {
       struct bfd_link_hash_entry *blhe;
-      bfd_boolean ironly;
       asection *owner_sec;
+      int res;
+
       if (syms[n].def != LDPK_UNDEF)
        blhe = bfd_link_hash_lookup (link_info.hash, syms[n].name,
                                     FALSE, FALSE, TRUE);
@@ -484,7 +519,7 @@ get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
                                             syms[n].name, FALSE, FALSE, TRUE);
       if (!blhe)
        {
-         syms[n].resolution = LDPR_UNKNOWN;
+         res = LDPR_UNKNOWN;
          goto report_symbol;
        }
 
@@ -492,7 +527,7 @@ get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
       if (blhe->type == bfd_link_hash_undefined
          || blhe->type == bfd_link_hash_undefweak)
        {
-         syms[n].resolution = LDPR_UNDEF;
+         res = LDPR_UNDEF;
          goto report_symbol;
        }
       if (blhe->type != bfd_link_hash_defined
@@ -511,13 +546,6 @@ get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
                   ? blhe->u.c.p->section
                   : blhe->u.def.section);
 
-      /* We need to know if the sym is referenced from non-IR files.  Or
-        even potentially-referenced, perhaps in a future final link if
-        this is a partial one, perhaps dynamically at load-time if the
-        symbol is externally visible.  */
-      ironly = (!is_visible_from_outside (&syms[n], owner_sec, blhe)
-               && !bfd_hash_lookup (non_ironly_hash, syms[n].name,
-                                    FALSE, FALSE));
 
       /* If it was originally undefined or common, then it has been
         resolved; determine how.  */
@@ -526,49 +554,67 @@ get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
          || syms[n].def == LDPK_COMMON)
        {
          if (owner_sec->owner == link_info.output_bfd)
-           syms[n].resolution = LDPR_RESOLVED_EXEC;
+           res = LDPR_RESOLVED_EXEC;
          else if (owner_sec->owner == abfd)
-           syms[n].resolution = (ironly
-                                 ? LDPR_PREVAILING_DEF_IRONLY
-                                 : LDPR_PREVAILING_DEF);
+           res = LDPR_PREVAILING_DEF_IRONLY;
          else if (is_ir_dummy_bfd (owner_sec->owner))
-           syms[n].resolution = LDPR_RESOLVED_IR;
+           res = LDPR_RESOLVED_IR;
          else if (owner_sec->owner != NULL
                   && (owner_sec->owner->flags & DYNAMIC) != 0)
-           syms[n].resolution =  LDPR_RESOLVED_DYN;
+           res = LDPR_RESOLVED_DYN;
          else
-           syms[n].resolution = LDPR_RESOLVED_EXEC;
-         goto report_symbol;
+           res = LDPR_RESOLVED_EXEC;
        }
 
       /* Was originally def, or weakdef.  Does it prevail?  If the
         owner is the original dummy bfd that supplied it, then this
         is the definition that has prevailed.  */
-      if (owner_sec->owner == link_info.output_bfd)
-       syms[n].resolution = LDPR_PREEMPTED_REG;
+      else if (owner_sec->owner == link_info.output_bfd)
+       res = LDPR_PREEMPTED_REG;
       else if (owner_sec->owner == abfd)
-       {
-         syms[n].resolution = (ironly
-                               ? LDPR_PREVAILING_DEF_IRONLY
-                               : LDPR_PREVAILING_DEF);
-         goto report_symbol;
-       }
+       res = LDPR_PREVAILING_DEF_IRONLY;
 
       /* Was originally def, weakdef, or common, but has been pre-empted.  */
-      syms[n].resolution = (is_ir_dummy_bfd (owner_sec->owner)
-                           ? LDPR_PREEMPTED_IR
-                           : LDPR_PREEMPTED_REG);
+      else if (is_ir_dummy_bfd (owner_sec->owner))
+       res = LDPR_PREEMPTED_IR;
+      else
+       res = LDPR_PREEMPTED_REG;
+
+      if (res == LDPR_PREVAILING_DEF_IRONLY)
+       {
+         /* We need to know if the sym is referenced from non-IR files.  Or
+            even potentially-referenced, perhaps in a future final link if
+            this is a partial one, perhaps dynamically at load-time if the
+            symbol is externally visible.  */
+         if (blhe->non_ir_ref)
+           res = LDPR_PREVAILING_DEF;
+         else if (is_visible_from_outside (&syms[n], blhe))
+           res = def_ironly_exp;
+       }
 
     report_symbol:
+      syms[n].resolution = res;
       if (report_plugin_symbols)
        einfo (_("%P: %B: symbol `%s' "
                 "definition: %d, visibility: %d, resolution: %d\n"),
               abfd, syms[n].name,
-              syms[n].def, syms[n].visibility, syms[n].resolution);
+              syms[n].def, syms[n].visibility, res);
     }
   return LDPS_OK;
 }
 
+static enum ld_plugin_status
+get_symbols_v1 (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
+{
+  return get_symbols (handle, nsyms, syms, LDPR_PREVAILING_DEF);
+}
+
+static enum ld_plugin_status
+get_symbols_v2 (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
+{
+  return get_symbols (handle, nsyms, syms, LDPR_PREVAILING_DEF_IRONLY_EXP);
+}
+
 /* Add a new (real) input file generated by a plugin.  */
 static enum ld_plugin_status
 add_input_file (const char *pathname)
@@ -636,7 +682,7 @@ message (int level, const char *format, ...)
 }
 
 /* Helper to size leading part of tv array and set it up. */
-static size_t
+static void
 set_tv_header (struct ld_plugin_tv *tv)
 {
   size_t i;
@@ -645,9 +691,6 @@ set_tv_header (struct ld_plugin_tv *tv)
   static const unsigned int major = (unsigned)(BFD_VERSION / 100000000UL);
   static const unsigned int minor = (unsigned)(BFD_VERSION / 1000000UL) % 100;
 
-  if (!tv)
-    return tv_header_size;
-
   for (i = 0; i < tv_header_size; i++)
     {
       tv[i].tv_tag = tv_header_tags[i];
@@ -666,7 +709,9 @@ set_tv_header (struct ld_plugin_tv *tv)
        case LDPT_LINKER_OUTPUT:
          TVU(val) = (link_info.relocatable
                      ? LDPO_REL
-                     : (link_info.shared ? LDPO_DYN : LDPO_EXEC));
+                     : (link_info.executable
+                        ? (link_info.pie ? LDPO_PIE : LDPO_EXEC)
+                        : LDPO_DYN));
          break;
        case LDPT_OUTPUT_NAME:
          TVU(string) = output_filename;
@@ -690,7 +735,10 @@ set_tv_header (struct ld_plugin_tv *tv)
          TVU(release_input_file) = release_input_file;
          break;
        case LDPT_GET_SYMBOLS:
-         TVU(get_symbols) = get_symbols;
+         TVU(get_symbols) = get_symbols_v1;
+         break;
+       case LDPT_GET_SYMBOLS_V2:
+         TVU(get_symbols) = get_symbols_v2;
          break;
        case LDPT_ADD_INPUT_FILE:
          TVU(add_input_file) = add_input_file;
@@ -708,7 +756,6 @@ set_tv_header (struct ld_plugin_tv *tv)
        }
 #undef TVU
     }
-  return tv_header_size;
 }
 
 /* Append the per-plugin args list and trailing LDPT_NULL to tv.  */
@@ -735,29 +782,8 @@ plugin_active_plugins_p (void)
   return plugins_list != NULL;
 }
 
-/* Init the non_ironly hash table.  */
-static void
-init_non_ironly_hash (void)
-{
-  struct bfd_sym_chain *sym;
-
-  non_ironly_hash
-    = (struct bfd_hash_table *) xmalloc (sizeof (struct bfd_hash_table));
-  if (!bfd_hash_table_init_n (non_ironly_hash,
-                             bfd_hash_newfunc,
-                             sizeof (struct bfd_hash_entry),
-                             61))
-    einfo (_("%P%F: bfd_hash_table_init failed: %E\n"));
-
-  for (sym = &entry_symbol; sym != NULL; sym = sym->next)
-    if (sym->name
-       && !bfd_hash_lookup (non_ironly_hash, sym->name, TRUE, TRUE))
-      einfo (_("%P%X: hash table failure adding symbol %s\n"),
-            sym->name);
-}
-
 /* Load up and initialise all plugins after argument parsing.  */
-int
+void
 plugin_load_plugins (void)
 {
   struct ld_plugin_tv *my_tv;
@@ -766,7 +792,7 @@ plugin_load_plugins (void)
 
   /* If there are no plugins, we need do nothing this run.  */
   if (!curplug)
-    return 0;
+    return;
 
   /* First pass over plugins to find max # args needed so that we
      can size and allocate the tv array.  */
@@ -786,17 +812,20 @@ plugin_load_plugins (void)
   while (curplug)
     {
       enum ld_plugin_status rv;
-      ld_plugin_onload onloadfn = dlsym (curplug->dlhandle, "onload");
+      ld_plugin_onload onloadfn;
+
+      onloadfn = (ld_plugin_onload) dlsym (curplug->dlhandle, "onload");
       if (!onloadfn)
-       onloadfn = dlsym (curplug->dlhandle, "_onload");
+       onloadfn = (ld_plugin_onload) dlsym (curplug->dlhandle, "_onload");
       if (!onloadfn)
-       return set_plugin_error (curplug->name);
+        einfo (_("%P%F: %s: error loading plugin: %s\n"),
+              curplug->name, dlerror ());
       set_tv_plugin_args (curplug, &my_tv[tv_header_size]);
       called_plugin = curplug;
       rv = (*onloadfn) (my_tv);
       called_plugin = NULL;
       if (rv != LDPS_OK)
-       return set_plugin_error (curplug->name);
+       einfo (_("%P%F: %s: plugin error: %d\n"), curplug->name, rv);
       curplug = curplug->next;
     }
 
@@ -808,14 +837,12 @@ plugin_load_plugins (void)
   plugin_callbacks = *orig_callbacks;
   plugin_callbacks.notice = &plugin_notice;
   link_info.notice_all = TRUE;
+  link_info.lto_plugin_active = TRUE;
   link_info.callbacks = &plugin_callbacks;
-  init_non_ironly_hash ();
-
-  return 0;
 }
 
 /* Call 'claim file' hook for all plugins.  */
-int
+static int
 plugin_call_claim_file (const struct ld_plugin_input_file *file, int *claimed)
 {
   plugin_t *curplug = plugins_list;
@@ -838,6 +865,42 @@ plugin_call_claim_file (const struct ld_plugin_input_file *file, int *claimed)
   return plugin_error_p () ? -1 : 0;
 }
 
+void
+plugin_maybe_claim (struct ld_plugin_input_file *file,
+                   lang_input_statement_type *entry)
+{
+  int claimed = 0;
+
+  /* We create a dummy BFD, initially empty, to house whatever symbols
+     the plugin may want to add.  */
+  file->handle = plugin_get_ir_dummy_bfd (entry->the_bfd->filename,
+                                         entry->the_bfd);
+  if (plugin_call_claim_file (file, &claimed))
+    einfo (_("%P%F: %s: plugin reported error claiming file\n"),
+          plugin_error_plugin ());
+  /* fd belongs to us, not the plugin; but we don't need it.  */
+  close (file->fd);
+  if (claimed)
+    {
+      /* Discard the real file's BFD and substitute the dummy one.  */
+
+      /* BFD archive handling caches elements so we can't call
+        bfd_close for archives.  */
+      if (entry->the_bfd->my_archive == NULL)
+       bfd_close (entry->the_bfd);
+      entry->the_bfd = file->handle;
+      entry->flags.claimed = TRUE;
+      bfd_make_readable (entry->the_bfd);
+    }
+  else
+    {
+      /* If plugin didn't claim the file, we don't need the dummy bfd.
+        Can't avoid speculatively creating it, alas.  */
+      bfd_close_all_done (file->handle);
+      entry->flags.claimed = FALSE;
+    }
+}
+
 /* Call 'all symbols read' hook for all plugins.  */
 int
 plugin_call_all_symbols_read (void)
@@ -847,14 +910,6 @@ plugin_call_all_symbols_read (void)
   /* Disable any further file-claiming.  */
   no_more_claiming = TRUE;
 
-  /* If --allow-multiple-definition is in effect, we need to disable it,
-     as the plugin infrastructure relies on the multiple_definition
-     callback to swap out the dummy IR-only BFDs for new real ones
-     when it starts opening the files added during this callback.  */
-  orig_allow_multiple_defs = link_info.allow_multiple_definition;
-  link_info.allow_multiple_definition = FALSE;
-  plugin_callbacks.multiple_definition = &plugin_multiple_definition;
-
   while (curplug)
     {
       if (curplug->all_symbols_read_handler)
@@ -886,91 +941,102 @@ plugin_call_cleanup (void)
          rv = (*curplug->cleanup_handler) ();
          called_plugin = NULL;
          if (rv != LDPS_OK)
-           set_plugin_error (curplug->name);
+           info_msg (_("%P: %s: error in plugin cleanup: %d (ignored)\n"),
+                     curplug->name, rv);
          dlclose (curplug->dlhandle);
        }
       curplug = curplug->next;
     }
-  if (plugin_error_p ())
-    info_msg (_("%P: %s: error in plugin cleanup (ignored)\n"),
-             plugin_error_plugin ());
 }
 
 /* To determine which symbols should be resolved LDPR_PREVAILING_DEF
    and which LDPR_PREVAILING_DEF_IRONLY, we notice all the symbols as
-   the linker adds them to the linker hash table.  If we see a symbol
-   being referenced from a non-IR file, we add it to the non_ironly hash
-   table.  If we can't find it there at get_symbols time, we know that
-   it was referenced only by IR files.  We have to notice_all symbols,
-   because we won't necessarily know until later which ones will be
-   contributed by IR files.  */
+   the linker adds them to the linker hash table.  Mark those
+   referenced from a non-IR file with non_ir_ref.  We have to
+   notice_all symbols, because we won't necessarily know until later
+   which ones will be contributed by IR files.  */
 static bfd_boolean
 plugin_notice (struct bfd_link_info *info,
-              const char *name,
+              struct bfd_link_hash_entry *h,
+              struct bfd_link_hash_entry *inh,
               bfd *abfd,
               asection *section,
-              bfd_vma value)
+              bfd_vma value,
+              flagword flags)
 {
-  if (name != NULL)
+  struct bfd_link_hash_entry *orig_h = h;
+
+  if (h != NULL)
     {
-      /* No further processing if this def/ref is from an IR dummy BFD.  */
+      bfd *sym_bfd;
+
+      if (h->type == bfd_link_hash_warning)
+       h = h->u.i.link;
+
+      /* Nothing to do here if this def/ref is from an IR dummy BFD.  */
       if (is_ir_dummy_bfd (abfd))
-       return TRUE;
+       ;
 
-      /* We only care about refs, not defs, indicated by section
-        pointing to the undefined section (according to the bfd
-        linker notice callback interface definition).  */
-      if (bfd_is_und_section (section))
+      /* Making an indirect symbol counts as a reference unless this
+        is a brand new symbol.  */
+      else if (bfd_is_ind_section (section)
+              || (flags & BSF_INDIRECT) != 0)
        {
-         /* This is a ref from a non-IR file, so note the ref'd
-            symbol in the non-IR-only hash.  */
-         if (!bfd_hash_lookup (non_ironly_hash, name, TRUE, TRUE))
-           einfo (_("%P%X: %s: hash table failure adding symbol %s\n"),
-                  abfd->filename, name);
+         /* ??? Some of this is questionable.  See comments in
+            _bfd_generic_link_add_one_symbol for case IND.  */
+         if (h->type != bfd_link_hash_new)
+           {
+             h->non_ir_ref = TRUE;
+             inh->non_ir_ref = TRUE;
+           }
+         else if (inh->type == bfd_link_hash_new)
+           inh->non_ir_ref = TRUE;
+       }
+
+      /* Nothing to do here for warning symbols.  */
+      else if ((flags & BSF_WARNING) != 0)
+       ;
+
+      /* Nothing to do here for constructor symbols.  */
+      else if ((flags & BSF_CONSTRUCTOR) != 0)
+       ;
+
+      /* If this is a ref, set non_ir_ref.  */
+      else if (bfd_is_und_section (section))
+       {
+         /* Replace the undefined dummy bfd with the real one.  */
+         if ((h->type == bfd_link_hash_undefined
+              || h->type == bfd_link_hash_undefweak)
+             && (h->u.undef.abfd == NULL
+                 || (h->u.undef.abfd->flags & BFD_PLUGIN) != 0))
+           h->u.undef.abfd = abfd;
+         h->non_ir_ref = TRUE;
+       }
+
+      /* Otherwise, it must be a new def.  Ensure any symbol defined
+        in an IR dummy BFD takes on a new value from a real BFD.
+        Weak symbols are not normally overridden by a new weak
+        definition, and strong symbols will normally cause multiple
+        definition errors.  Avoid this by making the symbol appear
+        to be undefined.  */
+      else if (((h->type == bfd_link_hash_defweak
+                || h->type == bfd_link_hash_defined)
+               && is_ir_dummy_bfd (sym_bfd = h->u.def.section->owner))
+              || (h->type == bfd_link_hash_common
+                  && is_ir_dummy_bfd (sym_bfd = h->u.c.p->section->owner)))
+       {
+         h->type = bfd_link_hash_undefweak;
+         h->u.undef.abfd = sym_bfd;
        }
     }
 
   /* Continue with cref/nocrossref/trace-sym processing.  */
-  if (name == NULL
+  if (orig_h == NULL
       || orig_notice_all
       || (info->notice_hash != NULL
-         && bfd_hash_lookup (info->notice_hash, name, FALSE, FALSE) != NULL))
-    return (*orig_callbacks->notice) (info, name, abfd, section, value);
+         && bfd_hash_lookup (info->notice_hash, orig_h->root.string,
+                             FALSE, FALSE) != NULL))
+    return (*orig_callbacks->notice) (info, orig_h, inh,
+                                     abfd, section, value, flags);
   return TRUE;
 }
-
-/* When we add new object files to the link at all symbols read time,
-   these contain the real code and symbols generated from the IR files,
-   and so duplicate all the definitions already supplied by the dummy
-   IR-only BFDs that we created at claim files time.  We use the linker's
-   multiple-definitions callback hook to fix up the clash, discarding
-   the symbol from the IR-only BFD in favour of the symbol from the
-   real BFD.  We return true if this was not-really-a-clash because
-   we've fixed it up, or anyway if --allow-multiple-definition was in
-   effect (before we disabled it to ensure we got called back).  */
-static bfd_boolean
-plugin_multiple_definition (struct bfd_link_info *info, const char *name,
-                           bfd *obfd, asection *osec, bfd_vma oval,
-                           bfd *nbfd, asection *nsec, bfd_vma nval)
-{
-  if (is_ir_dummy_bfd (obfd))
-    {
-      struct bfd_link_hash_entry *blhe
-       = bfd_link_hash_lookup (info->hash, name, FALSE, FALSE, FALSE);
-      if (!blhe)
-       einfo (_("%P%X: %s: can't find IR symbol '%s'\n"), nbfd->filename,
-              name);
-      else if (blhe->type != bfd_link_hash_defined)
-       einfo (_("%P%x: %s: bad IR symbol type %d\n"), name, blhe->type);
-      /* Replace it with new details.  */
-      blhe->u.def.section = nsec;
-      blhe->u.def.value = nval;
-      return TRUE;
-    }
-
-  if (orig_allow_multiple_defs)
-    return TRUE;
-
-  return (*orig_callbacks->multiple_definition) (info, name, obfd, osec, oval,
-                                                nbfd, nsec, nval);
-}