summaryrefslogtreecommitdiff
path: root/ld/plugin.c
diff options
context:
space:
mode:
authorDave Korn <dave.korn@artimi.com>2010-10-14 01:31:25 +0000
committerDave Korn <dave.korn@artimi.com>2010-10-14 01:31:25 +0000
commitb164dc0864f969baa5c09cebf2f3390ed1778614 (patch)
tree18266b4e37e112b8997f37d54cab832071c70cd5 /ld/plugin.c
parent88a19d49326ad9b004d2d653fe5cc4f225b266e5 (diff)
downloadbinutils-redhat-b164dc0864f969baa5c09cebf2f3390ed1778614.tar.gz
Applied patch series for LD plugin interface (six parts).
[PATCH] Add infrastructure for plugin API; functionality to follow. include/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 1/6). * plugin-api.h (LDPT_GNU_LD_VERSION): New ld_plugin_tag enum member. ld/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 1/6). * configure.in: Add AC_CHECKs for file io and dlfcn headers and functions and AC_SEARCH for -ldl. (enable_plugins): New shell variable set if above tests find dlopen functionality. (ENABLE_PLUGINS): Add related automake conditional. * configure: Regenerate. * config.in: Likewise. * Makefile.am (PLUGIN_C): Declare plugin C source file, conditional on ENABLE_PLUGINS being defined. (PLUGIN_H): Likewise for header file. (PLUGIN_OBJECT): Likewise for object file. (PLUGIN_CFLAGS): Likewise -D flag required to compile plugin support. (AM_CPPFLAGS): Use PLUGIN_CFLAGS. (CFILES): Use PLUGIN_C. (HFILES): Use PLUGIN_H. (OFILES): Use PLUGIN_OBJECT. (ld_new_SOURCES): Use PLUGIN_C. (noinst_LTLIBRARIES)[ENABLE_PLUGINS]: Declare test plugin. (libldtestplug_la_SOURCES)[ENABLE_PLUGINS]: Add automake definition for test plugin. (libldtestplug_la_CFLAGS)[ENABLE_PLUGINS]: Likewise. (libldtestplug_la_LDFLAGS)[ENABLE_PLUGINS]: Likewise. * Makefile.in: Regenerate. * sysdep.h: Include stdarg.h, unistd.h and one of fcntl.h or sys/file.h where available. Include dlfcn.h when ENABLE_PLUGINS. (O_RDONLY): Supply default definition likewise to bfd's sysdep.h (O_WRONLY): Likewise. (O_RDWR): Likewise. (O_ACCMODE): Likewise. (O_BINARY): Likewise. (SEEK_SET): Likewise. (SEEK_CUR): Likewise. (SEEK_END): Likewise. * ldmisc.c (vfinfo): Make non-static. Add %p format char. * ldmisc.h (vfinfo): Declare extern prototype. * lexsup.c (enum option_values)[ENABLE_PLUGINS]: Add new entries for OPTION_PLUGIN and OPTION_PLUGIN_OPT. (ld_options[])[ENABLE_PLUGINS]: Add option data for the above two. (parse_args)[ENABLE_PLUGINS]: Handle them, and load all plugins once option parsing is complete. * ldmain.c (main)[ENABLE_PLUGINS]: Call plugin cleanup hooks just after lang_finish. * plugin.c: New source file. * plugin.h: Likewise new header. * testplug.c: New source file. ld/testsuite/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 1/6). * ld-bootstrap/bootstrap.exp: Skip static tests also if LD plugins are enabled. * lib/ld-lib.exp (proc regexp_diff): Extend verbose debug output. (proc set_file_contents): Write a file with the supplied content. (run_ld_link_tests): Add new 'ld' action to test linker output. (proc check_plugin_api_available): Return true if linker under test supports the plugin API. * ld-plugin/func.c: New test source file. * ld-plugin/main.c: Likewise. * ld-plugin/text.c: Likewise. * ld-plugin/plugin-1.d: New dump test output pattern script. * ld-plugin/plugin-2.d: Likewise. * ld-plugin/plugin-3.d: Likewise. * ld-plugin/plugin-4.d: Likewise. * ld-plugin/plugin-5.d: Likewise. * ld-plugin/plugin.exp: New test control script. --- [PATCH] Implement claim file and all symbols read hooks and add symbols callback. ld/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 2/6). * ldfile.c (ldfile_try_open_bfd)[ENABLE_PLUGINS]: Don't return early during compat checks if they pass, instead offer any successfully opened and accepted file to the plugin claim file hooks chain. Create a dummy bfd to accept symbols added by the plugin, if the plugin claims the file. * ldlang.c (lang_process)[ENABLE_PLUGINS]: Call plugin all symbols read hook chain before ldemul_after_open. * ldlang.h (struct lang_input_statement_struct): Add new single-bit 'claimed' flag. * plugin.c (IRONLY_SUFFIX): New macro for dummy bfd file suffix. (IRONLY_SUFFIX_LEN): Length of the above string. (plugin_get_ir_dummy_bfd): New function to create the dummy bfd used to store symbols for ir-only files. (is_ir_dummy_bfd): New function to check if a bfd is ir-only. (asymbol_from_plugin_symbol): New function converts symbol formats. (add_symbols): Call it to convert plugin syms to bfd syms and add them to the dummy bfd. * plugin.h: Add missing include guards. (plugin_get_ir_dummy_bfd): Add prototype. (is_ir_dummy_bfd): Likewise. * testplug.c (TV_MESSAGE): New helper macro. (struct claim_file): New struct. (claim_file_t): New typedef. (tag_names[]): Make static and const. (claimfiles_list): New variable. (claimfiles_tail_chain_ptr): Likewise. (last_claimfile): Likewise. (record_claim_file): Record a file to claim on a singly-linked list. (parse_symdefstr): Parse an ASCII representation of a symbol from a plugin option into the fields of a struct ld_plugin_symbol. (record_claimed_file_symbol): Use it to parse plugin option for adding a symbol. (parse_option): Parse claim file and add symbol options. (dump_tv_tag): Use TV_MESSAGE. (onload): Likewise. (onclaim_file): Make static. Use TV_MESSAGE. Scan list of files to claim and claim this file if required, adding any symbols specified. (onall_symbols_read): Make static and use TV_MESSAGE. (oncleanup): Likewise. ld/testsuite/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 2/6). * ld-plugin/plugin-3.d: Enable regexes for new functionality. * ld-plugin/plugin-5.d: Likewise. * ld-plugin/plugin-6.d: New testcase. * ld-plugin/plugin-7.d: Likewise. * ld-plugin/plugin.exp: Use 'nm' on compiled test objects to determine whether symbols in plugin arguments need an underscore prefix. Add new plugin-6.d and plugin-7.d testcases. --- [PATCH] Implement get symbols callback. ld/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 3/6). * ldmain.c (notice)[ENABLE_PLUGINS]: Call plugin_notice. * plugin.c (non_ironly_hash): Add new bfd hash table. (plugin_load_plugins): Exit early if no plugins to load. If plugins do load successfully, set notice_all flag in link info. (get_symbols): Implement. (plugin_load_plugins): Exit early if no plugins to load, else after loading plugins successfully enable notice_all mode. (init_non_ironly_hash): Lazily init non_ironly_hash table. (plugin_notice): Record symbols referenced from non-IR files in the non_ironly_hash. Suppress tracing, cref generation and nocrossrefs tracking for symbols from dummy IR bfds. * plugin.h: Fix formatting. (plugin_notice): Add prototype. * testplug.c (dumpresolutions): New global var. (parse_options): Accept "dumpresolutions". (onall_symbols_read): Get syms and dump resolutions if it was given. ld/testsuite/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 3/6). * ld-plugin/plugin-8.d: New testcase. * ld-plugin/plugin.exp: Invoke it. --- [PATCH] Implement add input file, add input lib and set extra lib path callbacks. ld/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 4/6). * ldlang.c (lang_process)[ENABLE_PLUGINS]: Move invocation of plugin_call_all_symbols_read to before setting of gc_sym_list, and open any new input files that may have been added during it. * ldmain.c (multiple_definition)[ENABLE_PLUGINS]: Call out to plugin_multiple_definition and let it have first say over what to do with the clashing definitions. * plugin.c (no_more_claiming): New boolean variable. (plugin_cached_allow_multiple_defs): Likewise. (add_input_file): Implement. (add_input_library): Likewise. (set_extra_library_path): Likewise. (plugin_call_claim_file): Don't do anything when no_more_claiming set. (plugin_call_all_symbols_read): Set it. Disable link info "allow_multiple_definition" flag, but cache its value. (plugin_multiple_definition): New function. * plugin.h (plugin_multiple_definition): Add prototype. * testplug.c (addfile_enum_t): New enumerated typedef. (add_file_t): New struct typedef. (addfiles_list): New variable. (addfiles_tail_chain_ptr): Likewise. (record_add_file): New function. (parse_option): Parse "add:", "lib:" and "dir:" options and call it. (onall_symbols_read): Iterate the list of new files, libs and dirs, adding them. ld/testsuite/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 4/6). * ld-plugin/plugin-9.d: New testcase. * ld-plugin/plugin.exp: Invoke it. --- [PATCH] Add ELF symbol visibility support to plugin interface. ld/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 5/6). * plugin.c (asymbol_from_plugin_symbol): If the bfd is an ELF bfd, find the elf symbol data and set the visibility in the st_other field. ld/testsuite/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 5/6). * ld-plugin/plugin-ignore.d: New dump test control script. * ld-plugin/plugin-vis-1.d: Likewise. * ld-plugin/plugin.exp: Add list of ELF-only tests and run them if testing on an ELF target. --- [PATCH] Add archive support to plugin interface. bfd/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 6/6). * aoutx.h (aout_link_check_ar_symbols): Take new "subsbfd" reference parameter and pass it when invoking add_archive_element callback. (aout_link_check_archive_element): Handle substitute bfd if it was set during add_archive_element callback in the above. * cofflink.c (coff_link_check_ar_symbols): Take new "subsbfd" reference parameter and pass it when invoking add_archive_element callback. (coff_link_check_archive_element): Handle substitute bfd if it was set during add_archive_element callback in the above. * ecoff.c (read_ext_syms_and_strs): New function holds symbol-reading code factored-out from ecoff_link_check_archive_element. (reread_ext_syms_and_strs): Clear old symbols and call it. (ecoff_link_check_archive_element): Use the above. Handle substitute BFD if one is set by add_archive_element callback. (ecoff_link_add_archive_symbols): Likewise allow bfd substitution. * elflink.c (elf_link_add_archive_symbols): Likewise. * linker.c (generic_link_check_archive_element): Likewise. * pdp11.c (aout_link_check_ar_symbols): Take new "subsbfd" reference parameter and pass it when invoking add_archive_element callback. (aout_link_check_archive_element): Handle substitute bfd if it was set during add_archive_element callback in the above. * vms-alpha.c (alpha_vms_link_add_archive_symbols): Handle substitute BFD if one is set by add_archive_element callback. * xcofflink.c (xcoff_link_check_dynamic_ar_symbols): Take new "subsbfd" reference parameter and pass it when invoking add_archive_element callback. (xcoff_link_check_ar_symbols): Likewise. (xcoff_link_check_archive_element): Handle bfd substitution if it was set by callback in the above. include/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 6/6). * bfdlink.h (struct_bfd_link_callbacks): Document new argument to add_archive_element callback used to return a replacement bfd which is to be added to the hash table in place of the original element. ld/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 6/6). * ldlang.c (load_symbols): Handle bfd subsitution when calling the add_archive_element callback. * ldmain.c (add_archive_element)[ENABLE_PLUGINS]: Offer the archive member to the plugins and if claimed set "subsbfd" output parameter to point to the dummy IR-only BFD. ld/testsuite/ChangeLog: 2010-10-14 Dave Korn <dave.korn.cygwin@gmail.com> Apply LD plugin patch series (part 6/6). * ld-plugin/plugin-10.d: New dump test control script. * ld-plugin/plugin-11.d: Likewise. * ld-plugin/plugin.exp: Run them. ---
Diffstat (limited to 'ld/plugin.c')
-rw-r--r--ld/plugin.c795
1 files changed, 795 insertions, 0 deletions
diff --git a/ld/plugin.c b/ld/plugin.c
new file mode 100644
index 0000000000..b484bd045a
--- /dev/null
+++ b/ld/plugin.c
@@ -0,0 +1,795 @@
+/* Plugin control for the GNU linker.
+ Copyright 2010 Free Software Foundation, Inc.
+
+ This file is part of the GNU Binutils.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "sysdep.h"
+#include "libiberty.h"
+#include "bfd.h"
+#include "bfdlink.h"
+#include "bfdver.h"
+#include "ld.h"
+#include "ldmain.h"
+#include "ldmisc.h"
+#include "ldexp.h"
+#include "ldlang.h"
+#include "ldfile.h"
+#include "plugin.h"
+#include "plugin-api.h"
+#include "elf-bfd.h"
+
+/* The suffix to append to the name of the real (claimed) object file
+ when generating a dummy BFD to hold the IR symbols sent from the
+ plugin. */
+#define IRONLY_SUFFIX ".ironly\004"
+
+/* This is sizeof an array of chars, not sizeof a const char *. We
+ also have to avoid inadvertently counting the trailing NUL. */
+#define IRONLY_SUFFIX_LEN (sizeof (IRONLY_SUFFIX) - 1)
+
+/* Stores a single argument passed to a plugin. */
+typedef struct plugin_arg
+{
+ struct plugin_arg *next;
+ const char *arg;
+} plugin_arg_t;
+
+/* Holds all details of a single plugin. */
+typedef struct plugin
+{
+ /* Next on the list of plugins, or NULL at end of chain. */
+ struct plugin *next;
+ /* The argument string given to --plugin. */
+ const char *name;
+ /* The shared library handle returned by dlopen. */
+ void *dlhandle;
+ /* The list of argument string given to --plugin-opt. */
+ plugin_arg_t *args;
+ /* Number of args in the list, for convenience. */
+ size_t n_args;
+ /* The plugin's event handlers. */
+ ld_plugin_claim_file_handler claim_file_handler;
+ ld_plugin_all_symbols_read_handler all_symbols_read_handler;
+ ld_plugin_cleanup_handler cleanup_handler;
+ /* TRUE if the cleanup handlers have been called. */
+ bfd_boolean cleanup_done;
+} plugin_t;
+
+/* The master list of all plugins. */
+static plugin_t *plugins_list = NULL;
+
+/* We keep a tail pointer for easy linking on the end. */
+static plugin_t **plugins_tail_chain_ptr = &plugins_list;
+
+/* The last plugin added to the list, for receiving args. */
+static plugin_t *last_plugin = NULL;
+
+/* The tail of the arg chain of the last plugin added to the list. */
+static plugin_arg_t **last_plugin_args_tail_chain_ptr = NULL;
+
+/* The plugin which is currently having a callback executed. */
+static plugin_t *called_plugin = NULL;
+
+/* Last plugin to cause an error, if any. */
+static const char *error_plugin = NULL;
+
+/* A hash table that records symbols referenced by non-IR files. Used
+ at get_symbols time to determine whether any prevailing defs from
+ IR files are referenced only from other IR files, so tthat we can
+ we can distinguish the LDPR_PREVAILING_DEF and LDPR_PREVAILING_DEF_IRONLY
+ cases when establishing symbol resolutions. */
+static struct bfd_hash_table *non_ironly_hash = NULL;
+
+/* Set at all symbols read time, to avoid recursively offering the plugin
+ its own newly-added input files and libs to claim. */
+static bfd_boolean no_more_claiming = FALSE;
+
+/* If the --allow-multiple-definition command-line option is active, we
+ have to disable it so that BFD always calls our hook, and simulate the
+ effect (when not resolving IR vs. real symbols) ourselves by ensuring
+ TRUE is returned from the hook. */
+static bfd_boolean plugin_cached_allow_multiple_defs = FALSE;
+
+/* List of tags to set in the constant leading part of the tv array. */
+static const enum ld_plugin_tag tv_header_tags[] =
+{
+ LDPT_MESSAGE,
+ LDPT_API_VERSION,
+ LDPT_GNU_LD_VERSION,
+ LDPT_LINKER_OUTPUT,
+ LDPT_OUTPUT_NAME,
+ LDPT_REGISTER_CLAIM_FILE_HOOK,
+ LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
+ LDPT_REGISTER_CLEANUP_HOOK,
+ LDPT_ADD_SYMBOLS,
+ LDPT_GET_INPUT_FILE,
+ LDPT_RELEASE_INPUT_FILE,
+ LDPT_GET_SYMBOLS,
+ LDPT_ADD_INPUT_FILE,
+ LDPT_ADD_INPUT_LIBRARY,
+ LDPT_SET_EXTRA_LIBRARY_PATH
+};
+
+/* How many entries in the constant leading part of the tv array. */
+static const size_t tv_header_size = ARRAY_SIZE (tv_header_tags);
+
+/* Helper function for exiting with error status. */
+static int
+set_plugin_error (const char *plugin)
+{
+ error_plugin = plugin;
+ return -1;
+}
+
+/* Test if an error occurred. */
+static bfd_boolean
+plugin_error_p (void)
+{
+ return error_plugin != NULL;
+}
+
+/* Return name of plugin which caused an error if any. */
+const char *plugin_error_plugin (void)
+{
+ return error_plugin ? error_plugin : _("<no plugin>");
+}
+
+/* Handle -plugin arg: find and load plugin, or return error. */
+int plugin_opt_plugin (const char *plugin)
+{
+ plugin_t *newplug;
+
+ newplug = xmalloc (sizeof *newplug);
+ memset (newplug, 0, sizeof *newplug);
+ newplug->name = plugin;
+ newplug->dlhandle = dlopen (plugin, RTLD_NOW);
+ if (!newplug->dlhandle)
+ return set_plugin_error (plugin);
+
+ /* Chain on end, so when we run list it is in command-line order. */
+ *plugins_tail_chain_ptr = newplug;
+ plugins_tail_chain_ptr = &newplug->next;
+
+ /* Record it as current plugin for receiving args. */
+ last_plugin = newplug;
+ last_plugin_args_tail_chain_ptr = &newplug->args;
+ return 0;
+}
+
+/* Accumulate option arguments for last-loaded plugin, or return
+ error if none. */
+int plugin_opt_plugin_arg (const char *arg)
+{
+ plugin_arg_t *newarg;
+
+ if (!last_plugin)
+ return set_plugin_error (_("<no plugin>"));
+
+ newarg = xmalloc (sizeof *newarg);
+ newarg->arg = arg;
+ newarg->next = NULL;
+
+ /* Chain on end to preserve command-line order. */
+ *last_plugin_args_tail_chain_ptr = newarg;
+ last_plugin_args_tail_chain_ptr = &newarg->next;
+ last_plugin->n_args++;
+ return 0;
+}
+
+/* Create a dummy BFD. */
+bfd *
+plugin_get_ir_dummy_bfd (const char *name, bfd *srctemplate)
+{
+ asection *sec;
+ bfd *abfd = bfd_create (
+ concat (name, IRONLY_SUFFIX, (const char *)NULL),
+ srctemplate);
+ bfd_set_arch_info (abfd, bfd_get_arch_info (srctemplate));
+ bfd_make_writable (abfd);
+ /* Create a minimal set of sections to own the symbols. */
+ sec = bfd_make_section_old_way (abfd, ".text");
+ bfd_set_section_flags (abfd, sec,
+ SEC_CODE | SEC_HAS_CONTENTS | SEC_READONLY
+ | SEC_ALLOC | SEC_LOAD | SEC_KEEP);
+ sec->output_section = sec;
+ sec->output_offset = 0;
+ return abfd;
+}
+
+/* Check if the BFD is an IR dummy. */
+bfd_boolean
+is_ir_dummy_bfd (const bfd *abfd)
+{
+ size_t namlen = strlen (abfd->filename);
+ if (namlen < IRONLY_SUFFIX_LEN)
+ return FALSE;
+ return !strcmp (abfd->filename + namlen - IRONLY_SUFFIX_LEN, IRONLY_SUFFIX);
+}
+
+/* Helpers to convert between BFD and GOLD symbol formats. */
+static enum ld_plugin_status
+asymbol_from_plugin_symbol (bfd *abfd, asymbol *asym,
+ const struct ld_plugin_symbol *ldsym)
+{
+ flagword flags = BSF_NO_FLAGS;
+ struct bfd_section *section;
+
+ asym->the_bfd = abfd;
+ asym->name = ldsym->version
+ ? concat (ldsym->name, "@", ldsym->version, NULL)
+ : ldsym->name;
+ asym->value = 0;
+ switch (ldsym->def)
+ {
+ case LDPK_WEAKDEF:
+ flags = BSF_WEAK;
+ /* FALLTHRU */
+ case LDPK_DEF:
+ flags |= BSF_GLOBAL;
+ section = bfd_get_section_by_name (abfd, ".text");
+ break;
+
+ case LDPK_WEAKUNDEF:
+ flags = BSF_WEAK;
+ /* FALLTHRU */
+ case LDPK_UNDEF:
+ section = bfd_und_section_ptr;
+ break;
+
+ case LDPK_COMMON:
+ flags = BSF_GLOBAL;
+ section = bfd_com_section_ptr;
+ asym->value = ldsym->size;
+ break;
+
+ default:
+ return LDPS_ERR;
+ }
+ asym->flags = flags;
+ asym->section = section;
+
+ /* Visibility only applies on ELF targets. */
+ if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
+ {
+ elf_symbol_type *elfsym = elf_symbol_from (abfd, asym);
+ if (!elfsym)
+ einfo (_("%P%F: %s: non-ELF symbol in ELF BFD!"), asym->name);
+ elfsym->internal_elf_sym.st_other &= ~3;
+ elfsym->internal_elf_sym.st_other |= ldsym->visibility;
+ }
+
+ return LDPS_OK;
+}
+
+/* Register a claim-file handler. */
+static enum ld_plugin_status
+register_claim_file (ld_plugin_claim_file_handler handler)
+{
+ ASSERT (called_plugin);
+ called_plugin->claim_file_handler = handler;
+ return LDPS_OK;
+}
+
+/* Register an all-symbols-read handler. */
+static enum ld_plugin_status
+register_all_symbols_read (ld_plugin_all_symbols_read_handler handler)
+{
+ ASSERT (called_plugin);
+ called_plugin->all_symbols_read_handler = handler;
+ return LDPS_OK;
+}
+
+/* Register a cleanup handler. */
+static enum ld_plugin_status
+register_cleanup (ld_plugin_cleanup_handler handler)
+{
+ ASSERT (called_plugin);
+ called_plugin->cleanup_handler = handler;
+ return LDPS_OK;
+}
+
+/* Add symbols from a plugin-claimed input file. */
+static enum ld_plugin_status
+add_symbols (void *handle, int nsyms, const struct ld_plugin_symbol *syms)
+{
+ asymbol **symptrs;
+ bfd *abfd = handle;
+ int n;
+ ASSERT (called_plugin);
+ symptrs = xmalloc (nsyms * sizeof *symptrs);
+ for (n = 0; n < nsyms; n++)
+ {
+ enum ld_plugin_status rv;
+ asymbol *bfdsym = bfd_make_empty_symbol (abfd);
+ symptrs[n] = bfdsym;
+ rv = asymbol_from_plugin_symbol (abfd, bfdsym, syms + n);
+ if (rv != LDPS_OK)
+ return rv;
+ }
+ bfd_set_symtab (abfd, symptrs, nsyms);
+ return LDPS_OK;
+}
+
+/* Get the input file information with an open (possibly re-opened)
+ file descriptor. */
+static enum ld_plugin_status
+get_input_file (const void *handle, struct ld_plugin_input_file *file)
+{
+ ASSERT (called_plugin);
+ handle = handle;
+ file = file;
+ return LDPS_ERR;
+}
+
+/* Release the input file. */
+static enum ld_plugin_status
+release_input_file (const void *handle)
+{
+ ASSERT (called_plugin);
+ handle = handle;
+ return LDPS_ERR;
+}
+
+/* Get the symbol resolution info for a plugin-claimed input file. */
+static enum ld_plugin_status
+get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
+{
+ const bfd *abfd = handle;
+ int n;
+ ASSERT (called_plugin);
+ for (n = 0; n < nsyms; n++)
+ {
+ struct bfd_link_hash_entry *blhe;
+ bfd_boolean ironly;
+
+ blhe = bfd_link_hash_lookup (link_info.hash, syms[n].name,
+ FALSE, FALSE, TRUE);
+ if (!blhe)
+ {
+ syms[n].resolution = LDPR_UNKNOWN;
+ continue;
+ }
+
+ /* Determine resolution from blhe type and symbol's original type. */
+ if (blhe->type == bfd_link_hash_undefined
+ || blhe->type == bfd_link_hash_undefweak)
+ {
+ syms[n].resolution = LDPR_UNDEF;
+ continue;
+ }
+ if (blhe->type != bfd_link_hash_defined
+ && blhe->type != bfd_link_hash_defweak
+ && blhe->type != bfd_link_hash_common)
+ {
+ /* We should not have a new, indirect or warning symbol here. */
+ einfo ("%P%F: %s: plugin symbol table corrupt (sym type %d)",
+ called_plugin->name, blhe->type);
+ }
+
+ /* We need to know if the sym is referenced from non-IR files. */
+ ironly = !bfd_hash_lookup (non_ironly_hash, syms[n].name, FALSE, FALSE);
+
+ /* If it was originally undefined or common, then it has been
+ resolved; determine how. */
+ if (syms[n].def == LDPK_UNDEF || syms[n].def == LDPK_WEAKUNDEF
+ || syms[n].def == LDPK_COMMON)
+ {
+ asection *owner_sec = (syms[n].def == LDPK_COMMON)
+ ? blhe->u.c.p->section
+ : blhe->u.def.section;
+ if (owner_sec->owner == link_info.output_bfd)
+ syms[n].resolution = LDPR_RESOLVED_EXEC;
+ else if (owner_sec->owner == abfd)
+ syms[n].resolution = (ironly)
+ ? LDPR_PREVAILING_DEF_IRONLY
+ : LDPR_PREVAILING_DEF;
+ else if (is_ir_dummy_bfd (owner_sec->owner))
+ syms[n].resolution = LDPR_RESOLVED_IR;
+ else if (owner_sec->owner->flags & DYNAMIC)
+ syms[n].resolution = LDPR_RESOLVED_DYN;
+ else
+ syms[n].resolution = LDPR_RESOLVED_EXEC;
+ continue;
+ }
+
+ /* Was originally def, or weakdef. Does it prevail? If the
+ owner is the original dummy bfd that supplied it, then this
+ is the definition that has prevailed. */
+ if (blhe->u.def.section->owner == link_info.output_bfd)
+ syms[n].resolution = LDPR_PREEMPTED_REG;
+ else if (blhe->u.def.section->owner == abfd)
+ {
+ syms[n].resolution = (ironly)
+ ? LDPR_PREVAILING_DEF_IRONLY
+ : LDPR_PREVAILING_DEF;
+ continue;
+ }
+
+ /* Was originally def, weakdef, or common, but has been pre-empted. */
+ syms[n].resolution = is_ir_dummy_bfd (blhe->u.def.section->owner)
+ ? LDPR_PREEMPTED_IR
+ : LDPR_PREEMPTED_REG;
+ }
+ return LDPS_OK;
+}
+
+/* Add a new (real) input file generated by a plugin. */
+static enum ld_plugin_status
+add_input_file (const char *pathname)
+{
+ ASSERT (called_plugin);
+ if (!lang_add_input_file (pathname, lang_input_file_is_file_enum, NULL))
+ return LDPS_ERR;
+ return LDPS_OK;
+}
+
+/* Add a new (real) library required by a plugin. */
+static enum ld_plugin_status
+add_input_library (const char *pathname)
+{
+ ASSERT (called_plugin);
+ if (!lang_add_input_file (pathname, lang_input_file_is_l_enum, NULL))
+ return LDPS_ERR;
+ return LDPS_OK;
+}
+
+/* Set the extra library path to be used by libraries added via
+ add_input_library. */
+static enum ld_plugin_status
+set_extra_library_path (const char *path)
+{
+ ASSERT (called_plugin);
+ ldfile_add_library_path (path, FALSE);
+ return LDPS_OK;
+}
+
+/* Issue a diagnostic message from a plugin. */
+static enum ld_plugin_status
+message (int level, const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+
+ switch (level)
+ {
+ case LDPL_INFO:
+ vfinfo (stdout, format, args, FALSE);
+ break;
+ case LDPL_WARNING:
+ vfinfo (stdout, format, args, TRUE);
+ break;
+ case LDPL_FATAL:
+ case LDPL_ERROR:
+ default:
+ {
+ char *newfmt = ACONCAT ((level == LDPL_FATAL ? "%F" : "%X",
+ format, NULL));
+ vfinfo (stderr, newfmt, args, TRUE);
+ }
+ break;
+ }
+
+ va_end (args);
+ return LDPS_OK;
+}
+
+/* Helper to size leading part of tv array and set it up. */
+static size_t
+set_tv_header (struct ld_plugin_tv *tv)
+{
+ size_t i;
+
+ /* Version info. */
+ static const unsigned int major = (unsigned)(BFD_VERSION / 100000000UL);
+ static const unsigned int minor = (unsigned)(BFD_VERSION / 1000000UL) % 100;
+
+ if (!tv)
+ return tv_header_size;
+
+ for (i = 0; i < tv_header_size; i++)
+ {
+ tv[i].tv_tag = tv_header_tags[i];
+#define TVU(x) tv[i].tv_u.tv_ ## x
+ switch (tv[i].tv_tag)
+ {
+ case LDPT_MESSAGE:
+ TVU(message) = message;
+ break;
+ case LDPT_API_VERSION:
+ TVU(val) = LD_PLUGIN_API_VERSION;
+ break;
+ case LDPT_GNU_LD_VERSION:
+ TVU(val) = major * 100 + minor;
+ break;
+ case LDPT_LINKER_OUTPUT:
+ TVU(val) = link_info.relocatable ? LDPO_REL
+ : (link_info.shared ? LDPO_DYN : LDPO_EXEC);
+ break;
+ case LDPT_OUTPUT_NAME:
+ TVU(string) = output_filename;
+ break;
+ case LDPT_REGISTER_CLAIM_FILE_HOOK:
+ TVU(register_claim_file) = register_claim_file;
+ break;
+ case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
+ TVU(register_all_symbols_read) = register_all_symbols_read;
+ break;
+ case LDPT_REGISTER_CLEANUP_HOOK:
+ TVU(register_cleanup) = register_cleanup;
+ break;
+ case LDPT_ADD_SYMBOLS:
+ TVU(add_symbols) = add_symbols;
+ break;
+ case LDPT_GET_INPUT_FILE:
+ TVU(get_input_file) = get_input_file;
+ break;
+ case LDPT_RELEASE_INPUT_FILE:
+ TVU(release_input_file) = release_input_file;
+ break;
+ case LDPT_GET_SYMBOLS:
+ TVU(get_symbols) = get_symbols;
+ break;
+ case LDPT_ADD_INPUT_FILE:
+ TVU(add_input_file) = add_input_file;
+ break;
+ case LDPT_ADD_INPUT_LIBRARY:
+ TVU(add_input_library) = add_input_library;
+ break;
+ case LDPT_SET_EXTRA_LIBRARY_PATH:
+ TVU(set_extra_library_path) = set_extra_library_path;
+ break;
+ default:
+ /* Added a new entry to the array without adding
+ a new case to set up its value is a bug. */
+ FAIL ();
+ }
+#undef TVU
+ }
+ return tv_header_size;
+}
+
+/* Append the per-plugin args list and trailing LDPT_NULL to tv. */
+static void
+set_tv_plugin_args (plugin_t *plugin, struct ld_plugin_tv *tv)
+{
+ plugin_arg_t *arg = plugin->args;
+ while (arg)
+ {
+ tv->tv_tag = LDPT_OPTION;
+ tv->tv_u.tv_string = arg->arg;
+ arg = arg->next;
+ tv++;
+ }
+ tv->tv_tag = LDPT_NULL;
+ tv->tv_u.tv_val = 0;
+}
+
+/* Load up and initialise all plugins after argument parsing. */
+int plugin_load_plugins (void)
+{
+ struct ld_plugin_tv *my_tv;
+ unsigned int max_args = 0;
+ plugin_t *curplug = plugins_list;
+
+ /* If there are no plugins, we need do nothing this run. */
+ if (!curplug)
+ return 0;
+
+ /* First pass over plugins to find max # args needed so that we
+ can size and allocate the tv array. */
+ while (curplug)
+ {
+ if (curplug->n_args > max_args)
+ max_args = curplug->n_args;
+ curplug = curplug->next;
+ }
+
+ /* Allocate tv array and initialise constant part. */
+ my_tv = xmalloc ((max_args + 1 + tv_header_size) * sizeof *my_tv);
+ set_tv_header (my_tv);
+
+ /* Pass over plugins again, activating them. */
+ curplug = plugins_list;
+ while (curplug)
+ {
+ enum ld_plugin_status rv;
+ ld_plugin_onload onloadfn = dlsym (curplug->dlhandle, "onload");
+ if (!onloadfn)
+ onloadfn = dlsym (curplug->dlhandle, "_onload");
+ if (!onloadfn)
+ return set_plugin_error (curplug->name);
+ set_tv_plugin_args (curplug, &my_tv[tv_header_size]);
+ called_plugin = curplug;
+ rv = (*onloadfn) (my_tv);
+ called_plugin = NULL;
+ if (rv != LDPS_OK)
+ return set_plugin_error (curplug->name);
+ curplug = curplug->next;
+ }
+
+ /* Since plugin(s) inited ok, assume they're going to want symbol
+ resolutions, which needs us to track which symbols are referenced
+ by non-IR files using the linker's notice callback. */
+ link_info.notice_all = TRUE;
+
+ return 0;
+}
+
+/* Call 'claim file' hook for all plugins. */
+int
+plugin_call_claim_file (const struct ld_plugin_input_file *file, int *claimed)
+{
+ plugin_t *curplug = plugins_list;
+ *claimed = FALSE;
+ if (no_more_claiming)
+ return 0;
+ while (curplug && !*claimed)
+ {
+ if (curplug->claim_file_handler)
+ {
+ enum ld_plugin_status rv;
+ called_plugin = curplug;
+ rv = (*curplug->claim_file_handler) (file, claimed);
+ called_plugin = NULL;
+ if (rv != LDPS_OK)
+ set_plugin_error (curplug->name);
+ }
+ curplug = curplug->next;
+ }
+ return plugin_error_p () ? -1 : 0;
+}
+
+/* Call 'all symbols read' hook for all plugins. */
+int
+plugin_call_all_symbols_read (void)
+{
+ plugin_t *curplug = plugins_list;
+
+ /* Disable any further file-claiming. */
+ no_more_claiming = TRUE;
+
+ /* If --allow-multiple-definition is in effect, we need to disable it,
+ as the plugin infrastructure relies on the multiple_definition
+ callback to swap out the dummy IR-only BFDs for new real ones
+ when it starts opening the files added during this callback. */
+ plugin_cached_allow_multiple_defs = link_info.allow_multiple_definition;
+ link_info.allow_multiple_definition = FALSE;
+
+ while (curplug)
+ {
+ if (curplug->all_symbols_read_handler)
+ {
+ enum ld_plugin_status rv;
+ called_plugin = curplug;
+ rv = (*curplug->all_symbols_read_handler) ();
+ called_plugin = NULL;
+ if (rv != LDPS_OK)
+ set_plugin_error (curplug->name);
+ }
+ curplug = curplug->next;
+ }
+ return plugin_error_p () ? -1 : 0;
+}
+
+/* Call 'cleanup' hook for all plugins. */
+int
+plugin_call_cleanup (void)
+{
+ plugin_t *curplug = plugins_list;
+ while (curplug)
+ {
+ if (curplug->cleanup_handler && !curplug->cleanup_done)
+ {
+ enum ld_plugin_status rv;
+ curplug->cleanup_done = TRUE;
+ called_plugin = curplug;
+ rv = (*curplug->cleanup_handler) ();
+ called_plugin = NULL;
+ if (rv != LDPS_OK)
+ set_plugin_error (curplug->name);
+ dlclose (curplug->dlhandle);
+ }
+ curplug = curplug->next;
+ }
+ return plugin_error_p () ? -1 : 0;
+}
+
+/* Lazily init the non_ironly hash table. */
+static void
+init_non_ironly_hash (void)
+{
+ if (non_ironly_hash == NULL)
+ {
+ non_ironly_hash =
+ (struct bfd_hash_table *) xmalloc (sizeof (struct bfd_hash_table));
+ if (!bfd_hash_table_init_n (non_ironly_hash,
+ bfd_hash_newfunc,
+ sizeof (struct bfd_hash_entry),
+ 61))
+ einfo (_("%P%F: bfd_hash_table_init failed: %E\n"));
+ }
+}
+
+/* To determine which symbols should be resolved LDPR_PREVAILING_DEF
+ and which LDPR_PREVAILING_DEF_IRONLY, we notice all the symbols as
+ the linker adds them to the linker hash table. If we see a symbol
+ being referenced from a non-IR file, we add it to the non_ironly hash
+ table. If we can't find it there at get_symbols time, we know that
+ it was referenced only by IR files. We have to notice_all symbols,
+ because we won't necessarily know until later which ones will be
+ contributed by IR files. */
+bfd_boolean
+plugin_notice (struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ const char *name, bfd *abfd,
+ asection *section, bfd_vma value ATTRIBUTE_UNUSED)
+{
+ bfd_boolean is_ref = bfd_is_und_section (section);
+ bfd_boolean is_dummy = is_ir_dummy_bfd (abfd);
+ init_non_ironly_hash ();
+ /* We only care about refs, not defs, indicated by section pointing
+ to the undefined section (according to the bfd linker notice callback
+ interface definition). */
+ if (is_ref && !is_dummy)
+ {
+ /* This is a ref from a non-IR file, so note the ref'd symbol
+ in the non-IR-only hash. */
+ if (!bfd_hash_lookup (non_ironly_hash, name, TRUE, TRUE))
+ einfo (_("%P%X: %s: hash table failure adding symbol %s"),
+ abfd->filename, name);
+ }
+ else if (!is_ref && is_dummy)
+ {
+ /* No further processing since this is a def from an IR dummy BFD. */
+ return FALSE;
+ }
+
+ /* Continue with cref/nocrossref/trace-sym processing. */
+ return TRUE;
+}
+
+/* When we add new object files to the link at all symbols read time,
+ these contain the real code and symbols generated from the IR files,
+ and so duplicate all the definitions already supplied by the dummy
+ IR-only BFDs that we created at claim files time. We use the linker's
+ multiple-definitions callback hook to fix up the clash, discarding
+ the symbol from the IR-only BFD in favour of the symbol from the
+ real BFD. We return true if this was not-really-a-clash because
+ we've fixed it up, or anyway if --allow-multiple-definition was in
+ effect (before we disabled it to ensure we got called back). */
+bfd_boolean
+plugin_multiple_definition (struct bfd_link_info *info, const char *name,
+ bfd *obfd, asection *osec ATTRIBUTE_UNUSED,
+ bfd_vma oval ATTRIBUTE_UNUSED,
+ bfd *nbfd, asection *nsec, bfd_vma nval)
+{
+ if (is_ir_dummy_bfd (obfd))
+ {
+ struct bfd_link_hash_entry *blhe = bfd_link_hash_lookup (info->hash,
+ name, FALSE, FALSE, FALSE);
+ if (!blhe)
+ einfo (_("%P%X: %s: can't find IR symbol '%s'"), nbfd->filename,
+ name);
+ else if (blhe->type != bfd_link_hash_defined)
+ einfo (_("%P%x: %s: bad IR symbol type %d"), name, blhe->type);
+ /* Replace it with new details. */
+ blhe->u.def.section = nsec;
+ blhe->u.def.value = nval;
+ return TRUE;
+ }
+ return plugin_cached_allow_multiple_defs;
+}