Use GCC LTO wrapper to get real symbols from LTO IR objects

Message ID 20200210142435.397899-1-hjl.tools@gmail.com
State New
Headers show
Series
  • Use GCC LTO wrapper to get real symbols from LTO IR objects
Related show

Commit Message

H.J. Lu Feb. 10, 2020, 2:24 p.m.
GCC LTO wrapper is needed to extract real symbols from LTO IR objects.
This patch does the following:

1. Set up GCC LTO wrapper for each LTO IR object.
2. Run GCC LTO wrapper to get the real object.
3. Extract symbol info from the real object.
4. Cleanup afterwards.

bfd/

	PR binutils/25355
	* plugin.c (bfd_plugin_close_and_cleanup): Removed.
	(plugin_list_entry): Add all_symbols_read, cleanup_handler,
	gcc, lto_wrapper, resolution_file, resolution_option, gcc_env,
	real_bfd, real_nsyms, real_syms, lto_nsyms and lto_syms.
	(get_lto_wrapper): New.
	(setup_lto_wrapper_env): Likewise.
	(current_plugin): Likewise.
	(register_all_symbols_read): Likewise.
	(register_cleanup): Likewise.
	(get_symbols): Likewise.
	(add_input_file): Likewise.
	(bfd_plugin_close_and_cleanup): Likewise.
	(claim_file): Removed.
	(register_claim_file): Set current_plugin->claim_file.
	(add_symbols): Make a copy of LTO symbols.  Set lto_nsyms and
	lto_syms in current_plugin.
	(try_claim): Use current_plugin->claim_file.  Call LTO plugin
	all_symbols_read handler.  Copy real symbols to plugin_data.
	Call LTO plugin cleanup handler.  Clean up for LTO wrapper.
	(try_load_plugin): Don't reuse the previous plugin for LTO
	wrapper.  Set up GCC LTO wrapper if possible.  Don't set
	plugin_list_iter->claim_file.
	(bfd_plugin_canonicalize_symtab): Use real LTO symbols if
	possible.
	* plugin.h (plugin_data_struct): Add real_bfd, real_nsyms and
	real_syms.

ld/

	PR binutils/25355
	* testsuite/ld-plugin/lto.exp: Run PR binutils/25355 test.
	* testsuite/ld-plugin/pr25355.c: New file.
	* testsuite/ld-plugin/pr25355.d: Likewise.
	* testsuite/lib/ld-lib.exp (run_cc_link_tests): Support
	compile only dump.
---
 bfd/plugin.c                     | 415 +++++++++++++++++++++++++++++--
 bfd/plugin.h                     |   3 +
 ld/testsuite/ld-plugin/lto.exp   |   5 +
 ld/testsuite/ld-plugin/pr25355.c |   2 +
 ld/testsuite/ld-plugin/pr25355.d |   4 +
 ld/testsuite/lib/ld-lib.exp      |   1 +
 6 files changed, 410 insertions(+), 20 deletions(-)
 create mode 100644 ld/testsuite/ld-plugin/pr25355.c
 create mode 100644 ld/testsuite/ld-plugin/pr25355.d

-- 
2.24.1

Comments

H.J. Lu Feb. 10, 2020, 9:31 p.m. | #1
On Mon, Feb 10, 2020 at 6:24 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>

> GCC LTO wrapper is needed to extract real symbols from LTO IR objects.

> This patch does the following:

>

> 1. Set up GCC LTO wrapper for each LTO IR object.

> 2. Run GCC LTO wrapper to get the real object.

> 3. Extract symbol info from the real object.

> 4. Cleanup afterwards.

>

> bfd/

>

>         PR binutils/25355

>         * plugin.c (bfd_plugin_close_and_cleanup): Removed.

>         (plugin_list_entry): Add all_symbols_read, cleanup_handler,

>         gcc, lto_wrapper, resolution_file, resolution_option, gcc_env,

>         real_bfd, real_nsyms, real_syms, lto_nsyms and lto_syms.

>         (get_lto_wrapper): New.

>         (setup_lto_wrapper_env): Likewise.

>         (current_plugin): Likewise.

>         (register_all_symbols_read): Likewise.

>         (register_cleanup): Likewise.

>         (get_symbols): Likewise.

>         (add_input_file): Likewise.

>         (bfd_plugin_close_and_cleanup): Likewise.

>         (claim_file): Removed.

>         (register_claim_file): Set current_plugin->claim_file.

>         (add_symbols): Make a copy of LTO symbols.  Set lto_nsyms and

>         lto_syms in current_plugin.

>         (try_claim): Use current_plugin->claim_file.  Call LTO plugin

>         all_symbols_read handler.  Copy real symbols to plugin_data.

>         Call LTO plugin cleanup handler.  Clean up for LTO wrapper.

>         (try_load_plugin): Don't reuse the previous plugin for LTO

>         wrapper.  Set up GCC LTO wrapper if possible.  Don't set

>         plugin_list_iter->claim_file.

>         (bfd_plugin_canonicalize_symtab): Use real LTO symbols if

>         possible.

>         * plugin.h (plugin_data_struct): Add real_bfd, real_nsyms and

>         real_syms.

>

> ld/

>

>         PR binutils/25355

>         * testsuite/ld-plugin/lto.exp: Run PR binutils/25355 test.

>         * testsuite/ld-plugin/pr25355.c: New file.

>         * testsuite/ld-plugin/pr25355.d: Likewise.

>         * testsuite/lib/ld-lib.exp (run_cc_link_tests): Support

>         compile only dump.


Here is the updated patch to avoid mallocing plugin_list_iter for
each IR object.

-- 
H.J.
From 8c47018f1ba5f783d24230550d46919b3f0a1564 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sun, 9 Feb 2020 13:35:10 -0800
Subject: [PATCH] Use GCC LTO wrapper to get real symbols from LTO IR objects

GCC LTO wrapper is needed to extract real symbols from LTO IR objects.
This patch does the following:

1. Set up GCC LTO wrapper for each LTO IR object.
2. Run GCC LTO wrapper to get the real object.
3. Extract symbol info from the real object.
4. Cleanup afterwards.

bfd/

	PR binutils/25355
	* plugin.c (bfd_plugin_close_and_cleanup): Removed.
	(plugin_list_entry): Add all_symbols_read, cleanup_handler,
	gcc, lto_wrapper, resolution_file, resolution_option, gcc_env,
	real_bfd, real_nsyms, real_syms, lto_nsyms and lto_syms.
	(get_lto_wrapper): New.
	(setup_lto_wrapper_env): Likewise.
	(current_plugin): Likewise.
	(register_all_symbols_read): Likewise.
	(register_cleanup): Likewise.
	(get_symbols): Likewise.
	(add_input_file): Likewise.
	(bfd_plugin_close_and_cleanup): Likewise.
	(claim_file): Removed.
	(register_claim_file): Set current_plugin->claim_file.
	(add_symbols): Make a copy of LTO symbols.  Set lto_nsyms and
	lto_syms in current_plugin.
	(try_claim): Use current_plugin->claim_file.  Call LTO plugin
	all_symbols_read handler.  Copy real symbols to plugin_data.
	Call LTO plugin cleanup handler.  Clean up for LTO wrapper.
	(try_load_plugin): Don't reuse the previous plugin for LTO
	wrapper.  Set up GCC LTO wrapper if possible.  Don't set
	plugin_list_iter->claim_file.
	(bfd_plugin_canonicalize_symtab): Use real LTO symbols if
	possible.
	* plugin.h (plugin_data_struct): Add real_bfd, real_nsyms and
	real_syms.

ld/

	PR binutils/25355
	* testsuite/ld-plugin/lto.exp: Run PR binutils/25355 test.
	* testsuite/ld-plugin/pr25355.c: New file.
	* testsuite/ld-plugin/pr25355.d: Likewise.
	* testsuite/lib/ld-lib.exp (run_cc_link_tests): Support compile
	only dump.
---
 bfd/plugin.c                     | 456 +++++++++++++++++++++++++++++--
 bfd/plugin.h                     |   3 +
 ld/testsuite/ld-plugin/lto.exp   |   5 +
 ld/testsuite/ld-plugin/pr25355.c |   2 +
 ld/testsuite/ld-plugin/pr25355.d |   4 +
 ld/testsuite/lib/ld-lib.exp      |   1 +
 6 files changed, 450 insertions(+), 21 deletions(-)
 create mode 100644 ld/testsuite/ld-plugin/pr25355.c
 create mode 100644 ld/testsuite/ld-plugin/pr25355.d

diff --git a/bfd/plugin.c b/bfd/plugin.c
index 537ab60311..ff95558252 100644
--- a/bfd/plugin.c
+++ b/bfd/plugin.c
@@ -69,7 +69,6 @@ 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
@@ -124,13 +123,290 @@ message (int level ATTRIBUTE_UNUSED,
   return LDPS_OK;
 }
 
+struct plugin_list_entry
+{
+  /* These must be initialized for each IR object with LTO wrapper.  */
+  void *handle;
+  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;
+};
+
+/* 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 (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;
+
+  gcc_name = bfd_malloc (length + target_length + sizeof ("gcc"));
+  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", sizeof ("gcc"));
+  if (stat (gcc_name, &st) != 0 || !S_ISREG (st.st_mode))
+    {
+      /* Then try PREFIX/bin/gcc.  */
+      memcpy (p + 5, "gcc", sizeof ("gcc"));
+      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"));
+  memcpy (wrapper_name, real_name, length);
+  memcpy (wrapper_name + length, "lto-wrapper", sizeof ("lto-wrapper"));
+  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 = 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 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;
+  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 **) xmalloc (real_symsize);
+  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;
+  lto_symbol_found = FALSE;
+  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);
+
   return LDPS_OK;
 }
 
@@ -140,11 +416,23 @@ add_symbols (void * handle,
 	     const struct ld_plugin_symbol * syms)
 {
   bfd *abfd = handle;
+  struct ld_plugin_symbol *copy_of_syms;
   struct plugin_data_struct *plugin_data =
     bfd_alloc (abfd, sizeof (plugin_data_struct));
+  int i;
+
+  /* NB: LTO symbols are owned by LTO plugin.  Create a copy so that we
+     can use it in bfd_plugin_canonicalize_symtab.  */
+  copy_of_syms = bfd_alloc (abfd, nsyms * sizeof (*syms));
+  memcpy (copy_of_syms, syms, nsyms * sizeof (*syms));
+  for (i = 0; i < nsyms; i++)
+    copy_of_syms[i].name = xstrdup (syms[i].name);
 
   plugin_data->nsyms = nsyms;
-  plugin_data->syms = syms;
+  plugin_data->syms = copy_of_syms;
+
+  current_plugin->lto_nsyms = nsyms;
+  current_plugin->lto_syms = copy_of_syms;
 
   if (nsyms != 0)
     abfd->flags |= HAS_SYMS;
@@ -214,26 +502,49 @@ try_claim (bfd *abfd)
   file.handle = abfd;
   if (!bfd_plugin_open_input (abfd, &file))
     return 0;
-  if (claim_file)
-    claim_file (&file, &claimed);
+  if (current_plugin->claim_file)
+    {
+      current_plugin->claim_file (&file, &claimed);
+      if (claimed)
+	{
+	  if (current_plugin->all_symbols_read)
+	    {
+	      struct plugin_data_struct *plugin_data
+		= abfd->tdata.plugin_data;
+
+	      /* Get real symbols from LTO wrapper.  */
+	      current_plugin->all_symbols_read ();
+
+	      /* Copy real symbols to plugin_data.  */
+	      if (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 ();
+	    }
+	}
+
+      if (current_plugin->lto_wrapper)
+	{
+	  /* Clean up for LTO wrapper.  */
+	  unlink (current_plugin->resolution_file);
+	  free (current_plugin->resolution_option);
+	}
+    }
   close (file.fd);
   return claimed;
 }
 
-struct plugin_list_entry
-{
-  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];
+  struct ld_plugin_tv tv[12];
   int i;
   ld_plugin_onload onload;
   enum ld_plugin_status status;
@@ -241,6 +552,18 @@ try_load_plugin (const char *pname, bfd *abfd, int *has_plugin_p)
 
   *has_plugin_p = 0;
 
+  /* NB: Each object is inddependent.  Reuse the previous plugin from
+     the last LTO wrapper run will lead to wrong LTO data.  */
+  if (current_plugin
+      && current_plugin->handle
+      && current_plugin->lto_wrapper
+      && strcmp (current_plugin->plugin_name, pname) == 0)
+    {
+      dlclose (current_plugin->handle);
+      memset (current_plugin, 0,
+	      offsetof (struct plugin_list_entry, next));
+    }
+
   plugin_handle = dlopen (pname, RTLD_NOW);
   if (!plugin_handle)
     {
@@ -259,18 +582,24 @@ try_load_plugin (const char *pname, bfd *abfd, int *has_plugin_p)
 	    return 0;
 
 	  register_claim_file (plugin_list_iter->claim_file);
+	  current_plugin = plugin_list_iter;
 	  goto have_claim_file;
 	}
+      else if (strcmp (plugin_list_iter->plugin_name, pname) == 0)
+	goto have_lto_wrapper;
     }
 
   plugin_list_iter = bfd_malloc (sizeof *plugin_list_iter);
   if (plugin_list_iter == NULL)
     return 0;
-  plugin_list_iter->handle = plugin_handle;
-  plugin_list_iter->claim_file = NULL;
+  memset (plugin_list_iter, 0, sizeof (*plugin_list_iter));
+  plugin_list_iter->plugin_name = pname;
   plugin_list_iter->next = plugin_list;
   plugin_list = plugin_list_iter;
 
+have_lto_wrapper:
+  plugin_list_iter->handle = plugin_handle;
+
   onload = dlsym (plugin_handle, "onload");
   if (!onload)
     return 0;
@@ -287,23 +616,63 @@ try_load_plugin (const char *pname, bfd *abfd, int *has_plugin_p)
   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;
 
+  current_plugin = plugin_list_iter;
+
+  /* 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;
+  if (current_plugin->lto_wrapper
+      && setup_lto_wrapper_env (current_plugin))
+    return 0;
 
 have_claim_file:
   *has_plugin_p = 1;
 
   abfd->plugin_format = bfd_plugin_no;
 
-  if (!claim_file)
+  if (!current_plugin->claim_file)
     return 0;
 
   if (!try_claim (abfd))
@@ -562,7 +931,15 @@ bfd_plugin_canonicalize_symtab (bfd *abfd,
 			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;
+  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++)
     {
@@ -587,6 +964,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);
@@ -635,6 +1021,34 @@ 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))
+    {
+      const struct ld_plugin_symbol *syms = plugin_data->syms;
+
+      if (plugin_data->real_bfd)
+	bfd_close (plugin_data->real_bfd);
+
+      if (plugin_data->real_syms)
+	free (plugin_data->real_syms);
+
+      if (syms)
+	{
+	  long nsyms = plugin_data->nsyms;
+	  int i;
+	  for (i = 0; i < nsyms; i++)
+	    free (syms[i].name);
+	}
+    }
+
+  return _bfd_generic_close_and_cleanup (abfd);
+}
+
 const bfd_target plugin_vec =
 {
   "plugin",			/* Name.  */
diff --git a/bfd/plugin.h b/bfd/plugin.h
index 098bf08455..05c3573933 100644
--- a/bfd/plugin.h
+++ b/bfd/plugin.h
@@ -33,6 +33,9 @@ typedef struct plugin_data_struct
 {
   int nsyms;
   const struct ld_plugin_symbol *syms;
+  bfd *real_bfd;
+  long real_nsyms;
+  asymbol **real_syms;
 }
 plugin_data_struct;
 
diff --git a/ld/testsuite/ld-plugin/lto.exp b/ld/testsuite/ld-plugin/lto.exp
index 9b03b7b397..1b44b0da3f 100644
--- a/ld/testsuite/ld-plugin/lto.exp
+++ b/ld/testsuite/ld-plugin/lto.exp
@@ -234,6 +234,11 @@ set lto_link_tests [list \
   [list "Build pr24406-2b.o" \
    "" "-O2 -fno-lto" \
    {pr24406-2b.c}] \
+  [list "pr25355.o" \
+   "" \
+   "-flto -fno-common $lto_no_fat" \
+   {pr25355.c} \
+   [list [list "nm" "$plug_opt" "pr25355.d"]]] \
 ]
 
 if { [at_least_gcc_version 4 7] } {
diff --git a/ld/testsuite/ld-plugin/pr25355.c b/ld/testsuite/ld-plugin/pr25355.c
new file mode 100644
index 0000000000..99f01fb5c8
--- /dev/null
+++ b/ld/testsuite/ld-plugin/pr25355.c
@@ -0,0 +1,2 @@
+int nm_test_var;
+int nm_test_var2 = 1234;
diff --git a/ld/testsuite/ld-plugin/pr25355.d b/ld/testsuite/ld-plugin/pr25355.d
new file mode 100644
index 0000000000..98d10aba1c
--- /dev/null
+++ b/ld/testsuite/ld-plugin/pr25355.d
@@ -0,0 +1,4 @@
+#...
+[0-9a-f]+ B _?nm_test_var
+[0-9a-f]+ D _?nm_test_var2
+#pass
diff --git a/ld/testsuite/lib/ld-lib.exp b/ld/testsuite/lib/ld-lib.exp
index 08e2d6978a..015eda6eb9 100644
--- a/ld/testsuite/lib/ld-lib.exp
+++ b/ld/testsuite/lib/ld-lib.exp
@@ -928,6 +928,7 @@ proc run_cc_link_tests { ldtests } {
 
 	if { $binfile eq "tmpdir/" } {
 	    # compile only
+	    set binfile $objfile
 	} elseif { [regexp ".*\\.a$" $binfile] } {
 	    if { ![ar_simple_create $ar $ldflags $binfile "$objfiles"] } {
 		set failed 1
Alan Modra Feb. 10, 2020, 11:01 p.m. | #2
Wow, that's a lot of work to get proper symbol type.  What happens if
you run nm on a system where gcc isn't installed?  Or where the lto
version doesn't match?

On Mon, Feb 10, 2020 at 01:31:16PM -0800, H.J. Lu wrote:
> +  gcc_name = bfd_malloc (length + target_length + sizeof ("gcc"));

> +  memcpy (gcc_name, real_name, length);


No check of bfd_malloc return value before use.

> +  wrapper_name = bfd_malloc (length + sizeof ("lto-wrapper"));

> +  memcpy (wrapper_name, real_name, length);


Again.

> +  real_syms = (asymbol **) xmalloc (real_symsize);


xmalloc shouldn't be used in libbfd.

>    struct plugin_data_struct *plugin_data =

>      bfd_alloc (abfd, sizeof (plugin_data_struct));


This also doesn't check for NULL, preexisting bug.

> +  int i;

> +

> +  /* NB: LTO symbols are owned by LTO plugin.  Create a copy so that we

> +     can use it in bfd_plugin_canonicalize_symtab.  */

> +  copy_of_syms = bfd_alloc (abfd, nsyms * sizeof (*syms));

> +  memcpy (copy_of_syms, syms, nsyms * sizeof (*syms));

> +  for (i = 0; i < nsyms; i++)

> +    copy_of_syms[i].name = xstrdup (syms[i].name);


xstrdup shouldn't be used in libbfd.

-- 
Alan Modra
Australia Development Lab, IBM
H.J. Lu Feb. 11, 2020, 1:22 a.m. | #3
On Mon, Feb 10, 2020 at 3:02 PM Alan Modra <amodra@gmail.com> wrote:
>

> Wow, that's a lot of work to get proper symbol type.  What happens if


Very true.  I couldn't come up with a better solution without changing plugin
API.

> you run nm on a system where gcc isn't installed?  Or where the lto

> version doesn't match?


I assume standard GCC installation directory layout like

PREFIX/libexec/gcc/TARGET/VERSION/liblto_plugin.so
PREFIX/libexec/gcc/TARGET/VERSION/lto-wrapper

and

PREFIX/bin/TARGET-gcc

or

PREFIX/bin/gcc

If there is no gcc nor lto-wrapper at expected location, no real
symbols will be used.  If LTO version doesn't match, it is a
GCC installation problem.

> On Mon, Feb 10, 2020 at 01:31:16PM -0800, H.J. Lu wrote:

> > +  gcc_name = bfd_malloc (length + target_length + sizeof ("gcc"));

> > +  memcpy (gcc_name, real_name, length);

>

> No check of bfd_malloc return value before use.


Fixed.

> > +  wrapper_name = bfd_malloc (length + sizeof ("lto-wrapper"));

> > +  memcpy (wrapper_name, real_name, length);

>

> Again.


Fixed.

> > +  real_syms = (asymbol **) xmalloc (real_symsize);

>

> xmalloc shouldn't be used in libbfd.


Fixed.

> >    struct plugin_data_struct *plugin_data =

> >      bfd_alloc (abfd, sizeof (plugin_data_struct));

>

> This also doesn't check for NULL, preexisting bug.


Fixed.

> > +  int i;

> > +

> > +  /* NB: LTO symbols are owned by LTO plugin.  Create a copy so that we

> > +     can use it in bfd_plugin_canonicalize_symtab.  */

> > +  copy_of_syms = bfd_alloc (abfd, nsyms * sizeof (*syms));

> > +  memcpy (copy_of_syms, syms, nsyms * sizeof (*syms));

> > +  for (i = 0; i < nsyms; i++)

> > +    copy_of_syms[i].name = xstrdup (syms[i].name);

>

> xstrdup shouldn't be used in libbfd.


Fixed.

Here is the updated patch.

-- 
H.J.
From 36c9e09e5f50e8d00782d14528352412a690588d Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sun, 9 Feb 2020 13:35:10 -0800
Subject: [PATCH] Use GCC LTO wrapper to get real symbols from LTO IR objects

GCC LTO wrapper is needed to extract real symbols from LTO IR objects.
This patch does the following:

1. Set up GCC LTO wrapper for each LTO IR object.
2. Run GCC LTO wrapper to get the real object.
3. Extract symbol info from the real object.
4. Cleanup afterwards.

bfd/

	PR binutils/25355
	* configure.ac (HAVE_EXECUTABLE_SUFFIX): New AC_DEFINE.
	(EXECUTABLE_SUFFIX): Likewise.
	* config.in: Regenerated.
	* configure: Likewise.
	* plugin.c (bfd_plugin_close_and_cleanup): Removed.
	(plugin_list_entry): Add all_symbols_read, cleanup_handler,
	gcc, lto_wrapper, resolution_file, resolution_option, gcc_env,
	real_bfd, real_nsyms, real_syms, lto_nsyms and lto_syms.
	(get_lto_wrapper): New.
	(setup_lto_wrapper_env): Likewise.
	(current_plugin): Likewise.
	(register_all_symbols_read): Likewise.
	(register_cleanup): Likewise.
	(get_symbols): Likewise.
	(add_input_file): Likewise.
	(bfd_plugin_close_and_cleanup): Likewise.
	(claim_file): Removed.
	(register_claim_file): Set current_plugin->claim_file.
	(add_symbols): Make a copy of LTO symbols.  Set lto_nsyms and
	lto_syms in current_plugin.
	(try_claim): Use current_plugin->claim_file.  Call LTO plugin
	all_symbols_read handler.  Copy real symbols to plugin_data.
	Call LTO plugin cleanup handler.  Clean up for LTO wrapper.
	(try_load_plugin): Don't reuse the previous plugin for LTO
	wrapper.  Set up GCC LTO wrapper if possible.  Don't set
	plugin_list_iter->claim_file.
	(bfd_plugin_canonicalize_symtab): Use real LTO symbols if
	possible.
	* plugin.h (plugin_data_struct): Add real_bfd, real_nsyms and
	real_syms.

ld/

	PR binutils/25355
	* testsuite/ld-plugin/lto.exp: Run PR binutils/25355 test.
	* testsuite/ld-plugin/pr25355.c: New file.
	* testsuite/ld-plugin/pr25355.d: Likewise.
	* testsuite/lib/ld-lib.exp (run_cc_link_tests): Support compile
	only dump.
---
 bfd/config.in                    |   6 +
 bfd/configure                    |  10 +
 bfd/configure.ac                 |   6 +
 bfd/plugin.c                     | 494 +++++++++++++++++++++++++++++--
 bfd/plugin.h                     |   3 +
 ld/testsuite/ld-plugin/lto.exp   |   5 +
 ld/testsuite/ld-plugin/pr25355.c |   2 +
 ld/testsuite/ld-plugin/pr25355.d |   4 +
 ld/testsuite/lib/ld-lib.exp      |   1 +
 9 files changed, 508 insertions(+), 23 deletions(-)
 create mode 100644 ld/testsuite/ld-plugin/pr25355.c
 create mode 100644 ld/testsuite/ld-plugin/pr25355.d

diff --git a/bfd/config.in b/bfd/config.in
index be572969fc0..e1dc0f0c445 100644
--- a/bfd/config.in
+++ b/bfd/config.in
@@ -18,6 +18,9 @@
    language is requested. */
 #undef ENABLE_NLS
 
+/* Suffix used for executables, if any. */
+#undef EXECUTABLE_SUFFIX
+
 /* Define to 1 if you have the <alloca.h> header file. */
 #undef HAVE_ALLOCA_H
 
@@ -95,6 +98,9 @@
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #undef HAVE_DLFCN_H
 
+/* Does the platform use an executable suffix? */
+#undef HAVE_EXECUTABLE_SUFFIX
+
 /* Define to 1 if you have the `fcntl' function. */
 #undef HAVE_FCNTL
 
diff --git a/bfd/configure b/bfd/configure
index bcc1a4eaf2f..cca67ee1080 100755
--- a/bfd/configure
+++ b/bfd/configure
@@ -12813,6 +12813,16 @@ fi
 
 
 
+if test -n "$EXEEXT"; then
+
+$as_echo "#define HAVE_EXECUTABLE_SUFFIX 1" >>confdefs.h
+
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define EXECUTABLE_SUFFIX "${EXEEXT}"
+_ACEOF
+
 
 host64=false
 target64=false
diff --git a/bfd/configure.ac b/bfd/configure.ac
index c5bfbd5d129..af4d4b8c135 100644
--- a/bfd/configure.ac
+++ b/bfd/configure.ac
@@ -157,6 +157,12 @@ AM_MAINTAINER_MODE
 AM_CONDITIONAL(GENINSRC_NEVER, false)
 AM_INSTALL_LIBBFD
 AC_EXEEXT
+if test -n "$EXEEXT"; then
+  AC_DEFINE(HAVE_EXECUTABLE_SUFFIX, 1,
+	    [Does the platform use an executable suffix?])
+fi
+AC_DEFINE_UNQUOTED(EXECUTABLE_SUFFIX, "${EXEEXT}",
+		   [Suffix used for executables, if any.])
 
 host64=false
 target64=false
diff --git a/bfd/plugin.c b/bfd/plugin.c
index 537ab603110..fc702cbf8db 100644
--- a/bfd/plugin.c
+++ b/bfd/plugin.c
@@ -69,7 +69,6 @@ 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
@@ -124,13 +123,309 @@ message (int level ATTRIBUTE_UNUSED,
   return LDPS_OK;
 }
 
+struct plugin_list_entry
+{
+  /* These must be initialized for each IR object with LTO wrapper.  */
+  void *handle;
+  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;
+};
+
+/* 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 (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 = 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 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);
+
   return LDPS_OK;
 }
 
@@ -143,13 +438,50 @@ 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;
+
+      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;
 }
 
@@ -214,26 +546,48 @@ try_claim (bfd *abfd)
   file.handle = abfd;
   if (!bfd_plugin_open_input (abfd, &file))
     return 0;
-  if (claim_file)
-    claim_file (&file, &claimed);
+  if (current_plugin->claim_file)
+    {
+      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 ();
+		}
+	    }
+	}
+
+      if (current_plugin->lto_wrapper)
+	{
+	  /* Clean up for LTO wrapper.  */
+	  unlink (current_plugin->resolution_file);
+	  free (current_plugin->resolution_option);
+	}
+    }
   close (file.fd);
   return claimed;
 }
 
-struct plugin_list_entry
-{
-  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];
+  struct ld_plugin_tv tv[12];
   int i;
   ld_plugin_onload onload;
   enum ld_plugin_status status;
@@ -241,6 +595,18 @@ try_load_plugin (const char *pname, bfd *abfd, int *has_plugin_p)
 
   *has_plugin_p = 0;
 
+  /* NB: Each object is inddependent.  Reuse the previous plugin from
+     the last LTO wrapper run will lead to wrong LTO data.  */
+  if (current_plugin
+      && current_plugin->handle
+      && current_plugin->lto_wrapper
+      && strcmp (current_plugin->plugin_name, pname) == 0)
+    {
+      dlclose (current_plugin->handle);
+      memset (current_plugin, 0,
+	      offsetof (struct plugin_list_entry, next));
+    }
+
   plugin_handle = dlopen (pname, RTLD_NOW);
   if (!plugin_handle)
     {
@@ -259,18 +625,25 @@ try_load_plugin (const char *pname, bfd *abfd, int *has_plugin_p)
 	    return 0;
 
 	  register_claim_file (plugin_list_iter->claim_file);
+	  current_plugin = plugin_list_iter;
 	  goto have_claim_file;
 	}
+      else if (plugin_list_iter->lto_wrapper
+	       && strcmp (plugin_list_iter->plugin_name, pname) == 0)
+	goto have_lto_wrapper;
     }
 
   plugin_list_iter = bfd_malloc (sizeof *plugin_list_iter);
   if (plugin_list_iter == NULL)
     return 0;
-  plugin_list_iter->handle = plugin_handle;
-  plugin_list_iter->claim_file = NULL;
+  memset (plugin_list_iter, 0, sizeof (*plugin_list_iter));
+  plugin_list_iter->plugin_name = pname;
   plugin_list_iter->next = plugin_list;
   plugin_list = plugin_list_iter;
 
+have_lto_wrapper:
+  plugin_list_iter->handle = plugin_handle;
+
   onload = dlsym (plugin_handle, "onload");
   if (!onload)
     return 0;
@@ -287,23 +660,63 @@ try_load_plugin (const char *pname, bfd *abfd, int *has_plugin_p)
   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;
 
+  current_plugin = plugin_list_iter;
+
+  /* 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;
+  if (current_plugin->lto_wrapper
+      && setup_lto_wrapper_env (current_plugin))
+    return 0;
 
 have_claim_file:
   *has_plugin_p = 1;
 
   abfd->plugin_format = bfd_plugin_no;
 
-  if (!claim_file)
+  if (!current_plugin->claim_file)
     return 0;
 
   if (!try_claim (abfd))
@@ -562,7 +975,15 @@ bfd_plugin_canonicalize_symtab (bfd *abfd,
 			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;
+  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++)
     {
@@ -587,6 +1008,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);
@@ -635,6 +1065,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.  */
diff --git a/bfd/plugin.h b/bfd/plugin.h
index 098bf084554..05c3573933d 100644
--- a/bfd/plugin.h
+++ b/bfd/plugin.h
@@ -33,6 +33,9 @@ typedef struct plugin_data_struct
 {
   int nsyms;
   const struct ld_plugin_symbol *syms;
+  bfd *real_bfd;
+  long real_nsyms;
+  asymbol **real_syms;
 }
 plugin_data_struct;
 
diff --git a/ld/testsuite/ld-plugin/lto.exp b/ld/testsuite/ld-plugin/lto.exp
index 9b03b7b3977..1b44b0da3fd 100644
--- a/ld/testsuite/ld-plugin/lto.exp
+++ b/ld/testsuite/ld-plugin/lto.exp
@@ -234,6 +234,11 @@ set lto_link_tests [list \
   [list "Build pr24406-2b.o" \
    "" "-O2 -fno-lto" \
    {pr24406-2b.c}] \
+  [list "pr25355.o" \
+   "" \
+   "-flto -fno-common $lto_no_fat" \
+   {pr25355.c} \
+   [list [list "nm" "$plug_opt" "pr25355.d"]]] \
 ]
 
 if { [at_least_gcc_version 4 7] } {
diff --git a/ld/testsuite/ld-plugin/pr25355.c b/ld/testsuite/ld-plugin/pr25355.c
new file mode 100644
index 00000000000..99f01fb5c89
--- /dev/null
+++ b/ld/testsuite/ld-plugin/pr25355.c
@@ -0,0 +1,2 @@
+int nm_test_var;
+int nm_test_var2 = 1234;
diff --git a/ld/testsuite/ld-plugin/pr25355.d b/ld/testsuite/ld-plugin/pr25355.d
new file mode 100644
index 00000000000..98d10aba1cf
--- /dev/null
+++ b/ld/testsuite/ld-plugin/pr25355.d
@@ -0,0 +1,4 @@
+#...
+[0-9a-f]+ B _?nm_test_var
+[0-9a-f]+ D _?nm_test_var2
+#pass
diff --git a/ld/testsuite/lib/ld-lib.exp b/ld/testsuite/lib/ld-lib.exp
index 08e2d6978af..015eda6eb97 100644
--- a/ld/testsuite/lib/ld-lib.exp
+++ b/ld/testsuite/lib/ld-lib.exp
@@ -928,6 +928,7 @@ proc run_cc_link_tests { ldtests } {
 
 	if { $binfile eq "tmpdir/" } {
 	    # compile only
+	    set binfile $objfile
 	} elseif { [regexp ".*\\.a$" $binfile] } {
 	    if { ![ar_simple_create $ar $ldflags $binfile "$objfiles"] } {
 		set failed 1
Alan Modra Feb. 11, 2020, 2:31 a.m. | #4
On Mon, Feb 10, 2020 at 05:22:24PM -0800, H.J. Lu wrote:
> +  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);


I think you might want to free real_syms here on else branch.  OK with
that fix.

-- 
Alan Modra
Australia Development Lab, IBM
H.J. Lu Feb. 11, 2020, 12:20 p.m. | #5
On Mon, Feb 10, 2020 at 6:31 PM Alan Modra <amodra@gmail.com> wrote:
>

> On Mon, Feb 10, 2020 at 05:22:24PM -0800, H.J. Lu wrote:

> > +  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);

>

> I think you might want to free real_syms here on else branch.  OK with

> that fix.


I am checking in this patch to avoid uninitialized memory.


-- 
H.J.
From 04fe3341b310980507380118c0eb03537850f738 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Tue, 11 Feb 2020 04:09:32 -0800
Subject: [PATCH] Clear plugin_data memory

Clear plugin_data memory since it may be uninitialized.

	* plugin.c (add_symbols): Clear plugin_data memory.
---
 bfd/plugin.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/bfd/plugin.c b/bfd/plugin.c
index 1edcb57bf3a..5681a6a2913 100644
--- a/bfd/plugin.c
+++ b/bfd/plugin.c
@@ -448,6 +448,8 @@ add_symbols (void * handle,
       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
Michael Matz Feb. 11, 2020, 4:07 p.m. | #6
Hi,

On Mon, 10 Feb 2020, H.J. Lu wrote:

> > Wow, that's a lot of work to get proper symbol type.  What happens if

> 

> Very true.  I couldn't come up with a better solution without changing 

> plugin API.


Then, it should perhaps be changed?  The cure without that (i.e. your 
patch) looks worse than the disease (temporary files, hardcoding path 
layouts, dependence on gcc installed) :-/

I know why you wrote the patch as is, but ... ugh.


Ciao,
Michael.
H.J. Lu Feb. 11, 2020, 5:03 p.m. | #7
On Tue, Feb 11, 2020 at 8:07 AM Michael Matz <matz@suse.de> wrote:
>

> Hi,

>

> On Mon, 10 Feb 2020, H.J. Lu wrote:

>

> > > Wow, that's a lot of work to get proper symbol type.  What happens if

> >

> > Very true.  I couldn't come up with a better solution without changing

> > plugin API.

>

> Then, it should perhaps be changed?  The cure without that (i.e. your

> patch) looks worse than the disease (temporary files, hardcoding path

> layouts, dependence on gcc installed) :-/

>

> I know why you wrote the patch as is, but ... ugh.


I think plugin API should be extended.  In the meantime, we have a problem
to solve.

-- 
H.J.
H.J. Lu Feb. 11, 2020, 10:04 p.m. | #8
On Tue, Feb 11, 2020 at 4:20 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>

> On Mon, Feb 10, 2020 at 6:31 PM Alan Modra <amodra@gmail.com> wrote:

> >

> > On Mon, Feb 10, 2020 at 05:22:24PM -0800, H.J. Lu wrote:

> > > +  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);

> >

> > I think you might want to free real_syms here on else branch.  OK with

> > that fix.

>

> I am checking in this patch to avoid uninitialized memory.


This patch is needed to support multiple plugins in
${libdir}/bfd-plugins directory.
OK for master branch?

Thanks.

-- 
H.J.
Alan Modra Feb. 11, 2020, 10:22 p.m. | #9
On Tue, Feb 11, 2020 at 02:04:46PM -0800, H.J. Lu wrote:
> This patch is needed to support multiple plugins in

> ${libdir}/bfd-plugins directory.

> OK for master branch?


OK.

-- 
Alan Modra
Australia Development Lab, IBM
Alan Modra Feb. 13, 2020, 8:15 a.m. | #10
On Tue, Feb 11, 2020 at 09:03:18AM -0800, H.J. Lu wrote:
> On Tue, Feb 11, 2020 at 8:07 AM Michael Matz <matz@suse.de> wrote:

> >

> > Hi,

> >

> > On Mon, 10 Feb 2020, H.J. Lu wrote:

> >

> > > > Wow, that's a lot of work to get proper symbol type.  What happens if

> > >

> > > Very true.  I couldn't come up with a better solution without changing

> > > plugin API.

> >

> > Then, it should perhaps be changed?  The cure without that (i.e. your

> > patch) looks worse than the disease (temporary files, hardcoding path

> > layouts, dependence on gcc installed) :-/


Yes, I expect to see complaints about the paths.  You can also break
the support in more creative ways.  For example, when using
CC="${gccdir}gcc/xgcc -B${gccdir}gcc/" and a more complex CXX to build
binutils with a bleeding edge gcc.  I also have cross compilers
installed for a number of targets, and I used a trick to save building
some 32-bit targets that have a 64-bit bi-arch compiler.  Like this:

$ cat /usr/local/bin/s390-linux-gcc
#! /bin/sh
case " $@ " in
*" -m64 "*) p= ;;
" -V "*) shift; p="-V $1 -m31 "; shift;;
" -V"*) p="$1 -m31 "; shift;;
*) p="-m31 ";;
esac
exec s390x-linux-gcc ${p}"$@"
EOF

After HJ's patch I see
s390-linux  +FAIL: Build liblto-12.a
s390-linux  +FAIL: Build liblto-14.a
s390-linux  +FAIL: Build liblto-15.a
s390-linux  +FAIL: PR ld/19317 (1)
s390-linux  +FAIL: Build libpr20267a.a
s390-linux  +FAIL: Build libpr20267b.a
s390-linux  +FAIL: Build pr22751.a
s390-linux  +FAIL: pr25355.o
s390-linux  +FAIL: Build libpr15146a.a
s390-linux  +FAIL: Build libpr15146d.a
s390-linux  +FAIL: Build libpr16746b.a

Most of these are due to dejagnu not liking unexpected output, with
the newly built ar spitting out errors like the following:

Executing on host: sh -c {/home/alan/build/gas/s390-linux/ld/../binutils/ar  -rc --plugin /home/local/bin/../libexec/gcc/s390x-linux/8.1.1/liblto_plugin.so tmpdir/liblto-12.a tmpdir/lto-12c.o 2>&1}  /dev/null ld.tmp (timeout = 300)
spawn [open ...]
/home/local/bin/../lib/gcc/s390x-linux/8.1.1/../../../../s390x-linux/bin/ld: relocatable linking with relocations from format elf32-s390 (/tmp/cc32KkFydebugobjtem) to format elf64-s390 (/tmp/cc3YsYNydebugobj) is not supported
collect2: error: ld returned 1 exit status
lto-wrapper: fatal error: /home/local/bin/s390x-linux-gcc returned 1 exit status
compilation terminated.
bfd plugin: lto-wrapper failed

Oh well, time to get rid of my hacky s390-linux-gcc.

> > I know why you wrote the patch as is, but ... ugh.

> 

> I think plugin API should be extended.  In the meantime, we have a problem

> to solve.


-- 
Alan Modra
Australia Development Lab, IBM
Richard Biener March 10, 2020, 2:55 p.m. | #11
On Mon, 10 Feb 2020, H dot J dot  Lu wrote:

> On Mon, Feb 10, 2020 at 3:02 PM Alan Modra <amodra@gmail.com> wrote:

> >

> > Wow, that's a lot of work to get proper symbol type.  What happens if

> 

> Very true.  I couldn't come up with a better solution without changing plugin

> API.

> 

> > you run nm on a system where gcc isn't installed?  Or where the lto

> > version doesn't match?

> 

> I assume standard GCC installation directory layout like

> 

> PREFIX/libexec/gcc/TARGET/VERSION/liblto_plugin.so

> PREFIX/libexec/gcc/TARGET/VERSION/lto-wrapper

> 

> and

> 

> PREFIX/bin/TARGET-gcc

> 

> or

> 

> PREFIX/bin/gcc

> 

> If there is no gcc nor lto-wrapper at expected location, no real

> symbols will be used.  If LTO version doesn't match, it is a

> GCC installation problem.


GCC installation problem?  Uh.

The attempt to find the approprate GCC driver to do code generation
is bound to fail in interesting ways.  Not sure if the cases that
are fixed with this approach are better than the know shortcomings
of the symbol table part of the plugin API and the resulting
impreciseness of nm output.

For better heuristics you should probably parse the .comment section
of the object file which contains GCC version information:

> readelf -x 13 x.o 


Hex dump of section '.comment':
  0x00000000 00474343 3a202847 4e552920 31302e30 .GCC: (GNU) 10.0
  0x00000010 2e312032 30323030 33303520 28657870 .1 20200305 (exp
  0x00000020 6572696d 656e7461 6c2900            erimental).

which might help to disambiguate the choice between multiple
installed compiler candidates.

For SUSE installed systems it's common to have an "old"
lto-plugin but also newer compilers installed so using solely the
lto-plugin location is misleading.

As said, there's technically no way to get at the correct
lto-wrapper or compiler driver (for COLLECT_GCC) for an LTO
object.  The only solution is to extend the plugin API
(and the LTO bytecode symbol table).

Richard.

Patch

diff --git a/bfd/plugin.c b/bfd/plugin.c
index 537ab603110..891ce00fc34 100644
--- a/bfd/plugin.c
+++ b/bfd/plugin.c
@@ -69,7 +69,6 @@  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
@@ -124,13 +123,254 @@  message (int level ATTRIBUTE_UNUSED,
   return LDPS_OK;
 }
 
+struct plugin_list_entry
+{
+  void *handle;
+  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 *gcc;
+  char *lto_wrapper;
+  char *resolution_file;
+  char *resolution_option;
+  char *gcc_env;
+  bfd *real_bfd;
+  long real_nsyms;
+  asymbol **real_syms;
+  int lto_nsyms;
+  const struct ld_plugin_symbol *lto_syms;
+  struct plugin_list_entry *next;
+};
+
+/* Use GCC LTO wrapper to covert LTO IR object to the real object.  */
+
+static void
+get_lto_wrapper (struct plugin_list_entry *plugin, const char *pname)
+{
+  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';
+
+  /* Check for PREFIX/libexec/gcc/TARGET/VERSION/liblto_plugin.so.  */
+  real_name = lrealpath (pname);
+  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;
+
+  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;
+
+  gcc_name = bfd_malloc (length + target_length + sizeof ("gcc"));
+  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", sizeof ("gcc"));
+  if (stat (gcc_name, &st) != 0 || !S_ISREG (st.st_mode))
+    {
+      /* Then try PREFIX/bin/gcc.  */
+      memcpy (p + 5, "gcc", sizeof ("gcc"));
+      if (stat (gcc_name, &st) != 0 || !S_ISREG (st.st_mode))
+	{
+	  free (gcc_name);
+	  return;
+	}
+    }
+
+  /* lto-wrapper should be in the same directory with LTO plugin.  */
+  wrapper_name = bfd_malloc (length + sizeof ("lto-wrapper"));
+  memcpy (wrapper_name, real_name, length);
+  memcpy (wrapper_name + length, "lto-wrapper", sizeof ("lto-wrapper"));
+  if (stat (wrapper_name, &st) == 0 && S_ISREG (st.st_mode))
+    {
+      char *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;
+	}
+    }
+
+  free (gcc_name);
+  free (wrapper_name);
+}
+
+/* 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 = 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 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;
+  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 **) xmalloc (real_symsize);
+  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;
+  lto_symbol_found = FALSE;
+  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);
+
   return LDPS_OK;
 }
 
@@ -140,11 +380,23 @@  add_symbols (void * handle,
 	     const struct ld_plugin_symbol * syms)
 {
   bfd *abfd = handle;
+  struct ld_plugin_symbol *copy_of_syms;
   struct plugin_data_struct *plugin_data =
     bfd_alloc (abfd, sizeof (plugin_data_struct));
+  int i;
+
+  /* NB: LTO symbols are owned by LTO plugin.  Create a copy so that we
+     can use it in bfd_plugin_canonicalize_symtab.  */
+  copy_of_syms = bfd_alloc (abfd, nsyms * sizeof (*syms));
+  memcpy (copy_of_syms, syms, nsyms * sizeof (*syms));
+  for (i = 0; i < nsyms; i++)
+    copy_of_syms[i].name = xstrdup (syms[i].name);
 
   plugin_data->nsyms = nsyms;
-  plugin_data->syms = syms;
+  plugin_data->syms = copy_of_syms;
+
+  current_plugin->lto_nsyms = nsyms;
+  current_plugin->lto_syms = copy_of_syms;
 
   if (nsyms != 0)
     abfd->flags |= HAS_SYMS;
@@ -214,26 +466,52 @@  try_claim (bfd *abfd)
   file.handle = abfd;
   if (!bfd_plugin_open_input (abfd, &file))
     return 0;
-  if (claim_file)
-    claim_file (&file, &claimed);
+  if (current_plugin->claim_file)
+    {
+      current_plugin->claim_file (&file, &claimed);
+      if (claimed)
+	{
+	  if (current_plugin->all_symbols_read)
+	    {
+	      struct plugin_data_struct *plugin_data
+		= abfd->tdata.plugin_data;
+
+	      /* Get real symbols from LTO wrapper.  */
+	      current_plugin->all_symbols_read ();
+
+	      /* Copy real symbols to plugin_data.  */
+	      if (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 ();
+	    }
+	}
+
+      if (current_plugin->lto_wrapper)
+	{
+	  /* Clean up for LTO wrapper.  */
+	  unlink (current_plugin->resolution_file);
+	  free (current_plugin->lto_wrapper);
+	  free (current_plugin->gcc);
+	  free (current_plugin->gcc_env);
+	  free (current_plugin->resolution_option);
+	}
+    }
   close (file.fd);
   return claimed;
 }
 
-struct plugin_list_entry
-{
-  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];
+  struct ld_plugin_tv tv[12];
   int i;
   ld_plugin_onload onload;
   enum ld_plugin_status status;
@@ -241,6 +519,16 @@  try_load_plugin (const char *pname, bfd *abfd, int *has_plugin_p)
 
   *has_plugin_p = 0;
 
+  /* NB: Each object is inddependent.  Reuse the previous plugin from
+     the last LTO wrapper run will lead to wrong LTO data.  */
+  if (current_plugin
+      && current_plugin->handle
+      && current_plugin->all_symbols_read)
+    {
+      dlclose (current_plugin->handle);
+      memset (current_plugin, 0, sizeof (*current_plugin));
+    }
+
   plugin_handle = dlopen (pname, RTLD_NOW);
   if (!plugin_handle)
     {
@@ -259,6 +547,7 @@  try_load_plugin (const char *pname, bfd *abfd, int *has_plugin_p)
 	    return 0;
 
 	  register_claim_file (plugin_list_iter->claim_file);
+	  current_plugin = plugin_list_iter;
 	  goto have_claim_file;
 	}
     }
@@ -266,8 +555,8 @@  try_load_plugin (const char *pname, bfd *abfd, int *has_plugin_p)
   plugin_list_iter = bfd_malloc (sizeof *plugin_list_iter);
   if (plugin_list_iter == NULL)
     return 0;
+  memset (plugin_list_iter, 0, 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;
 
@@ -287,23 +576,64 @@  try_load_plugin (const char *pname, bfd *abfd, int *has_plugin_p)
   tv[i].tv_tag = LDPT_ADD_SYMBOLS;
   tv[i].tv_u.tv_add_symbols = add_symbols;
 
+  get_lto_wrapper (plugin_list_iter, pname);
+  if (plugin_list_iter->lto_wrapper)
+    {
+      ++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;
 
+  current_plugin = plugin_list_iter;
+
+  /* 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;
+  if (plugin_list_iter->lto_wrapper
+      && setup_lto_wrapper_env (plugin_list_iter))
+    return 0;
 
 have_claim_file:
   *has_plugin_p = 1;
 
   abfd->plugin_format = bfd_plugin_no;
 
-  if (!claim_file)
+  if (!current_plugin->claim_file)
     return 0;
 
   if (!try_claim (abfd))
@@ -562,7 +892,15 @@  bfd_plugin_canonicalize_symtab (bfd *abfd,
 			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;
+  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++)
     {
@@ -587,6 +925,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);
@@ -635,6 +982,34 @@  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))
+    {
+      const struct ld_plugin_symbol *syms = plugin_data->syms;
+
+      if (plugin_data->real_bfd)
+	bfd_close (plugin_data->real_bfd);
+
+      if (plugin_data->real_syms)
+	free (plugin_data->real_syms);
+
+      if (syms)
+	{
+	  long nsyms = plugin_data->nsyms;
+	  int i;
+	  for (i = 0; i < nsyms; i++)
+	    free (syms[i].name);
+	}
+    }
+
+  return _bfd_generic_close_and_cleanup (abfd);
+}
+
 const bfd_target plugin_vec =
 {
   "plugin",			/* Name.  */
diff --git a/bfd/plugin.h b/bfd/plugin.h
index 098bf084554..05c3573933d 100644
--- a/bfd/plugin.h
+++ b/bfd/plugin.h
@@ -33,6 +33,9 @@  typedef struct plugin_data_struct
 {
   int nsyms;
   const struct ld_plugin_symbol *syms;
+  bfd *real_bfd;
+  long real_nsyms;
+  asymbol **real_syms;
 }
 plugin_data_struct;
 
diff --git a/ld/testsuite/ld-plugin/lto.exp b/ld/testsuite/ld-plugin/lto.exp
index 9b03b7b3977..1b44b0da3fd 100644
--- a/ld/testsuite/ld-plugin/lto.exp
+++ b/ld/testsuite/ld-plugin/lto.exp
@@ -234,6 +234,11 @@  set lto_link_tests [list \
   [list "Build pr24406-2b.o" \
    "" "-O2 -fno-lto" \
    {pr24406-2b.c}] \
+  [list "pr25355.o" \
+   "" \
+   "-flto -fno-common $lto_no_fat" \
+   {pr25355.c} \
+   [list [list "nm" "$plug_opt" "pr25355.d"]]] \
 ]
 
 if { [at_least_gcc_version 4 7] } {
diff --git a/ld/testsuite/ld-plugin/pr25355.c b/ld/testsuite/ld-plugin/pr25355.c
new file mode 100644
index 00000000000..99f01fb5c89
--- /dev/null
+++ b/ld/testsuite/ld-plugin/pr25355.c
@@ -0,0 +1,2 @@ 
+int nm_test_var;
+int nm_test_var2 = 1234;
diff --git a/ld/testsuite/ld-plugin/pr25355.d b/ld/testsuite/ld-plugin/pr25355.d
new file mode 100644
index 00000000000..98d10aba1cf
--- /dev/null
+++ b/ld/testsuite/ld-plugin/pr25355.d
@@ -0,0 +1,4 @@ 
+#...
+[0-9a-f]+ B _?nm_test_var
+[0-9a-f]+ D _?nm_test_var2
+#pass
diff --git a/ld/testsuite/lib/ld-lib.exp b/ld/testsuite/lib/ld-lib.exp
index 08e2d6978af..015eda6eb97 100644
--- a/ld/testsuite/lib/ld-lib.exp
+++ b/ld/testsuite/lib/ld-lib.exp
@@ -928,6 +928,7 @@  proc run_cc_link_tests { ldtests } {
 
 	if { $binfile eq "tmpdir/" } {
 	    # compile only
+	    set binfile $objfile
 	} elseif { [regexp ".*\\.a$" $binfile] } {
 	    if { ![ar_simple_create $ar $ldflags $binfile "$objfiles"] } {
 		set failed 1