binutils-gdb/ld/plugin.c

796 lines
23 KiB
C
Raw Normal View History

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. ---
2010-10-14 03:31:33 +02:00
/* 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;
}