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 [2/4] Claim files and add symbols.



  This patch applies on top of the previous one to add the mentioned
functionality.

-  When a plugin claims an input file, we create an empty BFD from scratch and
use it to store the symbols returned by the plugin, so that they can then take
part in the link and be resolved by the linker.  Later on, we'll be able to
swap the resolved symbols in the linker hash table around so that they point
at the real versions of these symbols that are in the object files that the
plugin generates and adds back to the link.

- I added a single-bit flag to the input statement struct to track whether a
file has been claimed or not.  I didn't actually end up using it yet, so I
could remove it if desired, but it's only a single bit and doesn't change the
total size of the struct any.

- There's a minor bug in the definition of IRONLY_SUFFIX_LEN causing
is_ir_dummy_bfd to always return FALSE, but this function isn't used until the
next patch anyway.  (During an earlier stage of development I did use it
during this patch, until I reimplemented things slightly differently, but
since I knew I'd need it for the next patch anyway, I didn't take it out; at
which time it was corrected.)

- I'd certainly appreciate a second pair of eyes looking at the logic in
asymbol_from_plugin_symbol by which I derive the fags etc. for a bfd asymbol
from the ld plugin symbol struct.

  ld/ChangeLog:

	* Makefile.am (libldtestplug_la_LDFLAGS): Link against libiberty.
	* Makefile.in: Regenerate.
	* ldfile.c (ldfile_try_open_bfd)[ENABLE_PLUGINS]: Don't return early
	during compat checks if they pass, instead offer any successfully
	opened and accepted file to the plugin claim file hooks chain.  Create
	a dummy bfd to accept symbols added by the plugin, if the plugin
	claims the file.
	* ldlang.c (lang_process)[ENABLE_PLUGINS]: Call plugin all symbols
	read hook chain before ldemul_after_open.
	* ldlang.h (struct lang_input_statement_struct): Add new single-bit
	'claimed' flag.
	* plugin.c: Fix order of includes.
	(plugin_get_ir_dummy_bfd): New function to create the dummy bfd used
	to store symbols for ir-only files.
	(is_ir_dummy_bfd): New function to check if a bfd is ir-only.
	(asymbol_from_plugin_symbol): New function converts symbol formats.
	(add_symbols): Call it to convert plugin syms to bfd syms and add
	them to the dummy bfd.
	* plugin.h: Add missing include guards.
	(plugin_get_ir_dummy_bfd): Add prototype.
	(is_ir_dummy_bfd): Likewise.
	* testplug.c: Fix order of includes.
	(TV_MESSAGE): New helper macro.
	(struct claim_file): New struct.
	(claim_file_t): New typedef.
	(tag_names[]): Make static.
	(claimfiles_list): New variable.
	(claimfiles_tail_chain_ptr): Likewise.
	(last_claimfile): Likewise.
	(record_claim_file): Record a file to claim on a singly-linked list.
	(parse_symdefstr): Parse an ASCII representation of a symbol from a
	plugin option into the fields of a struct ld_plugin_symbol.
	(record_claimed_file_symbol):  Use it to parse plugin option for
	adding a symbol.
	(parse_option): Parse claim file and add symbol options.
	(dump_tv_tag): Use TV_MESSAGE.
	(onload): Likewise.
	(onclaim_file): Make static.  Use TV_MESSAGE.  Scan list of files to
	claim and claim this file if required, adding any symbols specified.
	(onall_symbols_read): Make static and use TV_MESSAGE.
	(oncleanup): Likewise.

  ld/testsuite/ChangeLog:

	* ld-plugin/plugin-3.d: Enable regexes for new functionality.
	* ld-plugin/plugin-5.d: Likewise.
	* ld-plugin/plugin-6.d: New testcase.
	* ld-plugin/plugin-7.d: Likewise.
	* ld-plugin/plugin.exp: Use 'nm' on compiled test objects to determine
	whether symbols in plugin arguments need an underscore prefix.  Add
	new plugin-6.d and plugin-7.d testcases.


    cheers,
      DaveK

>From 9a66dc5a47e91db76b656ea72ecebcab9504269c Mon Sep 17 00:00:00 2001
From: Dave Korn <dave.korn.cygwin@gmail.com>
Date: Mon, 20 Sep 2010 00:18:20 +0100
Subject: [PATCH] Implement claim file and all symbols read hooks and add symbols callback.

  ld/ChangeLog:

	* Makefile.am (libldtestplug_la_LDFLAGS): Link against libiberty.
	* Makefile.in: Regenerate.
	* ldfile.c (ldfile_try_open_bfd)[ENABLE_PLUGINS]: Don't return early
	during compat checks if they pass, instead offer any successfully
	opened and accepted file to the plugin claim file hooks chain.  Create
	a dummy bfd to accept symbols added by the plugin, if the plugin claims
	the file.
	* ldlang.c (lang_process)[ENABLE_PLUGINS]: Call plugin all symbols read
	hook chain before ldemul_after_open.
	* ldlang.h (struct lang_input_statement_struct): Add new single-bit
	'claimed' flag.
	* plugin.c: Fix order of includes.
	(plugin_get_ir_dummy_bfd): New function to create the dummy bfd used
	to store symbols for ir-only files.
	(is_ir_dummy_bfd): New function to check if a bfd is ir-only.
	(asymbol_from_plugin_symbol): New function converts symbol formats.
	(add_symbols): Call it to convert plugin syms to bfd syms and add
	them to the dummy bfd.
	* plugin.h: Add missing include guards.
	(plugin_get_ir_dummy_bfd): Add prototype.
	(is_ir_dummy_bfd): Likewise.
	* testplug.c: Fix order of includes.
	(TV_MESSAGE): New helper macro.
	(struct claim_file): New struct.
	(claim_file_t): New typedef.
	(tag_names[]): Make static.
	(claimfiles_list): New variable.
	(claimfiles_tail_chain_ptr): Likewise.
	(last_claimfile): Likewise.
	(record_claim_file): Record a file to claim on a singly-linked list.
	(parse_symdefstr): Parse an ASCII representation of a symbol from a
	plugin option into the fields of a struct ld_plugin_symbol.
	(record_claimed_file_symbol):  Use it to parse plugin option for
	adding a symbol.
	(parse_option): Parse claim file and add symbol options.
	(dump_tv_tag): Use TV_MESSAGE.
	(onload): Likewise.
	(onclaim_file): Make static.  Use TV_MESSAGE.  Scan list of files to
	claim and claim this file if required, adding any symbols specified.
	(onall_symbols_read): Make static and use TV_MESSAGE.
	(oncleanup): Likewise.

  ld/testsuite/ChangeLog:

	* ld-plugin/plugin-3.d: Enable regexes for new functionality.
	* ld-plugin/plugin-5.d: Likewise.
	* ld-plugin/plugin-6.d: New testcase.
	* ld-plugin/plugin-7.d: Likewise.
	* ld-plugin/plugin.exp: Use 'nm' on compiled test objects to determine
	whether symbols in plugin arguments need an underscore prefix.  Add
	new plugin-6.d and plugin-7.d testcases.
---
 ld/Makefile.am                    |    2 +-
 ld/Makefile.in                    |    2 +-
 ld/ldfile.c                       |   72 +++++++++++-
 ld/ldlang.c                       |   12 ++
 ld/ldlang.h                       |    4 +
 ld/plugin.c                       |  100 +++++++++++++++-
 ld/plugin.h                       |   17 +++
 ld/testplug.c                     |  240 +++++++++++++++++++++++++++++++------
 ld/testsuite/ld-plugin/plugin-3.d |    5 +-
 ld/testsuite/ld-plugin/plugin-5.d |   14 +--
 ld/testsuite/ld-plugin/plugin-6.d |   31 +++++
 ld/testsuite/ld-plugin/plugin-7.d |   30 +++++
 ld/testsuite/ld-plugin/plugin.exp |   39 +++++-
 13 files changed, 505 insertions(+), 63 deletions(-)

diff --git a/ld/Makefile.am b/ld/Makefile.am
index 5b0c74c..07ee3a8 100644
--- a/ld/Makefile.am
+++ b/ld/Makefile.am
@@ -1989,7 +1989,7 @@ if ENABLE_PLUGINS
 noinst_LTLIBRARIES = libldtestplug.la
 libldtestplug_la_SOURCES = testplug.c
 libldtestplug_la_CFLAGS= -g -O2
-libldtestplug_la_LDFLAGS = -no-undefined -rpath /nowhere
+libldtestplug_la_LDFLAGS = -no-undefined -rpath /nowhere -Wl,$(LIBIBERTY)
 endif
 
 # DOCUMENTATION TARGETS
diff --git a/ld/ldfile.c b/ld/ldfile.c
index 4661897..d010fcb 100644
--- a/ld/ldfile.c
+++ b/ld/ldfile.c
@@ -34,6 +34,10 @@
 #include "ldemul.h"
 #include "libiberty.h"
 #include "filenames.h"
+#ifdef ENABLE_PLUGINS
+#include "plugin-api.h"
+#include "plugin.h"
+#endif /* ENABLE_PLUGINS */
 
 const char * ldfile_input_filename;
 bfd_boolean  ldfile_assumed_script = FALSE;
@@ -150,7 +154,12 @@ ldfile_try_open_bfd (const char *attempt,
      compatible with the output file.  If it isn't, keep searching.
      If we can't open the file as an object file, stop the search
      here.  If we are statically linking, ensure that we don't link
-     a dynamic object.  */
+     a dynamic object.
+
+     In the code below, it's OK to exit early if the check fails,
+     closing the checked BFD and returning FALSE, but if the BFD
+     checks out compatible, do not exit early returning TRUE, or
+     the plugins will not get a chance to claim the file.  */
 
   if (entry->search_dirs_flag || !entry->dynamic)
     {
@@ -163,6 +172,7 @@ ldfile_try_open_bfd (const char *attempt,
 
       if (check != NULL)
 	{
+	  bfd_boolean checked_ok = FALSE;
 	  if (! bfd_check_format (check, bfd_object))
 	    {
 	      if (check == entry->the_bfd
@@ -259,10 +269,11 @@ ldfile_try_open_bfd (const char *attempt,
 		      return FALSE;
 		    }
 		}
-	      return TRUE;
+	      checked_ok = TRUE;
 	    }
 
-	  if (!entry->dynamic && (entry->the_bfd->flags & DYNAMIC) != 0)
+	  if (!checked_ok && !entry->dynamic
+		&& (entry->the_bfd->flags & DYNAMIC) != 0)
 	    {
 	      einfo (_("%F%P: attempted static link of dynamic object `%s'\n"),
 		     attempt);
@@ -271,7 +282,7 @@ ldfile_try_open_bfd (const char *attempt,
 	      return FALSE;
 	    }
 
-	  if (entry->search_dirs_flag
+	  if (!checked_ok && entry->search_dirs_flag
 	      && !bfd_arch_get_compatible (check, link_info.output_bfd,
 					   command_line.accept_unknown_input_arch)
 	      /* XCOFF archives can have 32 and 64 bit objects.  */
@@ -290,6 +301,59 @@ ldfile_try_open_bfd (const char *attempt,
 	}
     }
 
+#ifdef ENABLE_PLUGINS
+  /* If plugins are active, they get first chance to claim
+     any successfully-opened input file.  We skip archives
+     here; the plugin wants us to offer it the individual
+     members when we enumerate them, not the whole file.  We
+     also ignore corefiles, because that's just weird.  It is
+     a needed side-effect of calling  bfd_check_format with
+     bfd_object that it sets the bfd's arch and mach, which 
+     will be needed when and if we want to bfd_create a new
+     one using this one as a template.  */
+  int fildes = bfd_check_format (entry->the_bfd, bfd_object)
+		? open (attempt, O_RDONLY
+# ifdef O_BINARY
+				| O_BINARY
+# endif /* O_BINARY */
+			)
+		: -1;
+  if (fildes >= 0)
+    {
+      struct ld_plugin_input_file file;
+      
+      int claimed = 0;
+      file.name = attempt;
+      file.offset = 0;
+      file.filesize = lseek (fildes, 0, SEEK_END);
+      file.fd = fildes;
+      /* We create a dummy BFD, initially empty, to house
+	 whatever symbols the plugin may want to add.  */
+      file.handle = plugin_get_ir_dummy_bfd (attempt, entry->the_bfd);
+      if (plugin_call_claim_file (&file, &claimed))
+	einfo (_("%P%F: %s: plugin reported error claiming file\n"),
+		plugin_error_plugin ());
+      if (claimed)
+	{
+	  /* Discard the real file's BFD and substitute the dummy one.  */
+	  bfd_close (entry->the_bfd);
+	  entry->the_bfd = file.handle;
+	  entry->claimed = TRUE;
+	  bfd_make_readable (entry->the_bfd);
+	}
+      else
+	{
+	  /* If plugin didn't claim the file, we don't need the fd or the
+	     dummy bfd.  Can't avoid speculatively creating it, alas.  */
+	  bfd_close_all_done (file.handle);
+	  close (fildes);
+	  entry->claimed = FALSE;
+	}
+    }
+#endif /* ENABLE_PLUGINS */
+
+  /* It opened OK, the format checked out, and the plugins have had
+     their chance to claim it, so this is success.  */
   return TRUE;
 }
 
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 7f44445..cb1417c 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -40,6 +40,9 @@
 #include "fnmatch.h"
 #include "demangle.h"
 #include "hashtab.h"
+#ifdef ENABLE_PLUGINS
+#include "plugin.h"
+#endif /* ENABLE_PLUGINS */
 
 #ifndef offsetof
 #define offsetof(TYPE, MEMBER) ((size_t) & (((TYPE*) 0)->MEMBER))
@@ -6340,6 +6343,15 @@ lang_process (void)
   if (entry_symbol.name == NULL)
     link_info.gc_sym_list = ldlang_undef_chain_list_head;
 
+#ifdef ENABLE_PLUGINS
+  /* Now all files are read, let the plugin(s) decide if there
+     are any more to be added to the link before we call the
+     emulation's after_open hook.  */
+  if (plugin_call_all_symbols_read ())
+    einfo (_("%P%F: %s: plugin reported error after all symbols read\n"),
+      plugin_error_plugin ());
+#endif /* ENABLE_PLUGINS */
+
   ldemul_after_open ();
 
   bfd_section_already_linked_table_free ();
diff --git a/ld/ldlang.h b/ld/ldlang.h
index e9bcacf..f89eb53 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -286,6 +286,10 @@ typedef struct lang_input_statement_struct
 
   /* Set if the file does not exist.  */
   unsigned int missing_file : 1;
+
+  /* Set if the file was claimed by a plugin.  */
+  unsigned int claimed : 1;
+
 } lang_input_statement_type;
 
 typedef struct
diff --git a/ld/plugin.c b/ld/plugin.c
index e9f3b81..6c71978 100644
--- a/ld/plugin.c
+++ b/ld/plugin.c
@@ -18,8 +18,6 @@
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
    MA 02110-1301, USA.  */
 
-#include <dlfcn.h>
-
 #include "sysdep.h"
 #include "libiberty.h"
 #include "bfd.h"
@@ -30,6 +28,9 @@
 #include "ldmisc.h"
 #include "plugin.h"
 #include "plugin-api.h"
+/* Don't include std headers until after config.h, sysdeps.h etc.,
+   or you may end up with the wrong bitsize of off_t.  */
+#include <dlfcn.h>
 
 /* Alias to shorten static function prototype lines.  */
 #define PLUGAPIFUNC static enum ld_plugin_status
@@ -50,6 +51,13 @@ PLUGAPIFUNC add_input_file (const char *pathname);
 PLUGAPIFUNC add_input_library (const char *pathname);
 PLUGAPIFUNC set_extra_library_path (const char *path);
 
+/* The suffix to append to the name of the real (claimed) object file
+   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))
+
 /* Always use this macro when invoking a plugin function.  */
 #define INVOKE_PLUGIN_FN(plugin, retval, fn, args)	\
 	called_plugin = plugin;				\
@@ -376,6 +384,75 @@ plugin_call_cleanup (void)
   return plugin_error_p () ? -1 : 0;
 }
 
+/* Create a dummy BFD.  */
+bfd *
+plugin_get_ir_dummy_bfd (const char *name, bfd *srctemplate)
+{
+  asection *sec;
+  bfd *abfd = bfd_create (
+		concat (name, IRONLY_SUFFIX, (const char *)NULL),
+		srctemplate);
+  bfd_set_arch_info (abfd, bfd_get_arch_info (srctemplate));
+  bfd_make_writable (abfd);
+  /* Create a minimal set of sections to own the symbols.  */
+  sec = bfd_make_section_old_way (abfd, ".text");
+  bfd_set_section_flags (abfd, sec,
+	SEC_CODE | SEC_HAS_CONTENTS | SEC_READONLY 
+	| SEC_ALLOC | SEC_LOAD | SEC_KEEP);
+  sec->output_section = sec;
+  sec->output_offset = 0;
+  return abfd;
+}
+
+/* Check if the BFD is an IR dummy.  */
+bfd_boolean
+is_ir_dummy_bfd (const bfd *abfd)
+{
+  size_t namlen = strlen (abfd->filename);
+  if (namlen < IRONLY_SUFFIX_LEN)
+    return FALSE;
+  return !strcmp (abfd->filename + namlen - IRONLY_SUFFIX_LEN, IRONLY_SUFFIX);
+}
+
+/* Helpers to convert between BFD and GOLD symbol formats.  */
+static enum ld_plugin_status
+asymbol_from_plugin_symbol (bfd *abfd, asymbol *asym,
+				const struct ld_plugin_symbol *ldsym)
+{
+  asym->the_bfd = abfd;
+  asym->name = ldsym->version
+		? concat (ldsym->name, "@", ldsym->version, NULL)
+		: ldsym->name;
+  asym->value = 0;
+  switch (ldsym->def)
+    {
+      case LDPK_DEF:
+	asym->flags = BSF_GLOBAL;
+	break;
+      case LDPK_WEAKDEF:
+	asym->flags = BSF_GLOBAL | BSF_WEAK;
+	break;
+      case LDPK_UNDEF:
+	asym->flags = BSF_NO_FLAGS;
+	break;
+      case LDPK_WEAKUNDEF:
+	asym->flags = BSF_NO_FLAGS | BSF_WEAK;
+	break;
+      case LDPK_COMMON:
+	asym->flags = BSF_GLOBAL;
+	asym->value = ldsym->size;
+	break;
+      default:
+	return LDPS_ERR;
+    }
+  asym->section = ldsym->def == LDPK_COMMON
+	? bfd_com_section_ptr
+	: (ldsym->def == LDPK_UNDEF || ldsym->def == LDPK_WEAKUNDEF
+		? bfd_und_section_ptr
+		: bfd_get_section_by_name (abfd, ".text"));
+  return LDPS_OK;
+}
+
 /* Register a claim-file handler.  */
 static enum ld_plugin_status
 register_claim_file (ld_plugin_claim_file_handler handler)
@@ -407,11 +484,22 @@ register_cleanup (ld_plugin_cleanup_handler handler)
 static enum ld_plugin_status
 add_symbols (void *handle, int nsyms, const struct ld_plugin_symbol *syms)
 {
+  asymbol **symptrs;
+  bfd *abfd = handle;
+  int n;
   ASSERT (called_plugin);
-  handle = handle;
-  nsyms = nsyms;
-  syms = syms;
-  return LDPS_ERR;
+  symptrs = xmalloc (nsyms * sizeof *symptrs);
+  for (n = 0; n < nsyms; n++)
+    {
+      enum ld_plugin_status rv;
+      asymbol *bfdsym = bfd_make_empty_symbol (abfd);
+      symptrs[n] = bfdsym;
+      rv = asymbol_from_plugin_symbol (abfd, bfdsym, syms++);
+      if (rv != LDPS_OK)
+	return rv;
+    }
+  bfd_set_symtab (abfd, symptrs, nsyms);
+  return LDPS_OK;
 }
 
 /* Get the input file information with an open (possibly re-opened)
diff --git a/ld/plugin.h b/ld/plugin.h
index fe3d148..8839b8b 100644
--- a/ld/plugin.h
+++ b/ld/plugin.h
@@ -18,6 +18,10 @@
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
    MA 02110-1301, USA.  */
 
+#ifndef GLD_PLUGIN_H
+#define GLD_PLUGIN_H
+
+
 /* This is the only forward declaration we need to avoid having
    to include the plugin-api.h header in order to use this file.  */
 struct ld_plugin_input_file;
@@ -44,3 +48,16 @@ extern int plugin_call_all_symbols_read (void);
 /* Call 'cleanup' hook for all plugins.  */
 extern int plugin_call_cleanup (void);
 
+/* Generate a dummy BFD to represent an IR file, for any callers of
+   plugin_call_claim_file to use as the handle in the ld_plugin_input_file
+   struct that they build to pass in.  The BFD is initially writable, so
+   that symbols can be added to it; it must be made readable after the
+   add_symbols hook has been called so that it can be read when linking.  */
+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);
+
+
+#endif /* !def GLD_PLUGIN_H */
+
diff --git a/ld/testplug.c b/ld/testplug.c
index 46d7c0f..9746465 100644
--- a/ld/testplug.c
+++ b/ld/testplug.c
@@ -18,27 +18,48 @@
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
    MA 02110-1301, USA.  */
 
-#include <stdio.h>
-#include <string.h>
 #include "config.h"
 #include "bfd.h"
 #include "libiberty.h"
 #include "plugin-api.h"
+/* Don't include std headers until after config.h, sysdeps.h etc.,
+   or you may end up with the wrong bitsize of off_t.  */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
 
 extern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
-enum ld_plugin_status onclaim_file (const struct ld_plugin_input_file *file, int *claimed);
-enum ld_plugin_status onall_symbols_read (void);
-enum ld_plugin_status oncleanup (void);
+static enum ld_plugin_status onclaim_file (
+		const struct ld_plugin_input_file *file, int *claimed);
+static enum ld_plugin_status onall_symbols_read (void);
+static enum ld_plugin_status oncleanup (void);
+
+/* Helper for calling plugin api message function.  */
+#define TV_MESSAGE if (tv_message) (*tv_message)
 
+/* Struct for recording files to claim / files claimed.  */
+typedef struct claim_file
+{
+  struct claim_file *next;
+  struct ld_plugin_input_file file;
+  bfd_boolean claimed;
+  struct ld_plugin_symbol *symbols;
+  int n_syms_allocated;
+  int n_syms_used;
+} claim_file_t;
+
+/* Helper macro for defining array of transfer vector tags and names.  */
 #define ADDENTRY(tag) { tag, #tag }
 
+/* Struct for looking up human-readable versions of tag names.  */
 typedef struct tag_name
 {
   enum ld_plugin_tag tag;
   const char *name;
 } tag_name_t;
 
-tag_name_t tag_names[] =
+/* Array of all known tags and their names.  */
+static const tag_name_t tag_names[] =
 {
   ADDENTRY(LDPT_NULL),
   ADDENTRY(LDPT_API_VERSION),
@@ -86,6 +107,128 @@ static bfd_boolean register_claimfile_hook = FALSE;
 static bfd_boolean register_allsymbolsread_hook = FALSE;
 static bfd_boolean register_cleanup_hook = FALSE;
 
+/* The master list of all claimable/claimed files.  */
+static claim_file_t *claimfiles_list = NULL;
+
+/* We keep a tail pointer for easy linking on the end.  */
+static claim_file_t **claimfiles_tail_chain_ptr = &claimfiles_list;
+
+/* The last claimed file added to the list, for receiving syms.  */
+static claim_file_t *last_claimfile = NULL;
+
+/* Add a new claimfile on the end of the chain.  */
+static enum ld_plugin_status
+record_claim_file (const char *file)
+{
+  claim_file_t *newfile;
+
+  newfile = xmalloc (sizeof *newfile);
+  memset (newfile, 0, sizeof *newfile);
+  /* Only setup for now is remembering the name to look for.  */
+  newfile->file.name = file;
+  /* Chain it on the end of the list.  */
+  *claimfiles_tail_chain_ptr = newfile;
+  claimfiles_tail_chain_ptr = &newfile->next;
+  /* Record it as active for receiving symbols to register.  */
+  last_claimfile = newfile;
+  return LDPS_OK;
+}
+
+/* Parse a command-line argument string into a symbol definition.
+   Symbol-strings follow the colon-separated format:
+	NAME:VERSION:def:vis:size:COMDATKEY
+   where the fields in capitals are strings and those in lower
+   case are integers.  We don't allow to specify a resolution as
+   doing so is not meaningful when calling the add symbols hook.  */
+static enum ld_plugin_status
+parse_symdefstr (const char *str, struct ld_plugin_symbol *sym)
+{
+  int n;
+  long long size;
+  const char *colon1, *colon2, *colon5;
+
+  /* Locate the colons separating the first two strings.  */
+  colon1 = strchr (str, ':');
+  if (!colon1)
+    return LDPS_ERR;
+  colon2 = strchr (colon1+1, ':');
+  if (!colon2)
+    return LDPS_ERR;
+  /* Name must not be empty (version may be).  */
+  if (colon1 == str)
+    return LDPS_ERR;
+
+  /* The fifth colon and trailing comdat key string are optional,
+     but the intermediate ones must all be present.  */
+  colon5 = strchr (colon2+1, ':');	/* Actually only third so far.  */
+  if (!colon5)
+    return LDPS_ERR;
+  colon5 = strchr (colon5+1, ':');	/* Hopefully fourth now.  */
+  if (!colon5)
+    return LDPS_ERR;
+  colon5 = strchr (colon5+1, ':');	/* Optional fifth now.  */
+
+  /* Finally we'll use sscanf to parse the numeric fields, then
+     we'll split out the strings which we need to allocate separate
+     storage for anyway so that we can add nul termination.  */
+  n = sscanf (colon2 + 1, "%i:%i:%lli", &sym->def, &sym->visibility, &size);
+  if (n != 3)
+    return LDPS_ERR;
+
+  /* Parsed successfully, so allocate strings and fill out fields.  */
+  sym->size = size;
+  sym->resolution = LDPR_UNKNOWN;
+  sym->name = malloc (colon1 - str + 1);
+  memcpy (sym->name, str, colon1 - str);
+  sym->name[colon1 - str] = '\0';
+  if (colon2 > (colon1 + 1))
+    {
+      sym->version = malloc (colon2 - colon1);
+      memcpy (sym->version, colon1 + 1, colon2 - (colon1 + 1));
+      sym->version[colon2 - (colon1 + 1)] = '\0';
+    }
+  else
+    sym->version = NULL;
+  if (colon5 && colon5[1])
+    {
+      sym->comdat_key = malloc (strlen (colon5 + 1) + 1);
+      strcpy (sym->comdat_key, colon5 + 1);
+    }
+  else
+    sym->comdat_key = 0;
+  return LDPS_OK;
+}
+
+/* Record a symbol to be added for the last-added claimfile.  */
+static enum ld_plugin_status
+record_claimed_file_symbol (const char *symdefstr)
+{
+  struct ld_plugin_symbol sym;
+
+  /* Can't add symbols except as belonging to claimed files.  */
+  if (!last_claimfile)
+    return LDPS_ERR;
+
+  /* If string doesn't parse correctly, give an error.  */
+  if (parse_symdefstr (symdefstr, &sym) != LDPS_OK)
+    return LDPS_ERR;
+
+  /* Check for enough space, resize array if needed, and add it.  */
+  if (last_claimfile->n_syms_allocated == last_claimfile->n_syms_used)
+    {
+      int new_n_syms = last_claimfile->n_syms_allocated
+			? 2 * last_claimfile->n_syms_allocated
+			: 10;
+      last_claimfile->symbols = xrealloc (last_claimfile->symbols,
+			new_n_syms * sizeof *last_claimfile->symbols);
+      last_claimfile->n_syms_allocated = new_n_syms;
+    }
+  last_claimfile->symbols[last_claimfile->n_syms_used++] = sym;
+
+  return LDPS_OK;
+}
+
+/* Records the status to return from one of the registered hooks.  */
 static enum ld_plugin_status
 set_ret_val (const char *whichval, enum ld_plugin_status retval)
 {
@@ -102,6 +245,7 @@ set_ret_val (const char *whichval, enum ld_plugin_status retval)
   return LDPS_OK;
 }
 
+/* Records hooks which should be registered.  */
 static enum ld_plugin_status
 set_register_hook (const char *whichhook, bfd_boolean yesno)
 {
@@ -116,6 +260,7 @@ set_register_hook (const char *whichhook, bfd_boolean yesno)
   return LDPS_OK;
 }
 
+/* Determine type of plugin option and pass to individual parsers.  */
 static enum ld_plugin_status
 parse_option (const char *opt)
 {
@@ -127,11 +272,16 @@ parse_option (const char *opt)
     return set_register_hook (opt + 8, TRUE);
   else if (!strncmp ("noregister", opt, 10))
     return set_register_hook (opt + 10, FALSE);
+  else if (!strncmp ("claim:", opt, 6))
+    return record_claim_file (opt + 6);
+  else if (!strncmp ("sym:", opt, 4))
+    return record_claimed_file_symbol (opt + 4);
   else
     return LDPS_ERR;
   return LDPS_OK;
 }
 
+/* Output contents of transfer vector array entry in human-readable form.  */
 static void
 dump_tv_tag (size_t n, struct ld_plugin_tv *tv)
 {
@@ -144,14 +294,12 @@ dump_tv_tag (size_t n, struct ld_plugin_tv *tv)
       break;
   sprintf (unknownbuf, "unknown tag #%d", tv->tv_tag);
   name = (tag < ARRAY_SIZE (tag_names)) ? tag_names[tag].name : unknownbuf;
-  if (tv_message)
-    (*tv_message) (LDPL_INFO, "tv[%d]: %s ", n, name);
+  TV_MESSAGE (LDPL_INFO, "tv[%d]: %s ", n, name);
   switch (tv->tv_tag)
     {
       case LDPT_OPTION:
       case LDPT_OUTPUT_NAME:
-	if (tv_message)
-	  (*tv_message) (LDPL_INFO, "'%s'\n", tv->tv_u.tv_string);
+	TV_MESSAGE (LDPL_INFO, "'%s'\n", tv->tv_u.tv_string);
         break;
       case LDPT_REGISTER_CLAIM_FILE_HOOK:
       case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
@@ -164,8 +312,7 @@ dump_tv_tag (size_t n, struct ld_plugin_tv *tv)
       case LDPT_RELEASE_INPUT_FILE:
       case LDPT_ADD_INPUT_LIBRARY:
       case LDPT_SET_EXTRA_LIBRARY_PATH:
-	if (tv_message)
-	  (*tv_message) (LDPL_INFO, "func@0x%v\n",
+	TV_MESSAGE (LDPL_INFO, "func@0x%v\n",
 			(bfd_vma)(tv->tv_u.tv_message));
         break;
       case LDPT_NULL:
@@ -174,13 +321,13 @@ dump_tv_tag (size_t n, struct ld_plugin_tv *tv)
       case LDPT_LINKER_OUTPUT:
       case LDPT_GNU_LD_VERSION:
       default:
-	if (tv_message)
-	  (*tv_message) (LDPL_INFO, "value %W (%d)\n",
+	TV_MESSAGE (LDPL_INFO, "value %W (%d)\n",
 			(bfd_vma)tv->tv_u.tv_val, tv->tv_u.tv_val);
 	break;
     }
 }
 
+/* Handle/record information received in a transfer vector entry.  */
 static enum ld_plugin_status
 parse_tv_tag (struct ld_plugin_tv *tv)
 {
@@ -239,6 +386,8 @@ parse_tv_tag (struct ld_plugin_tv *tv)
   return LDPS_OK;
 }
 
+/* Record any useful information in transfer vector entry and display
+   it in human-readable form using the plugin API message() callback.  */
 enum ld_plugin_status
 parse_and_dump_tv_tag (size_t n, struct ld_plugin_tv *tv)
 {
@@ -247,6 +396,7 @@ parse_and_dump_tv_tag (size_t n, struct ld_plugin_tv *tv)
   return rv;
 }
 
+/* Standard plugin API entry point.  */
 enum ld_plugin_status
 onload (struct ld_plugin_tv *tv)
 {
@@ -264,24 +414,21 @@ onload (struct ld_plugin_tv *tv)
     tv_message = tv[0].tv_u.tv_message;
 
   fflush (NULL);
-  if (tv_message)
-    (*tv_message) (LDPL_INFO, "Hello from testplugin.\n");
+  TV_MESSAGE (LDPL_INFO, "Hello from testplugin.\n");
 
   do
     if ((rv = parse_and_dump_tv_tag (n++, tv)) != LDPS_OK)
       return rv;
   while ((tv++)->tv_tag != LDPT_NULL);
 
-  if (tv_message)
-    (*tv_message) (LDPL_INFO, "\n");
+  TV_MESSAGE (LDPL_INFO, "\n");
 
   /* Register hooks only if instructed by options.  */
   if (register_claimfile_hook)
     {
       if (!tv_register_claim_file)
 	{
-	  if (tv_message)
-	    (*tv_message) (LDPL_FATAL, "No register_claim_file hook\n");
+	  TV_MESSAGE (LDPL_FATAL, "No register_claim_file hook\n");
 	  fflush (NULL);
 	  return LDPS_ERR;
 	}
@@ -291,8 +438,7 @@ onload (struct ld_plugin_tv *tv)
     {
       if (!tv_register_all_symbols_read)
 	{
-	  if (tv_message)
-	    (*tv_message) (LDPL_FATAL, "No register_all_symbols_read hook\n");
+	  TV_MESSAGE (LDPL_FATAL, "No register_all_symbols_read hook\n");
 	  fflush (NULL);
 	  return LDPS_ERR;
 	}
@@ -302,8 +448,7 @@ onload (struct ld_plugin_tv *tv)
     {
       if (!tv_register_cleanup)
 	{
-	  if (tv_message)
-	    (*tv_message) (LDPL_FATAL, "No register_cleanup hook\n");
+	  TV_MESSAGE (LDPL_FATAL, "No register_cleanup hook\n");
 	  fflush (NULL);
 	  return LDPS_ERR;
 	}
@@ -313,31 +458,56 @@ onload (struct ld_plugin_tv *tv)
   return onload_ret;
 }
 
-
-enum ld_plugin_status
+/* Standard plugin API registerable hook.  */
+static enum ld_plugin_status
 onclaim_file (const struct ld_plugin_input_file *file, int *claimed)
 {
-  if (tv_message)
-    (*tv_message)(LDPL_INFO, "hook called: claim_file %s [@%ld/%ld]\n",
-      file->name, (long)file->offset, (long)file->filesize);
+  /* Let's see if we want to claim this file.  */
+  claim_file_t *claimfile = claimfiles_list;
+  while (claimfile)
+    {
+      if (!strcmp (file->name, claimfile->file.name))
+	break;
+      claimfile = claimfile->next;
+    }
+
+  /* Inform the user/testsuite.  */
+  TV_MESSAGE (LDPL_INFO, "hook called: claim_file %s [@%ld/%ld] %s\n",
+      file->name, (long)file->offset, (long)file->filesize,
+      claimfile ? "CLAIMED" : "not claimed");
   fflush (NULL);
+
+  /* If we decided to claim it, record that fact, and add any symbols
+     that were defined for it by plugin options.  */
+  *claimed = (claimfile != 0);
+  if (claimfile)
+    {
+      claimfile->claimed = TRUE;
+      claimfile->file = *file;
+      if (claimfile->n_syms_used && !tv_add_symbols)
+	return LDPS_ERR;
+      else if (claimfile->n_syms_used)
+	return (*tv_add_symbols) (claimfile->file.handle,
+				claimfile->n_syms_used, claimfile->symbols);
+    }
+
   return claim_file_ret;
 }
 
-enum ld_plugin_status
+/* Standard plugin API registerable hook.  */
+static enum ld_plugin_status
 onall_symbols_read (void)
 {
-  if (tv_message)
-    (*tv_message)(LDPL_INFO, "hook called: all symbols read.\n");
+  TV_MESSAGE (LDPL_INFO, "hook called: all symbols read.\n");
   fflush (NULL);
   return all_symbols_read_ret;
 }
 
-enum ld_plugin_status
+/* Standard plugin API registerable hook.  */
+static enum ld_plugin_status
 oncleanup (void)
 {
-  if (tv_message)
-    (*tv_message)(LDPL_INFO, "hook called: cleanup.\n");
+  TV_MESSAGE (LDPL_INFO, "hook called: cleanup.\n");
   fflush (NULL);
   return cleanup_ret;
 }
diff --git a/ld/testsuite/ld-plugin/plugin-3.d b/ld/testsuite/ld-plugin/plugin-3.d
index 9014870..73aba1b 100644
--- a/ld/testsuite/ld-plugin/plugin-3.d
+++ b/ld/testsuite/ld-plugin/plugin-3.d
@@ -17,7 +17,6 @@ tv\[14\]: LDPT_SET_EXTRA_LIBRARY_PATH func@0x.*
 tv\[15\]: LDPT_OPTION 'registerallsymbolsread'
 tv\[16\]: LDPT_OPTION 'failallsymbolsread'
 tv\[17\]: LDPT_NULL value        0x0 \(0\)
-# Enable these next two lines when all symbols read hook is being called by infrastructure.
-##...
-#.*ld.*:.*ldtestplug.*: plugin reported error after all symbols read
+#...
+.*ld.*:.*ldtestplug.*: plugin reported error after all symbols read
 #...
diff --git a/ld/testsuite/ld-plugin/plugin-5.d b/ld/testsuite/ld-plugin/plugin-5.d
index b9b02ab..c0ffa66 100644
--- a/ld/testsuite/ld-plugin/plugin-5.d
+++ b/ld/testsuite/ld-plugin/plugin-5.d
@@ -18,14 +18,12 @@ tv\[15\]: LDPT_OPTION 'registerclaimfile'
 tv\[16\]: LDPT_OPTION 'registerallsymbolsread'
 tv\[17\]: LDPT_OPTION 'registercleanup'
 tv\[18\]: LDPT_NULL value        0x0 \(0\)
-# Enable these next four lines when claim hook is being called by infrastructure.
-##...
-#hook called: claim_file tmpdir/main.o \[@0/.*
-#hook called: claim_file tmpdir/func.o \[@0/.*
-#hook called: claim_file tmpdir/text.o \[@0/.*
-# Enable these next two lines when all symbols read hook is being called by infrastructure.
-##...
-#hook called: all symbols read.
+#...
+hook called: claim_file tmpdir/main.o \[@0/.*
+hook called: claim_file tmpdir/func.o \[@0/.*
+hook called: claim_file tmpdir/text.o \[@0/.*
+#...
+hook called: all symbols read.
 #...
 hook called: cleanup.
 #...
diff --git a/ld/testsuite/ld-plugin/plugin-6.d b/ld/testsuite/ld-plugin/plugin-6.d
new file mode 100644
index 0000000..cb9257d
--- /dev/null
+++ b/ld/testsuite/ld-plugin/plugin-6.d
@@ -0,0 +1,31 @@
+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_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.
+tmpdir/main.o: In function `main':
+.*ld/testsuite/ld-plugin/main.c:12: undefined reference to `func'
+hook called: cleanup.
+#...
diff --git a/ld/testsuite/ld-plugin/plugin-7.d b/ld/testsuite/ld-plugin/plugin-7.d
new file mode 100644
index 0000000..75f25e0
--- /dev/null
+++ b/ld/testsuite/ld-plugin/plugin-7.d
@@ -0,0 +1,30 @@
+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_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.
+hook called: cleanup.
+#...
diff --git a/ld/testsuite/ld-plugin/plugin.exp b/ld/testsuite/ld-plugin/plugin.exp
index 1150d93..b983ce4 100644
--- a/ld/testsuite/ld-plugin/plugin.exp
+++ b/ld/testsuite/ld-plugin/plugin.exp
@@ -51,22 +51,51 @@ set regclm "-plugin-arg registerclaimfile"
 set regas "-plugin-arg registerallsymbolsread"
 set regcln "-plugin-arg registercleanup"
 
+# In order to define symbols in plugin options in the list of tests below,
+# we need to know if the platform prepends an underscore to C symbols,
+# which we find out by compiling the test objects now.  If there is any
+# error compiling, we defer reporting it until after the list of tests has
+# been initialised, so that we can use the names in the list to report;
+# otherwise, we scan one of the files with 'nm' and look for a known symbol
+# in the output to see if it is prefixed or not.
+set failed_compile 0
+set _ ""
+if { ![ld_compile "$CC $CFLAGS" $srcdir/$subdir/main.c tmpdir/main.o]
+	|| ![ld_compile "$CC $CFLAGS" $srcdir/$subdir/func.c tmpdir/func.o]
+	|| ![ld_compile "$CC $CFLAGS" $srcdir/$subdir/text.c tmpdir/text.o] } {
+    # Defer fail until we have list of tests set.
+    set failed_compile 1
+} else {
+    # Find out if symbols have prefix on this platform before setting tests.
+    catch "exec $NM tmpdir/func.o" nm_output
+    if { [regexp "_func" "$nm_output"] } {
+	set _ "_"
+    }
+}
+
 set plugin_tests [list \
     [list "load plugin" "-plugin $plugin_path \
     $testobjfiles $libs" "" "" {{ld plugin-1.d}} "main.x" ] \
     [list "fail plugin onload" "-plugin $plugin_path -plugin-arg failonload \
     $testobjfiles $libs" "" "" {{ld plugin-2.d}} "main.x" ] \
-    [list "fail plugin allsymbolsread" "-plugin $plugin_path $regas -plugin-arg failallsymbolsread \
+    [list "fail plugin allsymbolsread" "-plugin $plugin_path $regas \
+			-plugin-arg failallsymbolsread \
     $testobjfiles $libs" "" "" {{ld plugin-3.d}} "main.x" ] \
-    [list "fail plugin cleanup" "-plugin $plugin_path -plugin-arg failcleanup $regcln \
+    [list "fail plugin cleanup" "-plugin $plugin_path -plugin-arg failcleanup \
+			$regcln \
     $testobjfiles $libs" "" "" {{ld plugin-4.d}} "main.x" ] \
     [list "plugin all hooks" "-plugin $plugin_path $regclm $regas $regcln \
     $testobjfiles $libs" "" "" {{ld plugin-5.d}} "main.x" ] \
+    [list "plugin claimfile lost symbol" "-plugin $plugin_path $regclm \
+			$regas $regcln -plugin-arg claim:tmpdir/func.o \
+    $testobjfiles $libs" "" "" {{ld plugin-6.d}} "main.x" ] \
+    [list "plugin claimfile replace symbol" "-plugin $plugin_path $regclm \
+			$regas $regcln -plugin-arg claim:tmpdir/func.o \
+			-plugin-arg sym:${_}func::0:0:0 \
+    $testobjfiles $libs" "" "" {{ld plugin-7.d}} "main.x" ] \
 ]
 
-if { ![ld_compile "$CC $CFLAGS" $srcdir/$subdir/main.c tmpdir/main.o]
-	|| ![ld_compile "$CC $CFLAGS" $srcdir/$subdir/func.c tmpdir/func.o]
-	|| ![ld_compile "$CC $CFLAGS" $srcdir/$subdir/text.c tmpdir/text.o] } {
+if { $failed_compile != 0 } {
     foreach testitem $plugin_tests {
 	unresolved [lindex $testitem 0]
     }

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