bfd_cleanup for object_p
[deliverable/binutils-gdb.git] / bfd / plugin.c
index a068861641c2bac17a5234eb5aebcb5571dafd23..a0f172d363793753601db6afa892c97f79da0aab 100644 (file)
@@ -1,5 +1,5 @@
 /* Plugin support for BFD.
-   Copyright (C) 2009-2014 Free Software Foundation, Inc.
+   Copyright (C) 2009-2020 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -69,42 +69,46 @@ dlerror (void)
 
 #endif /* !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H)  */
 
-#define bfd_plugin_close_and_cleanup                  _bfd_generic_close_and_cleanup
-#define bfd_plugin_bfd_free_cached_info               _bfd_generic_bfd_free_cached_info
-#define bfd_plugin_new_section_hook                   _bfd_generic_new_section_hook
-#define bfd_plugin_get_section_contents               _bfd_generic_get_section_contents
+#define bfd_plugin_bfd_free_cached_info                      _bfd_generic_bfd_free_cached_info
+#define bfd_plugin_new_section_hook                  _bfd_generic_new_section_hook
+#define bfd_plugin_get_section_contents                      _bfd_generic_get_section_contents
 #define bfd_plugin_get_section_contents_in_window     _bfd_generic_get_section_contents_in_window
-#define bfd_plugin_bfd_copy_private_header_data       _bfd_generic_bfd_copy_private_header_data
-#define bfd_plugin_bfd_merge_private_bfd_data         _bfd_generic_bfd_merge_private_bfd_data
-#define bfd_plugin_bfd_copy_private_header_data       _bfd_generic_bfd_copy_private_header_data
-#define bfd_plugin_bfd_set_private_flags              _bfd_generic_bfd_set_private_flags
+#define bfd_plugin_bfd_copy_private_header_data              _bfd_generic_bfd_copy_private_header_data
+#define bfd_plugin_bfd_merge_private_bfd_data        _bfd_generic_bfd_merge_private_bfd_data
+#define bfd_plugin_bfd_copy_private_header_data              _bfd_generic_bfd_copy_private_header_data
+#define bfd_plugin_bfd_set_private_flags             _bfd_generic_bfd_set_private_flags
 #define bfd_plugin_core_file_matches_executable_p     generic_core_file_matches_executable_p
-#define bfd_plugin_bfd_is_local_label_name            _bfd_nosymbols_bfd_is_local_label_name
-#define bfd_plugin_bfd_is_target_special_symbol       ((bfd_boolean (*) (bfd *, asymbol *)) bfd_false)
-#define bfd_plugin_get_lineno                         _bfd_nosymbols_get_lineno
-#define bfd_plugin_find_nearest_line                  _bfd_nosymbols_find_nearest_line
-#define bfd_plugin_find_line                          _bfd_nosymbols_find_line
-#define bfd_plugin_find_inliner_info                  _bfd_nosymbols_find_inliner_info
-#define bfd_plugin_bfd_make_debug_symbol              _bfd_nosymbols_bfd_make_debug_symbol
-#define bfd_plugin_read_minisymbols                   _bfd_generic_read_minisymbols
-#define bfd_plugin_minisymbol_to_symbol               _bfd_generic_minisymbol_to_symbol
-#define bfd_plugin_set_arch_mach                      bfd_default_set_arch_mach
-#define bfd_plugin_set_section_contents               _bfd_generic_set_section_contents
+#define bfd_plugin_bfd_is_local_label_name           _bfd_nosymbols_bfd_is_local_label_name
+#define bfd_plugin_bfd_is_target_special_symbol              _bfd_bool_bfd_asymbol_false
+#define bfd_plugin_get_lineno                        _bfd_nosymbols_get_lineno
+#define bfd_plugin_find_nearest_line                 _bfd_nosymbols_find_nearest_line
+#define bfd_plugin_find_line                         _bfd_nosymbols_find_line
+#define bfd_plugin_find_inliner_info                 _bfd_nosymbols_find_inliner_info
+#define bfd_plugin_get_symbol_version_string         _bfd_nosymbols_get_symbol_version_string
+#define bfd_plugin_bfd_make_debug_symbol             _bfd_nosymbols_bfd_make_debug_symbol
+#define bfd_plugin_read_minisymbols                  _bfd_generic_read_minisymbols
+#define bfd_plugin_minisymbol_to_symbol                      _bfd_generic_minisymbol_to_symbol
+#define bfd_plugin_set_arch_mach                     bfd_default_set_arch_mach
+#define bfd_plugin_set_section_contents                      _bfd_generic_set_section_contents
 #define bfd_plugin_bfd_get_relocated_section_contents bfd_generic_get_relocated_section_contents
-#define bfd_plugin_bfd_relax_section                  bfd_generic_relax_section
-#define bfd_plugin_bfd_link_hash_table_create         _bfd_generic_link_hash_table_create
-#define bfd_plugin_bfd_link_add_symbols               _bfd_generic_link_add_symbols
-#define bfd_plugin_bfd_link_just_syms                 _bfd_generic_link_just_syms
-#define bfd_plugin_bfd_final_link                     _bfd_generic_final_link
-#define bfd_plugin_bfd_link_split_section             _bfd_generic_link_split_section
-#define bfd_plugin_bfd_gc_sections                    bfd_generic_gc_sections
-#define bfd_plugin_bfd_lookup_section_flags           bfd_generic_lookup_section_flags
-#define bfd_plugin_bfd_merge_sections                 bfd_generic_merge_sections
-#define bfd_plugin_bfd_is_group_section               bfd_generic_is_group_section
-#define bfd_plugin_bfd_discard_group                  bfd_generic_discard_group
-#define bfd_plugin_section_already_linked             _bfd_generic_section_already_linked
-#define bfd_plugin_bfd_define_common_symbol           bfd_generic_define_common_symbol
+#define bfd_plugin_bfd_relax_section                 bfd_generic_relax_section
+#define bfd_plugin_bfd_link_hash_table_create        _bfd_generic_link_hash_table_create
+#define bfd_plugin_bfd_link_add_symbols                      _bfd_generic_link_add_symbols
+#define bfd_plugin_bfd_link_just_syms                _bfd_generic_link_just_syms
+#define bfd_plugin_bfd_final_link                    _bfd_generic_final_link
+#define bfd_plugin_bfd_link_split_section            _bfd_generic_link_split_section
+#define bfd_plugin_bfd_gc_sections                   bfd_generic_gc_sections
+#define bfd_plugin_bfd_lookup_section_flags          bfd_generic_lookup_section_flags
+#define bfd_plugin_bfd_merge_sections                bfd_generic_merge_sections
+#define bfd_plugin_bfd_is_group_section                      bfd_generic_is_group_section
+#define bfd_plugin_bfd_group_name                    bfd_generic_group_name
+#define bfd_plugin_bfd_discard_group                 bfd_generic_discard_group
+#define bfd_plugin_section_already_linked            _bfd_generic_section_already_linked
+#define bfd_plugin_bfd_define_common_symbol          bfd_generic_define_common_symbol
+#define bfd_plugin_bfd_link_hide_symbol                      _bfd_generic_link_hide_symbol
+#define bfd_plugin_bfd_define_start_stop             bfd_generic_define_start_stop
 #define bfd_plugin_bfd_copy_link_hash_symbol_type     _bfd_generic_copy_link_hash_symbol_type
+#define bfd_plugin_bfd_link_check_relocs             _bfd_generic_link_check_relocs
 
 static enum ld_plugin_status
 message (int level ATTRIBUTE_UNUSED,
@@ -119,13 +123,325 @@ message (int level ATTRIBUTE_UNUSED,
   return LDPS_OK;
 }
 
+struct plugin_list_entry
+{
+  /* These must be initialized for each IR object with LTO wrapper.  */
+  ld_plugin_claim_file_handler claim_file;
+  ld_plugin_all_symbols_read_handler all_symbols_read;
+  ld_plugin_all_symbols_read_handler cleanup_handler;
+  char *resolution_file;
+  char *resolution_option;
+  bfd *real_bfd;
+  long real_nsyms;
+  asymbol **real_syms;
+  int lto_nsyms;
+  const struct ld_plugin_symbol *lto_syms;
+
+  struct plugin_list_entry *next;
+
+  /* These can be reused for all IR objects.  */
+  const char *plugin_name;
+  char *gcc;
+  char *lto_wrapper;
+  char *gcc_env;
+  bfd_boolean initialized;
+};
+
+static const char *plugin_program_name;
+static int need_lto_wrapper_p;
+
+void
+bfd_plugin_set_program_name (const char *program_name,
+                            int need_lto_wrapper)
+{
+  plugin_program_name = program_name;
+  need_lto_wrapper_p = need_lto_wrapper;
+}
+
+/* Use GCC LTO wrapper to covert LTO IR object to the real object.  */
+
+static bfd_boolean
+get_lto_wrapper (struct plugin_list_entry *plugin)
+{
+  struct stat st;
+  const char *real_name;
+  const char *base_name;
+  size_t length;
+  const char *target_start = NULL;
+  const char *target_end = NULL;
+  size_t target_length = 0;
+  char *gcc_name;
+  char *wrapper_name;
+  char *p;
+  char dir_seperator = '\0';
+  char *resolution_file;
+
+  if (!need_lto_wrapper_p)
+    return FALSE;
+
+  if (plugin->initialized)
+    {
+      if (plugin->lto_wrapper)
+       {
+         resolution_file = make_temp_file (".res");
+         if (resolution_file)
+           {
+             plugin->resolution_file = resolution_file;
+             plugin->resolution_option = concat ("-fresolution=",
+                                                 resolution_file, NULL);
+             return TRUE;
+           }
+         else
+           {
+             /* Something is wrong.  Give up.  */
+             free (plugin->gcc);
+             free (plugin->lto_wrapper);
+             free (plugin->gcc_env);
+             plugin->gcc = NULL;
+             plugin->gcc_env = NULL;
+             plugin->lto_wrapper = NULL;
+           }
+       }
+
+      return FALSE;
+    }
+
+  plugin->initialized = TRUE;
+
+  /* Check for PREFIX/libexec/gcc/TARGET/VERSION/liblto_plugin.so.  */
+  real_name = lrealpath (plugin->plugin_name);
+  base_name = lbasename (real_name);
+
+  /* The directory length in plugin pathname.  */
+  length = base_name - real_name;
+
+  /* Skip if there is no PREFIX.  */
+  if (!length)
+    return FALSE;
+
+  p = (char *) real_name + length - 1;
+  if (IS_DIR_SEPARATOR (*p))
+    {
+      int level = 0;
+      for (; p != real_name; p--)
+       if (IS_DIR_SEPARATOR (*p))
+         {
+           level++;
+           if (level == 2)
+             target_end = p;
+           else if (level == 3)
+             {
+               target_start = p + 1;
+               target_length = target_end - target_start;
+             }
+           else if (level == 5)
+             {
+               dir_seperator = *p;
+               break;
+             }
+         }
+    }
+
+  /* Skip if there is no TARGET nor PREFIX.  */
+  if (!target_length || !dir_seperator)
+    return FALSE;
+
+#ifdef HAVE_EXECUTABLE_SUFFIX
+# define GCC_EXECUTABLE                "gcc" EXECUTABLE_SUFFIX
+# define LTO_WRAPPER_EXECUTABLE        "lto-wrapper" EXECUTABLE_SUFFIX
+#else
+# define GCC_EXECUTABLE                "gcc"
+# define LTO_WRAPPER_EXECUTABLE        "lto-wrapper"
+#endif
+  gcc_name = bfd_malloc (length + target_length
+                        + sizeof (GCC_EXECUTABLE));
+  if (gcc_name == NULL)
+    return FALSE;
+  memcpy (gcc_name, real_name, length);
+
+  /* Get PREFIX/bin/.  */
+  p += gcc_name - real_name;
+  memcpy (p + 1, "bin", 3);
+  p[4] = dir_seperator;
+
+  /* Try PREFIX/bin/TARGET-gcc first.  */
+  memcpy (p + 5, target_start, target_length);
+  p[5 + target_length] = '-';
+  memcpy (p + 5 + target_length + 1, GCC_EXECUTABLE,
+         sizeof (GCC_EXECUTABLE));
+  if (stat (gcc_name, &st) != 0 || !S_ISREG (st.st_mode))
+    {
+      /* Then try PREFIX/bin/gcc.  */
+      memcpy (p + 5, GCC_EXECUTABLE, sizeof (GCC_EXECUTABLE));
+      if (stat (gcc_name, &st) != 0 || !S_ISREG (st.st_mode))
+       {
+         free (gcc_name);
+         return FALSE;
+       }
+    }
+
+  /* lto-wrapper should be in the same directory with LTO plugin.  */
+  wrapper_name = bfd_malloc (length + sizeof (LTO_WRAPPER_EXECUTABLE));
+  if (wrapper_name == NULL)
+    {
+      free (gcc_name);
+      return FALSE;
+    }
+  memcpy (wrapper_name, real_name, length);
+  memcpy (wrapper_name + length, LTO_WRAPPER_EXECUTABLE,
+         sizeof (LTO_WRAPPER_EXECUTABLE));
+  if (stat (wrapper_name, &st) == 0 && S_ISREG (st.st_mode))
+    {
+      resolution_file = make_temp_file (".res");
+      if (resolution_file)
+       {
+         plugin->gcc = gcc_name;
+         plugin->lto_wrapper = wrapper_name;
+         plugin->gcc_env = concat ("COLLECT_GCC=", gcc_name, NULL);
+         plugin->resolution_file = resolution_file;
+         plugin->resolution_option = concat ("-fresolution=",
+                                             resolution_file, NULL);
+         return TRUE;
+       }
+    }
+
+  free (gcc_name);
+  free (wrapper_name);
+  return FALSE;
+}
+
+/* Set environment variables for GCC LTO wrapper to covert LTO IR
+   object to the real object.  */
+
+static int
+setup_lto_wrapper_env (struct plugin_list_entry *plugin)
+{
+  return (putenv (plugin->gcc_env)
+         || putenv ("COLLECT_GCC_OPTIONS="));
+}
+
+static struct plugin_list_entry *plugin_list = NULL;
+static struct plugin_list_entry *current_plugin = NULL;
+
 /* Register a claim-file handler. */
-static ld_plugin_claim_file_handler claim_file;
 
 static enum ld_plugin_status
 register_claim_file (ld_plugin_claim_file_handler handler)
 {
-  claim_file = handler;
+  current_plugin->claim_file = handler;
+  return LDPS_OK;
+}
+
+/* Register an all-symbols-read handler.  */
+
+static enum ld_plugin_status
+register_all_symbols_read (ld_plugin_all_symbols_read_handler handler)
+{
+  current_plugin->all_symbols_read = handler;
+  return LDPS_OK;
+}
+
+/* Register a cleanup handler.  */
+
+static enum ld_plugin_status
+register_cleanup (ld_plugin_all_symbols_read_handler handler)
+{
+  current_plugin->cleanup_handler = handler;
+  return LDPS_OK;
+}
+
+/* Get the symbol resolution info for a plugin-claimed input file.  */
+
+static enum ld_plugin_status
+get_symbols (const void *handle ATTRIBUTE_UNUSED, int nsyms,
+            struct ld_plugin_symbol *syms)
+{
+  if (syms)
+    {
+      int n;
+      for (n = 0; n < nsyms; n++)
+       {
+         switch (syms[n].def)
+           {
+           default:
+             BFD_ASSERT (0);
+             break;
+           case LDPK_UNDEF:
+           case LDPK_WEAKUNDEF:
+             syms[n].resolution = LDPR_UNDEF;
+             break;
+           case LDPK_DEF:
+           case LDPK_WEAKDEF:
+           case LDPK_COMMON:
+             /* Tell plugin that LTO symbol has references from regular
+                object code. */
+             syms[n].resolution  = LDPR_PREVAILING_DEF;
+             break;
+           }
+      }
+    }
+
+  return LDPS_OK;
+}
+
+/* Add a new (real) input file generated by a plugin.  */
+
+static enum ld_plugin_status
+add_input_file (const char *pathname)
+{
+  /* Get symbols from the real LTO object.  */
+  char **matching;
+  long real_symsize;
+  long real_nsyms;
+  asymbol **real_syms;
+  int lto_nsyms;
+  bfd_boolean lto_symbol_found = FALSE;
+  const struct ld_plugin_symbol *lto_syms;
+  bfd *rbfd;
+  int i, j;
+
+  rbfd = bfd_openr (pathname, NULL);
+  if (!bfd_check_format_matches (rbfd, bfd_object, &matching))
+    BFD_ASSERT (0);
+
+  real_symsize = bfd_get_symtab_upper_bound (rbfd);
+  if (real_symsize < 0)
+    BFD_ASSERT (0);
+
+  real_syms = (asymbol **) bfd_malloc (real_symsize);
+  if (real_syms)
+    {
+      real_nsyms = bfd_canonicalize_symtab (rbfd, real_syms);
+      if (real_nsyms < 0)
+       BFD_ASSERT (0);
+
+      /* NB: LTO plugin may generate more than one real object from one
+        LTO IR object.  We use the one which contains LTO symbols.  */
+      lto_syms = current_plugin->lto_syms;
+      lto_nsyms = current_plugin->lto_nsyms;
+      for (i = 0; i < lto_nsyms; i++)
+       for (j = 0; j < real_nsyms; j++)
+         if (real_syms[j]->name
+             && strcmp (lto_syms[i].name, real_syms[j]->name) == 0)
+           {
+             lto_symbol_found = TRUE;
+             break;
+           }
+    }
+
+  if (lto_symbol_found)
+    {
+      current_plugin->real_nsyms = real_nsyms;
+      current_plugin->real_syms = real_syms;
+      /* NB: We can't close RBFD which own the real symbol info.  */
+      current_plugin->real_bfd = rbfd;
+    }
+  else
+    {
+      bfd_close (rbfd);
+      free (real_syms);
+    }
+
   return LDPS_OK;
 }
 
@@ -138,89 +454,204 @@ add_symbols (void * handle,
   struct plugin_data_struct *plugin_data =
     bfd_alloc (abfd, sizeof (plugin_data_struct));
 
-  plugin_data->nsyms = nsyms;
-  plugin_data->syms = syms;
+  if (plugin_data)
+    {
+      struct ld_plugin_symbol *sym_info;
+      char *strtab;
+      size_t sym_info_size, name_length;
+      int i;
+
+      memset (plugin_data, 0, sizeof (*plugin_data));
+
+      abfd->tdata.plugin_data = plugin_data;
+
+      /* NB: LTO symbols are owned by LTO plugin.  Create a copy so
+        that we can use it in bfd_plugin_canonicalize_symtab.  */
+      sym_info_size = nsyms * sizeof (*syms);
+
+      /* Allocate a string table  */
+      for (i = 0; i < nsyms; i++)
+       sym_info_size += strlen (syms[i].name) + 1;
+
+      sym_info = bfd_alloc (abfd, sym_info_size);
+      if (sym_info)
+       {
+         /* Copy symbol table.  */
+         memcpy (sym_info, syms, nsyms * sizeof (*syms));
+
+         /* Copy symbol names in symbol table.  */
+         strtab = (char *) (sym_info + nsyms);
+         for (i = 0; i < nsyms; i++)
+           {
+             name_length = strlen (syms[i].name);
+             memcpy (strtab, syms[i].name, name_length + 1);
+             sym_info[i].name = strtab;
+             strtab += name_length + 1;
+           }
+
+         plugin_data->nsyms = nsyms;
+         plugin_data->syms = sym_info;
+
+         current_plugin->lto_nsyms = nsyms;
+         current_plugin->lto_syms = sym_info;
+       }
+    }
 
   if (nsyms != 0)
     abfd->flags |= HAS_SYMS;
 
-  abfd->tdata.plugin_data = plugin_data;
   return LDPS_OK;
 }
 
-static const char *plugin_program_name;
-
-void
-bfd_plugin_set_program_name (const char *program_name)
+int
+bfd_plugin_open_input (bfd *ibfd, struct ld_plugin_input_file *file)
 {
-  plugin_program_name = program_name;
-}
-
-static int
-try_claim (bfd *abfd)
-{
-  int claimed = 0;
-  struct ld_plugin_input_file file;
   bfd *iobfd;
 
-  file.name = abfd->filename;
+  iobfd = ibfd;
+  while (iobfd->my_archive
+        && !bfd_is_thin_archive (iobfd->my_archive))
+    iobfd = iobfd->my_archive;
+  file->name = iobfd->filename;
+
+  if (!iobfd->iostream && !bfd_open_file (iobfd))
+    return 0;
+
+  /* The plugin API expects that the file descriptor won't be closed
+     and reused as done by the bfd file cache.  So open it again.
+     dup isn't good enough.  plugin IO uses lseek/read while BFD uses
+     fseek/fread.  It isn't wise to mix the unistd and stdio calls on
+     the same underlying file descriptor.  */
+  file->fd = open (file->name, O_RDONLY | O_BINARY);
+  if (file->fd < 0)
+    return 0;
 
-  if (abfd->my_archive)
+  if (iobfd == ibfd)
     {
-      iobfd = abfd->my_archive;
-      file.offset = abfd->origin;
-      file.filesize = arelt_size (abfd);
+      struct stat stat_buf;
+
+      if (fstat (file->fd, &stat_buf))
+       {
+         close(file->fd);
+         return 0;
+       }
+
+      file->offset = 0;
+      file->filesize = stat_buf.st_size;
     }
   else
     {
-      iobfd = abfd;
-      file.offset = 0;
-      file.filesize = 0;
+      file->offset = ibfd->origin;
+      file->filesize = arelt_size (ibfd);
     }
+  return 1;
+}
 
-  if (!iobfd->iostream && !bfd_open_file (iobfd))
-    return 0;
-
-  file.fd = fileno ((FILE *) iobfd->iostream);
+static int
+try_claim (bfd *abfd)
+{
+  int claimed = 0;
+  struct ld_plugin_input_file file;
 
-  if (!abfd->my_archive)
+  file.handle = abfd;
+  if (bfd_plugin_open_input (abfd, &file)
+      && current_plugin->claim_file)
     {
-      struct stat stat_buf;
-      if (fstat (file.fd, &stat_buf))
-        return 0;
-      file.filesize = stat_buf.st_size;
+      current_plugin->claim_file (&file, &claimed);
+      if (claimed)
+       {
+         if (current_plugin->all_symbols_read)
+           {
+             struct plugin_data_struct *plugin_data
+               = abfd->tdata.plugin_data;
+             if (plugin_data)
+               {
+                 /* Get real symbols from LTO wrapper.  */
+                 current_plugin->all_symbols_read ();
+
+                 /* Copy real symbols to plugin_data.  */
+                 plugin_data->real_bfd = current_plugin->real_bfd;
+                 plugin_data->real_nsyms = current_plugin->real_nsyms;
+                 plugin_data->real_syms = current_plugin->real_syms;
+
+                 /* Clean up LTO plugin.  */
+                 if (current_plugin->cleanup_handler)
+                   current_plugin->cleanup_handler ();
+               }
+           }
+       }
+
+      close (file.fd);
     }
 
-  file.handle = abfd;
-  off_t cur_offset = lseek(file.fd, 0, SEEK_CUR);
-  claim_file (&file, &claimed);
-  lseek(file.fd, cur_offset, SEEK_SET);
-  if (!claimed)
-    return 0;
+  if (current_plugin->lto_wrapper)
+    {
+      /* Clean up for LTO wrapper.  NB: Resolution file and option
+        have been created regardless if an IR object is claimed or
+        not.  */
+      unlink (current_plugin->resolution_file);
+      free (current_plugin->resolution_option);
+    }
 
-  return 1;
+  return claimed;
 }
 
 static int
-try_load_plugin (const char *pname, bfd *abfd)
+try_load_plugin (const char *pname,
+                struct plugin_list_entry *plugin_list_iter,
+                bfd *abfd, bfd_boolean build_list_p)
 {
   void *plugin_handle;
-  int tv_size = 4;
-  struct ld_plugin_tv tv[tv_size];
+  struct ld_plugin_tv tv[12];
   int i;
   ld_plugin_onload onload;
   enum ld_plugin_status status;
+  int result = 0;
+
+  /* NB: Each object is independent.  Reuse the previous plugin from
+     the last run will lead to wrong result.  */
+  if (current_plugin)
+    memset (current_plugin, 0,
+           offsetof (struct plugin_list_entry, next));
+
+  if (plugin_list_iter)
+    pname = plugin_list_iter->plugin_name;
 
   plugin_handle = dlopen (pname, RTLD_NOW);
   if (!plugin_handle)
     {
-      (*_bfd_error_handler)("%s\n", dlerror ());
+      _bfd_error_handler ("%s\n", dlerror ());
       return 0;
     }
 
+  if (plugin_list_iter == NULL)
+    {
+      size_t length_plugin_name = strlen (pname) + 1;
+      char *plugin_name = bfd_malloc (length_plugin_name);
+      if (plugin_name == NULL)
+       goto short_circuit;
+      plugin_list_iter = bfd_malloc (sizeof *plugin_list_iter);
+      if (plugin_list_iter == NULL)
+       {
+         free (plugin_name);
+         goto short_circuit;
+       }
+      /* Make a copy of PNAME since PNAME from load_plugin () will be
+        freed.  */
+      memcpy (plugin_name, pname, length_plugin_name);
+      memset (plugin_list_iter, 0, sizeof (*plugin_list_iter));
+      plugin_list_iter->plugin_name = plugin_name;
+      plugin_list_iter->next = plugin_list;
+      plugin_list = plugin_list_iter;
+    }
+
+  current_plugin = plugin_list_iter;
+  if (build_list_p)
+    goto short_circuit;
+
   onload = dlsym (plugin_handle, "onload");
   if (!onload)
-    goto err;
+    goto short_circuit;
 
   i = 0;
   tv[i].tv_tag = LDPT_MESSAGE;
@@ -234,28 +665,76 @@ try_load_plugin (const char *pname, bfd *abfd)
   tv[i].tv_tag = LDPT_ADD_SYMBOLS;
   tv[i].tv_u.tv_add_symbols = add_symbols;
 
+  if (get_lto_wrapper (plugin_list_iter))
+    {
+      ++i;
+      tv[i].tv_tag = LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK;
+      tv[i].tv_u.tv_register_all_symbols_read = register_all_symbols_read;
+
+      ++i;
+      tv[i].tv_tag = LDPT_REGISTER_CLEANUP_HOOK;
+      tv[i].tv_u.tv_register_cleanup = register_cleanup;
+
+      ++i;
+      tv[i].tv_tag = LDPT_GET_SYMBOLS;
+      tv[i].tv_u.tv_get_symbols = get_symbols;
+
+      ++i;
+      tv[i].tv_tag = LDPT_GET_SYMBOLS_V2;
+      tv[i].tv_u.tv_get_symbols = get_symbols;
+
+      ++i;
+      tv[i].tv_tag = LDPT_OPTION;
+      tv[i].tv_u.tv_string = plugin_list_iter->lto_wrapper;
+
+      ++i;
+      tv[i].tv_tag = LDPT_OPTION;
+      tv[i].tv_u.tv_string = plugin_list_iter->resolution_option;
+
+      ++i;
+      tv[i].tv_tag = LDPT_LINKER_OUTPUT;
+      tv[i].tv_u.tv_val = LDPO_EXEC;
+
+      ++i;
+      tv[i].tv_tag = LDPT_ADD_INPUT_FILE;
+      tv[i].tv_u.tv_add_input_file = add_input_file;
+    }
+
   ++i;
   tv[i].tv_tag = LDPT_NULL;
   tv[i].tv_u.tv_val = 0;
 
+  /* LTO plugin will call handler hooks to set up plugin handlers.  */
   status = (*onload)(tv);
 
   if (status != LDPS_OK)
-    goto err;
+    goto short_circuit;
+
+  if (current_plugin->lto_wrapper
+      && setup_lto_wrapper_env (current_plugin))
+    goto short_circuit;
+
+  abfd->plugin_format = bfd_plugin_no;
 
-  if (!claim_file)
-    goto err;
+  if (!current_plugin->claim_file)
+    goto short_circuit;
 
   if (!try_claim (abfd))
-    goto err;
+    goto short_circuit;
 
-  return 1;
+  abfd->plugin_format = bfd_plugin_yes;
+  result = 1;
 
err:
-  plugin_handle = NULL;
-  return 0;
short_circuit:
+  dlclose (plugin_handle);
+  return result;
 }
 
+/* There may be plugin libraries in lib/bfd-plugins.  */
+static int has_plugin_list = -1;
+
+static bfd_cleanup (*ld_plugin_object_p) (bfd *);
+
 static const char *plugin_name;
 
 void
@@ -264,61 +743,135 @@ bfd_plugin_set_plugin (const char *p)
   plugin_name = p;
 }
 
+/* Return TRUE if a plugin library is used.  */
+
+bfd_boolean
+bfd_plugin_specified_p (void)
+{
+  return plugin_list != NULL;
+}
+
+/* Return TRUE if ABFD can be claimed by linker LTO plugin.  */
+
+bfd_boolean
+bfd_link_plugin_object_p (bfd *abfd)
+{
+  if (ld_plugin_object_p)
+    return ld_plugin_object_p (abfd) != NULL;
+  return FALSE;
+}
+
+extern const bfd_target plugin_vec;
+
+/* Return TRUE if TARGET is a pointer to plugin_vec.  */
+
+bfd_boolean
+bfd_plugin_target_p (const bfd_target *target)
+{
+  return target == &plugin_vec;
+}
+
+/* Register OBJECT_P to be used by bfd_plugin_object_p.  */
+
+void
+register_ld_plugin_object_p (bfd_cleanup (*object_p) (bfd *))
+{
+  ld_plugin_object_p = object_p;
+}
+
+static void
+build_plugin_list (bfd *abfd)
+{
+  /* The intent was to search ${libdir}/bfd-plugins for plugins, but
+     unfortunately the original implementation wasn't precisely that
+     when configuring binutils using --libdir.  Search in the proper
+     path first, then the old one for backwards compatibility.  */
+  static const char *path[]
+    = { LIBDIR "/bfd-plugins", BINDIR "/../lib/bfd-plugins" };
+  struct stat last_st;
+  unsigned int i;
+
+  if (has_plugin_list >= 0)
+    return;
+
+  /* Try not to search the same dir twice, by looking at st_dev and
+     st_ino for the dir.  If we are on a file system that always sets
+     st_ino to zero or the actual st_ino is zero we might waste some
+     time, but that doesn't matter too much.  */
+  last_st.st_dev = 0;
+  last_st.st_ino = 0;
+  for (i = 0; i < sizeof (path) / sizeof (path[0]); i++)
+    {
+      char *plugin_dir = make_relative_prefix (plugin_program_name,
+                                              BINDIR,
+                                              path[i]);
+      if (plugin_dir)
+       {
+         struct stat st;
+         DIR *d;
+
+         if (stat (plugin_dir, &st) == 0
+             && S_ISDIR (st.st_mode)
+             && !(last_st.st_dev == st.st_dev
+                  && last_st.st_ino == st.st_ino
+                  && st.st_ino != 0)
+             && (d = opendir (plugin_dir)) != NULL)
+           {
+             struct dirent *ent;
+
+             last_st.st_dev = st.st_dev;
+             last_st.st_ino = st.st_ino;
+             while ((ent = readdir (d)) != NULL)
+               {
+                 char *full_name;
+
+                 full_name = concat (plugin_dir, "/", ent->d_name, NULL);
+                 if (stat (full_name, &st) == 0 && S_ISREG (st.st_mode))
+                   try_load_plugin (full_name, NULL, abfd, TRUE);
+                 free (full_name);
+               }
+             closedir (d);
+           }
+         free (plugin_dir);
+       }
+    }
+
+  has_plugin_list = plugin_list != NULL;
+}
+
 static int
 load_plugin (bfd *abfd)
 {
-  char *plugin_dir;
-  char *p;
-  DIR *d;
-  struct dirent *ent;
-  int found = 0;
+  struct plugin_list_entry *plugin_list_iter;
 
   if (plugin_name)
-    return try_load_plugin (plugin_name, abfd);
+    return try_load_plugin (plugin_name, plugin_list, abfd, FALSE);
 
   if (plugin_program_name == NULL)
     return 0;
 
-  plugin_dir = concat (BINDIR, "/../lib/bfd-plugins", NULL);
-  p = make_relative_prefix (plugin_program_name,
-                           BINDIR,
-                           plugin_dir);
-  free (plugin_dir);
-  plugin_dir = NULL;
+  build_plugin_list (abfd);
 
-  d = opendir (p);
-  if (!d)
-    goto out;
+  for (plugin_list_iter = plugin_list;
+       plugin_list_iter;
+       plugin_list_iter = plugin_list_iter->next)
+    if (try_load_plugin (NULL, plugin_list_iter, abfd, FALSE))
+      return 1;
 
-  while ((ent = readdir (d)))
-    {
-      char *full_name;
-      struct stat s;
-
-      full_name = concat (p, "/", ent->d_name, NULL);
-      if (stat(full_name, &s) == 0 && S_ISREG (s.st_mode))
-       found = try_load_plugin (full_name, abfd);
-      free (full_name);
-      if (found)
-       break;
-    }
-
- out:
-  free (p);
-  if (d)
-    closedir (d);
-
-  return found;
+  return 0;
 }
 
 
-static const bfd_target *
+static bfd_cleanup
 bfd_plugin_object_p (bfd *abfd)
 {
-  if (!load_plugin (abfd))
+  if (ld_plugin_object_p)
+    return ld_plugin_object_p (abfd);
+
+  if (abfd->plugin_format == bfd_plugin_unknown && !load_plugin (abfd))
     return NULL;
 
-  return abfd->xvec;
+  return abfd->plugin_format == bfd_plugin_yes ? _bfd_no_cleanup : NULL;
 }
 
 /* Copy any private info we understand from the input bfd
@@ -424,12 +977,20 @@ bfd_plugin_canonicalize_symtab (bfd *abfd,
   struct plugin_data_struct *plugin_data = abfd->tdata.plugin_data;
   long nsyms = plugin_data->nsyms;
   const struct ld_plugin_symbol *syms = plugin_data->syms;
-  static asection fake_section;
-  static asection fake_common_section;
-  int i;
-
-  fake_section.name = ".text";
-  fake_common_section.flags = SEC_IS_COMMON;
+  static asection fake_section
+    = BFD_FAKE_SECTION (fake_section, NULL, "plug", 0,
+                       SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS);
+  static asection fake_common_section
+    = BFD_FAKE_SECTION (fake_common_section, NULL, "plug", 0, SEC_IS_COMMON);
+  int i, j;
+  long real_nsyms;
+  asymbol **real_syms;
+
+  real_syms = plugin_data->real_syms;
+  if (real_syms)
+    real_nsyms = plugin_data->real_nsyms;
+  else
+    real_nsyms = 0;
 
   for (i = 0; i < nsyms; i++)
     {
@@ -454,6 +1015,15 @@ bfd_plugin_canonicalize_symtab (bfd *abfd,
        case LDPK_DEF:
        case LDPK_WEAKDEF:
          s->section = &fake_section;
+         if (real_nsyms)
+           /* Use real LTO symbols if possible.  */
+           for (j = 0; j < real_nsyms; j++)
+             if (real_syms[j]->name
+                 && strcmp (syms[i].name, real_syms[j]->name) == 0)
+               {
+                 s->section = real_syms[j]->section;
+                 break;
+               }
          break;
        default:
          BFD_ASSERT (0);
@@ -502,6 +1072,24 @@ bfd_plugin_sizeof_headers (bfd *a ATTRIBUTE_UNUSED,
   return 0;
 }
 
+static bfd_boolean
+bfd_plugin_close_and_cleanup (bfd *abfd)
+{
+  struct plugin_data_struct *plugin_data;
+
+  if (abfd->format != bfd_archive
+      && (plugin_data = abfd->tdata.plugin_data))
+    {
+      if (plugin_data->real_bfd)
+       bfd_close (plugin_data->real_bfd);
+
+      if (plugin_data->real_syms)
+       free (plugin_data->real_syms);
+    }
+
+  return _bfd_generic_close_and_cleanup (abfd);
+}
+
 const bfd_target plugin_vec =
 {
   "plugin",                    /* Name.  */
@@ -516,7 +1104,7 @@ const bfd_target plugin_vec =
   0,                           /* symbol_leading_char.  */
   '/',                         /* ar_pad_char.  */
   15,                          /* ar_max_namelen.  */
-  0,                           /* match priority.  */
+  255,                         /* match priority.  */
 
   bfd_getl64, bfd_getl_signed_64, bfd_putl64,
   bfd_getl32, bfd_getl_signed_32, bfd_putl32,
@@ -532,22 +1120,26 @@ const bfd_target plugin_vec =
     _bfd_dummy_target
   },
   {                            /* bfd_set_format.  */
-    bfd_false,
-    bfd_false,
+    _bfd_bool_bfd_false_error,
+    _bfd_bool_bfd_false_error,
     _bfd_generic_mkarchive,
-    bfd_false,
+    _bfd_bool_bfd_false_error,
   },
   {                            /* bfd_write_contents.  */
-    bfd_false,
-    bfd_false,
+    _bfd_bool_bfd_false_error,
+    _bfd_bool_bfd_false_error,
     _bfd_write_archive_contents,
-    bfd_false,
+    _bfd_bool_bfd_false_error,
   },
 
   BFD_JUMP_TABLE_GENERIC (bfd_plugin),
   BFD_JUMP_TABLE_COPY (bfd_plugin),
   BFD_JUMP_TABLE_CORE (bfd_plugin),
+#ifdef USE_64_BIT_ARCHIVE
+  BFD_JUMP_TABLE_ARCHIVE (_bfd_archive_64_bit),
+#else
   BFD_JUMP_TABLE_ARCHIVE (_bfd_archive_coff),
+#endif
   BFD_JUMP_TABLE_SYMBOLS (bfd_plugin),
   BFD_JUMP_TABLE_RELOCS (_bfd_norelocs),
   BFD_JUMP_TABLE_WRITE (bfd_plugin),
@@ -556,6 +1148,6 @@ const bfd_target plugin_vec =
 
   NULL,
 
-  NULL                         /* backend_data.  */
+  NULL                         /* backend_data.  */
 };
 #endif /* BFD_SUPPORTS_PLUGIN */
This page took 0.036738 seconds and 4 git commands to generate.