Add plugin functionality for link-time optimization (LTO).
authorCary Coutant <ccoutant@google.com>
Fri, 19 Sep 2008 22:54:57 +0000 (22:54 +0000)
committerCary Coutant <ccoutant@google.com>
Fri, 19 Sep 2008 22:54:57 +0000 (22:54 +0000)
include/:
* plugin-api.h: New file.

gold/:
* configure.ac (plugins): Add --enable-plugins option.
* configure: Regenerate.
* config.in: Regenerate.
* Makefile.am (LIBDL): New variable.
(CCFILES): Add plugin.cc.
(HFILES): Add plugin.h.
(ldadd_var): Add LIBDL.
* Makefile.in: Regenerate.

* archive.cc: Include "plugin.h".
(Archive::setup): Don't preread archive symbols when using a plugin.
(Archive::get_file_and_offset): Add memsize parameter.  Change callers.
(Archive::get_elf_object_for_member): Call plugin hooks for claiming
files.
(Archive::include_member): Add symbols from plugin objects.
* archive.h (Archive::get_file_and_offset): Add memsize parameter.
* descriptors.cc (Descriptors::open): Check for file descriptors
abandoned by plugins.
(Descriptors::claim_for_plugin): New function.
* descriptors.h (Descriptors::claim_for_plugin): New function.
(Open_descriptor::is_claimed): New field.
(claim_descriptor_for_plugin): New function.
* fileread.cc (File_read::claim_for_plugin): New function.
* fileread.h (File_read::claim_for_plugin): New function.
(File_read::descriptor): New function.
* gold.cc: Include "plugin.h".
(queue_initial_tasks): Add task to call plugin hooks for generating
new object files.
* main.cc: Include "plugin.h".
(main): Load plugin libraries.
* object.h (Pluginobj): Declare.
(Object::pluginobj): New function.
(Object::do_pluginobj): New function.
(Object::set_target): New function.
* options.cc: Include "plugin.h".
(General_options::parse_plugin): New function.
(General_options::General_options): Initialize plugins_ field.
(General_options::add_plugin): New function.
* options.h (Plugin_manager): Declare.
(General_options): Add --plugin option.
(General_options::has_plugins): New function.
(General_options::plugins): New function.
(General_options::add_plugin): New function.
(General_options::plugins_): New field.
* plugin.cc: New file.
* plugin.h: New file.
* readsyms.cc: Include "plugin.h".
(Read_symbols::do_read_symbols): Check for archive before checking
for ELF file.  Call plugin hooks to claim files.
* resolve.cc (Symbol_table::resolve): Record when symbol is referenced
from a real object file; force override when processing replacement
files.
* symtab.cc (Symbol::init_fields): Initialize in_real_elf_ field.
(Symbol::init_base_object): Likewise.
(Symbol::init_base_output_data): Likewise.
(Symbol::init_base_output_segment): Likewise.
(Symbol::init_base_constant): Likewise.
(Symbol::init_base_undefined): Likewise.
(Symbol::output_section): Assert that object is not a plugin.
(Symbol_table::add_from_pluginobj): New function.
(Symbol_table::sized_finalize_symbol): Treat symbols from plugins as
undefined.
(Symbol_table::sized_write_globals): Likewise.
(Symbol_table::add_from_pluginobj): Instantiate template.
* symtab.h (Sized_pluginobj): Declare.
(Symbol::in_real_elf): New function.
(Symbol::set_in_real_elf): New function.
(Symbol::in_real_elf_): New field.
(Symbol_table::add_from_pluginobj): New function.

* testsuite/Makefile.am (AM_CFLAGS): New variable.
(LIBDL): New variable.
(LDADD): Add LIBDL.
(check_PROGRAMS): Add plugin_test_1 and plugin_test_2.
(check_SCRIPTS): Add plugin_test_1.sh and plugin_test_2.sh.
(check_DATA): Add plugin_test_1.err and plugin_test_2.err.
(MOSTLYCLEANFILES): Likewise.
* testsuite/Makefile.in: Regenerate.
* testsuite/plugin_test.c: New file.
* testsuite/plugin_test_1.sh: New file.
* testsuite/plugin_test_2.sh: New file.

30 files changed:
gold/ChangeLog
gold/Makefile.am
gold/Makefile.in
gold/archive.cc
gold/archive.h
gold/config.in
gold/configure
gold/configure.ac
gold/descriptors.cc
gold/descriptors.h
gold/fileread.cc
gold/fileread.h
gold/gold.cc
gold/main.cc
gold/object.h
gold/options.cc
gold/options.h
gold/plugin.cc [new file with mode: 0644]
gold/plugin.h [new file with mode: 0644]
gold/readsyms.cc
gold/resolve.cc
gold/symtab.cc
gold/symtab.h
gold/testsuite/Makefile.am
gold/testsuite/Makefile.in
gold/testsuite/plugin_test.c [new file with mode: 0644]
gold/testsuite/plugin_test_1.sh [new file with mode: 0755]
gold/testsuite/plugin_test_2.sh [new file with mode: 0755]
include/ChangeLog
include/plugin-api.h [new file with mode: 0644]

index 22e4263b9b5e70ccfa723aece6c48b459e6aab7b..6b1a4f6762d0e3e8181055f5a23c4e2c1c84c3f3 100644 (file)
@@ -1,3 +1,88 @@
+2008-09-19  Cary Coutant  <ccoutant@google.com>
+
+       Add plugin functionality for link-time optimization (LTO).
+       * configure.ac (plugins): Add --enable-plugins option.
+       * configure: Regenerate.
+       * config.in: Regenerate.
+       * Makefile.am (LIBDL): New variable.
+       (CCFILES): Add plugin.cc.
+       (HFILES): Add plugin.h.
+       (ldadd_var): Add LIBDL.
+       * Makefile.in: Regenerate.
+
+       * archive.cc: Include "plugin.h".
+       (Archive::setup): Don't preread archive symbols when using a plugin.
+       (Archive::get_file_and_offset): Add memsize parameter.  Change callers.
+       (Archive::get_elf_object_for_member): Call plugin hooks for claiming
+       files.
+       (Archive::include_member): Add symbols from plugin objects.
+       * archive.h (Archive::get_file_and_offset): Add memsize parameter.
+       * descriptors.cc (Descriptors::open): Check for file descriptors
+       abandoned by plugins.
+       (Descriptors::claim_for_plugin): New function.
+       * descriptors.h (Descriptors::claim_for_plugin): New function.
+       (Open_descriptor::is_claimed): New field.
+       (claim_descriptor_for_plugin): New function.
+       * fileread.cc (File_read::claim_for_plugin): New function.
+       * fileread.h (File_read::claim_for_plugin): New function.
+       (File_read::descriptor): New function.
+       * gold.cc: Include "plugin.h".
+       (queue_initial_tasks): Add task to call plugin hooks for generating
+       new object files.
+       * main.cc: Include "plugin.h".
+       (main): Load plugin libraries.
+       * object.h (Pluginobj): Declare.
+       (Object::pluginobj): New function.
+       (Object::do_pluginobj): New function.
+       (Object::set_target): New function.
+       * options.cc: Include "plugin.h".
+       (General_options::parse_plugin): New function.
+       (General_options::General_options): Initialize plugins_ field.
+       (General_options::add_plugin): New function.
+       * options.h (Plugin_manager): Declare.
+       (General_options): Add --plugin option.
+       (General_options::has_plugins): New function.
+       (General_options::plugins): New function.
+       (General_options::add_plugin): New function.
+       (General_options::plugins_): New field.
+       * plugin.cc: New file.
+       * plugin.h: New file.
+       * readsyms.cc: Include "plugin.h".
+       (Read_symbols::do_read_symbols): Check for archive before checking
+       for ELF file.  Call plugin hooks to claim files.
+       * resolve.cc (Symbol_table::resolve): Record when symbol is referenced
+       from a real object file; force override when processing replacement
+       files.
+       * symtab.cc (Symbol::init_fields): Initialize in_real_elf_ field.
+       (Symbol::init_base_object): Likewise.
+       (Symbol::init_base_output_data): Likewise.
+       (Symbol::init_base_output_segment): Likewise.
+       (Symbol::init_base_constant): Likewise.
+       (Symbol::init_base_undefined): Likewise.
+       (Symbol::output_section): Assert that object is not a plugin.
+       (Symbol_table::add_from_pluginobj): New function.
+       (Symbol_table::sized_finalize_symbol): Treat symbols from plugins as
+       undefined.
+       (Symbol_table::sized_write_globals): Likewise.
+       (Symbol_table::add_from_pluginobj): Instantiate template.
+       * symtab.h (Sized_pluginobj): Declare.
+       (Symbol::in_real_elf): New function.
+       (Symbol::set_in_real_elf): New function.
+       (Symbol::in_real_elf_): New field.
+       (Symbol_table::add_from_pluginobj): New function.
+
+       * testsuite/Makefile.am (AM_CFLAGS): New variable.
+       (LIBDL): New variable.
+       (LDADD): Add LIBDL.
+       (check_PROGRAMS): Add plugin_test_1 and plugin_test_2.
+       (check_SCRIPTS): Add plugin_test_1.sh and plugin_test_2.sh.
+       (check_DATA): Add plugin_test_1.err and plugin_test_2.err.
+       (MOSTLYCLEANFILES): Likewise.
+       * testsuite/Makefile.in: Regenerate.
+       * testsuite/plugin_test.c: New file.
+       * testsuite/plugin_test_1.sh: New file.
+       * testsuite/plugin_test_2.sh: New file.
+
 2008-09-16  Ian Lance Taylor  <iant@google.com>
 
        * target-reloc.h (relocate_section): Check whether a symbol is
index e706703303bb3f3b14b07f598289a1ab9835164c..74db37a336dc668e4594d4ec330b2fa6392de72a 100644 (file)
@@ -19,6 +19,10 @@ INCLUDES = \
 
 LIBIBERTY = ../libiberty/libiberty.a
 
+if PLUGINS
+LIBDL = -ldl
+endif
+
 if THREADS
 THREADSLIB = -lpthread
 endif
@@ -53,6 +57,7 @@ CCFILES = \
        options.cc \
        output.cc \
        parameters.cc \
+       plugin.cc \
        readsyms.cc \
        reduced_debug_output.cc \
        reloc.cc \
@@ -90,6 +95,7 @@ HFILES = \
        options.h \
        output.h \
        parameters.h \
+       plugin.h \
        readsyms.h \
        reduced_debug_output.h \
        reloc.h \
@@ -122,7 +128,8 @@ libgold_a_SOURCES = $(CCFILES) $(HFILES) $(YFILES)
 
 sources_var = main.cc
 deps_var = $(TARGETOBJS) libgold.a $(LIBIBERTY) $(LIBINTL_DEP)
-ldadd_var = $(TARGETOBJS) libgold.a $(LIBIBERTY) $(LIBINTL) $(THREADSLIB)
+ldadd_var = $(TARGETOBJS) libgold.a $(LIBIBERTY) $(LIBINTL) \
+        $(THREADSLIB) $(LIBDL)
 
 ld_new_SOURCES = $(sources_var)
 ld_new_DEPENDENCIES = $(deps_var) $(LIBOBJS)
index bbc54171f2d6f98e9e729b75aaaa9edee92b5f29..f6a80d020dc654eeb86b134203488f217d7c1755 100644 (file)
@@ -82,7 +82,7 @@ am__objects_1 = archive.$(OBJEXT) binary.$(OBJEXT) common.$(OBJEXT) \
        fileread.$(OBJEXT) gold.$(OBJEXT) gold-threads.$(OBJEXT) \
        layout.$(OBJEXT) mapfile.$(OBJEXT) merge.$(OBJEXT) \
        object.$(OBJEXT) options.$(OBJEXT) output.$(OBJEXT) \
-       parameters.$(OBJEXT) readsyms.$(OBJEXT) \
+       parameters.$(OBJEXT) plugin.$(OBJEXT) readsyms.$(OBJEXT) \
        reduced_debug_output.$(OBJEXT) reloc.$(OBJEXT) \
        resolve.$(OBJEXT) script-sections.$(OBJEXT) script.$(OBJEXT) \
        stringpool.$(OBJEXT) symtab.$(OBJEXT) target-select.$(OBJEXT) \
@@ -101,7 +101,7 @@ am__DEPENDENCIES_1 =
 am__DEPENDENCIES_2 = ../libiberty/libiberty.a
 am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) libgold.a \
        $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 am__DEPENDENCIES_4 = @LIBOBJS@
 am__ld1_SOURCES_DIST = main.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@am_ld1_OBJECTS = $(am__objects_4)
@@ -227,6 +227,8 @@ PACKAGE_STRING = @PACKAGE_STRING@
 PACKAGE_TARNAME = @PACKAGE_TARNAME@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PATH_SEPARATOR = @PATH_SEPARATOR@
+PLUGINS_FALSE = @PLUGINS_FALSE@
+PLUGINS_TRUE = @PLUGINS_TRUE@
 POSUB = @POSUB@
 RANDOM_SEED_CFLAGS = @RANDOM_SEED_CFLAGS@
 RANLIB = @RANLIB@
@@ -308,6 +310,7 @@ INCLUDES = \
        @INCINTL@
 
 LIBIBERTY = ../libiberty/libiberty.a
+@PLUGINS_TRUE@LIBDL = -ldl
 @THREADS_TRUE@THREADSLIB = -lpthread
 AM_YFLAGS = -d
 noinst_LIBRARIES = libgold.a
@@ -336,6 +339,7 @@ CCFILES = \
        options.cc \
        output.cc \
        parameters.cc \
+       plugin.cc \
        readsyms.cc \
        reduced_debug_output.cc \
        reloc.cc \
@@ -373,6 +377,7 @@ HFILES = \
        options.h \
        output.h \
        parameters.h \
+       plugin.h \
        readsyms.h \
        reduced_debug_output.h \
        reloc.h \
@@ -403,7 +408,9 @@ ALL_TARGETOBJS = \
 libgold_a_SOURCES = $(CCFILES) $(HFILES) $(YFILES)
 sources_var = main.cc
 deps_var = $(TARGETOBJS) libgold.a $(LIBIBERTY) $(LIBINTL_DEP)
-ldadd_var = $(TARGETOBJS) libgold.a $(LIBIBERTY) $(LIBINTL) $(THREADSLIB)
+ldadd_var = $(TARGETOBJS) libgold.a $(LIBIBERTY) $(LIBINTL) \
+        $(THREADSLIB) $(LIBDL)
+
 ld_new_SOURCES = $(sources_var)
 ld_new_DEPENDENCIES = $(deps_var) $(LIBOBJS)
 ld_new_LDADD = $(ldadd_var) $(LIBOBJS)
@@ -550,6 +557,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parameters.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerpc.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readsyms.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reduced_debug_output.Po@am__quote@
index f5ad5744ec9e0479961c0008274bb6f779e8fe25..7fd1a1785f4651a21e57e4584a7e7d71eebbe419 100644 (file)
@@ -37,6 +37,7 @@
 #include "symtab.h"
 #include "object.h"
 #include "archive.h"
+#include "plugin.h"
 
 namespace gold
 {
@@ -126,6 +127,9 @@ Archive::setup(Input_objects* input_objects)
                        && parameters->options().preread_archive_symbols());
 #ifndef ENABLE_THREADS
   preread_syms = false;
+#else
+  if (parameters->options().has_plugins())
+    preread_syms = false;
 #endif
   if (preread_syms)
     this->read_all_symbols(input_objects);
@@ -439,11 +443,11 @@ Archive::end()
 bool
 Archive::get_file_and_offset(off_t off, Input_objects* input_objects,
                              Input_file** input_file, off_t* memoff,
-                             std::string* member_name)
+                             off_t* memsize, std::string* member_name)
 {
   off_t nested_off;
 
-  this->read_header(off, false, member_name, &nested_off);
+  *memsize = this->read_header(off, false, member_name, &nested_off);
 
   *input_file = this->input_file_;
   *memoff = off + static_cast<off_t>(sizeof(Archive_header));
@@ -488,8 +492,8 @@ Archive::get_file_and_offset(off_t off, Input_objects* input_objects,
             this->nested_archives_.insert(std::make_pair(*member_name, arch));
           gold_assert(ins.second);
         }
-      return arch->get_file_and_offset(nested_off, input_objects,
-                                       input_file, memoff, member_name);
+      return arch->get_file_and_offset(nested_off, input_objects, input_file,
+                                       memoff, memsize, member_name);
     }
 
   // This is an external member of a thin archive.  Open the
@@ -503,6 +507,7 @@ Archive::get_file_and_offset(off_t off, Input_objects* input_objects,
     return false;
 
   *memoff = 0;
+  *memsize = (*input_file)->file().filesize();
   return true;
 }
 
@@ -515,11 +520,26 @@ Archive::get_elf_object_for_member(off_t off, Input_objects* input_objects)
   std::string member_name;
   Input_file* input_file;
   off_t memoff;
+  off_t memsize;
 
   if (!this->get_file_and_offset(off, input_objects, &input_file, &memoff,
-                                 &member_name))
+                                 &memsize, &member_name))
     return NULL;
 
+  if (parameters->options().has_plugins())
+    {
+      Object* obj = parameters->options().plugins()->claim_file(input_file,
+                                                                memoff,
+                                                                memsize);
+      if (obj != NULL)
+        {
+          // The input file was claimed by a plugin, and its symbols
+          // have been provided by the plugin.
+         input_file->file().claim_for_plugin();
+          return obj;
+        }
+    }
+
   off_t filesize = input_file->file().filesize();
   int read_size = elfcpp::Elf_sizes<64>::ehdr_size;
   if (filesize - memoff < read_size)
@@ -753,6 +773,13 @@ Archive::include_member(Symbol_table* symtab, Layout* layout,
   if (mapfile != NULL)
     mapfile->report_include_archive_member(obj->name(), sym, why);
 
+  Pluginobj* pluginobj = obj->pluginobj();
+  if (pluginobj != NULL)
+    {
+      pluginobj->add_symbols(symtab, layout);
+      return;
+    }
+
   if (input_objects->add_object(obj))
     {
       Read_symbols_data sd;
index 24fdb2b1f27ab0fbed187386270db0d519535019..6b99aed1e80168a3a87c6bcd195e1f298453922d 100644 (file)
@@ -184,7 +184,7 @@ class Archive
   bool
   get_file_and_offset(off_t off, Input_objects* input_objects,
                       Input_file** input_file, off_t* memoff,
-                      std::string* member_name);
+                      off_t* memsize, std::string* member_name);
 
   // Return an ELF object for the member at offset OFF.  Set *MEMBER_NAME to
   // the name of the member.
index 73cc6e9eb58e000a1be7184f05542a7037441201..309f84b6e5289fa41b0af117fdce3017a5b7d64b 100644 (file)
@@ -4,6 +4,9 @@
    language is requested. */
 #undef ENABLE_NLS
 
+/* Define to enable linker plugins */
+#undef ENABLE_PLUGINS
+
 /* Define to do multi-threaded linking */
 #undef ENABLE_THREADS
 
index f2ec1efa2bd5115c8541a3e22ce6375c231cb431..d898870a6e27e80a9576e694153a03040ff3ae1a 100755 (executable)
@@ -309,7 +309,7 @@ ac_includes_default="\
 # include <unistd.h>
 #endif"
 
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar THREADS_TRUE THREADS_FALSE TARGETOBJS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE YACC RANLIB ac_ct_RANLIB LN_S USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE NATIVE_LINKER_TRUE NATIVE_LINKER_FALSE GCC_TRUE GCC_FALSE FN_PTRS_IN_SO_WITHOUT_PIC_TRUE FN_PTRS_IN_SO_WITHOUT_PIC_FALSE TLS_TRUE TLS_FALSE STATIC_TLS_TRUE STATIC_TLS_FALSE OMP_SUPPORT_TRUE OMP_SUPPORT_FALSE TLS_GNU2_DIALECT_TRUE TLS_GNU2_DIALECT_FALSE TLS_DESCRIPTORS_TRUE TLS_DESCRIPTORS_FALSE CONSTRUCTOR_PRIORITY_TRUE CONSTRUCTOR_PRIORITY_FALSE RANDOM_SEED_CFLAGS WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CFLAGS LIBOBJS CPP EGREP HAVE_ZLIB_TRUE HAVE_ZLIB_FALSE CXXCPP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LTLIBOBJS'
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar THREADS_TRUE THREADS_FALSE PLUGINS_TRUE PLUGINS_FALSE TARGETOBJS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE YACC RANLIB ac_ct_RANLIB LN_S USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE NATIVE_LINKER_TRUE NATIVE_LINKER_FALSE GCC_TRUE GCC_FALSE FN_PTRS_IN_SO_WITHOUT_PIC_TRUE FN_PTRS_IN_SO_WITHOUT_PIC_FALSE TLS_TRUE TLS_FALSE STATIC_TLS_TRUE STATIC_TLS_FALSE OMP_SUPPORT_TRUE OMP_SUPPORT_FALSE TLS_GNU2_DIALECT_TRUE TLS_GNU2_DIALECT_FALSE TLS_DESCRIPTORS_TRUE TLS_DESCRIPTORS_FALSE CONSTRUCTOR_PRIORITY_TRUE CONSTRUCTOR_PRIORITY_FALSE RANDOM_SEED_CFLAGS WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CFLAGS LIBOBJS CPP EGREP HAVE_ZLIB_TRUE HAVE_ZLIB_FALSE CXXCPP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LTLIBOBJS'
 ac_subst_files=''
 ac_pwd=`pwd`
 
@@ -866,6 +866,7 @@ Optional Features:
   --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
   --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
   --enable-threads        multi-threaded linking
+  --enable-plugins        linker plugins
   --enable-targets        alternative target configurations
   --disable-dependency-tracking  speeds up one-time build
   --enable-dependency-tracking   do not reject slow dependency extractors
@@ -1959,6 +1960,35 @@ else
 fi
 
 
+# Check whether --enable-plugins or --disable-plugins was given.
+if test "${enable_plugins+set}" = set; then
+  enableval="$enable_plugins"
+  case "${enableval}" in
+  yes | "") plugins=yes ;;
+  no) plugins=no ;;
+  *) plugins=yes ;;
+ esac
+else
+  plugins=no
+fi;
+if test "$plugins" = "yes"; then
+
+cat >>confdefs.h <<\_ACEOF
+#define ENABLE_PLUGINS 1
+_ACEOF
+
+fi
+
+
+if test "$plugins" = "yes"; then
+  PLUGINS_TRUE=
+  PLUGINS_FALSE='#'
+else
+  PLUGINS_TRUE='#'
+  PLUGINS_FALSE=
+fi
+
+
 # Check whether --enable-targets or --disable-targets was given.
 if test "${enable_targets+set}" = set; then
   enableval="$enable_targets"
@@ -6720,6 +6750,13 @@ echo "$as_me: error: conditional \"THREADS\" was never defined.
 Usually this means the macro was only invoked conditionally." >&2;}
    { (exit 1); exit 1; }; }
 fi
+if test -z "${PLUGINS_TRUE}" && test -z "${PLUGINS_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"PLUGINS\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"PLUGINS\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
 if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
   { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined.
 Usually this means the macro was only invoked conditionally." >&5
@@ -7396,6 +7433,8 @@ s,@am__tar@,$am__tar,;t t
 s,@am__untar@,$am__untar,;t t
 s,@THREADS_TRUE@,$THREADS_TRUE,;t t
 s,@THREADS_FALSE@,$THREADS_FALSE,;t t
+s,@PLUGINS_TRUE@,$PLUGINS_TRUE,;t t
+s,@PLUGINS_FALSE@,$PLUGINS_FALSE,;t t
 s,@TARGETOBJS@,$TARGETOBJS,;t t
 s,@CC@,$CC,;t t
 s,@CFLAGS@,$CFLAGS,;t t
index 3fbcd0ea076761f0d512b3af215a130f905ccba4..b0efef1e55c97e1e679044cefd48650fbff5ba37 100644 (file)
@@ -53,6 +53,20 @@ if test "$threads" = "yes"; then
 fi
 AM_CONDITIONAL(THREADS, test "$threads" = "yes")
 
+AC_ARG_ENABLE([plugins],
+[  --enable-plugins        linker plugins],
+[case "${enableval}" in
+  yes | "") plugins=yes ;;
+  no) plugins=no ;;
+  *) plugins=yes ;;
+ esac],
+[plugins=no])
+if test "$plugins" = "yes"; then
+  AC_DEFINE(ENABLE_PLUGINS, 1,
+           [Define to enable linker plugins])
+fi
+AM_CONDITIONAL(PLUGINS, test "$plugins" = "yes")
+
 AC_ARG_ENABLE([targets],
 [  --enable-targets        alternative target configurations],
 [case "${enableval}" in
index 75a7a869c594b13cac91dda4c9060f9d0272aaca..73c03bf4a457552baafd7e13ba6e9a3b3c3e458c 100644 (file)
@@ -115,7 +115,9 @@ Descriptors::open(int descriptor, const char* name, int flags, int mode)
          pod->inuse = true;
          pod->is_write = (flags & O_ACCMODE) != O_RDONLY;
 
-         ++this->current_;
+          if (!pod->is_claimed)
+           ++this->current_;
+          pod->is_claimed = false;
          if (this->current_ >= this->limit_)
            this->close_some_descriptor();
 
@@ -166,6 +168,24 @@ Descriptors::release(int descriptor, bool permanent)
     }
 }
 
+// Claim the file descriptor DESCRIPTOR for a plugin.  This effectively
+// removes the descriptor from the pool of linker-managed descriptors,
+// as the plugin will assume responsibility for closing it.
+// The IS_CLAIMED flag allows us to recognize when a file descriptor
+// has been reused after being closed by the plugin.
+
+void
+Descriptors::claim_for_plugin(int descriptor)
+{
+  Hold_lock hl(*this->lock_);
+
+  gold_assert(descriptor >= 0
+             && (static_cast<size_t>(descriptor)
+                 < this->open_descriptors_.size()));
+  Open_descriptor* pod = &this->open_descriptors_[descriptor];
+  pod->is_claimed = true;
+}
+
 // Close some descriptor.  The lock is held when this is called.  We
 // close the descriptor on the top of the free stack.  Note that this
 // is the opposite of an LRU algorithm--we close the most recently
index 6a6ab61be0d655ff31d41b8cf0a839957a2148f9..359008c1f077e8b77a3291ca88c412cdfd6ca1c1 100644 (file)
@@ -56,6 +56,12 @@ class Descriptors
   void
   release(int descriptor, bool permanent);
 
+  // Claim the file descriptor DESCRIPTOR for a plugin.  This effectively
+  // removes the descriptor from the pool of linker-managed descriptors,
+  // as the plugin will assume responsibility for closing it.
+  void
+  claim_for_plugin(int descriptor);
+
  private:
   // Information kept for a descriptor.
   struct Open_descriptor
@@ -69,6 +75,8 @@ class Descriptors
     bool inuse;
     // Whether this is a write descriptor.
     bool is_write;
+    // Whether the descriptor has been claimed for a plugin.
+    bool is_claimed;
   };
 
   bool
@@ -100,6 +108,10 @@ inline void
 release_descriptor(int descriptor, bool permanent)
 { descriptors.release(descriptor, permanent); }
 
+inline void
+claim_descriptor_for_plugin(int descriptor)
+{ descriptors.claim_for_plugin(descriptor); }
+
 } // End namespace gold.
 
 #endif // !defined(GOLD_DESCRIPTORS_H)
index 883f8c80895b987ddb84969666568ef1146b1132..72abd6bc2edbdec3a024e5d3e8f07a0fdb5f5c4d 100644 (file)
@@ -191,6 +191,19 @@ File_read::release()
   this->released_ = true;
 }
 
+// Claim the file for a plugin.  This effectively releases the file without
+// closing it; the plugin will assume responsibility for closing it.
+
+void
+File_read::claim_for_plugin()
+{
+  gold_assert(this->is_locked());
+  claim_descriptor_for_plugin(this->descriptor_);
+  this->descriptor_ = -1;
+  this->is_descriptor_opened_ = false;
+  this->released_ = true;
+}
+
 // Lock the file.
 
 void
index 4236ce0ccef66edae4275d05a02a8ff512205973..1b776f1c16744fdf8e1dc8430a13349d1b18fb89 100644 (file)
@@ -109,6 +109,11 @@ class File_read
   void
   release();
 
+  // Claim the file for a plugin.  This effectively releases the file without
+  // closing it; the plugin will assume responsibility for closing it.
+  void
+  claim_for_plugin();
+
   // Return the size of the file.
   off_t
   filesize() const
@@ -183,6 +188,14 @@ class File_read
   static void
   print_stats();
 
+  // Return the open file descriptor (for plugins).
+  int
+  descriptor() const
+  {
+    gold_assert(this->descriptor_ >= 0);
+    return this->descriptor_;
+  }
+
  private:
   // This class may not be copied.
   File_read(const File_read&);
index 42c6248d4b2b875bf8cca0b34c33410edcdc81b9..6a536b8c7690fe71bcbe1ce318beb00417aa046f 100644 (file)
@@ -40,6 +40,7 @@
 #include "layout.h"
 #include "reloc.h"
 #include "defstd.h"
+#include "plugin.h"
 
 namespace gold
 {
@@ -151,6 +152,16 @@ queue_initial_tasks(const General_options& options,
       this_blocker = next_blocker;
     }
 
+  if (options.has_plugins())
+    {
+      Task_token* next_blocker = new Task_token(true);
+      next_blocker->add_blocker();
+      workqueue->queue(new Plugin_hook(options, input_objects, symtab, layout,
+                                      &search_path, mapfile, this_blocker,
+                                      next_blocker));
+      this_blocker = next_blocker;
+    }
+
   workqueue->queue(new Task_function(new Middle_runner(options,
                                                       input_objects,
                                                       symtab,
index b842159030081b0a9f68852af8d8029363e61290..8e8e8f9d2f40e663f380205ce332aa85aa34ccec 100644 (file)
@@ -42,6 +42,7 @@
 #include "archive.h"
 #include "symtab.h"
 #include "layout.h"
+#include "plugin.h"
 
 using namespace gold;
 
@@ -190,6 +191,10 @@ main(int argc, char** argv)
   if (parameters->options().relocatable())
     command_line.script_options().version_script_info()->clear();
 
+  // Load plugin libraries.
+  if (command_line.options().has_plugins())
+    command_line.options().plugins()->load_plugins();
+
   // The work queue.
   Workqueue workqueue(command_line.options());
 
index 188f1f208fe23845b37d3b7f77dde7b8efcc6a8f..1c4fc6768ce58e446bfd66ea01396eabf4738cba 100644 (file)
@@ -42,6 +42,7 @@ class Layout;
 class Output_section;
 class Output_file;
 class Output_symtab_xindex;
+class Pluginobj;
 class Dynobj;
 class Object_merge_map;
 class Relocatable_relocs;
@@ -214,6 +215,12 @@ class Object
   is_dynamic() const
   { return this->is_dynamic_; }
 
+  // Returns NULL for Objects that are not plugin objects.  This method
+  // is overridden in the Pluginobj class.
+  Pluginobj*
+  pluginobj()
+  { return this->do_pluginobj(); }
+
   // Return the target structure associated with this object.
   Target*
   target() const
@@ -431,7 +438,18 @@ class Object
                           size_t* used) const
   { this->do_get_global_symbol_counts(symtab, defined, used); }
 
+  // Set the target.
+  void
+  set_target(Target* target)
+  { this->target_ = target; }
+
  protected:
+  // Returns NULL for Objects that are not plugin objects.  This method
+  // is overridden in the Pluginobj class.
+  virtual Pluginobj*
+  do_pluginobj()
+  { return NULL; }
+
   // Read the symbols--implemented by child class.
   virtual void
   do_read_symbols(Read_symbols_data*) = 0;
index 78f0b1aa2baca818009d21ff0861ccae9efc2f97..08b67fdf46b7e0a751ce5e3a241554bf5d5dfafe 100644 (file)
@@ -36,6 +36,7 @@
 #include "script.h"
 #include "target-select.h"
 #include "options.h"
+#include "plugin.h"
 
 namespace gold
 {
@@ -296,6 +297,15 @@ General_options::parse_library(const char*, const char* arg,
   cmdline->inputs().add_file(file);
 }
 
+#ifdef ENABLE_PLUGINS
+void
+General_options::parse_plugin(const char*, const char* arg,
+                              Command_line*)
+{
+  this->add_plugin(arg);
+}
+#endif // ENABLE_PLUGINS
+
 void
 General_options::parse_R(const char* option, const char* arg,
                          Command_line* cmdline)
@@ -594,7 +604,7 @@ namespace gold
 
 General_options::General_options()
   : execstack_status_(General_options::EXECSTACK_FROM_INPUT), static_(false),
-    do_demangle_(false)
+    do_demangle_(false), plugins_()
 {
 }
 
@@ -632,6 +642,16 @@ General_options::add_sysroot()
   free(canonical_sysroot);
 }
 
+// Add a plugin and its arguments to the list of plugins.
+
+void
+General_options::add_plugin(const char* arg)
+{
+  if (this->plugins_ == NULL)
+    this->plugins_ = new Plugin_manager(*this);
+  this->plugins_->add_plugin(arg);
+}
+
 // Set up variables and other state that isn't set up automatically by
 // the parse routine, and ensure options don't contradict each other
 // and are otherwise kosher.
index 292673018f00ab7b5c5b08bd0c269595d3e6f026..167e58a09ea932fea2298ef645432b0f84140e78 100644 (file)
@@ -54,6 +54,7 @@ class Search_directory;
 class Input_file_group;
 class Position_dependent_options;
 class Target;
+class Plugin_manager;
 
 // The nested namespace is to contain all the global variables and
 // structs that need to be defined in the .h file, but do not need to
@@ -689,8 +690,14 @@ class General_options
   DEFINE_string(oformat, options::EXACTLY_TWO_DASHES, '\0', "elf",
                N_("Set output format"), N_("[binary]"));
 
+#ifdef ENABLE_PLUGINS
+  DEFINE_special(plugin, options::TWO_DASHES, '\0',
+                 N_("Load a plugin library"), N_("PLUGIN[,ARG,...]"));
+#endif
+
   DEFINE_bool(preread_archive_symbols, options::TWO_DASHES, '\0', false,
               N_("Preread archive symbols when multi-threaded"), NULL);
+
   DEFINE_string(print_symbol_counts, options::TWO_DASHES, '\0', NULL,
                N_("Print symbols defined and used for each input"),
                N_("FILENAME"));
@@ -916,6 +923,16 @@ class General_options
   do_demangle() const
   { return this->do_demangle_; }
 
+  // Returns TRUE if any plugin libraries have been loaded.
+  bool
+  has_plugins() const
+  { return this->plugins_ != NULL; }
+
+  // Return a pointer to the plugin manager.
+  Plugin_manager*
+  plugins() const
+  { return this->plugins_; }
+
  private:
   // Don't copy this structure.
   General_options(const General_options&);
@@ -953,12 +970,18 @@ class General_options
   void
   add_sysroot();
 
+  // Add a plugin and its arguments to the list of plugins.
+  void
+  add_plugin(const char* arg);
+
   // Whether to mark the stack as executable.
   Execstack execstack_status_;
   // Whether to do a static link.
   bool static_;
   // Whether to do demangling.
   bool do_demangle_;
+  // List of plugin libraries.
+  Plugin_manager* plugins_;
 };
 
 // The position-dependent options.  We use this to store the state of
diff --git a/gold/plugin.cc b/gold/plugin.cc
new file mode 100644 (file)
index 0000000..19f7619
--- /dev/null
@@ -0,0 +1,968 @@
+// plugin.c -- plugin manager for gold      -*- C++ -*-
+
+// Copyright 2008 Free Software Foundation, Inc.
+// Written by Cary Coutant <ccoutant@google.com>.
+
+// This file is part of gold.
+
+// 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 <cstdio>
+#include <cstdarg>
+#include <cstring>
+#include <string>
+#include <vector>
+#include <dlfcn.h>
+
+#include "gold.h"
+#include "parameters.h"
+#include "errors.h"
+#include "fileread.h"
+#include "layout.h"
+#include "options.h"
+#include "plugin.h"
+#include "target.h"
+#include "readsyms.h"
+#include "symtab.h"
+#include "elfcpp.h"
+
+namespace gold
+{
+
+#ifdef ENABLE_PLUGINS
+
+// The linker's exported interfaces.
+
+extern "C"
+{
+
+static enum ld_plugin_status
+register_claim_file(ld_plugin_claim_file_handler handler);
+
+static enum ld_plugin_status
+register_all_symbols_read(ld_plugin_all_symbols_read_handler handler);
+
+static enum ld_plugin_status
+register_cleanup(ld_plugin_cleanup_handler handler);
+
+static enum ld_plugin_status
+add_symbols(void *handle, int nsyms, const struct ld_plugin_symbol *syms);
+
+static enum ld_plugin_status
+get_symbols(const void *handle, int nsyms, struct ld_plugin_symbol *syms);
+
+static enum ld_plugin_status
+add_input_file(char *pathname);
+
+static enum ld_plugin_status
+message(int level, char *format, ...);
+
+};
+
+#endif // ENABLE_PLUGINS
+
+static Pluginobj* make_sized_plugin_object(Input_file* input_file,
+                                           off_t offset);
+
+// Plugin methods.
+
+// Load one plugin library.
+
+void
+Plugin::load()
+{
+#ifdef ENABLE_PLUGINS
+  std::string filename;
+  std::vector<std::string> args;
+
+  // Parse the filename and arguments, each separated by commas.
+  // FIXME:  Temporarily allowing semicolon as an argument separator
+  // so args can be passed through gcc's -Wl,... option, which
+  // breaks arguments at the commas.
+  const char* p = this->args_;
+  int n = strcspn(p, ",;");
+  filename.assign(p, n);
+  p += n;
+  while (*p == ',' || *p == ';')
+    {
+      ++p;
+      n = strcspn(p, ",;");
+      args.push_back(std::string(p, n));
+      p += n;
+    }
+
+  // Load the plugin library.
+  // FIXME: Look for the library in standard locations.
+  this->handle_ = dlopen(filename.c_str(), RTLD_NOW);
+  if (this->handle_ == NULL)
+    {
+      gold_error(_("%s: could not load plugin library"), filename.c_str());
+      return;
+    }
+
+  // Find the plugin's onload entry point.
+  ld_plugin_onload onload = reinterpret_cast<ld_plugin_onload>
+    (dlsym(this->handle_, "onload"));
+  if (onload == NULL)
+    {
+      gold_error(_("%s: could not find onload entry point"), filename.c_str());
+      return;
+    }
+
+  // Get the linker's version number.
+  const char* ver = get_version_string();
+  int major = 0;
+  int minor = 0;
+  sscanf(ver, "%d.%d", &major, &minor);
+
+  // Allocate and populate a transfer vector.
+  const int tv_fixed_size = 11;
+  int tv_size = args.size() + tv_fixed_size;
+  ld_plugin_tv *tv = new ld_plugin_tv[tv_size];
+
+  int i = 0;
+  tv[i].tv_tag = LDPT_API_VERSION;
+  tv[i].tv_u.tv_val = LD_PLUGIN_API_VERSION;
+
+  ++i;
+  tv[i].tv_tag = LDPT_GOLD_VERSION;
+  tv[i].tv_u.tv_val = major * 100 + minor;
+
+  ++i;
+  tv[i].tv_tag = LDPT_LINKER_OUTPUT;
+  if (parameters->options().relocatable())
+    tv[i].tv_u.tv_val = LDPO_REL;
+  else if (parameters->options().shared())
+    tv[i].tv_u.tv_val = LDPO_DYN;
+  else
+    tv[i].tv_u.tv_val = LDPO_EXEC;
+
+  for (unsigned int j = 0; j < args.size(); ++j)
+    {
+      ++i;
+      tv[i].tv_tag = LDPT_OPTION;
+      tv[i].tv_u.tv_string = args[j].c_str();
+    }
+
+  ++i;
+  tv[i].tv_tag = LDPT_REGISTER_CLAIM_FILE_HOOK;
+  tv[i].tv_u.tv_register_claim_file = register_claim_file;
+
+  ++i;
+  tv[i].tv_tag = LDPT_REGISTER_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_ADD_SYMBOLS;
+  tv[i].tv_u.tv_add_symbols = add_symbols;
+
+  ++i;
+  tv[i].tv_tag = LDPT_GET_SYMBOLS;
+  tv[i].tv_u.tv_get_symbols = get_symbols;
+
+  ++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_MESSAGE;
+  tv[i].tv_u.tv_message = message;
+
+  ++i;
+  tv[i].tv_tag = LDPT_NULL;
+  tv[i].tv_u.tv_val = 0;
+
+  gold_assert(i == tv_size - 1);
+
+  // Call the onload entry point.
+  (*onload)(tv);
+
+  delete tv;
+#endif // ENABLE_PLUGINS
+}
+
+// Call the plugin claim-file handler.
+
+inline bool
+Plugin::claim_file(struct ld_plugin_input_file *plugin_input_file)
+{
+  int claimed = 0;
+
+  if (this->claim_file_handler_ != NULL)
+    {
+      (*this->claim_file_handler_)(plugin_input_file, &claimed);
+      if (claimed)
+        return true;
+    }
+  return false;
+}
+
+// Call the all-symbols-read handler.
+
+inline void
+Plugin::all_symbols_read()
+{
+  if (this->all_symbols_read_handler_ != NULL)
+    (*this->all_symbols_read_handler_)();
+}
+
+// Call the cleanup handler.
+
+inline void
+Plugin::cleanup()
+{
+  if (this->cleanup_handler_ != NULL)
+    (*this->cleanup_handler_)();
+}
+
+// Plugin_manager methods.
+
+Plugin_manager::~Plugin_manager()
+{
+  for (Plugin_list::iterator p = this->plugins_.begin();
+       p != this->plugins_.end();
+       ++p)
+    delete *p;
+  this->plugins_.clear();
+  for (Object_list::iterator obj = this->objects_.begin();
+       obj != this->objects_.end();
+       ++obj)
+    delete *obj;
+  this->objects_.clear();
+}
+
+// Load all plugin libraries.
+
+void
+Plugin_manager::load_plugins()
+{
+  for (this->current_ = this->plugins_.begin();
+       this->current_ != this->plugins_.end();
+       ++this->current_)
+    (*this->current_)->load();
+}
+
+// Call the plugin claim-file handlers in turn to see if any claim the file.
+
+Pluginobj*
+Plugin_manager::claim_file(Input_file* input_file, off_t offset,
+                           off_t filesize)
+{
+  if (this->in_replacement_phase_)
+    return NULL;
+
+  unsigned int handle = this->objects_.size();
+  this->input_file_ = input_file;
+  this->plugin_input_file_.name = input_file->filename().c_str();
+  this->plugin_input_file_.fd = input_file->file().descriptor();
+  this->plugin_input_file_.offset = offset;
+  this->plugin_input_file_.filesize = filesize;
+  this->plugin_input_file_.handle = reinterpret_cast<void*>(handle);
+
+  for (this->current_ = this->plugins_.begin();
+       this->current_ != this->plugins_.end();
+       ++this->current_)
+    {
+      if ((*this->current_)->claim_file(&this->plugin_input_file_))
+        {
+          if (this->objects_.size() <= handle)
+            {
+              gold_error(_("%s: plugin claimed the file "
+                           "but did not provide any symbols"),
+                         this->plugin_input_file_.name);
+              return NULL;
+            }
+          return this->objects_[handle];
+        }
+    }
+
+  return NULL;
+}
+
+// Call the all-symbols-read handlers.
+
+void
+Plugin_manager::all_symbols_read(Workqueue* workqueue,
+                                 Input_objects* input_objects,
+                                Symbol_table* symtab, Layout* layout,
+                                Dirsearch* dirpath, Mapfile* mapfile,
+                                Task_token** last_blocker)
+{
+  this->in_replacement_phase_ = true;
+  this->workqueue_ = workqueue;
+  this->input_objects_ = input_objects;
+  this->symtab_ = symtab;
+  this->layout_ = layout;
+  this->dirpath_ = dirpath;
+  this->mapfile_ = mapfile;
+  this->this_blocker_ = NULL;
+
+  for (this->current_ = this->plugins_.begin();
+       this->current_ != this->plugins_.end();
+       ++this->current_)
+    (*this->current_)->all_symbols_read();
+
+  *last_blocker = this->this_blocker_;
+}
+
+// Call the cleanup handlers.
+
+void
+Plugin_manager::cleanup()
+{
+  for (this->current_ = this->plugins_.begin();
+       this->current_ != this->plugins_.end();
+       ++this->current_)
+    (*this->current_)->cleanup();
+}
+
+// Make a new Pluginobj object.  This is called when the plugin calls
+// the add_symbols API.
+
+Pluginobj*
+Plugin_manager::make_plugin_object(unsigned int handle)
+{
+  // Make sure we aren't asked to make an object for the same handle twice.
+  if (this->objects_.size() != handle)
+    return NULL;
+
+  Pluginobj* obj = make_sized_plugin_object(this->input_file_,
+                                            this->plugin_input_file_.offset);
+  this->objects_.push_back(obj);
+  return obj;
+}
+
+// Add a new input file.
+
+ld_plugin_status
+Plugin_manager::add_input_file(char *pathname)
+{
+  Input_file_argument file(pathname, false, "", false, this->options_);
+  Input_argument* input_argument = new Input_argument(file);
+  Task_token* next_blocker = new Task_token(true);
+  next_blocker->add_blocker();
+  this->workqueue_->queue_soon(new Read_symbols(this->options_,
+                                                this->input_objects_,
+                                                this->symtab_,
+                                                this->layout_,
+                                                this->dirpath_,
+                                                this->mapfile_,
+                                                input_argument,
+                                                NULL,
+                                                this->this_blocker_,
+                                                next_blocker));
+  this->this_blocker_ = next_blocker;
+  return LDPS_OK;
+}
+
+// Class Pluginobj.
+
+Pluginobj::Pluginobj(const std::string& name, Input_file* input_file,
+                     off_t offset)
+  : Object(name, input_file, false, offset),
+    nsyms_(0), syms_(NULL), symbols_(), comdat_map_()
+{
+}
+
+// Get symbol resolution info.
+
+ld_plugin_status
+Pluginobj::get_symbol_resolution_info(int nsyms, ld_plugin_symbol* syms) const
+{
+  if (this->nsyms_ == 0)
+    return LDPS_NO_SYMS;
+  for (int i = 0; i < nsyms; i++)
+    {
+      ld_plugin_symbol* isym = &syms[i];
+      Symbol* lsym = this->symbols_[i];
+      ld_plugin_symbol_resolution res = LDPR_UNKNOWN;
+
+      if (lsym->is_undefined())
+        // The symbol remains undefined.
+        res = LDPR_UNDEF;
+      else if (isym->def == LDPK_UNDEF
+               || isym->def == LDPK_WEAKUNDEF
+               || isym->def == LDPK_COMMON)
+        {
+          // The original symbol was undefined or common.
+          if (lsym->source() != Symbol::FROM_OBJECT)
+            res = LDPR_RESOLVED_EXEC;
+          else if (lsym->object()->pluginobj() != NULL)
+            res = LDPR_RESOLVED_IR;
+          else if (lsym->object()->is_dynamic())
+            res = LDPR_RESOLVED_DYN;
+          else
+            res = LDPR_RESOLVED_EXEC;
+        }
+      else
+        {
+          // The original symbol was a definition.
+          if (lsym->source() != Symbol::FROM_OBJECT)
+            res = LDPR_PREEMPTED_REG;
+          else if (lsym->object() == static_cast<const Object*>(this))
+            res = (lsym->in_real_elf()
+                   ? LDPR_PREVAILING_DEF
+                   : LDPR_PREVAILING_DEF_IRONLY);
+          else
+            res = (lsym->object()->pluginobj() != NULL
+                   ? LDPR_PREEMPTED_IR
+                   : LDPR_PREEMPTED_REG);
+        }
+      isym->resolution = res;
+    }
+  return LDPS_OK;
+}
+
+// Return TRUE if the comdat group with key COMDAT_KEY from this object
+// should be kept.
+
+bool
+Pluginobj::include_comdat_group(std::string comdat_key, Layout* layout)
+{
+  std::pair<Comdat_map::iterator, bool> ins =
+    this->comdat_map_.insert(std::make_pair(comdat_key, false));
+
+  // If this is the first time we've seen this comdat key, ask the
+  // layout object whether it should be included.
+  if (ins.second)
+    ins.first->second = layout->add_comdat(NULL, 1, comdat_key, true);
+
+  return ins.first->second;
+}
+
+// Class Sized_pluginobj.
+
+template<int size, bool big_endian>
+Sized_pluginobj<size, big_endian>::Sized_pluginobj(
+    const std::string& name,
+    Input_file* input_file,
+    off_t offset)
+  : Pluginobj(name, input_file, offset)
+{
+}
+
+// Read the symbols.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+void
+Sized_pluginobj<size, big_endian>::do_read_symbols(Read_symbols_data*)
+{
+  gold_unreachable();
+}
+
+// Lay out the input sections.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+void
+Sized_pluginobj<size, big_endian>::do_layout(Symbol_table*, Layout*,
+                                             Read_symbols_data*)
+{
+  gold_unreachable();
+}
+
+// Add the symbols to the symbol table.
+
+template<int size, bool big_endian>
+void
+Sized_pluginobj<size, big_endian>::do_add_symbols(Symbol_table*,
+                                                  Read_symbols_data*)
+{
+  gold_unreachable();
+}
+
+template<int size, bool big_endian>
+void
+Sized_pluginobj<size, big_endian>::do_add_symbols(Symbol_table* symtab,
+                                                  Layout* layout)
+{
+  const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
+  unsigned char symbuf[sym_size];
+  elfcpp::Sym<size, big_endian> sym(symbuf);
+  elfcpp::Sym_write<size, big_endian> osym(symbuf);
+
+  typedef typename elfcpp::Elf_types<size>::Elf_WXword Elf_size_type;
+
+  this->symbols_.resize(this->nsyms_);
+
+  for (int i = 0; i < this->nsyms_; ++i)
+    {
+      const struct ld_plugin_symbol *isym = &this->syms_[i];
+      const char* name = isym->name;
+      const char* ver = isym->version;
+      elfcpp::Elf_Half shndx;
+      elfcpp::STB bind;
+      elfcpp::STV vis;
+
+      if (name != NULL && name[0] == '\0')
+        name = NULL;
+      if (ver != NULL && ver[0] == '\0')
+        ver = NULL;
+
+      switch (isym->def)
+        {
+        case LDPK_WEAKDEF:
+        case LDPK_WEAKUNDEF:
+          bind = elfcpp::STB_WEAK;
+          break;
+        case LDPK_DEF:
+        case LDPK_UNDEF:
+        case LDPK_COMMON:
+        default:
+          bind = elfcpp::STB_GLOBAL;
+          break;
+        }
+
+      switch (isym->def)
+        {
+        case LDPK_DEF:
+        case LDPK_WEAKDEF:
+          shndx = elfcpp::SHN_ABS;
+          break;
+        case LDPK_COMMON:
+          shndx = elfcpp::SHN_COMMON;
+          break;
+        case LDPK_UNDEF:
+        case LDPK_WEAKUNDEF:
+        default:
+          shndx = elfcpp::SHN_UNDEF;
+          break;
+        }
+
+      switch (isym->visibility)
+        {
+        case LDPV_PROTECTED:
+          vis = elfcpp::STV_DEFAULT;
+          break;
+        case LDPV_INTERNAL:
+          vis = elfcpp::STV_DEFAULT;
+          break;
+        case LDPV_HIDDEN:
+          vis = elfcpp::STV_DEFAULT;
+          break;
+        case LDPV_DEFAULT:
+        default:
+          vis = elfcpp::STV_DEFAULT;
+          break;
+        }
+
+      if (isym->comdat_key != NULL
+          && isym->comdat_key[0] != '\0'
+          && !this->include_comdat_group(isym->comdat_key, layout))
+        shndx = elfcpp::SHN_UNDEF;
+
+      osym.put_st_name(0);
+      osym.put_st_value(0);
+      osym.put_st_size(static_cast<Elf_size_type>(isym->size));
+      osym.put_st_info(bind, elfcpp::STT_NOTYPE);
+      osym.put_st_other(vis, 0);
+      osym.put_st_shndx(shndx);
+
+      this->symbols_[i] =
+        symtab->add_from_pluginobj<size, big_endian>(this, name, ver, &sym);
+    }
+}
+
+// Get the size of a section.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+uint64_t
+Sized_pluginobj<size, big_endian>::do_section_size(unsigned int)
+{
+  gold_unreachable();
+  return 0;
+}
+
+// Get the name of a section.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+std::string
+Sized_pluginobj<size, big_endian>::do_section_name(unsigned int)
+{
+  gold_unreachable();
+  return std::string();
+}
+
+// Return a view of the contents of a section.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+Object::Location
+Sized_pluginobj<size, big_endian>::do_section_contents(unsigned int)
+{
+  Location loc(0, 0);
+
+  gold_unreachable();
+  return loc;
+}
+
+// Return section flags.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+uint64_t
+Sized_pluginobj<size, big_endian>::do_section_flags(unsigned int)
+{
+  gold_unreachable();
+  return 0;
+}
+
+// Return section address.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+uint64_t
+Sized_pluginobj<size, big_endian>::do_section_address(unsigned int)
+{
+  gold_unreachable();
+  return 0;
+}
+
+// Return section type.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+unsigned int
+Sized_pluginobj<size, big_endian>::do_section_type(unsigned int)
+{
+  gold_unreachable();
+  return 0;
+}
+
+// Return the section link field.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+unsigned int
+Sized_pluginobj<size, big_endian>::do_section_link(unsigned int)
+{
+  gold_unreachable();
+  return 0;
+}
+
+// Return the section link field.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+unsigned int
+Sized_pluginobj<size, big_endian>::do_section_info(unsigned int)
+{
+  gold_unreachable();
+  return 0;
+}
+
+// Return the section alignment.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+uint64_t
+Sized_pluginobj<size, big_endian>::do_section_addralign(unsigned int)
+{
+  gold_unreachable();
+  return 0;
+}
+
+// Return the Xindex structure to use.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+Xindex*
+Sized_pluginobj<size, big_endian>::do_initialize_xindex()
+{
+  gold_unreachable();
+  return NULL;
+}
+
+// Get symbol counts.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+void
+Sized_pluginobj<size, big_endian>::do_get_global_symbol_counts(const Symbol_table*,
+                                                   size_t*, size_t*) const
+{
+  gold_unreachable();
+}
+
+// Class Add_plugin_symbols.
+
+Add_plugin_symbols::~Add_plugin_symbols()
+{
+  if (this->this_blocker_ != NULL)
+    delete this->this_blocker_;
+  // next_blocker_ is deleted by the task associated with the next
+  // input file.
+}
+
+// We are blocked by this_blocker_.  We block next_blocker_.  We also
+// lock the file.
+
+Task_token*
+Add_plugin_symbols::is_runnable()
+{
+  if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
+    return this->this_blocker_;
+  if (this->obj_->is_locked())
+    return this->obj_->token();
+  return NULL;
+}
+
+void
+Add_plugin_symbols::locks(Task_locker* tl)
+{
+  tl->add(this, this->next_blocker_);
+  tl->add(this, this->obj_->token());
+}
+
+// Add the symbols in the object to the symbol table.
+
+void
+Add_plugin_symbols::run(Workqueue*)
+{
+  this->obj_->add_symbols(this->symtab_, this->layout_);
+}
+
+// Class Plugin_cleanup.  This task calls the plugin cleanup hooks once all
+// replacement files have been added.
+
+class Plugin_cleanup : public Task
+{
+ public:
+  Plugin_cleanup(Task_token* this_blocker, Task_token* next_blocker)
+    : this_blocker_(this_blocker), next_blocker_(next_blocker)
+  { }
+
+  ~Plugin_cleanup()
+  {
+    if (this->this_blocker_ != NULL)
+      delete this->this_blocker_;
+  }
+
+  Task_token*
+  is_runnable()
+  {
+    if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
+      return this->this_blocker_;
+    return NULL;
+  }
+
+  void
+  locks(Task_locker* tl)
+  { tl->add(this, this->next_blocker_); }
+
+  void
+  run(Workqueue*)
+  { parameters->options().plugins()->cleanup(); }
+
+  std::string
+  get_name() const
+  { return "Plugin_cleanup"; }
+
+ private:
+  Task_token* this_blocker_;
+  Task_token* next_blocker_;
+};
+
+// Class Plugin_hook.
+
+Plugin_hook::~Plugin_hook()
+{
+}
+
+// Return whether a Plugin_hook task is runnable.
+
+Task_token*
+Plugin_hook::is_runnable()
+{
+  if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
+    return this->this_blocker_;
+  return NULL;
+}
+
+// Return a Task_locker for a Plugin_hook task.  We don't need any
+// locks here.
+
+void
+Plugin_hook::locks(Task_locker*)
+{
+}
+
+// Run a Plugin_hook task.
+
+void
+Plugin_hook::run(Workqueue* workqueue)
+{
+  this->do_plugin_hook(workqueue);
+}
+
+// Run the "all symbols read" plugin hook.
+
+void
+Plugin_hook::do_plugin_hook(Workqueue* workqueue)
+{
+  gold_assert(this->options_.has_plugins());
+  this->options_.plugins()->all_symbols_read(workqueue,
+                                             this->input_objects_,
+                                             this->symtab_,
+                                             this->layout_,
+                                             this->dirpath_,
+                                             this->mapfile_,
+                                             &this->this_blocker_);
+  workqueue->queue_soon(new Plugin_cleanup(this->this_blocker_,
+                                          this->next_blocker_));
+}
+
+// The C interface routines called by the plugins.
+
+#ifdef ENABLE_PLUGINS
+
+// Register a claim-file handler.
+
+static enum ld_plugin_status
+register_claim_file(ld_plugin_claim_file_handler handler)
+{
+  gold_assert(parameters->options().has_plugins());
+  parameters->options().plugins()->set_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)
+{
+  gold_assert(parameters->options().has_plugins());
+  parameters->options().plugins()->set_all_symbols_read_handler(handler);
+  return LDPS_OK;
+}
+
+// Register a cleanup handler.
+
+static enum ld_plugin_status
+register_cleanup(ld_plugin_cleanup_handler handler)
+{
+  gold_assert(parameters->options().has_plugins());
+  parameters->options().plugins()->set_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 ld_plugin_symbol *syms)
+{
+  gold_assert(parameters->options().has_plugins());
+  Pluginobj* obj = parameters->options().plugins()->make_plugin_object(
+      static_cast<unsigned int>(reinterpret_cast<intptr_t>(handle)));
+  if (obj == NULL)
+    return LDPS_ERR;
+  obj->store_incoming_symbols(nsyms, syms);
+  return LDPS_OK;
+}
+
+// Get the symbol resolution info for a plugin-claimed input file.
+
+static enum ld_plugin_status
+get_symbols(const void * handle, int nsyms, ld_plugin_symbol* syms)
+{
+  gold_assert(parameters->options().has_plugins());
+  Pluginobj* obj = parameters->options().plugins()->object(
+      static_cast<unsigned int>(reinterpret_cast<intptr_t>(handle)));
+  if (obj == NULL)
+    return LDPS_ERR;
+  return obj->get_symbol_resolution_info(nsyms, syms);
+}
+
+// Add a new (real) input file generated by a plugin.
+
+static enum ld_plugin_status
+add_input_file(char *pathname)
+{
+  gold_assert(parameters->options().has_plugins());
+  return parameters->options().plugins()->add_input_file(pathname);
+}
+
+// Issue a diagnostic message from a plugin.
+
+static enum ld_plugin_status
+message(int level, char * format, ...)
+{
+  va_list args;
+  va_start(args, format);
+
+  switch (level)
+    {
+    case LDPL_INFO:
+      parameters->errors()->info(format, args);
+      break;
+    case LDPL_WARNING:
+      parameters->errors()->warning(format, args);
+      break;
+    case LDPL_ERROR:
+    default:
+      parameters->errors()->error(format, args);
+      break;
+    case LDPL_FATAL:
+      parameters->errors()->fatal(format, args);
+      break;
+    }
+
+  va_end(args);
+  return LDPS_OK;
+}
+
+#endif // ENABLE_PLUGINS
+
+// Allocate a Pluginobj object of the appropriate size and endianness.
+
+static Pluginobj*
+make_sized_plugin_object(Input_file* input_file, off_t offset)
+{
+  Target* target;
+  Pluginobj* obj = NULL;
+
+  if (parameters->target_valid())
+    target = const_cast<Target*>(&parameters->target());
+  else
+    target = const_cast<Target*>(&parameters->default_target());
+
+  if (target->get_size() == 32)
+    {
+#ifdef HAVE_TARGET_32_BIG
+      if (target->is_big_endian())
+        obj = new Sized_pluginobj<32, true>(input_file->filename(),
+                                            input_file, offset);
+#endif
+#ifdef HAVE_TARGET_32_LITTLE
+      else
+        obj = new Sized_pluginobj<32, false>(input_file->filename(),
+                                             input_file, offset);
+#endif
+    }
+  else if (target->get_size() == 64)
+    {
+#ifdef HAVE_TARGET_64_BIG
+      if (target->is_big_endian())
+        obj = new Sized_pluginobj<64, true>(input_file->filename(),
+                                            input_file, offset);
+#endif
+#ifdef HAVE_TARGET_64_LITTLE
+      else
+        obj = new Sized_pluginobj<64, false>(input_file->filename(),
+                                             input_file, offset);
+#endif
+    }
+
+  gold_assert(obj != NULL);
+  obj->set_target(target);
+  return obj;
+}
+
+} // End namespace gold.
diff --git a/gold/plugin.h b/gold/plugin.h
new file mode 100644 (file)
index 0000000..3f573ce
--- /dev/null
@@ -0,0 +1,457 @@
+// plugin.h -- plugin manager for gold      -*- C++ -*-
+
+// Copyright 2008 Free Software Foundation, Inc.
+// Written by Cary Coutant <ccoutant@google.com>.
+
+// This file is part of gold.
+
+// 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.
+
+#ifndef GOLD_PLUGIN_H
+#define GOLD_PLUGIN_H
+
+#include <list>
+#include <string>
+
+#include "object.h"
+#include "plugin-api.h"
+#include "workqueue.h"
+
+namespace gold
+{
+
+class General_options;
+class Input_file;
+class Input_objects;
+class Symbol_table;
+class Layout;
+class Dirsearch;
+class Mapfile;
+class Task_token;
+class Pluginobj;
+
+// This class represents a single plugin library.
+
+class Plugin
+{
+ public:
+  Plugin(const char* args)
+    : handle_(NULL),
+      args_(args),
+      claim_file_handler_(NULL),
+      all_symbols_read_handler_(NULL),
+      cleanup_handler_(NULL)      
+  { }
+
+  ~Plugin()
+  { }
+
+  // Load the library and call its entry point.
+  void
+  load();
+
+  // Call the claim-file handler.
+  bool
+  claim_file(struct ld_plugin_input_file *plugin_input_file);
+
+  // Call the all-symbols-read handler.
+  void
+  all_symbols_read();
+
+  // Call the cleanup handler.
+  void
+  cleanup();
+
+  // Register a claim-file handler.
+  void
+  set_claim_file_handler(ld_plugin_claim_file_handler handler)
+  { this->claim_file_handler_ = handler; }
+
+  // Register an all-symbols-read handler.
+  void
+  set_all_symbols_read_handler(ld_plugin_all_symbols_read_handler handler)
+  { this->all_symbols_read_handler_ = handler; }
+
+  // Register a claim-file handler.
+  void
+  set_cleanup_handler(ld_plugin_cleanup_handler handler)
+  { this->cleanup_handler_ = handler; }
+
+ private:
+  Plugin(const Plugin&);
+  Plugin& operator=(const Plugin&);
+
+  // The shared library handle returned by dlopen.
+  void* handle_;
+  // The argument string given to --plugin.
+  const char* 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_;
+};
+
+// A manager class for plugins.
+
+class Plugin_manager
+{
+ public:
+  Plugin_manager(const General_options& options)
+    : plugins_(), in_replacement_phase_(false), options_(options),
+      workqueue_(NULL), input_objects_(NULL), symtab_(NULL), layout_(NULL),
+      dirpath_(NULL), mapfile_(NULL), this_blocker_(NULL)
+  { this->current_ = plugins_.end(); }
+
+  ~Plugin_manager();
+
+  // Add a plugin library.
+  void
+  add_plugin(const char* args)
+  { this->plugins_.push_back(new Plugin(args)); }
+
+  // Load all plugin libraries.
+  void
+  load_plugins();
+
+  // Call the plugin claim-file handlers in turn to see if any claim the file.
+  Pluginobj*
+  claim_file(Input_file *input_file, off_t offset, off_t filesize);
+
+  // Call the all-symbols-read handlers.
+  void
+  all_symbols_read(Workqueue* workqueue, Input_objects* input_objects,
+                   Symbol_table* symtab, Layout* layout, Dirsearch* dirpath,
+                   Mapfile* mapfile, Task_token** last_blocker);
+
+  // Call the cleanup handlers.
+  void
+  cleanup();
+
+  // Register a claim-file handler.
+  void
+  set_claim_file_handler(ld_plugin_claim_file_handler handler)
+  {
+    gold_assert(this->current_ != plugins_.end());
+    (*this->current_)->set_claim_file_handler(handler);
+  }
+
+  // Register an all-symbols-read handler.
+  void
+  set_all_symbols_read_handler(ld_plugin_all_symbols_read_handler handler)
+  {
+    gold_assert(this->current_ != plugins_.end());
+    (*this->current_)->set_all_symbols_read_handler(handler);
+  }
+
+  // Register a claim-file handler.
+  void
+  set_cleanup_handler(ld_plugin_cleanup_handler handler)
+  {
+    gold_assert(this->current_ != plugins_.end());
+    (*this->current_)->set_cleanup_handler(handler);
+  }
+
+  // Make a new Pluginobj object.  This is called when the plugin calls
+  // the add_symbols API.
+  Pluginobj*
+  make_plugin_object(unsigned int handle);
+
+  // Return the Pluginobj associated with the given HANDLE.
+  Pluginobj*
+  object(unsigned int handle) const
+  {
+    if (handle >= this->objects_.size())
+      return NULL;
+    return this->objects_[handle];
+  }
+
+  // Add a new input file.
+  ld_plugin_status
+  add_input_file(char *pathname);
+
+  // Return TRUE if we are in the replacement phase.
+  bool
+  in_replacement_phase() const
+  { return this->in_replacement_phase_; }
+
+ private:
+  Plugin_manager(const Plugin_manager&);
+  Plugin_manager& operator=(const Plugin_manager&);
+
+  typedef std::list<Plugin*> Plugin_list;
+  typedef std::vector<Pluginobj*> Object_list;
+
+  // The list of plugin libraries.
+  Plugin_list plugins_;
+  // A pointer to the current plugin.  Used while loading plugins.
+  Plugin_list::iterator current_;
+
+  // The list of plugin objects.  The index of an item in this list
+  // serves as the "handle" that we pass to the plugins.
+  Object_list objects_;
+
+  // The file currently up for claim by the plugins.
+  Input_file* input_file_;
+  struct ld_plugin_input_file plugin_input_file_;
+
+  // TRUE after the all symbols read event; indicates that we are
+  // processing replacement files whose symbols should replace the
+  // placeholder symbols from the Pluginobj objects.
+  bool in_replacement_phase_;
+
+  const General_options& options_;
+  Workqueue* workqueue_;
+  Input_objects* input_objects_;
+  Symbol_table* symtab_;
+  Layout* layout_;
+  Dirsearch* dirpath_;
+  Mapfile* mapfile_;
+  Task_token* this_blocker_;
+};
+
+
+// An object file claimed by a plugin.  This is an abstract base class.
+// The implementation is the template class Sized_pluginobj.
+
+class Pluginobj : public Object
+{
+ public:
+
+  typedef std::vector<Symbol*> Symbols;
+
+  Pluginobj(const std::string& name, Input_file* input_file, off_t offset);
+
+  // Fill in the symbol resolution status for the given plugin symbols.
+  ld_plugin_status
+  get_symbol_resolution_info(int nsyms, ld_plugin_symbol* syms) const;
+
+  // Add symbol information to the global symbol table.
+  void
+  add_symbols(Symbol_table* symtab, Layout* layout)
+  { this->do_add_symbols(symtab, layout); }
+
+  // Store the incoming symbols from the plugin for later processing.
+  void
+  store_incoming_symbols(int nsyms, const struct ld_plugin_symbol* syms)
+  {
+    this->nsyms_ = nsyms;
+    this->syms_ = syms;
+  }
+
+  // Return TRUE if the comdat group with key COMDAT_KEY from this object
+  // should be kept.
+  bool
+  include_comdat_group(std::string comdat_key, Layout* layout);
+
+ protected:
+  // Return TRUE if this is an object claimed by a plugin.
+  virtual Pluginobj*
+  do_pluginobj()
+  { return this; }
+
+  // Add symbol information to the global symbol table--implemented by
+  // child class.
+  virtual void
+  do_add_symbols(Symbol_table*, Layout*) = 0;
+
+  // The number of symbols provided by the plugin.
+  int nsyms_;
+  
+  // The symbols provided by the plugin.
+  const struct ld_plugin_symbol* syms_;
+
+  // The entries in the symbol table for the external symbols.
+  Symbols symbols_;
+
+ private:
+  // Map a comdat key symbol to a boolean indicating whether the comdat
+  // group in this object with that key should be kept.
+  typedef Unordered_map<std::string, bool> Comdat_map;
+  Comdat_map comdat_map_;
+};
+
+// A plugin object, size-specific version.
+
+template<int size, bool big_endian>
+class Sized_pluginobj : public Pluginobj
+{
+ public:
+  Sized_pluginobj(const std::string& name, Input_file* input_file,
+                  off_t offset);
+
+  // Read the symbols.
+  void
+  do_read_symbols(Read_symbols_data*);
+
+  // Lay out the input sections.
+  void
+  do_layout(Symbol_table*, Layout*, Read_symbols_data*);
+
+  // Add the symbols to the symbol table.
+  void
+  do_add_symbols(Symbol_table*, Read_symbols_data*);
+
+  void
+  do_add_symbols(Symbol_table*, Layout*);
+
+  // Get the size of a section.
+  uint64_t
+  do_section_size(unsigned int shndx);
+
+  // Get the name of a section.
+  std::string
+  do_section_name(unsigned int shndx);
+
+  // Return a view of the contents of a section.
+  Object::Location
+  do_section_contents(unsigned int shndx);
+
+  // Return section flags.
+  uint64_t
+  do_section_flags(unsigned int shndx);
+
+  // Return section address.
+  uint64_t
+  do_section_address(unsigned int shndx);
+
+  // Return section type.
+  unsigned int
+  do_section_type(unsigned int shndx);
+
+  // Return the section link field.
+  unsigned int
+  do_section_link(unsigned int shndx);
+
+  // Return the section link field.
+  unsigned int
+  do_section_info(unsigned int shndx);
+
+  // Return the section alignment.
+  uint64_t
+  do_section_addralign(unsigned int shndx);
+
+  // Return the Xindex structure to use.
+  Xindex*
+  do_initialize_xindex();
+
+  // Get symbol counts.
+  void
+  do_get_global_symbol_counts(const Symbol_table*, size_t*, size_t*) const;
+
+  // Add placeholder symbols from a claimed file.
+  ld_plugin_status
+  add_symbols_from_plugin(int nsyms, const ld_plugin_symbol* syms);
+
+ protected:
+
+ private:
+};
+
+// This Task handles adding the symbols to the symbol table.  These
+// tasks must be run in the same order as the arguments appear on the
+// command line.
+
+class Add_plugin_symbols : public Task
+{
+ public:
+  // THIS_BLOCKER is used to prevent this task from running before the
+  // one for the previous input file.  NEXT_BLOCKER is used to prevent
+  // the next task from running.
+  Add_plugin_symbols(Symbol_table* symtab,
+                    Layout* layout,
+                    Pluginobj* obj,
+                    Task_token* this_blocker,
+                    Task_token* next_blocker)
+    : symtab_(symtab), layout_(layout), obj_(obj),
+      this_blocker_(this_blocker), next_blocker_(next_blocker)
+  { }
+
+  ~Add_plugin_symbols();
+
+  // The standard Task methods.
+
+  Task_token*
+  is_runnable();
+
+  void
+  locks(Task_locker*);
+
+  void
+  run(Workqueue*);
+
+  std::string
+  get_name() const
+  { return "Add_plugin_symbols " + this->obj_->name(); }
+
+private:
+  Symbol_table* symtab_;
+  Layout* layout_;
+  Pluginobj* obj_;
+  Task_token* this_blocker_;
+  Task_token* next_blocker_;
+};
+
+// This Task handles handles the "all symbols read" event hook.
+// The plugin may add additional input files at this time, which must
+// be queued for reading.
+
+class Plugin_hook : public Task
+{
+ public:
+  Plugin_hook(const General_options& options, Input_objects* input_objects,
+             Symbol_table* symtab, Layout* layout, Dirsearch* dirpath,
+             Mapfile* mapfile, Task_token* this_blocker,
+             Task_token* next_blocker)
+    : options_(options), input_objects_(input_objects), symtab_(symtab),
+      layout_(layout), dirpath_(dirpath), mapfile_(mapfile),
+      this_blocker_(this_blocker), next_blocker_(next_blocker)
+  { }
+
+  ~Plugin_hook();
+
+  // The standard Task methods.
+
+  Task_token*
+  is_runnable();
+
+  void
+  locks(Task_locker*);
+
+  void
+  run(Workqueue*);
+
+  std::string
+  get_name() const
+  { return "Plugin_hook"; }
+
+ private:
+  // Call the plugin hook.
+  void
+  do_plugin_hook(Workqueue*);
+
+  const General_options& options_;
+  Input_objects* input_objects_;
+  Symbol_table* symtab_;
+  Layout* layout_;
+  Dirsearch* dirpath_;
+  Mapfile* mapfile_;
+  Task_token* this_blocker_;
+  Task_token* next_blocker_;
+};
+
+} // End namespace gold.
+
+#endif // !defined(GOLD_PLUGIN_H)
index 05c80f8ff74501dece30212a306469eed25e46fe..ac646d95d1ca517326eb2c2a8a5ace89bf06bf41 100644 (file)
@@ -32,6 +32,7 @@
 #include "archive.h"
 #include "script.h"
 #include "readsyms.h"
+#include "plugin.h"
 
 namespace gold
 {
@@ -159,6 +160,53 @@ Read_symbols::do_read_symbols(Workqueue* workqueue)
   const unsigned char* ehdr = input_file->file().get_view(0, 0, read_size,
                                                          true, false);
 
+  if (read_size >= Archive::sarmag)
+    {
+      bool is_thin_archive
+          = memcmp(ehdr, Archive::armagt, Archive::sarmag) == 0;
+      if (is_thin_archive 
+          || memcmp(ehdr, Archive::armag, Archive::sarmag) == 0)
+       {
+         // This is an archive.
+         Archive* arch = new Archive(this->input_argument_->file().name(),
+                                     input_file, is_thin_archive,
+                                     this->dirpath_, this);
+         arch->setup(this->input_objects_);
+         
+         // Unlock the archive so it can be used in the next task.
+         arch->unlock(this);
+
+         workqueue->queue_next(new Add_archive_symbols(this->symtab_,
+                                                       this->layout_,
+                                                       this->input_objects_,
+                                                       this->mapfile_,
+                                                       arch,
+                                                       this->input_group_,
+                                                       this->this_blocker_,
+                                                       this->next_blocker_));
+         return true;
+       }
+    }
+
+  if (parameters->options().has_plugins())
+    {
+      Pluginobj* obj = parameters->options().plugins()->claim_file(input_file,
+                                                                   0, filesize);
+      if (obj != NULL)
+        {
+          // The input file was claimed by a plugin, and its symbols
+          // have been provided by the plugin.
+         input_file->file().claim_for_plugin();
+         input_file->file().unlock(this);
+          workqueue->queue_next(new Add_plugin_symbols(this->symtab_,
+                                                       this->layout_,
+                                                       obj,
+                                                       this->this_blocker_,
+                                                       this->next_blocker_));
+          return true;
+        }
+    }
+
   if (read_size >= 4)
     {
       static unsigned char elfmagic[4] =
@@ -201,34 +249,6 @@ Read_symbols::do_read_symbols(Workqueue* workqueue)
        }
     }
 
-  if (read_size >= Archive::sarmag)
-    {
-      bool is_thin_archive
-          = memcmp(ehdr, Archive::armagt, Archive::sarmag) == 0;
-      if (is_thin_archive 
-          || memcmp(ehdr, Archive::armag, Archive::sarmag) == 0)
-       {
-         // This is an archive.
-         Archive* arch = new Archive(this->input_argument_->file().name(),
-                                     input_file, is_thin_archive,
-                                     this->dirpath_, this);
-         arch->setup(this->input_objects_);
-         
-         // Unlock the archive so it can be used in the next task.
-         arch->unlock(this);
-
-         workqueue->queue_next(new Add_archive_symbols(this->symtab_,
-                                                       this->layout_,
-                                                       this->input_objects_,
-                                                       this->mapfile_,
-                                                       arch,
-                                                       this->input_group_,
-                                                       this->this_blocker_,
-                                                       this->next_blocker_));
-         return true;
-       }
-    }
-
   // Queue up a task to try to parse this file as a script.  We use a
   // separate task so that the script will be read in order with other
   // objects named on the command line.  Also so that we don't try to
index 0ad990cd055db328df05adf6a657a00d04931a52..17544f82a5c2d7b135d2970f18d4fb6894de7047 100644 (file)
@@ -26,6 +26,7 @@
 #include "target.h"
 #include "object.h"
 #include "symtab.h"
+#include "plugin.h"
 
 namespace gold
 {
@@ -240,6 +241,24 @@ Symbol_table::resolve(Sized_symbol<size>* to,
       to->set_in_dyn();
     }
 
+  // Record if we've seen this symbol in a real ELF object (i.e., the
+  // symbol is referenced from outside the world known to the plugin).
+  if (object->pluginobj() == NULL)
+    to->set_in_real_elf();
+
+  // If we're processing replacement files, allow new symbols to override
+  // the placeholders from the plugin objects.
+  if (to->source() == Symbol::FROM_OBJECT)
+    {
+      Pluginobj* obj = to->object()->pluginobj();
+      if (obj != NULL
+          && parameters->options().plugins()->in_replacement_phase())
+        {
+          this->override(to, sym, st_shndx, is_ordinary, object, version);
+          return;
+        }
+    }
+
   unsigned int frombits = symbol_to_bits(sym.get_st_bind(),
                                          object->is_dynamic(),
                                         st_shndx, is_ordinary,
index 393d71aceac7630ae6e2c3c475a48972db871c14..3bb88d8b2be77277f90c3745473d7421e421b801 100644 (file)
@@ -37,6 +37,7 @@
 #include "target.h"
 #include "workqueue.h"
 #include "symtab.h"
+#include "plugin.h"
 
 namespace gold
 {
@@ -73,6 +74,7 @@ Symbol::init_fields(const char* name, const char* version,
   this->is_copied_from_dynobj_ = false;
   this->is_forced_local_ = false;
   this->is_ordinary_shndx_ = false;
+  this->in_real_elf_ = false;
 }
 
 // Return the demangled version of the symbol's name, but only
@@ -117,6 +119,7 @@ Symbol::init_base_object(const char* name, const char* version, Object* object,
   this->source_ = FROM_OBJECT;
   this->in_reg_ = !object->is_dynamic();
   this->in_dyn_ = object->is_dynamic();
+  this->in_real_elf_ = object->pluginobj() == NULL;
 }
 
 // Initialize the fields in the base class Symbol for a symbol defined
@@ -133,6 +136,7 @@ Symbol::init_base_output_data(const char* name, const char* version,
   this->u_.in_output_data.offset_is_from_end = offset_is_from_end;
   this->source_ = IN_OUTPUT_DATA;
   this->in_reg_ = true;
+  this->in_real_elf_ = true;
 }
 
 // Initialize the fields in the base class Symbol for a symbol defined
@@ -150,6 +154,7 @@ Symbol::init_base_output_segment(const char* name, const char* version,
   this->u_.in_output_segment.offset_base = offset_base;
   this->source_ = IN_OUTPUT_SEGMENT;
   this->in_reg_ = true;
+  this->in_real_elf_ = true;
 }
 
 // Initialize the fields in the base class Symbol for a symbol defined
@@ -163,6 +168,7 @@ Symbol::init_base_constant(const char* name, const char* version,
   this->init_fields(name, version, type, binding, visibility, nonvis);
   this->source_ = IS_CONSTANT;
   this->in_reg_ = true;
+  this->in_real_elf_ = true;
 }
 
 // Initialize the fields in the base class Symbol for an undefined
@@ -177,6 +183,7 @@ Symbol::init_base_undefined(const char* name, const char* version,
   this->dynsym_index_ = -1U;
   this->source_ = IS_UNDEFINED;
   this->in_reg_ = true;
+  this->in_real_elf_ = true;
 }
 
 // Allocate a common symbol in the base.
@@ -357,6 +364,7 @@ Symbol::output_section() const
        if (shndx != elfcpp::SHN_UNDEF && this->is_ordinary_shndx_)
          {
            gold_assert(!this->u_.from_object.object->is_dynamic());
+           gold_assert(this->u_.from_object.object->pluginobj() == NULL);
            Relobj* relobj = static_cast<Relobj*>(this->u_.from_object.object);
            return relobj->output_section(shndx);
          }
@@ -973,6 +981,68 @@ Symbol_table::add_from_relobj(
     }
 }
 
+// Add a symbol from a plugin-claimed file.
+
+template<int size, bool big_endian>
+Symbol*
+Symbol_table::add_from_pluginobj(
+    Sized_pluginobj<size, big_endian>* obj,
+    const char* name,
+    const char* ver,
+    elfcpp::Sym<size, big_endian>* sym)
+{
+  unsigned int st_shndx = sym->get_st_shndx();
+
+  Stringpool::Key ver_key = 0;
+  bool def = false;
+  bool local = false;
+
+  if (ver != NULL)
+    {
+      ver = this->namepool_.add(ver, true, &ver_key);
+    }
+  // We don't want to assign a version to an undefined symbol,
+  // even if it is listed in the version script.  FIXME: What
+  // about a common symbol?
+  else
+    {
+      if (!this->version_script_.empty()
+          && st_shndx != elfcpp::SHN_UNDEF)
+        {
+          // The symbol name did not have a version, but the
+          // version script may assign a version anyway.
+          std::string version;
+          if (this->version_script_.get_symbol_version(name, &version))
+            {
+              // The version can be empty if the version script is
+              // only used to force some symbols to be local.
+              if (!version.empty())
+                {
+                  ver = this->namepool_.add_with_length(version.c_str(),
+                                                        version.length(),
+                                                        true,
+                                                        &ver_key);
+                  def = true;
+                }
+            }
+          else if (this->version_script_.symbol_is_local(name))
+            local = true;
+        }
+    }
+
+  Stringpool::Key name_key;
+  name = this->namepool_.add(name, true, &name_key);
+
+  Sized_symbol<size>* res;
+  res = this->add_from_object(obj, name, name_key, ver, ver_key,
+                             def, *sym, st_shndx, true, st_shndx);
+
+  if (local)
+       this->force_local(res);
+
+  return res;
+}
+
 // Add all the symbols in a dynamic object to the hash table.
 
 template<int size, bool big_endian>
@@ -2043,6 +2113,11 @@ Symbol_table::sized_finalize_symbol(Symbol* unsized_sym)
            value = 0;
            shndx = elfcpp::SHN_UNDEF;
          }
+       else if (symobj->pluginobj() != NULL)
+         {
+           value = 0;
+           shndx = elfcpp::SHN_UNDEF;
+         }
        else if (shndx == elfcpp::SHN_UNDEF)
          value = 0;
        else if (!is_ordinary
@@ -2261,6 +2336,8 @@ Symbol_table::sized_write_globals(const Input_objects* input_objects,
                      dynsym_value = target.dynsym_value(sym);
                    shndx = elfcpp::SHN_UNDEF;
                  }
+               else if (symobj->pluginobj() != NULL)
+                 shndx = elfcpp::SHN_UNDEF;
                else if (in_shndx == elfcpp::SHN_UNDEF
                         || (!is_ordinary
                             && (in_shndx == elfcpp::SHN_ABS
@@ -2701,6 +2778,46 @@ Symbol_table::add_from_relobj<64, true>(
     size_t* defined);
 #endif
 
+#ifdef HAVE_TARGET_32_LITTLE
+template
+Symbol*
+Symbol_table::add_from_pluginobj<32, false>(
+    Sized_pluginobj<32, false>* obj,
+    const char* name,
+    const char* ver,
+    elfcpp::Sym<32, false>* sym);
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+Symbol*
+Symbol_table::add_from_pluginobj<32, true>(
+    Sized_pluginobj<32, true>* obj,
+    const char* name,
+    const char* ver,
+    elfcpp::Sym<32, true>* sym);
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+Symbol*
+Symbol_table::add_from_pluginobj<64, false>(
+    Sized_pluginobj<64, false>* obj,
+    const char* name,
+    const char* ver,
+    elfcpp::Sym<64, false>* sym);
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+Symbol*
+Symbol_table::add_from_pluginobj<64, true>(
+    Sized_pluginobj<64, true>* obj,
+    const char* name,
+    const char* ver,
+    elfcpp::Sym<64, true>* sym);
+#endif
+
 #ifdef HAVE_TARGET_32_LITTLE
 template
 void
index 043fb50f2288c3e2930f96f268a65f651f3915fd..cfd0c73d42313451596012328dd60ca1b4408864 100644 (file)
@@ -43,6 +43,8 @@ class Object;
 class Relobj;
 template<int size, bool big_endian>
 class Sized_relobj;
+template<int size, bool big_endian>
+class Sized_pluginobj;
 class Dynobj;
 template<int size, bool big_endian>
 class Sized_dynobj;
@@ -273,6 +275,18 @@ class Symbol
   set_in_dyn()
   { this->in_dyn_ = true; }
 
+  // Return whether this symbol has been seen in a real ELF object.
+  // (IN_REG will return TRUE if the symbol has been seen in either
+  // a real ELF object or an object claimed by a plugin.)
+  bool
+  in_real_elf() const
+  { return this->in_real_elf_; }
+
+  // Mark this symbol as having been seen in a real ELF object.
+  void
+  set_in_real_elf()
+  { this->in_real_elf_ = true; }
+
   // Return the index of this symbol in the output file symbol table.
   // A value of -1U means that this symbol is not going into the
   // output file.  This starts out as zero, and is set to a non-zero
@@ -871,8 +885,10 @@ class Symbol
   bool is_forced_local_ : 1;
   // True if the field u_.from_object.shndx is an ordinary section
   // index, not one of the special codes from SHN_LORESERVE to
-  // SHN_HIRESERVE.
+  // SHN_HIRESERVE (bit 31).
   bool is_ordinary_shndx_ : 1;
+  // True if we've seen this symbol in a real ELF object.
+  bool in_real_elf_ : 1;
 };
 
 // The parts of a symbol which are size specific.  Using a template
@@ -1139,6 +1155,14 @@ class Symbol_table
                  typename Sized_relobj<size, big_endian>::Symbols*,
                  size_t* defined);
 
+  // Add one external symbol from the plugin object OBJ to the symbol table.
+  // Returns a pointer to the resolved symbol in the symbol table.
+  template<int size, bool big_endian>
+  Symbol*
+  add_from_pluginobj(Sized_pluginobj<size, big_endian>* obj,
+                     const char* name, const char* ver,
+                     elfcpp::Sym<size, big_endian>* sym);
+
   // Add COUNT dynamic symbols from the dynamic object DYNOBJ to the
   // symbol table.  SYMS is the symbols.  SYM_NAMES is their names.
   // SYM_NAME_SIZE is the size of SYM_NAMES.  The other parameters are
index 1f08b5a30a9d44298b2f6ebd6f8760c0ba8460d1..14efd73093ca7eb6cfe010de5e5706c57919e527 100644 (file)
@@ -9,6 +9,7 @@ AUTOMAKE_OPTIONS =
 # The two_file_test tests -fmerge-constants, so we simply always turn
 # it on.  This may need to be controlled by a configure option
 # eventually.
+AM_CFLAGS = $(WARN_CFLAGS) $(LFS_CFLAGS) -fmerge-constants
 AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CFLAGS) -fmerge-constants
 
 INCLUDES = \
@@ -22,6 +23,10 @@ TEST_OBJDUMP = $(top_builddir)/../binutils/objdump
 TEST_CXXFILT = $(top_builddir)/../binutils/cxxfilt
 TEST_STRIP = $(top_builddir)/../binutils/strip-new
 
+if PLUGINS
+LIBDL = -ldl
+endif
+
 if THREADS
 THREADSLIB = -lpthread
 endif
@@ -57,7 +62,7 @@ libgoldtest_a_SOURCES = test.cc testmain.cc testfile.cc
 DEPENDENCIES = \
        libgoldtest.a ../libgold.a ../../libiberty/libiberty.a $(LIBINTL_DEP)
 LDADD = libgoldtest.a ../libgold.a ../../libiberty/libiberty.a $(LIBINTL) \
-       $(THREADSLIB)
+       $(THREADSLIB) $(LIBDL)
 
 
 # The unittests themselves
@@ -897,5 +902,41 @@ script_test_4: basic_test.o gcctestdir/ld $(srcdir)/script_test_4.t
 script_test_4.stdout: script_test_4
        $(TEST_READELF) -SlW script_test_4 > script_test_4.stdout
 
+if PLUGINS
+
+check_PROGRAMS += plugin_test_1
+check_SCRIPTS += plugin_test_1.sh
+check_DATA += plugin_test_1.err
+MOSTLYCLEANFILES += plugin_test_1.err
+plugin_test_1: two_file_test_main.o two_file_test_1.syms two_file_test_1b.syms two_file_test_2.syms gcctestdir/ld plugin_test.so
+       $(CXXLINK) -Bgcctestdir/ -Wl,--no-demangle,--plugin,"./plugin_test.so;_Z4f13iv" two_file_test_main.o two_file_test_1.syms two_file_test_1b.syms two_file_test_2.syms 2>plugin_test_1.err
+plugin_test_1.err: plugin_test_1
+       @touch plugin_test_1.err
+
+check_PROGRAMS += plugin_test_2
+check_SCRIPTS += plugin_test_2.sh
+check_DATA += plugin_test_2.err
+MOSTLYCLEANFILES += plugin_test_2.err
+plugin_test_2: two_file_test_main.o two_file_test_1.syms two_file_test_1b.syms two_file_shared_2.so gcctestdir/ld plugin_test.so
+       $(CXXLINK) -Bgcctestdir/ -Wl,--no-demangle,-R,.,--plugin,"./plugin_test.so" two_file_test_main.o two_file_test_1.syms two_file_test_1b.syms two_file_shared_2.so 2>plugin_test_2.err
+plugin_test_2.err: plugin_test_2
+       @touch plugin_test_2.err
+
+plugin_test.so: plugin_test.o
+       $(LINK) -Bgcctestdir/ -shared plugin_test.o
+plugin_test.o: plugin_test.c
+       $(COMPILE) -O0 -c -fpic -o $@ $<
+
+two_file_test_main.syms: two_file_test_main.o
+       $(TEST_READELF) -sW $< >$@ 2>/dev/null
+two_file_test_1.syms: two_file_test_1.o
+       $(TEST_READELF) -sW $< >$@ 2>/dev/null
+two_file_test_1b.syms: two_file_test_1b.o
+       $(TEST_READELF) -sW $< >$@ 2>/dev/null
+two_file_test_2.syms: two_file_test_2.o
+       $(TEST_READELF) -sW $< >$@ 2>/dev/null
+
+endif PLUGINS
+
 endif GCC
 endif NATIVE_LINKER
index e3d8dcaef040deac0188f3e2f6d5ca73d87bd826..9860835c10769f024093b8d8c35ffcdb0b03b97e 100644 (file)
@@ -47,7 +47,8 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \
        $(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6) \
        $(am__EXEEXT_7) $(am__EXEEXT_8) $(am__EXEEXT_9) \
        $(am__EXEEXT_10) $(am__EXEEXT_11) $(am__EXEEXT_12) \
-       $(am__EXEEXT_13) $(am__EXEEXT_14) $(am__EXEEXT_15)
+       $(am__EXEEXT_13) $(am__EXEEXT_14) $(am__EXEEXT_15) \
+       $(am__EXEEXT_16)
 @GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_1 = basic_test \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ basic_static_test basic_pic_test \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ basic_static_pic_test \
@@ -66,33 +67,39 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_relocatable_test
 @GCC_FALSE@constructor_test_DEPENDENCIES = libgoldtest.a ../libgold.a \
 @GCC_FALSE@    ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-@GCC_FALSE@    $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@constructor_test_DEPENDENCIES = libgoldtest.a \
 @NATIVE_LINKER_FALSE@  ../libgold.a ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
 @GCC_FALSE@constructor_static_test_DEPENDENCIES = libgoldtest.a \
 @GCC_FALSE@    ../libgold.a ../../libiberty/libiberty.a \
-@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+@GCC_FALSE@    $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@constructor_static_test_DEPENDENCIES =  \
 @NATIVE_LINKER_FALSE@  libgoldtest.a ../libgold.a \
 @NATIVE_LINKER_FALSE@  ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
 @GCC_FALSE@two_file_test_DEPENDENCIES = libgoldtest.a ../libgold.a \
 @GCC_FALSE@    ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-@GCC_FALSE@    $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@two_file_test_DEPENDENCIES = libgoldtest.a \
 @NATIVE_LINKER_FALSE@  ../libgold.a ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
 @GCC_FALSE@two_file_static_test_DEPENDENCIES = libgoldtest.a \
 @GCC_FALSE@    ../libgold.a ../../libiberty/libiberty.a \
-@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+@GCC_FALSE@    $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@two_file_static_test_DEPENDENCIES =  \
 @NATIVE_LINKER_FALSE@  libgoldtest.a ../libgold.a \
 @NATIVE_LINKER_FALSE@  ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
 
 # The nonpic tests will fail on platforms which can not put non-PIC
@@ -116,32 +123,37 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ weak_test weak_undef_test
 @GCC_FALSE@common_test_1_DEPENDENCIES = libgoldtest.a ../libgold.a \
 @GCC_FALSE@    ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-@GCC_FALSE@    $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@common_test_1_DEPENDENCIES = libgoldtest.a \
 @NATIVE_LINKER_FALSE@  ../libgold.a ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
 @GCC_FALSE@exception_test_DEPENDENCIES = libgoldtest.a ../libgold.a \
 @GCC_FALSE@    ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-@GCC_FALSE@    $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@exception_test_DEPENDENCIES = libgoldtest.a \
 @NATIVE_LINKER_FALSE@  ../libgold.a ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
 @GCC_FALSE@exception_static_test_DEPENDENCIES = libgoldtest.a \
 @GCC_FALSE@    ../libgold.a ../../libiberty/libiberty.a \
-@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+@GCC_FALSE@    $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@exception_static_test_DEPENDENCIES =  \
 @NATIVE_LINKER_FALSE@  libgoldtest.a ../libgold.a \
 @NATIVE_LINKER_FALSE@  ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
 @GCC_FALSE@weak_test_DEPENDENCIES = libgoldtest.a ../libgold.a \
 @GCC_FALSE@    ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-@GCC_FALSE@    $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@weak_test_DEPENDENCIES = libgoldtest.a \
 @NATIVE_LINKER_FALSE@  ../libgold.a ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
 @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_4 = weak_undef_nonpic_test
 @GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_5 = weak_alias_test weak_plt \
@@ -187,10 +199,12 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ many_sections_r_test
 @GCC_FALSE@many_sections_test_DEPENDENCIES = libgoldtest.a \
 @GCC_FALSE@    ../libgold.a ../../libiberty/libiberty.a \
-@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+@GCC_FALSE@    $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@many_sections_test_DEPENDENCIES = libgoldtest.a \
 @NATIVE_LINKER_FALSE@  ../libgold.a ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
 @GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_14 = many_sections_define.h \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ many_sections_check.h
@@ -199,13 +213,15 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \
 @CONSTRUCTOR_PRIORITY_FALSE@   ../libgold.a \
 @CONSTRUCTOR_PRIORITY_FALSE@   ../../libiberty/libiberty.a \
 @CONSTRUCTOR_PRIORITY_FALSE@   $(am__DEPENDENCIES_1) \
+@CONSTRUCTOR_PRIORITY_FALSE@   $(am__DEPENDENCIES_1) \
 @CONSTRUCTOR_PRIORITY_FALSE@   $(am__DEPENDENCIES_1)
 @GCC_FALSE@initpri1_DEPENDENCIES = libgoldtest.a ../libgold.a \
 @GCC_FALSE@    ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-@GCC_FALSE@    $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@initpri1_DEPENDENCIES = libgoldtest.a \
 @NATIVE_LINKER_FALSE@  ../libgold.a ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
 @GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_16 = debug_msg.err \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ debug_msg_so.err \
@@ -235,32 +251,48 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ script_test_3
 @GCC_FALSE@script_test_1_DEPENDENCIES = libgoldtest.a ../libgold.a \
 @GCC_FALSE@    ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-@GCC_FALSE@    $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@script_test_1_DEPENDENCIES = libgoldtest.a \
 @NATIVE_LINKER_FALSE@  ../libgold.a ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
 @GCC_FALSE@script_test_2_DEPENDENCIES = libgoldtest.a ../libgold.a \
 @GCC_FALSE@    ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-@GCC_FALSE@    $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@script_test_2_DEPENDENCIES = libgoldtest.a \
 @NATIVE_LINKER_FALSE@  ../libgold.a ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
 @GCC_FALSE@justsyms_DEPENDENCIES = libgoldtest.a ../libgold.a \
 @GCC_FALSE@    ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-@GCC_FALSE@    $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@justsyms_DEPENDENCIES = libgoldtest.a \
 @NATIVE_LINKER_FALSE@  ../libgold.a ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
 @GCC_FALSE@binary_test_DEPENDENCIES = libgoldtest.a ../libgold.a \
 @GCC_FALSE@    ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-@GCC_FALSE@    $(am__DEPENDENCIES_1)
+@GCC_FALSE@    $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 @NATIVE_LINKER_FALSE@binary_test_DEPENDENCIES = libgoldtest.a \
 @NATIVE_LINKER_FALSE@  ../libgold.a ../../libiberty/libiberty.a \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
+@NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1) \
 @NATIVE_LINKER_FALSE@  $(am__DEPENDENCIES_1)
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@am__append_20 =  \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   plugin_test_1 \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   plugin_test_2
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@am__append_21 =  \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   plugin_test_1.sh \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   plugin_test_2.sh
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@am__append_22 =  \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   plugin_test_1.err \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   plugin_test_2.err
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@am__append_23 =  \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   plugin_test_1.err \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   plugin_test_2.err
 subdir = testsuite
 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@@ -358,31 +390,33 @@ libgoldtest_a_OBJECTS = $(am_libgoldtest_a_OBJECTS)
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ justsyms$(EXEEXT) \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ binary_test$(EXEEXT) \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ script_test_3$(EXEEXT)
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@am__EXEEXT_16 = plugin_test_1$(EXEEXT) \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   plugin_test_2$(EXEEXT)
 basic_pic_test_SOURCES = basic_pic_test.c
 basic_pic_test_OBJECTS = basic_pic_test.$(OBJEXT)
 basic_pic_test_LDADD = $(LDADD)
 am__DEPENDENCIES_1 =
 basic_pic_test_DEPENDENCIES = libgoldtest.a ../libgold.a \
        ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 basic_static_pic_test_SOURCES = basic_static_pic_test.c
 basic_static_pic_test_OBJECTS = basic_static_pic_test.$(OBJEXT)
 basic_static_pic_test_LDADD = $(LDADD)
 basic_static_pic_test_DEPENDENCIES = libgoldtest.a ../libgold.a \
        ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 basic_static_test_SOURCES = basic_static_test.c
 basic_static_test_OBJECTS = basic_static_test.$(OBJEXT)
 basic_static_test_LDADD = $(LDADD)
 basic_static_test_DEPENDENCIES = libgoldtest.a ../libgold.a \
        ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 basic_test_SOURCES = basic_test.c
 basic_test_OBJECTS = basic_test.$(OBJEXT)
 basic_test_LDADD = $(LDADD)
 basic_test_DEPENDENCIES = libgoldtest.a ../libgold.a \
        ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 am__binary_test_SOURCES_DIST = binary_test.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@am_binary_test_OBJECTS =  \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ binary_test.$(OBJEXT)
@@ -393,7 +427,7 @@ binary_unittest_OBJECTS = $(am_binary_unittest_OBJECTS)
 binary_unittest_LDADD = $(LDADD)
 binary_unittest_DEPENDENCIES = libgoldtest.a ../libgold.a \
        ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 am__common_test_1_SOURCES_DIST = common_test_1.c
 @GCC_TRUE@@NATIVE_LINKER_TRUE@am_common_test_1_OBJECTS =  \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ common_test_1.$(OBJEXT)
@@ -472,13 +506,13 @@ flagstest_compress_debug_sections_OBJECTS =  \
 flagstest_compress_debug_sections_LDADD = $(LDADD)
 flagstest_compress_debug_sections_DEPENDENCIES = libgoldtest.a \
        ../libgold.a ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 flagstest_o_specialfile_SOURCES = flagstest_o_specialfile.c
 flagstest_o_specialfile_OBJECTS = flagstest_o_specialfile.$(OBJEXT)
 flagstest_o_specialfile_LDADD = $(LDADD)
 flagstest_o_specialfile_DEPENDENCIES = libgoldtest.a ../libgold.a \
        ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 flagstest_o_specialfile_and_compress_debug_sections_SOURCES =  \
        flagstest_o_specialfile_and_compress_debug_sections.c
 flagstest_o_specialfile_and_compress_debug_sections_OBJECTS =  \
@@ -486,7 +520,8 @@ flagstest_o_specialfile_and_compress_debug_sections_OBJECTS =  \
 flagstest_o_specialfile_and_compress_debug_sections_LDADD = $(LDADD)
 flagstest_o_specialfile_and_compress_debug_sections_DEPENDENCIES =  \
        libgoldtest.a ../libgold.a ../../libiberty/libiberty.a \
-       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+       $(am__DEPENDENCIES_1)
 am__initpri1_SOURCES_DIST = initpri1.c
 @CONSTRUCTOR_PRIORITY_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am_initpri1_OBJECTS = initpri1.$(OBJEXT)
 initpri1_OBJECTS = $(am_initpri1_OBJECTS)
@@ -501,7 +536,7 @@ many_sections_r_test_OBJECTS = many_sections_r_test.$(OBJEXT)
 many_sections_r_test_LDADD = $(LDADD)
 many_sections_r_test_DEPENDENCIES = libgoldtest.a ../libgold.a \
        ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 am__many_sections_test_SOURCES_DIST = many_sections_test.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@am_many_sections_test_OBJECTS =  \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ many_sections_test.$(OBJEXT)
@@ -512,7 +547,19 @@ object_unittest_OBJECTS = $(am_object_unittest_OBJECTS)
 object_unittest_LDADD = $(LDADD)
 object_unittest_DEPENDENCIES = libgoldtest.a ../libgold.a \
        ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+plugin_test_1_SOURCES = plugin_test_1.c
+plugin_test_1_OBJECTS = plugin_test_1.$(OBJEXT)
+plugin_test_1_LDADD = $(LDADD)
+plugin_test_1_DEPENDENCIES = libgoldtest.a ../libgold.a \
+       ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+plugin_test_2_SOURCES = plugin_test_2.c
+plugin_test_2_OBJECTS = plugin_test_2.$(OBJEXT)
+plugin_test_2_LDADD = $(LDADD)
+plugin_test_2_DEPENDENCIES = libgoldtest.a ../libgold.a \
+       ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 am__protected_1_SOURCES_DIST = protected_main_1.cc protected_main_2.cc \
        protected_main_3.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@am_protected_1_OBJECTS =  \
@@ -551,7 +598,7 @@ script_test_3_OBJECTS = script_test_3.$(OBJEXT)
 script_test_3_LDADD = $(LDADD)
 script_test_3_DEPENDENCIES = libgoldtest.a ../libgold.a \
        ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 am__tls_pic_test_SOURCES_DIST = tls_test_main.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@am_tls_pic_test_OBJECTS = tls_test_main.$(OBJEXT)
 tls_pic_test_OBJECTS = $(am_tls_pic_test_OBJECTS)
@@ -708,7 +755,7 @@ two_file_strip_test_OBJECTS = two_file_strip_test.$(OBJEXT)
 two_file_strip_test_LDADD = $(LDADD)
 two_file_strip_test_DEPENDENCIES = libgoldtest.a ../libgold.a \
        ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 am__two_file_test_SOURCES_DIST = two_file_test_1.cc \
        two_file_test_1b.cc two_file_test_2.cc two_file_test_main.cc \
        two_file_test.h
@@ -748,7 +795,7 @@ weak_plt_OBJECTS = weak_plt.$(OBJEXT)
 weak_plt_LDADD = $(LDADD)
 weak_plt_DEPENDENCIES = libgoldtest.a ../libgold.a \
        ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 am__weak_test_SOURCES_DIST = weak_test.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@am_weak_test_OBJECTS =  \
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ weak_test.$(OBJEXT)
@@ -788,11 +835,11 @@ SOURCES = $(libgoldtest_a_SOURCES) basic_pic_test.c \
        flagstest_o_specialfile_and_compress_debug_sections.c \
        $(initpri1_SOURCES) $(justsyms_SOURCES) many_sections_r_test.c \
        $(many_sections_test_SOURCES) $(object_unittest_SOURCES) \
-       $(protected_1_SOURCES) $(protected_2_SOURCES) \
-       $(relro_script_test_SOURCES) $(relro_test_SOURCES) \
-       $(script_test_1_SOURCES) $(script_test_2_SOURCES) \
-       script_test_3.c $(tls_pic_test_SOURCES) \
-       $(tls_shared_gd_to_ie_test_SOURCES) \
+       plugin_test_1.c plugin_test_2.c $(protected_1_SOURCES) \
+       $(protected_2_SOURCES) $(relro_script_test_SOURCES) \
+       $(relro_test_SOURCES) $(script_test_1_SOURCES) \
+       $(script_test_2_SOURCES) script_test_3.c \
+       $(tls_pic_test_SOURCES) $(tls_shared_gd_to_ie_test_SOURCES) \
        $(tls_shared_gnu2_gd_to_ie_test_SOURCES) \
        $(tls_shared_gnu2_test_SOURCES) $(tls_shared_ie_test_SOURCES) \
        $(tls_shared_nonpic_test_SOURCES) $(tls_shared_test_SOURCES) \
@@ -838,7 +885,8 @@ DIST_SOURCES = $(libgoldtest_a_SOURCES) basic_pic_test.c \
        flagstest_o_specialfile_and_compress_debug_sections.c \
        $(am__initpri1_SOURCES_DIST) $(am__justsyms_SOURCES_DIST) \
        many_sections_r_test.c $(am__many_sections_test_SOURCES_DIST) \
-       $(object_unittest_SOURCES) $(am__protected_1_SOURCES_DIST) \
+       $(object_unittest_SOURCES) plugin_test_1.c plugin_test_2.c \
+       $(am__protected_1_SOURCES_DIST) \
        $(am__protected_2_SOURCES_DIST) \
        $(am__relro_script_test_SOURCES_DIST) \
        $(am__relro_test_SOURCES_DIST) \
@@ -954,6 +1002,8 @@ PACKAGE_STRING = @PACKAGE_STRING@
 PACKAGE_TARNAME = @PACKAGE_TARNAME@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PATH_SEPARATOR = @PATH_SEPARATOR@
+PLUGINS_FALSE = @PLUGINS_FALSE@
+PLUGINS_TRUE = @PLUGINS_TRUE@
 POSUB = @POSUB@
 RANDOM_SEED_CFLAGS = @RANDOM_SEED_CFLAGS@
 RANLIB = @RANLIB@
@@ -1027,6 +1077,7 @@ AUTOMAKE_OPTIONS =
 # The two_file_test tests -fmerge-constants, so we simply always turn
 # it on.  This may need to be controlled by a configure option
 # eventually.
+AM_CFLAGS = $(WARN_CFLAGS) $(LFS_CFLAGS) -fmerge-constants
 AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CFLAGS) -fmerge-constants
 INCLUDES = \
        -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../../include \
@@ -1038,6 +1089,7 @@ TEST_READELF = $(top_builddir)/../binutils/readelf
 TEST_OBJDUMP = $(top_builddir)/../binutils/objdump
 TEST_CXXFILT = $(top_builddir)/../binutils/cxxfilt
 TEST_STRIP = $(top_builddir)/../binutils/strip-new
+@PLUGINS_TRUE@LIBDL = -ldl
 @THREADS_TRUE@THREADSLIB = -lpthread
 @OMP_SUPPORT_TRUE@TLS_TEST_C_CFLAGS = -fopenmp
 
@@ -1045,13 +1097,13 @@ TEST_STRIP = $(top_builddir)/../binutils/strip-new
 # .o's), but not all of them (such as .so's and .err files).  We
 # improve on that here.  automake-1.9 info docs say "mostlyclean" is
 # the right choice for files 'make' builds that people rebuild.
-MOSTLYCLEANFILES = *.so $(am__append_16)
+MOSTLYCLEANFILES = *.so $(am__append_16) $(am__append_23)
 
 # We will add to these later, for each individual test.  Note
 # that we add each test under check_SCRIPTS or check_PROGRAMS;
 # the TESTS variable is automatically populated from these.
-check_SCRIPTS = $(am__append_6)
-check_DATA = $(am__append_7)
+check_SCRIPTS = $(am__append_6) $(am__append_21)
+check_DATA = $(am__append_7) $(am__append_22)
 BUILT_SOURCES = $(am__append_14)
 TESTS = $(check_SCRIPTS) $(check_PROGRAMS)
 
@@ -1065,7 +1117,7 @@ DEPENDENCIES = \
        libgoldtest.a ../libgold.a ../../libiberty/libiberty.a $(LIBINTL_DEP)
 
 LDADD = libgoldtest.a ../libgold.a ../../libiberty/libiberty.a $(LIBINTL) \
-       $(THREADSLIB)
+       $(THREADSLIB) $(LIBDL)
 
 object_unittest_SOURCES = object_unittest.cc
 binary_unittest_SOURCES = binary_unittest.cc
@@ -1515,6 +1567,24 @@ many_sections_test$(EXEEXT): $(many_sections_test_OBJECTS) $(many_sections_test_
 object_unittest$(EXEEXT): $(object_unittest_OBJECTS) $(object_unittest_DEPENDENCIES) 
        @rm -f object_unittest$(EXEEXT)
        $(CXXLINK) $(object_unittest_LDFLAGS) $(object_unittest_OBJECTS) $(object_unittest_LDADD) $(LIBS)
+@GCC_FALSE@plugin_test_1$(EXEEXT): $(plugin_test_1_OBJECTS) $(plugin_test_1_DEPENDENCIES) 
+@GCC_FALSE@    @rm -f plugin_test_1$(EXEEXT)
+@GCC_FALSE@    $(LINK) $(plugin_test_1_LDFLAGS) $(plugin_test_1_OBJECTS) $(plugin_test_1_LDADD) $(LIBS)
+@NATIVE_LINKER_FALSE@plugin_test_1$(EXEEXT): $(plugin_test_1_OBJECTS) $(plugin_test_1_DEPENDENCIES) 
+@NATIVE_LINKER_FALSE@  @rm -f plugin_test_1$(EXEEXT)
+@NATIVE_LINKER_FALSE@  $(LINK) $(plugin_test_1_LDFLAGS) $(plugin_test_1_OBJECTS) $(plugin_test_1_LDADD) $(LIBS)
+@PLUGINS_FALSE@plugin_test_1$(EXEEXT): $(plugin_test_1_OBJECTS) $(plugin_test_1_DEPENDENCIES) 
+@PLUGINS_FALSE@        @rm -f plugin_test_1$(EXEEXT)
+@PLUGINS_FALSE@        $(LINK) $(plugin_test_1_LDFLAGS) $(plugin_test_1_OBJECTS) $(plugin_test_1_LDADD) $(LIBS)
+@GCC_FALSE@plugin_test_2$(EXEEXT): $(plugin_test_2_OBJECTS) $(plugin_test_2_DEPENDENCIES) 
+@GCC_FALSE@    @rm -f plugin_test_2$(EXEEXT)
+@GCC_FALSE@    $(LINK) $(plugin_test_2_LDFLAGS) $(plugin_test_2_OBJECTS) $(plugin_test_2_LDADD) $(LIBS)
+@NATIVE_LINKER_FALSE@plugin_test_2$(EXEEXT): $(plugin_test_2_OBJECTS) $(plugin_test_2_DEPENDENCIES) 
+@NATIVE_LINKER_FALSE@  @rm -f plugin_test_2$(EXEEXT)
+@NATIVE_LINKER_FALSE@  $(LINK) $(plugin_test_2_LDFLAGS) $(plugin_test_2_OBJECTS) $(plugin_test_2_LDADD) $(LIBS)
+@PLUGINS_FALSE@plugin_test_2$(EXEEXT): $(plugin_test_2_OBJECTS) $(plugin_test_2_DEPENDENCIES) 
+@PLUGINS_FALSE@        @rm -f plugin_test_2$(EXEEXT)
+@PLUGINS_FALSE@        $(LINK) $(plugin_test_2_LDFLAGS) $(plugin_test_2_OBJECTS) $(plugin_test_2_LDADD) $(LIBS)
 protected_1$(EXEEXT): $(protected_1_OBJECTS) $(protected_1_DEPENDENCIES) 
        @rm -f protected_1$(EXEEXT)
        $(CXXLINK) $(protected_1_LDFLAGS) $(protected_1_OBJECTS) $(protected_1_LDADD) $(LIBS)
@@ -1692,6 +1762,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/many_sections_r_test.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/many_sections_test.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/object_unittest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_test_1.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_test_2.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protected_3.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protected_main_1.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protected_main_2.Po@am__quote@
@@ -2313,6 +2385,28 @@ uninstall-am: uninstall-info-am
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ basic_test.o -T $(srcdir)/script_test_4.t
 @GCC_TRUE@@NATIVE_LINKER_TRUE@script_test_4.stdout: script_test_4
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_READELF) -SlW script_test_4 > script_test_4.stdout
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_test_1: two_file_test_main.o two_file_test_1.syms two_file_test_1b.syms two_file_test_2.syms gcctestdir/ld plugin_test.so
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   $(CXXLINK) -Bgcctestdir/ -Wl,--no-demangle,--plugin,"./plugin_test.so;_Z4f13iv" two_file_test_main.o two_file_test_1.syms two_file_test_1b.syms two_file_test_2.syms 2>plugin_test_1.err
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_test_1.err: plugin_test_1
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   @touch plugin_test_1.err
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_test_2: two_file_test_main.o two_file_test_1.syms two_file_test_1b.syms two_file_shared_2.so gcctestdir/ld plugin_test.so
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   $(CXXLINK) -Bgcctestdir/ -Wl,--no-demangle,-R,.,--plugin,"./plugin_test.so" two_file_test_main.o two_file_test_1.syms two_file_test_1b.syms two_file_shared_2.so 2>plugin_test_2.err
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_test_2.err: plugin_test_2
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   @touch plugin_test_2.err
+
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_test.so: plugin_test.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   $(LINK) -Bgcctestdir/ -shared plugin_test.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_test.o: plugin_test.c
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   $(COMPILE) -O0 -c -fpic -o $@ $<
+
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@two_file_test_main.syms: two_file_test_main.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   $(TEST_READELF) -sW $< >$@ 2>/dev/null
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@two_file_test_1.syms: two_file_test_1.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   $(TEST_READELF) -sW $< >$@ 2>/dev/null
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@two_file_test_1b.syms: two_file_test_1b.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   $(TEST_READELF) -sW $< >$@ 2>/dev/null
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@two_file_test_2.syms: two_file_test_2.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@   $(TEST_READELF) -sW $< >$@ 2>/dev/null
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:
diff --git a/gold/testsuite/plugin_test.c b/gold/testsuite/plugin_test.c
new file mode 100644 (file)
index 0000000..c60c7a1
--- /dev/null
@@ -0,0 +1,427 @@
+/* test_plugin.c -- simple linker plugin test
+
+   Copyright 2008 Free Software Foundation, Inc.
+   Written by Cary Coutant <ccoutant@google.com>.
+
+   This file is part of gold.
+
+   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 <stdlib.h>
+#include <string.h>
+#include "plugin-api.h"
+
+struct claimed_file
+{
+  const char* name;
+  void* handle;
+  int nsyms;
+  struct ld_plugin_symbol* syms;
+  struct claimed_file* next;
+};
+
+static struct claimed_file* first_claimed_file = NULL;
+static struct claimed_file* last_claimed_file = NULL;
+
+static ld_plugin_register_claim_file register_claim_file_hook = NULL;
+static ld_plugin_register_all_symbols_read register_all_symbols_read_hook = NULL;
+static ld_plugin_register_cleanup register_cleanup_hook = NULL;
+static ld_plugin_add_symbols add_symbols = NULL;
+static ld_plugin_get_symbols get_symbols = NULL;
+static ld_plugin_add_input_file add_input_file = NULL;
+static ld_plugin_message message = NULL;
+
+#define MAXOPTS 10
+
+static const char *opts[MAXOPTS];
+static int nopts = 0;
+
+enum ld_plugin_status onload(struct ld_plugin_tv *tv);
+enum ld_plugin_status claim_file_hook(const struct ld_plugin_input_file *file,
+                                      int *claimed);
+enum ld_plugin_status all_symbols_read_hook(void);
+enum ld_plugin_status cleanup_hook(void);
+
+enum ld_plugin_status
+onload(struct ld_plugin_tv *tv)
+{
+  struct ld_plugin_tv *entry;
+  int api_version = 0;
+  int gold_version = 0;
+  int i;
+
+  for (entry = tv; entry->tv_tag != LDPT_NULL; ++entry)
+    {
+      switch (entry->tv_tag)
+        {
+        case LDPT_API_VERSION:
+          api_version = entry->tv_u.tv_val;
+          break;
+        case LDPT_GOLD_VERSION:
+          gold_version = entry->tv_u.tv_val;
+          break;
+        case LDPT_LINKER_OUTPUT:
+          break;
+        case LDPT_OPTION:
+          if (nopts < MAXOPTS)
+            opts[nopts++] = entry->tv_u.tv_string;
+          break;
+        case LDPT_REGISTER_CLAIM_FILE_HOOK:
+          register_claim_file_hook = entry->tv_u.tv_register_claim_file;
+          break;
+        case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
+          register_all_symbols_read_hook =
+            entry->tv_u.tv_register_all_symbols_read;
+          break;
+        case LDPT_REGISTER_CLEANUP_HOOK:
+          register_cleanup_hook = entry->tv_u.tv_register_cleanup;
+          break;
+        case LDPT_ADD_SYMBOLS:
+          add_symbols = entry->tv_u.tv_add_symbols;
+          break;
+        case LDPT_GET_SYMBOLS:
+          get_symbols = entry->tv_u.tv_get_symbols;
+          break;
+        case LDPT_ADD_INPUT_FILE:
+          add_input_file = entry->tv_u.tv_add_input_file;
+          break;
+        case LDPT_MESSAGE:
+          message = entry->tv_u.tv_message;
+          break;
+        default:
+          break;
+        }
+    }
+
+  if (message == NULL)
+    {
+      fprintf(stderr, "tv_message interface missing\n");
+      return LDPS_ERR;
+    }
+
+  if (register_claim_file_hook == NULL)
+    {
+      fprintf(stderr, "tv_register_claim_file_hook interface missing\n");
+      return LDPS_ERR;
+    }
+
+  if (register_all_symbols_read_hook == NULL)
+    {
+      fprintf(stderr, "tv_register_all_symbols_read_hook interface missing\n");
+      return LDPS_ERR;
+    }
+
+  if (register_cleanup_hook == NULL)
+    {
+      fprintf(stderr, "tv_register_cleanup_hook interface missing\n");
+      return LDPS_ERR;
+    }
+
+  (*message)(LDPL_INFO, "API version:   %d", api_version);
+  (*message)(LDPL_INFO, "gold version:  %d", gold_version);
+
+  for (i = 0; i < nopts; ++i)
+    (*message)(LDPL_INFO, "option: %s", opts[i]);
+
+  if ((*register_claim_file_hook)(claim_file_hook) != LDPS_OK)
+    {
+      (*message)(LDPL_ERROR, "error registering claim file hook");
+      return LDPS_ERR;
+    }
+
+  if ((*register_all_symbols_read_hook)(all_symbols_read_hook) != LDPS_OK)
+    {
+      (*message)(LDPL_ERROR, "error registering all symbols read hook");
+      return LDPS_ERR;
+    }
+
+  if ((*register_cleanup_hook)(cleanup_hook) != LDPS_OK)
+    {
+      (*message)(LDPL_ERROR, "error registering cleanup hook");
+      return LDPS_ERR;
+    }
+
+  return LDPS_OK;
+}
+
+enum ld_plugin_status
+claim_file_hook (const struct ld_plugin_input_file* file, int* claimed)
+{
+  int len;
+  char buf[160];
+  struct claimed_file* claimed_file;
+  struct ld_plugin_symbol* syms;
+  int nsyms = 0;
+  int maxsyms = 0;
+  FILE* irfile;
+  char *p;
+  char *pbind;
+  char *pvis;
+  char *psect;
+  int weak;
+  int def;
+  int vis;
+  int size;
+  char* name;
+  int is_comdat;
+  int i;
+
+  (*message)(LDPL_INFO,
+             "%s: claim file hook called (offset = %ld, size = %ld)",
+             file->name, (long)file->offset, (long)file->filesize);
+
+  /* Look for the beginning of output from readelf -s.  */
+  irfile = fdopen(file->fd, "r");
+  (void)fseek(irfile, file->offset, SEEK_SET);
+  len = fread(buf, 1, 13, irfile);
+  if (len < 13 || strncmp(buf, "\nSymbol table", 13) != 0)
+    return LDPS_OK;
+
+  /* Skip the two header lines.  */
+  (void) fgets(buf, sizeof(buf), irfile);
+  (void) fgets(buf, sizeof(buf), irfile);
+
+  if (add_symbols == NULL)
+    {
+      fprintf(stderr, "tv_add_symbols interface missing\n");
+      return LDPS_ERR;
+    }
+
+  /* Parse the output from readelf. The columns are:
+     Index Value Size Type Binding Visibility Section Name.  */
+  syms = (struct ld_plugin_symbol*)malloc(sizeof(struct ld_plugin_symbol) * 8);
+  if (syms == NULL)
+    return LDPS_ERR;
+  maxsyms = 8;
+  while (fgets(buf, sizeof(buf), irfile) != NULL)
+    {
+      p = buf;
+      p += strspn(p, " ");
+
+      /* Index field.  */
+      p += strcspn(p, " ");
+      p += strspn(p, " ");
+
+      /* Value field.  */
+      p += strcspn(p, " ");
+      p += strspn(p, " ");
+
+      /* Size field.  */
+      size = atoi(p);
+      p += strcspn(p, " ");
+      p += strspn(p, " ");
+
+      /* Type field.  */
+      p += strcspn(p, " ");
+      p += strspn(p, " ");
+
+      /* Binding field.  */
+      pbind = p;
+      p += strcspn(p, " ");
+      p += strspn(p, " ");
+
+      /* Visibility field.  */
+      pvis = p;
+      p += strcspn(p, " ");
+      p += strspn(p, " ");
+
+      /* Section field.  */
+      psect = p;
+      p += strcspn(p, " ");
+      p += strspn(p, " ");
+
+      /* Name field.  */
+      /* FIXME:  Look for version.  */
+      len = strlen(p);
+      if (p[len-1] == '\n')
+        p[--len] = '\0';
+      name = malloc(len + 1);
+      strncpy(name, p, len + 1);
+
+      /* Ignore local symbols.  */
+      if (strncmp(pbind, "LOCAL", 5) == 0)
+        continue;
+
+      weak = strncmp(pbind, "WEAK", 4) == 0;
+      if (strncmp(psect, "UND", 3) == 0)
+        def = weak ? LDPK_WEAKUNDEF : LDPK_UNDEF;
+      else if (strncmp(psect, "COM", 3) == 0)
+        def = LDPK_COMMON;
+      else
+        def = weak ? LDPK_WEAKDEF : LDPK_DEF;
+
+      if (strncmp(pvis, "INTERNAL", 8) == 0)
+        vis = LDPV_INTERNAL;
+      else if (strncmp(pvis, "HIDDEN", 6) == 0)
+        vis = LDPV_HIDDEN;
+      else if (strncmp(pvis, "PROTECTED", 9) == 0)
+        vis = LDPV_PROTECTED;
+      else
+        vis = LDPV_DEFAULT;
+
+      /* If the symbol is listed in the options list, special-case
+         it as a comdat symbol.  */
+      is_comdat = 0;
+      for (i = 0; i < nopts; ++i)
+        {
+          if (name != NULL && strcmp(name, opts[i]) == 0)
+            {
+              is_comdat = 1;
+              break;
+            }
+        }
+
+      if (nsyms >= maxsyms)
+        {
+          syms = (struct ld_plugin_symbol*)
+            realloc(syms, sizeof(struct ld_plugin_symbol) * maxsyms * 2);
+          if (syms == NULL)
+            return LDPS_ERR;
+          maxsyms *= 2;
+        }
+
+      syms[nsyms].name = name;
+      syms[nsyms].version = NULL;
+      syms[nsyms].def = def;
+      syms[nsyms].visibility = vis;
+      syms[nsyms].size = size;
+      syms[nsyms].comdat_key = is_comdat ? name : NULL;
+      syms[nsyms].resolution = LDPR_UNKNOWN;
+      ++nsyms;
+    }
+
+  claimed_file = (struct claimed_file*) malloc(sizeof(struct claimed_file));
+  if (claimed_file == NULL)
+    return LDPS_ERR;
+
+  claimed_file->name = file->name;
+  claimed_file->handle = file->handle;
+  claimed_file->nsyms = nsyms;
+  claimed_file->syms = syms;
+  claimed_file->next = NULL;
+  if (last_claimed_file == NULL)
+    first_claimed_file = claimed_file;
+  else
+    last_claimed_file->next = claimed_file;
+  last_claimed_file = claimed_file;
+
+  (*add_symbols)(file->handle, nsyms, syms);
+
+  *claimed = 1;
+  return LDPS_OK;
+}
+
+enum ld_plugin_status
+all_symbols_read_hook(void)
+{
+  int i;
+  const char* res;
+  struct claimed_file* claimed_file;
+  char buf[160];
+  char *p;
+
+  (*message)(LDPL_INFO, "all symbols read hook called");
+
+  if (get_symbols == NULL)
+    {
+      fprintf(stderr, "tv_get_symbols interface missing\n");
+      return LDPS_ERR;
+    }
+
+  for (claimed_file = first_claimed_file;
+       claimed_file != NULL;
+       claimed_file = claimed_file->next)
+    {
+      (*get_symbols)(claimed_file->handle, claimed_file->nsyms,
+                     claimed_file->syms);
+      for (i = 0; i < claimed_file->nsyms; ++i)
+        {
+          switch (claimed_file->syms[i].resolution)
+            {
+            case LDPR_UNKNOWN:
+              res = "UNKNOWN";
+              break;
+            case LDPR_UNDEF:
+              res = "UNDEF";
+              break;
+            case LDPR_PREVAILING_DEF:
+              res = "PREVAILING_DEF_REG";
+              break;
+            case LDPR_PREVAILING_DEF_IRONLY:
+              res = "PREVAILING_DEF_IRONLY";
+              break;
+            case LDPR_PREEMPTED_REG:
+              res = "PREEMPTED_REG";
+              break;
+            case LDPR_PREEMPTED_IR:
+              res = "PREEMPTED_IR";
+              break;
+            case LDPR_RESOLVED_IR:
+              res = "RESOLVED_IR";
+              break;
+            case LDPR_RESOLVED_EXEC:
+              res = "RESOLVED_EXEC";
+              break;
+            case LDPR_RESOLVED_DYN:
+              res = "RESOLVED_DYN";
+              break;
+            default:
+              res = "?";
+              break;
+            }
+          (*message)(LDPL_INFO, "%s: %s: %s", claimed_file->name,
+                     claimed_file->syms[i].name, res);
+        }
+    }
+
+  if (add_input_file == NULL)
+    {
+      fprintf(stderr, "tv_add_input_file interface missing\n");
+      return LDPS_ERR;
+    }
+
+  for (claimed_file = first_claimed_file;
+       claimed_file != NULL;
+       claimed_file = claimed_file->next)
+    {
+      if (strlen(claimed_file->name) >= sizeof(buf))
+        {
+          (*message)(LDPL_FATAL, "%s: filename too long", claimed_file->name);
+          return LDPS_ERR;
+        }
+      strcpy(buf, claimed_file->name);
+      p = strrchr(buf, '.');
+      if (p == NULL || strcmp(p, ".syms") != 0)
+        {
+          (*message)(LDPL_FATAL, "%s: filename must have '.syms' suffix",
+                     claimed_file->name);
+          return LDPS_ERR;
+        }
+      p[1] = 'o';
+      p[2] = '\0';
+      (*add_input_file)(buf);
+    }
+
+  return LDPS_OK;
+}
+
+enum ld_plugin_status
+cleanup_hook(void)
+{
+  (*message)(LDPL_INFO, "cleanup hook called");
+  return LDPS_OK;
+}
diff --git a/gold/testsuite/plugin_test_1.sh b/gold/testsuite/plugin_test_1.sh
new file mode 100755 (executable)
index 0000000..5e16139
--- /dev/null
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+# plugin_test_1.sh -- a test case for the plugin API.
+
+# Copyright 2008 Free Software Foundation, Inc.
+# Written by Cary Coutant <ccoutant@google.com>.
+
+# This file is part of gold.
+
+# 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 file goes with plugin_test_1.c, a simple plug-in library that
+# exercises the basic interfaces and prints out version numbers and
+# options passed to the plugin.
+
+check()
+{
+    if ! grep -q "$2" "$1"
+    then
+       echo "Did not find expected output in $1:"
+       echo "   $2"
+       echo ""
+       echo "Actual output below:"
+       cat "$1"
+       exit 1
+    fi
+}
+
+check plugin_test_1.err "API version:"
+check plugin_test_1.err "gold version:"
+check plugin_test_1.err "option: _Z4f13iv"
+check plugin_test_1.err "two_file_test_main.o: claim file hook called"
+check plugin_test_1.err "two_file_test_1.syms: claim file hook called"
+check plugin_test_1.err "two_file_test_1b.syms: claim file hook called"
+check plugin_test_1.err "two_file_test_2.syms: claim file hook called"
+check plugin_test_1.err "two_file_test_1.syms: _Z4f13iv: PREVAILING_DEF_IRONLY"
+check plugin_test_1.err "two_file_test_1.syms: _Z2t2v: PREVAILING_DEF_REG"
+check plugin_test_1.err "two_file_test_1.syms: v2: RESOLVED_IR"
+check plugin_test_1.err "two_file_test_1.syms: t17data: RESOLVED_IR"
+check plugin_test_1.err "two_file_test_2.syms: _Z4f13iv: PREEMPTED_IR"
+check plugin_test_1.err "cleanup hook called"
+
+exit 0
diff --git a/gold/testsuite/plugin_test_2.sh b/gold/testsuite/plugin_test_2.sh
new file mode 100755 (executable)
index 0000000..41865ac
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+# plugin_test_2.sh -- a test case for the plugin API.
+
+# Copyright 2008 Free Software Foundation, Inc.
+# Written by Cary Coutant <ccoutant@google.com>.
+
+# This file is part of gold.
+
+# 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 file goes with plugin_test_1.c, a simple plug-in library that
+# exercises the basic interfaces and prints out version numbers and
+# options passed to the plugin.
+
+check()
+{
+    if ! grep -q "$2" "$1"
+    then
+       echo "Did not find expected output in $1:"
+       echo "   $2"
+       echo ""
+       echo "Actual output below:"
+       cat "$1"
+       exit 1
+    fi
+}
+
+check plugin_test_2.err "API version:"
+check plugin_test_2.err "gold version:"
+check plugin_test_2.err "two_file_test_main.o: claim file hook called"
+check plugin_test_2.err "two_file_test_1.syms: claim file hook called"
+check plugin_test_2.err "two_file_test_1b.syms: claim file hook called"
+check plugin_test_2.err "two_file_shared_2.so: claim file hook called"
+check plugin_test_2.err "two_file_test_1.syms: _Z4f13iv: PREVAILING_DEF_REG"
+check plugin_test_2.err "two_file_test_1.syms: _Z2t2v: PREVAILING_DEF_REG"
+check plugin_test_2.err "two_file_test_1.syms: v2: RESOLVED_DYN"
+check plugin_test_2.err "two_file_test_1.syms: t17data: RESOLVED_DYN"
+check plugin_test_2.err "cleanup hook called"
+
+exit 0
index ece1aae367f47d66bb52db8ed22fa59dfc241cb1..adcc2b776cd5a61abe5fead904b029659a7eb989 100644 (file)
@@ -1,3 +1,8 @@
+2008-09-18  Cary Coutant  <ccoutant@google.com>
+
+       Add plugin functionality for link-time optimization (LTO).
+       * plugin-api.h: New file.
+
 2008-09-09  Jason Merrill  <jason@redhat.com>
 
        * demangle.h (enum demangle_component_type): Add
diff --git a/include/plugin-api.h b/include/plugin-api.h
new file mode 100644 (file)
index 0000000..9863086
--- /dev/null
@@ -0,0 +1,242 @@
+/* plugin-api.h -- External linker plugin API.  */
+
+/* Copyright 2008 Free Software Foundation, Inc.
+   Written by Cary Coutant <ccoutant@google.com>.
+
+   This file is part of 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 file defines the interface for writing a linker plugin, which is
+   described at < http://gcc.gnu.org/wiki/whopr/driver >.  */
+
+#ifndef PLUGIN_API_H
+#define PLUGIN_API_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Status code returned by most API routines.  */
+
+enum ld_plugin_status
+{
+  LDPS_OK = 0,
+  LDPS_NO_SYMS,         // Attempt to get symbols that haven't been added.
+  LDPS_ERR,
+  /* Additional Error codes TBD.  */
+};
+
+/* The version of the API specification.  */
+
+enum ld_plugin_api_version
+{
+  LD_PLUGIN_API_VERSION = 1,
+};
+
+/* The type of output file being generated by the linker.  */
+
+enum ld_plugin_output_file_type
+{
+  LDPO_REL,
+  LDPO_EXEC,
+  LDPO_DYN,
+};
+
+/* An input file managed by the plugin library.  */
+
+struct ld_plugin_input_file
+{
+  const char *name;
+  int fd;
+  off_t offset;
+  off_t filesize;
+  void *handle;
+};
+
+/* A symbol belonging to an input file managed by the plugin library.  */
+
+struct ld_plugin_symbol
+{
+  char *name;
+  char *version;
+  int def;
+  int visibility;
+  uint64_t size;
+  char *comdat_key;
+  int resolution;
+};
+
+/* Whether the symbol is a definition, reference, or common, weak or not.  */
+
+enum ld_plugin_symbol_kind
+{
+  LDPK_DEF,
+  LDPK_WEAKDEF,
+  LDPK_UNDEF,
+  LDPK_WEAKUNDEF,
+  LDPK_COMMON,
+};
+
+/* The visibility of the symbol.  */
+
+enum ld_plugin_symbol_visibility
+{
+  LDPV_DEFAULT,
+  LDPV_PROTECTED,
+  LDPV_INTERNAL,
+  LDPV_HIDDEN,
+};
+
+/* How a symbol is resolved.  */
+
+enum ld_plugin_symbol_resolution
+{
+  LDPR_UNKNOWN = 0,
+  LDPR_UNDEF,
+  LDPR_PREVAILING_DEF,
+  LDPR_PREVAILING_DEF_IRONLY,
+  LDPR_PREEMPTED_REG,
+  LDPR_PREEMPTED_IR,
+  LDPR_RESOLVED_IR,
+  LDPR_RESOLVED_EXEC,
+  LDPR_RESOLVED_DYN,
+};
+
+/* The plugin library's "claim file" handler.  */
+
+typedef
+enum ld_plugin_status
+(*ld_plugin_claim_file_handler) (
+  const struct ld_plugin_input_file *file, int *claimed);
+
+/* The plugin library's "all symbols read" handler.  */
+
+typedef
+enum ld_plugin_status
+(*ld_plugin_all_symbols_read_handler) (void);
+
+/* The plugin library's cleanup handler.  */
+
+typedef
+enum ld_plugin_status
+(*ld_plugin_cleanup_handler) (void);
+
+/* The linker's interface for registering the "claim file" handler.  */
+
+typedef
+enum ld_plugin_status
+(*ld_plugin_register_claim_file) (ld_plugin_claim_file_handler handler);
+
+/* The linker's interface for registering the "all symbols read" handler.  */
+
+typedef
+enum ld_plugin_status
+(*ld_plugin_register_all_symbols_read) (
+  ld_plugin_all_symbols_read_handler handler);
+
+/* The linker's interface for registering the cleanup handler.  */
+
+typedef
+enum ld_plugin_status
+(*ld_plugin_register_cleanup) (ld_plugin_cleanup_handler handler);
+
+/* The linker's interface for adding symbols from a claimed input file.  */
+
+typedef
+enum ld_plugin_status
+(*ld_plugin_add_symbols) (void *handle, int nsyms,
+                          const struct ld_plugin_symbol *syms);
+
+/* The linker's interface for retrieving symbol resolution information.  */
+
+typedef
+enum ld_plugin_status
+(*ld_plugin_get_symbols) (const void *handle, int nsyms,
+                          struct ld_plugin_symbol *syms);
+
+/* The linker's interface for adding a compiled input file.  */
+
+typedef
+enum ld_plugin_status
+(*ld_plugin_add_input_file) (char *pathname);
+
+/* The linker's interface for issuing a warning or error message.  */
+
+typedef
+enum ld_plugin_status
+(*ld_plugin_message) (int level, char *format, ...);
+
+enum ld_plugin_level
+{
+  LDPL_INFO,
+  LDPL_WARNING,
+  LDPL_ERROR,
+  LDPL_FATAL,
+};
+
+/* Values for the tv_tag field of the transfer vector.  */
+
+enum ld_plugin_tag
+{
+  LDPT_NULL = 0,
+  LDPT_API_VERSION,
+  LDPT_GOLD_VERSION,
+  LDPT_LINKER_OUTPUT,
+  LDPT_OPTION,
+  LDPT_REGISTER_CLAIM_FILE_HOOK,
+  LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
+  LDPT_REGISTER_CLEANUP_HOOK,
+  LDPT_ADD_SYMBOLS,
+  LDPT_GET_SYMBOLS,
+  LDPT_ADD_INPUT_FILE,
+  LDPT_MESSAGE,
+};
+
+/* The plugin transfer vector.  */
+
+struct ld_plugin_tv
+{
+  enum ld_plugin_tag tv_tag;
+  union
+  {
+    int tv_val;
+    const char *tv_string;
+    ld_plugin_register_claim_file tv_register_claim_file;
+    ld_plugin_register_all_symbols_read tv_register_all_symbols_read;
+    ld_plugin_register_cleanup tv_register_cleanup;
+    ld_plugin_add_symbols tv_add_symbols;
+    ld_plugin_get_symbols tv_get_symbols;
+    ld_plugin_add_input_file tv_add_input_file;
+    ld_plugin_message tv_message;
+  } tv_u;
+};
+
+/* The plugin library's "onload" entry point.  */
+
+typedef
+enum ld_plugin_status
+(*ld_plugin_onload) (struct ld_plugin_tv *tv);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* !defined(PLUGIN_API_H) */