This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Add plugin interface to LD [3/4] Get symbols and resolutions.


  This one works by using the bfd linker's "notice symbol" callback to track
all symbols that are referenced by non-IR files, so that we can later
determine for prevailing defs from IR files whether or not they are IR-only.
It's pretty simple and the only minor trickiness is turing on notice_all
without inadvertently behaving like we've invoked --trace-sym on the whole world.

- As with the asymbol conversion in the previous patch, I'd appreciate extra
eyeballs looking at the logic here in get_symbols by which I determine
LDPR_xxx resolution types from the info in the linker hash table, just to
double-check my working.

  ld/ChangeLog:

	* ldmain.c (notice)[ENABLE_PLUGINS]: Call plugin_notice.
	* plugin.c (non_ironly_hash): Add new bfd hash table.
	(plugin_load_plugins): Exit early if no plugins to load.  If plugins
	do load successfully, set notice_all flag in link info.
	(add_symbols): Clean up for-loop slightly.
	(get_symbols): Implement.
	(init_non_ironly_hash): Lazily init non_ironly_hash table.
	(plugin_notice): Record symbols referenced from non-IR files in the
	non_ironly_hash.  Suppress tracing, cref generation and nocrossrefs
	tracking for symbols from dummy IR bfds.
	* plugin.h: Fix formatting.
	(plugin_notice): Add prototype.
	* testplug.c (dumpresolutions): New global var.
	(parse_options): Accept "dumpresolutions".
	(onall_symbols_read): Get syms and dump resolutions if it was given.

  ld/testsuite/ChangeLog:

	* ld-plugin/plugin-8.d: New testcase.
	* ld-plugin/plugin.exp: Invoke it.

    cheers,
      DaveK


>From f09bc8ba9165c81d82a8a027129601867b93edd9 Mon Sep 17 00:00:00 2001
From: Dave Korn <dave.korn.cygwin@gmail.com>
Date: Wed, 22 Sep 2010 04:37:28 +0100
Subject: [PATCH] Implement get symbols callback.

  ld/ChangeLog:

	* ldmain.c (notice)[ENABLE_PLUGINS]: Call plugin_notice.
	* plugin.c (IRONLY_SUFFIX_LEN): Correct off-by-one in definition.
	(non_ironly_hash): Add new bfd hash table.
	(plugin_load_plugins): Exit early if no plugins to load.  If plugins
	do load successfully, set notice_all flag in link info.
	(add_symbols): Clean up for-loop slightly.
	(get_symbols): Implement.
	(init_non_ironly_hash): Lazily init non_ironly_hash table.
	(plugin_notice): Record symbols referenced from non-IR files in the
	non_ironly_hash.  Suppress tracing, cref generation and nocrossrefs
	tracking for symbols from dummy IR bfds.
	* plugin.h: Fix formatting.
	(plugin_notice): Add prototype.
	* testplug.c (dumpresolutions): New global var.
	(parse_options): Accept "dumpresolutions".
	(onall_symbols_read): Get syms and dump resolutions if it was given.

  ld/testsuite/ChangeLog:

	* ld-plugin/plugin-8.d: New testcase.
	* ld-plugin/plugin.exp: Invoke it.
---
 ld/ldmain.c                       |   19 ++++-
 ld/plugin.c                       |  149 +++++++++++++++++++++++++++++++++++--
 ld/plugin.h                       |    7 ++-
 ld/testplug.c                     |   36 +++++++++
 ld/testsuite/ld-plugin/plugin-8.d |   34 +++++++++
 ld/testsuite/ld-plugin/plugin.exp |    6 ++
 6 files changed, 238 insertions(+), 13 deletions(-)

diff --git a/ld/ldmain.c b/ld/ldmain.c
index 85236ed..ed1738e 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -1380,7 +1380,10 @@ unattached_reloc (struct bfd_link_info *info ATTRIBUTE_UNUSED,
 
 /* This is called if link_info.notice_all is set, or when a symbol in
    link_info.notice_hash is found.  Symbols are put in notice_hash
-   using the -y option.  */
+   using the -y option, while notice_all is set if the --cref option
+   has been supplied, or if there are any NOCROSSREFS sections in the
+   linker script; and if plugins are active, since they need to monitor
+   all references from non-IR files.  */
 
 static bfd_boolean
 notice (struct bfd_link_info *info,
@@ -1396,9 +1399,17 @@ notice (struct bfd_link_info *info,
       return TRUE;
     }
 
-  if (! info->notice_all
-      || (info->notice_hash != NULL
-	  && bfd_hash_lookup (info->notice_hash, name, FALSE, FALSE) != NULL))
+#ifdef ENABLE_PLUGINS
+  /* We should hide symbols in the dummy IR BFDs from the nocrossrefs list
+     and let the real object files that are generated and added later trip
+     the error instead.  Similarly would be better to trace the real symbol
+     from the real file than the temporary dummy.  */
+  if (!plugin_notice (info, name, abfd, section, value))
+    return TRUE;
+#endif /* ENABLE_PLUGINS */
+
+  if (info->notice_hash != NULL
+	&& bfd_hash_lookup (info->notice_hash, name, FALSE, FALSE) != NULL)
     {
       if (bfd_is_und_section (section))
 	einfo ("%B: reference to %s\n", abfd, name);
diff --git a/ld/plugin.c b/ld/plugin.c
index 6c71978..4e8505d 100644
--- a/ld/plugin.c
+++ b/ld/plugin.c
@@ -55,8 +55,10 @@ PLUGAPIFUNC set_extra_library_path (const char *path);
    when generating a dummy BFD to hold the IR symbols sent from the
    plugin.  */
 #define IRONLY_SUFFIX		".ironly\004"
-/* This is sizeof an array of chars, not sizeof a const char *.  */
-#define IRONLY_SUFFIX_LEN	(sizeof (IRONLY_SUFFIX))
+
+/* This is sizeof an array of chars, not sizeof a const char *.  We
+   also have to avoid inadvertently counting the trailing NUL.  */
+#define IRONLY_SUFFIX_LEN	(sizeof (IRONLY_SUFFIX) - 1)
 
 /* Always use this macro when invoking a plugin function.  */
 #define INVOKE_PLUGIN_FN(plugin, retval, fn, args)	\
@@ -110,6 +112,13 @@ static plugin_t *called_plugin = NULL;
 /* Last plugin to cause an error, if any.  */
 static const char *error_plugin = NULL;
 
+/* A hash table that records symbols referenced by non-IR files.  Used
+   at get_symbols time to determine whether any prevailing defs from 
+   IR files are referenced only from other IR files, so tthat we can
+   we can distinguish the LDPR_PREVAILING_DEF and LDPR_PREVAILING_DEF_IRONLY
+   cases when establishing symbol resolutions.  */
+static struct bfd_hash_table *non_ironly_hash = NULL;
+
 /* Helper to size leading part of tv array and set it up. */
 static size_t
 set_tv_header (struct ld_plugin_tv *tv)
@@ -292,6 +301,10 @@ int plugin_load_plugins (void)
   unsigned int max_args = 0;
   plugin_t *curplug = plugins_list;
 
+  /* If there are no plugins, we need do nothing this run.  */
+  if (!curplug)
+    return 0;
+
   /* First pass over plugins to find max # args needed so that we
      can size and allocate the tv array.  */
   while (curplug)
@@ -319,6 +332,12 @@ int plugin_load_plugins (void)
         return set_plugin_error (curplug->name);
       curplug = curplug->next;
     }
+
+  /* Since plugin(s) inited ok, assume they're going to want symbol
+     resolutions, which needs us to track which symbols are referenced
+     by non-IR files using the linker's notice callback.  */
+  link_info.notice_all = TRUE;
+
   return 0;
 }
 
@@ -489,12 +508,12 @@ add_symbols (void *handle, int nsyms, const struct ld_plugin_symbol *syms)
   int n;
   ASSERT (called_plugin);
   symptrs = xmalloc (nsyms * sizeof *symptrs);
-  for (n = 0; n < nsyms; n++)
+  for (n = 0; n < nsyms; n++, syms++)
     {
       enum ld_plugin_status rv;
       asymbol *bfdsym = bfd_make_empty_symbol (abfd);
       symptrs[n] = bfdsym;
-      rv = asymbol_from_plugin_symbol (abfd, bfdsym, syms++);
+      rv = asymbol_from_plugin_symbol (abfd, bfdsym, syms);
       if (rv != LDPS_OK)
 	return rv;
     }
@@ -526,11 +545,71 @@ release_input_file (const void *handle)
 static enum ld_plugin_status
 get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
 {
+  const bfd *abfd = handle;
+  int n;
   ASSERT (called_plugin);
-  handle = handle;
-  nsyms = nsyms;
-  syms = syms;
-  return LDPS_ERR;
+  for (n = 0; n < nsyms; n++)
+    {
+      struct bfd_link_hash_entry *blhe;
+      blhe = bfd_link_hash_lookup (link_info.hash, syms[n].name,
+				FALSE, FALSE, TRUE);
+      if (!blhe)
+	{
+	  syms[n].resolution = LDPR_UNKNOWN;
+	  continue;
+	}
+      /* Determine resolution from blhe type and symbol's original type.  */
+      if (blhe->type == bfd_link_hash_undefined
+		|| blhe->type == bfd_link_hash_undefweak)
+	{
+	  syms[n].resolution = LDPR_UNDEF;
+	  continue;
+	}
+      if (blhe->type == bfd_link_hash_common)
+	{
+	  /* Is this right?  The COMMON section only exists in the
+	     output file, so it seems reasonable.  */
+	  syms[n].resolution = LDPR_RESOLVED_EXEC;
+	  continue;
+	}
+      if (blhe->type != bfd_link_hash_defined
+		&& blhe->type != bfd_link_hash_defweak)
+	{
+	  /* We should not have a new, indirect or warning symbol here.  */
+	  einfo ("%P%F: %s: plugin symbol table corrupt (sym type %d)",
+		called_plugin->name, blhe->type);
+	}
+      /* Defined or defweak.  If it was originally undefined, then it
+         has been resolved; determine how.  */
+      if (syms[n].def == LDPK_UNDEF || syms[n].def == LDPK_WEAKUNDEF)
+	{
+	  if (is_ir_dummy_bfd (blhe->u.def.section->owner))
+	    syms[n].resolution = LDPR_RESOLVED_IR;
+	  else if (blhe->u.def.section->owner == link_info.output_bfd)
+	    syms[n].resolution = LDPR_RESOLVED_EXEC;
+	  else
+	    syms[n].resolution = blhe->u.def.section->owner->flags
+		& DYNAMIC ? LDPR_RESOLVED_DYN : LDPR_RESOLVED_EXEC;
+	  continue;
+	}
+      /* Was originally def, weakdef, or common.  Does it prevail?  If
+         the owner is the original dummy bfd that supplied it, then this
+	 is the definition that has prevailed.  */
+      if (blhe->u.def.section->owner == abfd)
+	{
+	  bfd_boolean ironly = !bfd_hash_lookup (non_ironly_hash,
+					syms[n].name, FALSE, FALSE);
+	  syms[n].resolution = (ironly)
+				? LDPR_PREVAILING_DEF_IRONLY
+				: LDPR_PREVAILING_DEF;
+	  continue;
+	}
+      /* Was originally def, weakdef, or common, but has been pre-empted.  */
+      syms[n].resolution = is_ir_dummy_bfd (blhe->u.def.section->owner)
+				? LDPR_PREEMPTED_IR
+				: LDPR_PREEMPTED_REG;
+    }
+  return LDPS_OK;
 }
 
 /* Add a new (real) input file generated by a plugin.  */
@@ -593,3 +672,57 @@ message (int level, const char *format, ...)
   return LDPS_OK;
 }
 
+/* Lazily init the non_ironly hash table.  */
+static void
+init_non_ironly_hash (void)
+{
+  if (non_ironly_hash == NULL)
+    {
+      non_ironly_hash =
+          (struct bfd_hash_table *) xmalloc (sizeof (struct bfd_hash_table));
+      if (!bfd_hash_table_init_n (non_ironly_hash,
+				  bfd_hash_newfunc,
+				  sizeof (struct bfd_hash_entry),
+				  61))
+	einfo (_("%P%F: bfd_hash_table_init failed: %E\n"));
+    }
+}
+
+/* To determine which symbols should be resolved LDPR_PREVAILING_DEF
+   and which LDPR_PREVAILING_DEF_IRONLY, we notice all the symbols as
+   the linker adds them to the linker hash table.  If we see a symbol
+   being referenced from a non-IR file, we add it to the non_ironly hash
+   table.  If we can't find it there at get_symbols time, we know that
+   it was referenced only by IR files.  We have to notice_all symbols,
+   because we won't necessarily know until later which ones will be
+   contributed by IR files.  */
+bfd_boolean
+plugin_notice (struct bfd_link_info *info, const char *name, bfd *abfd,
+		asection *section, bfd_vma value)
+{
+  bfd_boolean is_ref = bfd_is_und_section (section);
+  bfd_boolean is_dummy = is_ir_dummy_bfd (abfd);
+  init_non_ironly_hash ();
+  /* We only care about refs, not defs, indicated by section pointing
+     to the undefined section (according to the bfd linker notice callback
+     interface definition).  */
+  if (is_ref && !is_dummy)
+    {
+      /* This is a ref from a non-IR file, so note the ref'd symbol
+         in the non-IR-only hash.  */
+      if (!bfd_hash_lookup (non_ironly_hash, name, TRUE, TRUE))
+        einfo (_("%P%X: %s: hash table failure adding symbol %s"),
+		abfd->filename, name);
+    }
+  else if (!is_ref && is_dummy)
+    {
+      /* No further processing since this is a def from an IR dummy BFD.  */
+      return FALSE;
+    }
+  
+  /* Suppresses "unused" warnings without relying on GCC attribute.  */
+  info = info;
+  value = value;
+  /* Continue with cref/nocrossref/trace-sym processing.  */
+  return TRUE;
+}
diff --git a/ld/plugin.h b/ld/plugin.h
index 8839b8b..5bbe93d 100644
--- a/ld/plugin.h
+++ b/ld/plugin.h
@@ -40,7 +40,8 @@ extern int plugin_load_plugins (void);
 extern const char *plugin_error_plugin (void);
 
 /* Call 'claim file' hook for all plugins.  */
-extern int plugin_call_claim_file (const struct ld_plugin_input_file *file, int *claimed);
+extern int plugin_call_claim_file (const struct ld_plugin_input_file *file,
+		int *claimed);
 
 /* Call 'all symbols read' hook for all plugins.  */
 extern int plugin_call_all_symbols_read (void);
@@ -58,6 +59,10 @@ extern bfd *plugin_get_ir_dummy_bfd (const char *name, bfd *template);
 /* Check if the BFD passed in is an IR dummy object file.  */
 extern bfd_boolean is_ir_dummy_bfd (const bfd *abfd);
 
+/* Notice-symbol bfd linker callback hook.  */
+extern bfd_boolean plugin_notice (struct bfd_link_info *info,
+		const char *name, bfd *abfd, asection *section,
+		bfd_vma value);
 
 #endif /* !def GLD_PLUGIN_H */
 
diff --git a/ld/testplug.c b/ld/testplug.c
index 9746465..ad1bdf6 100644
--- a/ld/testplug.c
+++ b/ld/testplug.c
@@ -106,6 +106,7 @@ static enum ld_plugin_status cleanup_ret = LDPS_OK;
 static bfd_boolean register_claimfile_hook = FALSE;
 static bfd_boolean register_allsymbolsread_hook = FALSE;
 static bfd_boolean register_cleanup_hook = FALSE;
+static bfd_boolean dumpresolutions = FALSE;
 
 /* The master list of all claimable/claimed files.  */
 static claim_file_t *claimfiles_list = NULL;
@@ -276,6 +277,8 @@ parse_option (const char *opt)
     return record_claim_file (opt + 6);
   else if (!strncmp ("sym:", opt, 4))
     return record_claimed_file_symbol (opt + 4);
+  else if (!strcmp ("dumpresolutions", opt))
+    dumpresolutions = TRUE;
   else
     return LDPS_ERR;
   return LDPS_OK;
@@ -498,7 +501,40 @@ onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
 static enum ld_plugin_status
 onall_symbols_read (void)
 {
+  static const char *resolutions[] =
+    {
+      "LDPR_UNKNOWN",
+      "LDPR_UNDEF",
+      "LDPR_PREVAILING_DEF",
+      "LDPR_PREVAILING_DEF_IRONLY",
+      "LDPR_PREEMPTED_REG",
+      "LDPR_PREEMPTED_IR",
+      "LDPR_RESOLVED_IR",
+      "LDPR_RESOLVED_EXEC",
+      "LDPR_RESOLVED_DYN",
+    };
+  claim_file_t *claimfile = dumpresolutions ? claimfiles_list : NULL;
   TV_MESSAGE (LDPL_INFO, "hook called: all symbols read.\n");
+  for ( ; claimfile; claimfile = claimfile->next)
+    {
+      enum ld_plugin_status rv;
+      int n;
+      if (claimfile->n_syms_used && !tv_get_symbols)
+	return LDPS_ERR;
+      else if (!claimfile->n_syms_used)
+        continue;
+      rv = tv_get_symbols (claimfile->file.handle, claimfile->n_syms_used,
+				claimfile->symbols);
+      if (rv != LDPS_OK)
+	return rv;
+      for (n = 0; n < claimfile->n_syms_used; n++)
+	TV_MESSAGE (LDPL_INFO, "Sym: '%s%s%s' Resolution: %s\n",
+		claimfile->symbols[n].name,
+		claimfile->symbols[n].version ? "@" : "",
+		claimfile->symbols[n].version ? claimfile->symbols[n].version
+					      : "",
+		resolutions[claimfile->symbols[n].resolution]);
+    }
   fflush (NULL);
   return all_symbols_read_ret;
 }
diff --git a/ld/testsuite/ld-plugin/plugin-8.d b/ld/testsuite/ld-plugin/plugin-8.d
new file mode 100644
index 0000000..e72b039
--- /dev/null
+++ b/ld/testsuite/ld-plugin/plugin-8.d
@@ -0,0 +1,34 @@
+Hello from testplugin.
+tv\[0\]: LDPT_MESSAGE func@0x.*
+tv\[1\]: LDPT_API_VERSION value        0x1 \(1\)
+tv\[2\]: LDPT_GNU_LD_VERSION value       0x.*
+tv\[3\]: LDPT_LINKER_OUTPUT value        0x1 \(1\)
+tv\[4\]: LDPT_OUTPUT_NAME 'tmpdir/main.x'
+tv\[5\]: LDPT_REGISTER_CLAIM_FILE_HOOK func@0x.*
+tv\[6\]: LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK func@0x.*
+tv\[7\]: LDPT_REGISTER_CLEANUP_HOOK func@0x.*
+tv\[8\]: LDPT_ADD_SYMBOLS func@0x.*
+tv\[9\]: LDPT_GET_INPUT_FILE func@0x.*
+tv\[10\]: LDPT_RELEASE_INPUT_FILE func@0x.*
+tv\[11\]: LDPT_GET_SYMBOLS func@0x.*
+tv\[12\]: LDPT_ADD_INPUT_FILE func@0x.*
+tv\[13\]: LDPT_ADD_INPUT_LIBRARY func@0x.*
+tv\[14\]: LDPT_SET_EXTRA_LIBRARY_PATH func@0x.*
+tv\[15\]: LDPT_OPTION 'registerclaimfile'
+tv\[16\]: LDPT_OPTION 'registerallsymbolsread'
+tv\[17\]: LDPT_OPTION 'registercleanup'
+tv\[18\]: LDPT_OPTION 'claim:tmpdir/func.o'
+tv\[19\]: LDPT_OPTION 'sym:_?func::0:0:0'
+tv\[20\]: LDPT_OPTION 'sym:_?func2::0:0:0'
+tv\[21\]: LDPT_OPTION 'dumpresolutions'
+tv\[22\]: LDPT_NULL value        0x0 \(0\)
+#...
+hook called: claim_file tmpdir/main.o \[@0/.* not claimed
+hook called: claim_file tmpdir/func.o \[@0/.* CLAIMED
+hook called: claim_file tmpdir/text.o \[@0/.* not claimed
+#...
+hook called: all symbols read.
+Sym: '_?func' Resolution: LDPR_PREVAILING_DEF
+Sym: '_?func2' Resolution: LDPR_PREVAILING_DEF_IRONLY
+hook called: cleanup.
+#...
diff --git a/ld/testsuite/ld-plugin/plugin.exp b/ld/testsuite/ld-plugin/plugin.exp
index b983ce4..6ed2787 100644
--- a/ld/testsuite/ld-plugin/plugin.exp
+++ b/ld/testsuite/ld-plugin/plugin.exp
@@ -93,6 +93,12 @@ set plugin_tests [list \
 			$regas $regcln -plugin-arg claim:tmpdir/func.o \
 			-plugin-arg sym:${_}func::0:0:0 \
     $testobjfiles $libs" "" "" {{ld plugin-7.d}} "main.x" ] \
+    [list "plugin claimfile resolve symbol" "-plugin $plugin_path $regclm \
+			$regas $regcln -plugin-arg claim:tmpdir/func.o \
+			-plugin-arg sym:${_}func::0:0:0 \
+			-plugin-arg sym:${_}func2::0:0:0 \
+			-plugin-arg dumpresolutions \
+    $testobjfiles $libs" "" "" {{ld plugin-8.d}} "main.x" ] \
 ]
 
 if { $failed_compile != 0 } {

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]