LoongArch: Correct comments.
[binutils-gdb.git] / bfd / plugin.c
index fe3411531d7b8d2732ce680d3dd80db9c98c091d..b798d3477aecbab760fc3262f7c37a49b3095316 100644 (file)
@@ -1,5 +1,5 @@
 /* Plugin support for BFD.
-   Copyright (C) 2009-2018 Free Software Foundation, Inc.
+   Copyright (C) 2009-2023 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -83,6 +83,7 @@ dlerror (void)
 #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_nearest_line_with_alt        _bfd_nosymbols_find_nearest_line_with_alt
 #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
@@ -102,6 +103,7 @@ dlerror (void)
 #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
@@ -123,13 +125,48 @@ 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_claim_file_handler_v2 claim_file_v2;
+  ld_plugin_all_symbols_read_handler all_symbols_read;
+  ld_plugin_all_symbols_read_handler cleanup_handler;
+  bool has_symbol_type;
+
+  struct plugin_list_entry *next;
+
+  /* These can be reused for all IR objects.  */
+  const char *plugin_name;
+};
+
+static const char *plugin_program_name;
+
+void
+bfd_plugin_set_program_name (const char *program_name)
+{
+  plugin_program_name = program_name;
+}
+
+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 = NULL;
 
 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 a claim-file handler, version 2. */
+
+static enum ld_plugin_status
+register_claim_file_v2 (ld_plugin_claim_file_handler_v2 handler)
+{
+  current_plugin->claim_file_v2 = handler;
   return LDPS_OK;
 }
 
@@ -142,6 +179,9 @@ add_symbols (void * handle,
   struct plugin_data_struct *plugin_data =
     bfd_alloc (abfd, sizeof (plugin_data_struct));
 
+  if (!plugin_data)
+    return LDPS_ERR;
+
   plugin_data->nsyms = nsyms;
   plugin_data->syms = syms;
 
@@ -152,44 +192,83 @@ add_symbols (void * handle,
   return LDPS_OK;
 }
 
-static const char *plugin_program_name;
-
-void
-bfd_plugin_set_program_name (const char *program_name)
+static enum ld_plugin_status
+add_symbols_v2 (void *handle, int nsyms,
+               const struct ld_plugin_symbol *syms)
 {
-  plugin_program_name = program_name;
+  current_plugin->has_symbol_type = true;
+  return add_symbols (handle, nsyms, syms);
 }
 
 int
 bfd_plugin_open_input (bfd *ibfd, struct ld_plugin_input_file *file)
 {
   bfd *iobfd;
+  int fd;
 
   iobfd = ibfd;
   while (iobfd->my_archive
         && !bfd_is_thin_archive (iobfd->my_archive))
     iobfd = iobfd->my_archive;
-  file->name = iobfd->filename;
+  file->name = bfd_get_filename (iobfd);
 
   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;
+  /* Reuse the archive plugin file descriptor.  */
+  if (iobfd != ibfd)
+    fd = iobfd->archive_plugin_fd;
+  else
+    fd = -1;
+
+  if (fd < 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.  */
+      fd = open (file->name, O_RDONLY | O_BINARY);
+      if (fd < 0)
+       {
+#ifndef EMFILE
+         return 0;
+#else
+         if (errno != EMFILE)
+           return 0;
+
+#ifdef HAVE_GETRLIMIT
+         struct rlimit lim;
+
+         /* Complicated links involving lots of files and/or large
+            archives can exhaust the number of file descriptors
+            available to us.  If possible, try to allocate more
+            descriptors.  */
+         if (getrlimit (RLIMIT_NOFILE, & lim) == 0
+             && lim.rlim_cur < lim.rlim_max)
+           {
+             lim.rlim_cur = lim.rlim_max;
+             if (setrlimit (RLIMIT_NOFILE, &lim) == 0)
+               fd = open (file->name, O_RDONLY | O_BINARY);
+           }
+
+         if (fd < 0)
+#endif
+           {
+             _bfd_error_handler (_("plugin framework: out of file descriptors. Try using fewer objects/archives\n"));
+             return 0;
+           }
+#endif
+       }
+    }
 
   if (iobfd == ibfd)
     {
       struct stat stat_buf;
 
-      if (fstat (file->fd, &stat_buf))
+      if (fstat (fd, &stat_buf))
        {
-         close(file->fd);
+         close (fd);
          return 0;
        }
 
@@ -198,12 +277,51 @@ bfd_plugin_open_input (bfd *ibfd, struct ld_plugin_input_file *file)
     }
   else
     {
+      /* Cache the archive plugin file descriptor.  */
+      iobfd->archive_plugin_fd = fd;
+      iobfd->archive_plugin_fd_open_count++;
+
       file->offset = ibfd->origin;
       file->filesize = arelt_size (ibfd);
     }
+
+  file->fd = fd;
   return 1;
 }
 
+/* Close the plugin file descriptor FD.  If ABFD isn't NULL, it is an
+   archive member.   */
+
+void
+bfd_plugin_close_file_descriptor (bfd *abfd, int fd)
+{
+  if (abfd == NULL)
+    close (fd);
+  else
+    {
+      while (abfd->my_archive
+            && !bfd_is_thin_archive (abfd->my_archive))
+       abfd = abfd->my_archive;
+
+      /* Close the file descriptor if there is no archive plugin file
+        descriptor.  */
+      if (abfd->archive_plugin_fd == -1)
+       {
+         close (fd);
+         return;
+       }
+
+      abfd->archive_plugin_fd_open_count--;
+      /* Dup the archive plugin file descriptor for later use, which
+        will be closed by _bfd_archive_close_and_cleanup.  */
+      if (abfd->archive_plugin_fd_open_count == 0)
+       {
+         abfd->archive_plugin_fd = dup (fd);
+         close (fd);
+       }
+    }
+}
+
 static int
 try_claim (bfd *abfd)
 {
@@ -211,66 +329,81 @@ try_claim (bfd *abfd)
   struct ld_plugin_input_file file;
 
   file.handle = abfd;
-  if (!bfd_plugin_open_input (abfd, &file))
-    return 0;
-  if (claim_file)
-    claim_file (&file, &claimed);
-  close (file.fd);
+  if (bfd_plugin_open_input (abfd, &file)
+      && current_plugin->claim_file)
+    {
+      current_plugin->claim_file (&file, &claimed);
+      bfd_plugin_close_file_descriptor ((abfd->my_archive != NULL
+                                        ? abfd : NULL),
+                                       file.fd);
+    }
+
   return claimed;
 }
 
-struct plugin_list_entry
+static bool
+try_load_plugin (const char *pname,
+                struct plugin_list_entry *plugin_list_iter,
+                bfd *abfd,
+                bool build_list_p)
 {
-  void *                        handle;
-  ld_plugin_claim_file_handler  claim_file;
-  struct plugin_list_entry *    next;
-};
-
-static struct plugin_list_entry * plugin_list = NULL;
-
-static int
-try_load_plugin (const char *pname, bfd *abfd, int *has_plugin_p)
-{
-  void *plugin_handle = NULL;
-  struct ld_plugin_tv tv[4];
+  void *plugin_handle;
+  struct ld_plugin_tv tv[6];
   int i;
   ld_plugin_onload onload;
   enum ld_plugin_status status;
-  struct plugin_list_entry *plugin_list_iter;
+  bool result = false;
 
-  *has_plugin_p = 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 ());
-      return 0;
+      /* If we are building a list of viable plugins, then
+        we do not bother the user with the details of any
+        plugins that cannot be loaded.  */
+      if (! build_list_p)
+       _bfd_error_handler ("Failed to load plugin '%s', reason: %s\n",
+                           pname, dlerror ());
+      return false;
     }
 
-  for (plugin_list_iter = plugin_list;
-       plugin_list_iter;
-       plugin_list_iter = plugin_list_iter->next)
+  if (plugin_list_iter == NULL)
     {
-      if (plugin_handle == plugin_list_iter->handle)
-        {
-          dlclose (plugin_handle);
-          if (!plugin_list_iter->claim_file)
-            return 0;
-
-          register_claim_file (plugin_list_iter->claim_file);
-          goto have_claim_file;
-        }
+      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;
     }
 
-  plugin_list_iter = (struct plugin_list_entry *) xmalloc (sizeof *plugin_list_iter);
-  plugin_list_iter->handle = plugin_handle;
-  plugin_list_iter->claim_file = NULL;
-  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)
-    return 0;
+    goto short_circuit;
 
   i = 0;
   tv[i].tv_tag = LDPT_MESSAGE;
@@ -280,41 +413,48 @@ try_load_plugin (const char *pname, bfd *abfd, int *has_plugin_p)
   tv[i].tv_tag = LDPT_REGISTER_CLAIM_FILE_HOOK;
   tv[i].tv_u.tv_register_claim_file = register_claim_file;
 
+  ++i;
+  tv[i].tv_tag = LDPT_REGISTER_CLAIM_FILE_HOOK_V2;
+  tv[i].tv_u.tv_register_claim_file_v2 = register_claim_file_v2;
+
   ++i;
   tv[i].tv_tag = LDPT_ADD_SYMBOLS;
   tv[i].tv_u.tv_add_symbols = add_symbols;
 
+  ++i;
+  tv[i].tv_tag = LDPT_ADD_SYMBOLS_V2;
+  tv[i].tv_u.tv_add_symbols = add_symbols_v2;
+
   ++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)
-    return 0;
-
-  plugin_list_iter->claim_file = claim_file;
-
-have_claim_file:
-  *has_plugin_p = 1;
+    goto short_circuit;
 
   abfd->plugin_format = bfd_plugin_no;
 
-  if (!claim_file)
-    return 0;
+  if (!current_plugin->claim_file)
+    goto short_circuit;
 
   if (!try_claim (abfd))
-    return 0;
+    goto short_circuit;
 
   abfd->plugin_format = bfd_plugin_yes;
-  return 1;
+  result = true;
+
+ short_circuit:
+  dlclose (plugin_handle);
+  return result;
 }
 
 /* There may be plugin libraries in lib/bfd-plugins.  */
+static int has_plugin_list = -1;
 
-static int has_plugin = -1;
-
-static const bfd_target *(*ld_plugin_object_p) (bfd *);
+static bfd_cleanup (*ld_plugin_object_p) (bfd *, bool);
 
 static const char *plugin_name;
 
@@ -322,32 +462,31 @@ void
 bfd_plugin_set_plugin (const char *p)
 {
   plugin_name = p;
-  has_plugin = p != NULL;
 }
 
 /* Return TRUE if a plugin library is used.  */
 
-bfd_boolean
+bool
 bfd_plugin_specified_p (void)
 {
-  return has_plugin > 0;
+  return plugin_list != NULL;
 }
 
 /* Return TRUE if ABFD can be claimed by linker LTO plugin.  */
 
-bfd_boolean
+bool
 bfd_link_plugin_object_p (bfd *abfd)
 {
   if (ld_plugin_object_p)
-    return ld_plugin_object_p (abfd) != NULL;
-  return FALSE;
+    return ld_plugin_object_p (abfd, false) != NULL;
+  return false;
 }
 
 extern const bfd_target plugin_vec;
 
 /* Return TRUE if TARGET is a pointer to plugin_vec.  */
 
-bfd_boolean
+bool
 bfd_plugin_target_p (const bfd_target *target)
 {
   return target == &plugin_vec;
@@ -356,119 +495,148 @@ bfd_plugin_target_p (const bfd_target *target)
 /* Register OBJECT_P to be used by bfd_plugin_object_p.  */
 
 void
-register_ld_plugin_object_p (const bfd_target *(*object_p) (bfd *))
+register_ld_plugin_object_p (bfd_cleanup (*object_p) (bfd *, bool))
 {
   ld_plugin_object_p = object_p;
 }
 
-static int
+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))
+                   (void) try_load_plugin (full_name, NULL, abfd, true);
+                 free (full_name);
+               }
+             closedir (d);
+           }
+         free (plugin_dir);
+       }
+    }
+
+  has_plugin_list = plugin_list != NULL;
+}
+
+static bool
 load_plugin (bfd *abfd)
 {
-  char *plugin_dir;
-  char *p;
-  DIR *d;
-  struct dirent *ent;
-  int found = 0;
-
-  if (!has_plugin)
-    return found;
+  struct plugin_list_entry *plugin_list_iter;
 
   if (plugin_name)
-    return try_load_plugin (plugin_name, abfd, &has_plugin);
+    return try_load_plugin (plugin_name, plugin_list, abfd, false);
 
   if (plugin_program_name == NULL)
-    return found;
-
-  plugin_dir = concat (BINDIR, "/../lib/bfd-plugins", NULL);
-  p = make_relative_prefix (plugin_program_name,
-                           BINDIR,
-                           plugin_dir);
-  free (plugin_dir);
-  plugin_dir = NULL;
+    return false;
 
-  d = opendir (p);
-  if (!d)
-    goto out;
+  build_plugin_list (abfd);
 
-  while ((ent = readdir (d)))
-    {
-      char *full_name;
-      struct stat s;
-      int valid_plugin;
-
-      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, &valid_plugin);
-      if (has_plugin <= 0)
-       has_plugin = valid_plugin;
-      free (full_name);
-      if (found)
-       break;
-    }
-
- out:
-  free (p);
-  if (d)
-    closedir (d);
+  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 true;
 
-  return found;
+  return false;
 }
 
 
-static const bfd_target *
+static bfd_cleanup
 bfd_plugin_object_p (bfd *abfd)
 {
   if (ld_plugin_object_p)
-    return ld_plugin_object_p (abfd);
+    return ld_plugin_object_p (abfd, false);
 
   if (abfd->plugin_format == bfd_plugin_unknown && !load_plugin (abfd))
     return NULL;
 
-  return abfd->plugin_format == bfd_plugin_yes ? abfd->xvec : NULL;
+  return abfd->plugin_format == bfd_plugin_yes ? _bfd_no_cleanup : NULL;
 }
 
 /* Copy any private info we understand from the input bfd
    to the output bfd.  */
 
-static bfd_boolean
+static bool
 bfd_plugin_bfd_copy_private_bfd_data (bfd *ibfd ATTRIBUTE_UNUSED,
                                      bfd *obfd ATTRIBUTE_UNUSED)
 {
   BFD_ASSERT (0);
-  return TRUE;
+  return true;
 }
 
 /* Copy any private info we understand from the input section
    to the output section.  */
 
-static bfd_boolean
+static bool
 bfd_plugin_bfd_copy_private_section_data (bfd *ibfd ATTRIBUTE_UNUSED,
                                          asection *isection ATTRIBUTE_UNUSED,
                                          bfd *obfd ATTRIBUTE_UNUSED,
                                          asection *osection ATTRIBUTE_UNUSED)
 {
   BFD_ASSERT (0);
-  return TRUE;
+  return true;
 }
 
 /* Copy any private info we understand from the input symbol
    to the output symbol.  */
 
-static bfd_boolean
+static bool
 bfd_plugin_bfd_copy_private_symbol_data (bfd *ibfd ATTRIBUTE_UNUSED,
                                         asymbol *isymbol ATTRIBUTE_UNUSED,
                                         bfd *obfd ATTRIBUTE_UNUSED,
                                         asymbol *osymbol ATTRIBUTE_UNUSED)
 {
   BFD_ASSERT (0);
-  return TRUE;
+  return true;
 }
 
-static bfd_boolean
-bfd_plugin_bfd_print_private_bfd_data (bfd *abfd ATTRIBUTE_UNUSED, PTR ptr ATTRIBUTE_UNUSED)
+static bool
+bfd_plugin_bfd_print_private_bfd_data (bfd *abfd ATTRIBUTE_UNUSED, void *ptr ATTRIBUTE_UNUSED)
 {
   BFD_ASSERT (0);
-  return TRUE;
+  return true;
 }
 
 static char *
@@ -530,13 +698,19 @@ 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;
+  static asection fake_text_section
+    = BFD_FAKE_SECTION (fake_text_section, NULL, "plug", 0,
+                       SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS);
+  static asection fake_data_section
+    = BFD_FAKE_SECTION (fake_data_section, NULL, "plug", 0,
+                       SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_HAS_CONTENTS);
+  static asection fake_bss_section
+    = BFD_FAKE_SECTION (fake_bss_section, NULL, "plug", 0,
+                       SEC_ALLOC);
+  static asection fake_common_section
+    = BFD_FAKE_SECTION (fake_common_section, NULL, "plug", 0, SEC_IS_COMMON);
   int i;
 
-  fake_section.name = ".text";
-  fake_common_section.flags = SEC_IS_COMMON;
-
   for (i = 0; i < nsyms; i++)
     {
       asymbol *s = bfd_alloc (abfd, sizeof (asymbol));
@@ -559,7 +733,25 @@ bfd_plugin_canonicalize_symtab (bfd *abfd,
          break;
        case LDPK_DEF:
        case LDPK_WEAKDEF:
-         s->section = &fake_section;
+         if (current_plugin->has_symbol_type)
+           switch (syms[i].symbol_type)
+             {
+             default:
+               /* FIXME: Should we issue an error here ?  */
+             case LDST_UNKNOWN:
+               /* What is the best fake section for LDST_UNKNOWN?  */
+             case LDST_FUNCTION:
+               s->section = &fake_text_section;
+               break;
+             case LDST_VARIABLE:
+               if (syms[i].section_kind == LDSSK_BSS)
+                 s->section = &fake_bss_section;
+               else
+                 s->section = &fake_data_section;
+               break;
+             }
+         else
+           s->section = &fake_text_section;
          break;
        default:
          BFD_ASSERT (0);
@@ -573,7 +765,7 @@ bfd_plugin_canonicalize_symtab (bfd *abfd,
 
 static void
 bfd_plugin_print_symbol (bfd *abfd ATTRIBUTE_UNUSED,
-                        PTR afile ATTRIBUTE_UNUSED,
+                        void *afile ATTRIBUTE_UNUSED,
                         asymbol *symbol ATTRIBUTE_UNUSED,
                         bfd_print_symbol_type how ATTRIBUTE_UNUSED)
 {
@@ -623,6 +815,7 @@ const bfd_target plugin_vec =
   '/',                         /* ar_pad_char.  */
   15,                          /* ar_max_namelen.  */
   255,                         /* match priority.  */
+  TARGET_KEEP_UNUSED_SECTION_SYMBOLS, /* keep unused section symbols.  */
 
   bfd_getl64, bfd_getl_signed_64, bfd_putl64,
   bfd_getl32, bfd_getl_signed_32, bfd_putl32,
@@ -668,4 +861,4 @@ const bfd_target plugin_vec =
 
   NULL                         /* backend_data.  */
 };
-#endif /* BFD_SUPPORTS_PLUGIN */
+#endif /* BFD_SUPPORTS_PLUGINS */