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]

[RFC/WIP] Add plugin infrastructure to ld.


    Hi list,

  For the sake of really properly supporting LTO in GCC on COFF targets, I
figure the best line of attack is to try and support the GOLD plugin
architecture in GNU LD.  Since 2.21 is becoming imminent, I figure it's best
to discuss this idea now, in case there's still time to get it done for that.

  What I've attached here is basically what would be the 1/N in a series of
patches to do just that.  It adds the entire infrastructure for LD to load
plugins, initialise them, and trigger callbacks, but none of the actual
functionality such as claiming files, passing or receiving symbols, etc.  All
the code is only conditionally included in the build under control of a new
--enable-plugins configure option.

  There's no testsuite or docs yet, like I said, it's WIP and just
infrastructure.  However I did add a couple of dummy plugins that can be used
to verify the interface basically works at the command line.

  This, of course, is the easy bit.  The more complex part is what I'd like to
ask if anyone else has been thinking about or has any advice or suggestions:
how to make LD play correctly with the plugin API.  I'm not even certain that
their modes of operation are going to be commensurate enough to make them fit,
so I wrote this framework as a testbed to try and prove the concept.

  For instance, it seems to me that LD has many locations that open input
files, all of which error out if they fail to find their file, and all of
which would need patching to call the 'claim files' hook and leave the file
alone but still not error out if the file got claimed.  I'm even less certain
if LD's way of dealing with archives and archive members is commensurate
enough with GOLD's to be compatible.

  So before I plough ahead with trying to design a solution, I thought I'd ask
if anyone already has clear ideas about how this should be done.  Anyone?

    cheers,
      DaveK

include/ChangeLog:

	* plugin-api.h (LDPT_GNU_LD_VERSION): New ld_plugin_tag enum member.

ld/ChangeLog:

	* configure.in: Add --enable-plugins option.
	(ENABLE_PLUGINS): Add related automake conditional.
	* configure: Regenerate.
	* Makefile.am (PLUGIN_C): Declare plugin C source file, conditional
	on ENABLE_PLUGINS being defined.
	(PLUGIN_H): Likewise for header file.
	(PLUGIN_OBJEXT): Likewise for object file.
	(PLUGIN_CFLAGS): Likewise -D flag required to compile plugin support.
	(AM_CFLAGS): Use PLUGIN_CFLAGS.
	(CFILES): Use PLUGIN_C.
	(HFILES): Use PLUGIN_H.
	(OFILES): Use PLUGIN_OBJEXT.
	(ld_new_SOURCES): Use PLUGIN_C.
	(noinst_LTLIBRARIES)[ENABLE_PLUGINS]: Declare test plugins.
	(libldtestplug_la_SOURCES)[ENABLE_PLUGINS]: Add automake definition
	for test plugin.
	(libldtestplug_la_CFLAGS)[ENABLE_PLUGINS]: Likewise.
	(libldtestplug_la_LDFLAGS)[ENABLE_PLUGINS]: Likewise.
	(libldtestplugbad_la_SOURCES)[ENABLE_PLUGINS]: Likewise.
	(libldtestplugbad_la_CFLAGS)[ENABLE_PLUGINS]: Likewise.
	(libldtestplugbad_la_LDFLAGS)[ENABLE_PLUGINS]: Likewise.
	* Makefile.in: Regenerate.
	* ldmisc.c (vfinfo): Make non-static.
	* ldmisc.h: Include stdarg.h for va_list type.
	(vfinfo): Declare extern prototype.
	* lexsup.c (enum option_values)[ENABLE_PLUGINS]: Add new entries for
	OPTION_PLUGIN and OPTION_PLUGIN_ARG.
	(ld_options[])[ENABLE_PLUGINS]: Add option data for the above two.
	(parse_args)[ENABLE_PLUGINS]: Handle them, and load all plugins once
	option parsing is complete.
	* plugin.c: New source file.
	* plugin.h: Likewise new header.
	* testplug.c: New source file.

Index: include/plugin-api.h
===================================================================
RCS file: /cvs/src/src/include/plugin-api.h,v
retrieving revision 1.11
diff -p -u -r1.11 plugin-api.h
--- include/plugin-api.h	21 Jun 2010 21:21:25 -0000	1.11
+++ include/plugin-api.h	9 Sep 2010 03:41:37 -0000
@@ -268,7 +268,8 @@ enum ld_plugin_tag
   LDPT_RELEASE_INPUT_FILE,
   LDPT_ADD_INPUT_LIBRARY,
   LDPT_OUTPUT_NAME,
-  LDPT_SET_EXTRA_LIBRARY_PATH
+  LDPT_SET_EXTRA_LIBRARY_PATH,
+  LDPT_GNU_LD_VERSION
 };
 
 /* The plugin transfer vector.  */
Index: ld/configure.in
===================================================================
RCS file: /cvs/src/src/ld/configure.in,v
retrieving revision 1.63
diff -p -u -r1.63 configure.in
--- ld/configure.in	27 Apr 2010 14:12:31 -0000	1.63
+++ ld/configure.in	9 Sep 2010 03:41:37 -0000
@@ -117,6 +117,16 @@ case "${got_handling}" in
   *)  AC_MSG_ERROR(bad value ${got_handling} for --enable-got option) ;;
 esac
 
+AC_ARG_ENABLE(plugins,
+[[  --enable-plugins        enable plugin interface]],
+[case "${enableval}" in
+  yes | "")  enable_plugins=true  ;;
+  no)   enable_plugins=false ;;
+  *)    AC_MSG_ERROR(bad value ${enableval} for --enable-plugins option) ;;
+esac],
+[enable_plugins=false])dnl
+AM_CONDITIONAL([ENABLE_PLUGINS], [test x$enable_plugins = xtrue])
+
 AM_BINUTILS_WARNINGS
 
 AC_CONFIG_HEADERS([config.h:config.in])
Index: ld/Makefile.am
===================================================================
RCS file: /cvs/src/src/ld/Makefile.am,v
retrieving revision 1.288
diff -p -u -r1.288 Makefile.am
--- ld/Makefile.am	20 Aug 2010 21:18:39 -0000	1.288
+++ ld/Makefile.am	9 Sep 2010 03:41:37 -0000
@@ -21,6 +21,21 @@ WARN_CFLAGS = @WARN_CFLAGS@
 NO_WERROR = @NO_WERROR@
 AM_CFLAGS = $(WARN_CFLAGS)
 
+# Conditionally enable the plugin interface.
+if ENABLE_PLUGINS
+PLUGIN_C = plugin.c
+PLUGIN_H = plugin.h
+PLUGIN_OBJEXT = plugin.@OBJEXT@
+PLUGIN_CFLAGS = -DENABLE_PLUGINS
+else
+PLUGIN_C =
+PLUGIN_H =
+PLUGIN_OBJEXT =
+PLUGIN_CFLAGS =
+endif
+
+AM_CFLAGS += $(PLUGIN_CFLAGS)
+
 # We put the scripts in the directory $(scriptdir)/ldscripts.
 # We can't put the scripts in $(datadir) because the SEARCH_DIR
 # directives need to be different for native and cross linkers.
@@ -455,11 +470,13 @@ ALL_EMUL_EXTRA_OFILES = \
 
 CFILES = ldctor.c ldemul.c ldexp.c ldfile.c ldlang.c \
 	ldmain.c ldmisc.c ldver.c ldwrite.c lexsup.c \
-	mri.c ldcref.c pe-dll.c pep-dll.c ldlex-wrapper.c
+	mri.c ldcref.c pe-dll.c pep-dll.c ldlex-wrapper.c \
+	$(PLUGIN_C)
 
 HFILES = ld.h ldctor.h ldemul.h ldexp.h ldfile.h \
 	ldlang.h ldlex.h ldmain.h ldmisc.h ldver.h \
-	ldwrite.h mri.h deffile.h pe-dll.h pep-dll.h elf-hints-local.h
+	ldwrite.h mri.h deffile.h pe-dll.h pep-dll.h \
+	elf-hints-local.h $(PLUGIN_H)
 
 GENERATED_CFILES = ldgram.c ldlex.c deffilep.c
 GENERATED_HFILES = ldgram.h ldemul-list.h deffilep.h
@@ -468,7 +485,8 @@ GENERATED_HFILES = ldgram.h ldemul-list.
 # tracking will not cause them to be built beforehand.
 BUILT_SOURCES = $(GENERATED_HFILES)
 
-OFILES = ldgram.@OBJEXT@ ldlex-wrapper.@OBJEXT@ lexsup.@OBJEXT@ ldlang.@OBJEXT@ mri.@OBJEXT@ ldctor.@OBJEXT@ ldmain.@OBJEXT@ \
+OFILES = ldgram.@OBJEXT@ ldlex-wrapper.@OBJEXT@ lexsup.@OBJEXT@ ldlang.@OBJEXT@ \
+	mri.@OBJEXT@ ldctor.@OBJEXT@ ldmain.@OBJEXT@ $(PLUGIN_OBJEXT) \
 	ldwrite.@OBJEXT@ ldexp.@OBJEXT@  ldemul.@OBJEXT@ ldver.@OBJEXT@ ldmisc.@OBJEXT@ \
 	ldfile.@OBJEXT@ ldcref.@OBJEXT@ ${EMULATION_OFILES} ${EMUL_EXTRA_OFILES}
 
@@ -1882,7 +1900,7 @@ EXTRA_ld_new_SOURCES = deffilep.y ldlex.
 EXTRA_ld_new_SOURCES += pep-dll.c pe-dll.c
 
 ld_new_SOURCES = ldgram.y ldlex-wrapper.c lexsup.c ldlang.c mri.c ldctor.c ldmain.c \
-	ldwrite.c ldexp.c ldemul.c ldver.c ldmisc.c ldfile.c ldcref.c
+	ldwrite.c ldexp.c ldemul.c ldver.c ldmisc.c ldfile.c ldcref.c $(PLUGIN_C)
 ld_new_DEPENDENCIES = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) $(BFDLIB) $(LIBIBERTY) $(LIBINTL_DEP)
 ld_new_LDADD = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) $(BFDLIB) $(LIBIBERTY) $(LIBINTL)
 
@@ -1964,6 +1982,19 @@ bootstrap: ld3$(EXEEXT)
 
 # END OF CHECK TARGETS
 
+# 
+# Build two dummy plugins using libtool.
+#
+if ENABLE_PLUGINS
+noinst_LTLIBRARIES = libldtestplug.la libldtestplugbad.la
+libldtestplug_la_SOURCES = testplug.c
+libldtestplug_la_CFLAGS= -g -O2 -DRETVAL=LDPS_OK
+libldtestplug_la_LDFLAGS = -no-undefined -rpath /nowhere
+libldtestplugbad_la_SOURCES = testplug.c
+libldtestplugbad_la_CFLAGS= -g -O2 -DRETVAL=LDPS_ERR
+libldtestplugbad_la_LDFLAGS = -no-undefined -rpath /nowhere
+endif
+
 # DOCUMENTATION TARGETS
 # Manual configuration file; not usually attached to normal configuration,
 # because almost all configs use "gen" version of manual.
Index: ld/ldmisc.c
===================================================================
RCS file: /cvs/src/src/ld/ldmisc.c,v
retrieving revision 1.36
diff -p -u -r1.36 ldmisc.c
--- ld/ldmisc.c	17 Aug 2008 03:12:50 -0000	1.36
+++ ld/ldmisc.c	9 Sep 2010 03:41:37 -0000
@@ -62,7 +62,7 @@
  %v hex bfd_vma, no leading zeros
 */
 
-static void
+void
 vfinfo (FILE *fp, const char *fmt, va_list arg, bfd_boolean is_warning)
 {
   bfd_boolean fatal = FALSE;
Index: ld/ldmisc.h
===================================================================
RCS file: /cvs/src/src/ld/ldmisc.h,v
retrieving revision 1.11
diff -p -u -r1.11 ldmisc.h
--- ld/ldmisc.h	2 Sep 2009 07:25:35 -0000	1.11
+++ ld/ldmisc.h	9 Sep 2010 03:41:37 -0000
@@ -22,6 +22,9 @@
 #ifndef LDMISC_H
 #define LDMISC_H
 
+#include <stdarg.h>
+
+extern void vfinfo (FILE *fp, const char *fmt, va_list arg, bfd_boolean is_warning);
 extern void einfo (const char *, ...);
 extern void minfo (const char *, ...);
 extern void info_msg (const char *, ...);
Index: ld/lexsup.c
===================================================================
RCS file: /cvs/src/src/ld/lexsup.c,v
retrieving revision 1.118
diff -p -u -r1.118 lexsup.c
--- ld/lexsup.c	30 Nov 2009 00:27:45 -0000	1.118
+++ ld/lexsup.c	9 Sep 2010 03:41:37 -0000
@@ -40,6 +40,9 @@
 #include "ldver.h"
 #include "ldemul.h"
 #include "demangle.h"
+#ifdef ENABLE_PLUGINS
+#include "plugin.h"
+#endif /* ENABLE_PLUGINS */
 
 #ifndef PATH_SEPARATOR
 #if defined (__MSDOS__) || (defined (_WIN32) && ! defined (__CYGWIN32__))
@@ -167,7 +170,11 @@ enum option_values
   OPTION_WARN_SHARED_TEXTREL,
   OPTION_WARN_ALTERNATE_EM,
   OPTION_REDUCE_MEMORY_OVERHEADS,
-  OPTION_DEFAULT_SCRIPT
+#ifdef ENABLE_PLUGINS
+  OPTION_PLUGIN,
+  OPTION_PLUGIN_ARG,
+#endif /* ENABLE_PLUGINS */
+  OPTION_DEFAULT_SCRIPT,
 };
 
 /* The long options.  This structure is used for both the option
@@ -271,6 +278,12 @@ static const struct ld_option ld_options
     'o', N_("FILE"), N_("Set output file name"), EXACTLY_TWO_DASHES },
   { {NULL, required_argument, NULL, '\0'},
     'O', NULL, N_("Optimize output file"), ONE_DASH },
+#ifdef ENABLE_PLUGINS
+  { {"plugin", required_argument, NULL, OPTION_PLUGIN},
+    '\0', N_("PLUGIN"), N_("Load named plugin"), ONE_DASH },
+  { {"plugin-arg", required_argument, NULL, OPTION_PLUGIN_ARG},
+    '\0', N_("ARG"), N_("Send arg to last-loaded plugin"), ONE_DASH },
+#endif /* ENABLE_PLUGINS */
   { {"Qy", no_argument, NULL, OPTION_IGNORE},
     '\0', NULL, N_("Ignored for SVR4 compatibility"), ONE_DASH },
   { {"emit-relocs", no_argument, NULL, 'q'},
@@ -1040,6 +1053,16 @@ parse_args (unsigned argc, char **argv)
 	case OPTION_OFORMAT:
 	  lang_add_output_format (optarg, NULL, NULL, 0);
 	  break;
+#ifdef ENABLE_PLUGINS
+	case OPTION_PLUGIN:
+	  if (plugin_opt_plugin (optarg))
+	    einfo(_("%P%F: bad -plugin option\n"));
+	  break;
+	case OPTION_PLUGIN_ARG:
+	  if (plugin_opt_plugin_arg (optarg))
+	    einfo(_("%P%F: bad -plugin-arg option\n"));
+	  break;
+#endif /* ENABLE_PLUGINS */
 	case 'q':
 	  link_info.emitrelocations = TRUE;
 	  break;
@@ -1517,6 +1540,12 @@ parse_args (unsigned argc, char **argv)
   if (link_info.unresolved_syms_in_shared_libs == RM_NOT_YET_SET)
     /* FIXME: Should we allow emulations a chance to set this ?  */
     link_info.unresolved_syms_in_shared_libs = how_to_report_unresolved_symbols;
+
+#ifdef ENABLE_PLUGINS
+  /* Now all the plugin arguments have been gathered, we can load them.  */
+  if (plugin_load_plugins ())
+    einfo (_("%P%F: %s: error loading plugin\n"), plugin_error_plugin ());
+#endif /* ENABLE_PLUGINS */
 }
 
 /* Add the (colon-separated) elements of DIRLIST_PTR to the
Index: ld/plugin.c
===================================================================
RCS file: ld/plugin.c
diff -N ld/plugin.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/plugin.c	9 Sep 2010 03:41:37 -0000
@@ -0,0 +1,507 @@
+/* Plugin control for the GNU linker.
+   Copyright 2010 Free Software Foundation, Inc.
+
+   This file is part of the GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#include <dlfcn.h>
+
+#include "sysdep.h"
+#include "libiberty.h"
+#include "bfd.h"
+#include "bfdlink.h"
+#include "bfdver.h"
+#include "ld.h"
+#include "ldmain.h"
+#include "ldmisc.h"
+#include "plugin.h"
+#include "plugin-api.h"
+
+/* Alias to shorten static function prototype lines.  */
+#define PLUGAPIFUNC static enum ld_plugin_status
+
+PLUGAPIFUNC message (int level, const char *format, ...);
+PLUGAPIFUNC register_claim_file (ld_plugin_claim_file_handler handler);
+PLUGAPIFUNC register_all_symbols_read (ld_plugin_all_symbols_read_handler handler);
+PLUGAPIFUNC register_cleanup (ld_plugin_cleanup_handler handler);
+PLUGAPIFUNC add_symbols (void *handle, int nsyms, const struct ld_plugin_symbol *syms);
+PLUGAPIFUNC get_input_file (const void *handle, struct ld_plugin_input_file *file);
+PLUGAPIFUNC release_input_file (const void *handle);
+PLUGAPIFUNC get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms);
+PLUGAPIFUNC add_input_file (const char *pathname);
+PLUGAPIFUNC add_input_library (const char *pathname);
+PLUGAPIFUNC set_extra_library_path (const char *path);
+
+/* Always use this macro when invoking a plugin function.  */
+#define INVOKE_PLUGIN_FN(plugin, retval, fn, args)	\
+	called_plugin = plugin;				\
+	retval = (*fn) args;				\
+	called_plugin = NULL;
+
+/* Stores a single argument passed to a plugin.  */
+typedef struct plugin_arg
+{
+  struct plugin_arg *next;
+  const char *arg;
+} plugin_arg_t;
+
+/* Holds all details of a single plugin.  */
+typedef struct plugin
+{
+  /* Next on the list of plugins, or NULL at end of chain.  */
+  struct plugin *next;
+  /* The argument string given to --plugin.  */
+  const char *name;
+  /* The shared library handle returned by dlopen.  */
+  void *dlhandle;
+  /* The list of argument string given to --plugin-opt.  */
+  plugin_arg_t *args;
+  /* Number of args in the list, for convenience.  */
+  size_t n_args;
+  /* The plugin's event handlers.  */
+  ld_plugin_claim_file_handler claim_file_handler;
+  ld_plugin_all_symbols_read_handler all_symbols_read_handler;
+  ld_plugin_cleanup_handler cleanup_handler;
+  /* TRUE if the cleanup handlers have been called.  */
+  bfd_boolean cleanup_done;
+} plugin_t;
+
+/* The master list of all plugins.  */
+static plugin_t *plugins_list = NULL;
+
+/* The last plugin added to the list, for receiving args.  */
+static plugin_t *last_plugin = NULL;
+
+/* The plugin which is currently having a callback executed.  */
+static plugin_t *called_plugin = NULL;
+
+/* Last plugin to cause an error, if any.  */
+static const char *error_plugin = NULL;
+
+/* Helper to size leading part of tv array and set it up. */
+static size_t
+set_tv_header (struct ld_plugin_tv *tv)
+{
+  size_t i;
+  /* List of tags to set in the constant leading part of the tv array. */
+  static const enum ld_plugin_tag tv_header_tags[] =
+  {
+    LDPT_MESSAGE,
+    LDPT_API_VERSION,
+    LDPT_GNU_LD_VERSION,
+    LDPT_LINKER_OUTPUT,
+    LDPT_OUTPUT_NAME,
+    LDPT_REGISTER_CLAIM_FILE_HOOK,
+    LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
+    LDPT_REGISTER_CLEANUP_HOOK,
+    LDPT_ADD_SYMBOLS,
+    LDPT_GET_INPUT_FILE,
+    LDPT_RELEASE_INPUT_FILE,
+    LDPT_GET_SYMBOLS,
+    LDPT_ADD_INPUT_FILE,
+    LDPT_ADD_INPUT_LIBRARY,
+    LDPT_SET_EXTRA_LIBRARY_PATH
+  };
+  /* How many entries.  */
+  static const size_t tv_header_size = ARRAY_SIZE (tv_header_tags);
+
+  /* Version info.  */
+  static const unsigned int major = (unsigned)(BFD_VERSION / 100000000UL);
+  static const unsigned int minor = (unsigned)(BFD_VERSION / 1000000UL) % 100;
+
+  if (!tv)
+    return tv_header_size;
+
+  for (i = 0; i < tv_header_size; i++)
+    {
+      tv[i].tv_tag = tv_header_tags[i];
+#define TVU(x) tv[i].tv_u.tv_ ## x
+      switch (tv[i].tv_tag)
+	{
+	  case LDPT_MESSAGE:
+	    TVU(message) = message;
+	    break;
+	  case LDPT_API_VERSION:
+	    TVU(val) = LD_PLUGIN_API_VERSION;
+	    break;
+	  case LDPT_GNU_LD_VERSION:
+	    TVU(val) = major * 100 + minor;
+	    break;
+	  case LDPT_LINKER_OUTPUT:
+	    TVU(val) = link_info.relocatable ? LDPO_REL
+			: (link_info.shared ? LDPO_DYN : LDPO_EXEC);
+	    break;
+	  case LDPT_OUTPUT_NAME:
+	    TVU(string) = output_filename;
+	    break;
+	  case LDPT_REGISTER_CLAIM_FILE_HOOK:
+	    TVU(register_claim_file) = register_claim_file;
+	    break;
+	  case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
+	    TVU(register_all_symbols_read) = register_all_symbols_read;
+	    break;
+	  case LDPT_REGISTER_CLEANUP_HOOK:
+	    TVU(register_cleanup) = register_cleanup;
+	    break;
+	  case LDPT_ADD_SYMBOLS:
+	    TVU(add_symbols) = add_symbols;
+	    break;
+	  case LDPT_GET_INPUT_FILE:
+	    TVU(get_input_file) = get_input_file;
+	    break;
+	  case LDPT_RELEASE_INPUT_FILE:
+	    TVU(release_input_file) = release_input_file;
+	    break;
+	  case LDPT_GET_SYMBOLS:
+	    TVU(get_symbols) = get_symbols;
+	    break;
+	  case LDPT_ADD_INPUT_FILE:
+	    TVU(add_input_file) = add_input_file;
+	    break;
+	  case LDPT_ADD_INPUT_LIBRARY:
+	    TVU(add_input_library) = add_input_library;
+	    break;
+	  case LDPT_SET_EXTRA_LIBRARY_PATH:
+	    TVU(set_extra_library_path) = set_extra_library_path;
+	    break;
+	  default:
+	    /* Added a new entry to the array without adding
+	       a new case to set up its value is a bug.  */
+	    FAIL ();
+	}
+#undef TVU
+    }
+  return tv_header_size;
+}
+
+/* Append the per-plugin args list and trailing LDPT_NULL.  */
+static void
+set_tv_plugin_args (plugin_t *plugin, struct ld_plugin_tv *tv)
+{
+  plugin_arg_t *arg = plugin->args;
+  while (arg)
+    {
+      tv->tv_tag = LDPT_OPTION;
+      tv->tv_u.tv_string = arg->arg;
+      arg = arg->next;
+      tv++;
+    }
+  tv->tv_tag = LDPT_NULL;
+  tv->tv_u.tv_val = 0;
+}
+
+/* Helper function for exiting with error status.  */
+static int
+set_plugin_error (const char *plugin)
+{
+  error_plugin = plugin;
+  return -1;
+}
+
+/* Test if an error occurred.  */
+static bfd_boolean
+plugin_error_p (void)
+{
+  return error_plugin != NULL;
+}
+
+/* Return name of plugin which caused an error if any.  */
+const char *plugin_error_plugin (void)
+{
+  return error_plugin ? error_plugin : _("<no plugin>");
+}
+
+/* Handle -plugin arg: find and load plugin, or return error.  */
+int plugin_opt_plugin (const char *plugin)
+{
+  plugin_t *listptr, *newplug;
+
+  newplug = xmalloc (sizeof *newplug);
+  memset (newplug, 0, sizeof *newplug);
+  newplug->name = plugin;
+  newplug->dlhandle = dlopen (plugin, RTLD_NOW);
+  if (!newplug->dlhandle)
+    return set_plugin_error (plugin);
+
+  /* Chain on end, so when we run list it is in command-line order.  */
+  listptr = plugins_list;
+  if (!listptr)
+    plugins_list = newplug;
+  else
+    {
+      while (listptr->next)
+	listptr = listptr->next;
+      listptr->next = newplug;
+    }
+  last_plugin = newplug;
+  return 0;
+}
+
+/* Accumulate option arguments for last-loaded plugin, or return
+   error if none.  */
+int plugin_opt_plugin_arg (const char *arg)
+{
+  plugin_arg_t *newarg, *listptr;
+
+  if (!last_plugin)
+    return set_plugin_error (_("<no plugin>"));
+
+  newarg = xmalloc (sizeof *newarg);
+  newarg->arg = arg;
+  newarg->next = NULL;
+
+  /* Chain on end to preserve command-line order.  */
+  listptr = last_plugin->args;
+  if (!listptr)
+    last_plugin->args = newarg;
+  else
+    {
+      while (listptr->next)
+	listptr = listptr->next;
+      listptr->next = newarg;
+    }
+  last_plugin->n_args++;
+  return 0;
+}
+
+/* Load up and initialise all plugins after argument parsing.  */
+int plugin_load_plugins (void)
+{
+  struct ld_plugin_tv *my_tv;
+  unsigned int max_args = 0;
+  plugin_t *curplug = plugins_list;
+
+  /* First pass over plugins to find max # args needed so that we
+     can size and allocate the tv array.  */
+  while (curplug)
+    {
+      if (curplug->n_args > max_args)
+	max_args = curplug->n_args;
+      curplug = curplug->next;
+    }
+  /* Allocate tv array and initialise constant part.  */
+  my_tv = xmalloc ((max_args + 1 + set_tv_header (NULL)) * sizeof *my_tv);
+  set_tv_header (my_tv);
+
+  curplug = plugins_list;
+  while (curplug)
+    {
+      enum ld_plugin_status rv;
+      ld_plugin_onload onloadfn = dlsym (curplug->dlhandle, "onload");
+      if (!onloadfn)
+	onloadfn = dlsym (curplug->dlhandle, "_onload");
+      if (!onloadfn)
+        return set_plugin_error (curplug->name);
+      set_tv_plugin_args (curplug, &my_tv[set_tv_header (NULL)]);
+      INVOKE_PLUGIN_FN (curplug, rv, onloadfn, (my_tv));
+      if (rv != LDPS_OK)
+        return set_plugin_error (curplug->name);
+      curplug = curplug->next;
+    }
+  return 0;
+}
+
+/* Call 'claim file' hook for all plugins.  */
+int
+plugin_call_claim_file (const struct ld_plugin_input_file *file, int *claimed)
+{
+  plugin_t *curplug = plugins_list;
+  *claimed = FALSE;
+  while (curplug && !*claimed)
+    {
+      if (curplug->claim_file_handler)
+	{
+	  enum ld_plugin_status rv;
+	  curplug->cleanup_done = TRUE;
+	  INVOKE_PLUGIN_FN (curplug, rv, curplug->claim_file_handler, (file, claimed));
+	  if (rv != LDPS_OK)
+	    set_plugin_error (curplug->name);
+	}
+      curplug = curplug->next;
+    }
+  return plugin_error_p () ? -1 : 0;
+}
+
+/* Call 'all symbols read' hook for all plugins.  */
+int
+plugin_call_all_symbols_read (void)
+{
+  plugin_t *curplug = plugins_list;
+  while (curplug)
+    {
+      if (curplug->all_symbols_read_handler)
+	{
+	  enum ld_plugin_status rv;
+	  curplug->cleanup_done = TRUE;
+	  INVOKE_PLUGIN_FN (curplug, rv, curplug->all_symbols_read_handler, ());
+	  if (rv != LDPS_OK)
+	    set_plugin_error (curplug->name);
+	}
+      curplug = curplug->next;
+    }
+  return plugin_error_p () ? -1 : 0;
+}
+
+/* Call 'cleanup' hook for all plugins.  */
+int
+plugin_call_cleanup (void)
+{
+  plugin_t *curplug = plugins_list;
+  while (curplug)
+    {
+      if (curplug->cleanup_handler && !curplug->cleanup_done)
+	{
+	  enum ld_plugin_status rv;
+	  curplug->cleanup_done = TRUE;
+	  INVOKE_PLUGIN_FN (curplug, rv, curplug->cleanup_handler, ());
+	  if (rv != LDPS_OK)
+	    set_plugin_error (curplug->name);
+	}
+      curplug = curplug->next;
+    }
+  return plugin_error_p () ? -1 : 0;
+}
+
+/* Register a claim-file handler.  */
+static enum ld_plugin_status
+register_claim_file (ld_plugin_claim_file_handler handler)
+{
+  ASSERT (called_plugin);
+  called_plugin->claim_file_handler = 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)
+{
+  ASSERT (called_plugin);
+  called_plugin->all_symbols_read_handler = handler;
+  return LDPS_OK;
+}
+
+/* Register a cleanup handler.  */
+static enum ld_plugin_status
+register_cleanup (ld_plugin_cleanup_handler handler)
+{
+  ASSERT (called_plugin);
+  called_plugin->cleanup_handler = handler;
+  return LDPS_OK;
+}
+
+/* Add symbols from a plugin-claimed input file.  */
+static enum ld_plugin_status
+add_symbols (void *handle, int nsyms, const struct ld_plugin_symbol *syms)
+{
+  ASSERT (called_plugin);
+  handle = handle;
+  nsyms = nsyms;
+  syms = syms;
+  return LDPS_ERR;
+}
+
+/* Get the input file information with an open (possibly re-opened)
+   file descriptor.  */
+static enum ld_plugin_status
+get_input_file (const void *handle, struct ld_plugin_input_file *file)
+{
+  ASSERT (called_plugin);
+  handle = handle;
+  file = file;
+  return LDPS_ERR;
+}
+
+/* Release the input file.  */
+static enum ld_plugin_status
+release_input_file (const void *handle)
+{
+  ASSERT (called_plugin);
+  handle = handle;
+  return LDPS_ERR;
+}
+
+/* Get the symbol resolution info for a plugin-claimed input file.  */
+static enum ld_plugin_status
+get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
+{
+  ASSERT (called_plugin);
+  handle = handle;
+  nsyms = nsyms;
+  syms = syms;
+  return LDPS_ERR;
+}
+
+/* Add a new (real) input file generated by a plugin.  */
+static enum ld_plugin_status
+add_input_file (const char *pathname)
+{
+  ASSERT (called_plugin);
+  pathname = pathname;
+  return LDPS_ERR;
+}
+
+/* Add a new (real) library required by a plugin.  */
+static enum ld_plugin_status
+add_input_library (const char *pathname)
+{
+  ASSERT (called_plugin);
+  pathname = pathname;
+  return LDPS_ERR;
+}
+
+/* Set the extra library path to be used by libraries added via
+   add_input_library.  */
+static enum ld_plugin_status
+set_extra_library_path (const char *path)
+{
+  ASSERT (called_plugin);
+  path = path;
+  return LDPS_ERR;
+}
+
+/* Issue a diagnostic message from a plugin.  */
+static enum ld_plugin_status
+message (int level, const char *format, ...)
+{
+  va_list args;
+  va_start (args, format);
+
+  switch (level)
+    {
+    case LDPL_INFO:
+      vfinfo (stdout, format, args, FALSE);
+      break;
+    case LDPL_WARNING:
+      vfinfo (stdout, format, args, TRUE);
+      break;
+    case LDPL_FATAL:
+    case LDPL_ERROR:
+    default:
+      {
+	char *newfmt = xmalloc (strlen (format) + 3);
+	newfmt[0] = '%';
+	newfmt[1] = (level == LDPL_FATAL) ? 'F' : 'X';
+	strcpy (&newfmt[2], format);
+	vfinfo (stderr, newfmt, args, TRUE);
+      }
+      break;
+    }
+
+  va_end (args);
+  return LDPS_OK;
+}
+
Index: ld/plugin.h
===================================================================
RCS file: ld/plugin.h
diff -N ld/plugin.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/plugin.h	9 Sep 2010 03:41:37 -0000
@@ -0,0 +1,46 @@
+/* Plugin control for the GNU linker.
+   Copyright 2010 Free Software Foundation, Inc.
+
+   This file is part of the GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+/* 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;
+
+/* Handle -plugin arg: find and load plugin, or return error.  */
+extern int plugin_opt_plugin (const char *plugin);
+
+/* Accumulate option arguments for last-loaded plugin, or return
+   error if none.  */
+extern int plugin_opt_plugin_arg (const char *arg);
+
+/* Load up and initialise all plugins after argument parsing.  */
+extern int plugin_load_plugins (void);
+
+/* Return name of plugin which caused an error in any of the above.  */
+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);
+
+/* Call 'all symbols read' hook for all plugins.  */
+extern int plugin_call_all_symbols_read (void);
+
+/* Call 'cleanup' hook for all plugins.  */
+extern int plugin_call_cleanup (void);
+
Index: ld/testplug.c
===================================================================
RCS file: ld/testplug.c
diff -N ld/testplug.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testplug.c	9 Sep 2010 03:41:37 -0000
@@ -0,0 +1,119 @@
+/* Test plugin for the GNU linker.
+   Copyright 2010 Free Software Foundation, Inc.
+
+   This file is part of the GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#include <stdio.h>
+#include "libiberty.h"
+#include "plugin-api.h"
+
+extern enum ld_plugin_status onload (struct ld_plugin_tv *tv);
+
+#define ADDENTRY(tag) { tag, #tag }
+
+typedef struct tag_name
+{
+  enum ld_plugin_tag tag;
+  const char *name;
+} tag_name_t;
+
+tag_name_t tag_names[] =
+{
+  ADDENTRY(LDPT_NULL),
+  ADDENTRY(LDPT_API_VERSION),
+  ADDENTRY(LDPT_GOLD_VERSION),
+  ADDENTRY(LDPT_LINKER_OUTPUT),
+  ADDENTRY(LDPT_OPTION),
+  ADDENTRY(LDPT_REGISTER_CLAIM_FILE_HOOK),
+  ADDENTRY(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK),
+  ADDENTRY(LDPT_REGISTER_CLEANUP_HOOK),
+  ADDENTRY(LDPT_ADD_SYMBOLS),
+  ADDENTRY(LDPT_GET_SYMBOLS),
+  ADDENTRY(LDPT_ADD_INPUT_FILE),
+  ADDENTRY(LDPT_MESSAGE),
+  ADDENTRY(LDPT_GET_INPUT_FILE),
+  ADDENTRY(LDPT_RELEASE_INPUT_FILE),
+  ADDENTRY(LDPT_ADD_INPUT_LIBRARY),
+  ADDENTRY(LDPT_OUTPUT_NAME),
+  ADDENTRY(LDPT_SET_EXTRA_LIBRARY_PATH),
+  ADDENTRY(LDPT_GNU_LD_VERSION)
+};
+
+static void
+dump_tv_tag (size_t n, struct ld_plugin_tv *tv)
+{
+  size_t tag;
+  char unknownbuf[40];
+  const char *name;
+
+  for (tag = 0; tag < ARRAY_SIZE (tag_names); tag++)
+    if (tag_names[tag].tag == tv->tv_tag)
+      break;
+  sprintf (unknownbuf, "unknown tag #%d", tv->tv_tag);
+  name = (tag < ARRAY_SIZE (tag_names)) ? tag_names[tag].name : unknownbuf;
+  printf ("tv[%d]: %s ", n, name);
+  switch (tv->tv_tag)
+    {
+      case LDPT_OPTION:
+      case LDPT_OUTPUT_NAME:
+	printf ("'%s'\n", tv->tv_u.tv_string);
+        break;
+      case LDPT_REGISTER_CLAIM_FILE_HOOK:
+      case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
+      case LDPT_REGISTER_CLEANUP_HOOK:
+      case LDPT_ADD_SYMBOLS:
+      case LDPT_GET_SYMBOLS:
+      case LDPT_ADD_INPUT_FILE:
+      case LDPT_MESSAGE:
+      case LDPT_GET_INPUT_FILE:
+      case LDPT_RELEASE_INPUT_FILE:
+      case LDPT_ADD_INPUT_LIBRARY:
+      case LDPT_SET_EXTRA_LIBRARY_PATH:
+	printf ("func@%p\n", (void *)(tv->tv_u.tv_message));
+        break;
+      case LDPT_NULL:
+      case LDPT_API_VERSION:
+      case LDPT_GOLD_VERSION:
+      case LDPT_LINKER_OUTPUT:
+      case LDPT_GNU_LD_VERSION:
+      default:
+	printf ("value 0x%08x (%d)\n", tv->tv_u.tv_val, tv->tv_u.tv_val);
+	break;
+    }
+}
+
+enum ld_plugin_status
+onload (struct ld_plugin_tv *tv)
+{
+  size_t n = 0;
+
+  /* This plugin does nothing but dump the tv array.  It would
+     be an error if this function was called without one.  */
+  if (!tv)
+    return LDPS_ERR;
+
+  printf ("Hello from testplugin.\n");
+  do
+    dump_tv_tag (n++, tv);
+  while ((tv++)->tv_tag != LDPT_NULL);
+  printf ("\n");
+
+  /* This will be defined by a -D option in the CFLAGS during
+     compilation, to one of LDPS_OK or LDPS_ERR.  */
+  return RETVAL;
+}

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