/* Plugin control for the GNU linker.
- Copyright 2010 Free Software Foundation, Inc.
+ Copyright (C) 2010-2018 Free Software Foundation, Inc.
This file is part of the GNU Binutils.
#include "ldexp.h"
#include "ldlang.h"
#include "ldfile.h"
-#include "plugin.h"
#include "plugin-api.h"
+#include "../bfd/plugin.h"
+#include "plugin.h"
#include "elf-bfd.h"
+#if HAVE_MMAP
+# include <sys/mman.h>
+# ifndef MAP_FAILED
+# define MAP_FAILED ((void *) -1)
+# endif
+# ifndef PROT_READ
+# define PROT_READ 0
+# endif
+# ifndef MAP_PRIVATE
+# define MAP_PRIVATE 0
+# endif
+#endif
+#include <errno.h>
+#if !(defined(errno) || defined(_MSC_VER) && defined(_INC_ERRNO))
+extern int errno;
+#endif
#if !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H)
-#include <Windows.h>
+#include <windows.h>
#endif
+/* Report plugin symbols. */
+bfd_boolean report_plugin_symbols;
+
/* 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)
+ plugin. For cosmetic use only; appears in maps, crefs etc. */
+#define IRONLY_SUFFIX " (symbol from plugin)"
/* Stores a single argument passed to a plugin. */
typedef struct plugin_arg
bfd_boolean cleanup_done;
} plugin_t;
+typedef struct view_buffer
+{
+ char *addr;
+ size_t filesize;
+ off_t offset;
+} view_buffer_t;
+
+/* The internal version of struct ld_plugin_input_file with a BFD
+ pointer. */
+typedef struct plugin_input_file
+{
+ bfd *abfd;
+ view_buffer_t view_buffer;
+ char *name;
+ int fd;
+ bfd_boolean use_mmap;
+ off_t offset;
+ off_t filesize;
+} plugin_input_file_t;
+
/* The master list of all plugins. */
static plugin_t *plugins_list = 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;
+/* State of linker "notice" interface before we poked at it. */
+static bfd_boolean orig_notice_all;
+
+/* Original linker callbacks, and the plugin version. */
+static const struct bfd_link_callbacks *orig_callbacks;
+static struct bfd_link_callbacks plugin_callbacks;
/* 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;
+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;
+#if HAVE_MMAP && HAVE_GETPAGESIZE
+/* Page size used by mmap. */
+static off_t plugin_pagesize;
+#endif
/* List of tags to set in the constant leading part of the tv array. */
static const enum ld_plugin_tag tv_header_tags[] =
LDPT_REGISTER_CLEANUP_HOOK,
LDPT_ADD_SYMBOLS,
LDPT_GET_INPUT_FILE,
+ LDPT_GET_VIEW,
LDPT_RELEASE_INPUT_FILE,
LDPT_GET_SYMBOLS,
+ LDPT_GET_SYMBOLS_V2,
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);
+/* Forward references. */
+static bfd_boolean plugin_notice (struct bfd_link_info *,
+ struct bfd_link_hash_entry *,
+ struct bfd_link_hash_entry *,
+ bfd *, asection *, bfd_vma, flagword);
+
+static const bfd_target * plugin_object_p (bfd *);
+
#if !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H)
#define RTLD_NOW 0 /* Dummy value. */
#endif /* !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H) */
+#ifndef HAVE_DLFCN_H
+static const char *
+dlerror (void)
+{
+ return "";
+}
+#endif
+
/* Helper function for exiting with error status. */
static int
set_plugin_error (const char *plugin)
}
/* Handle -plugin arg: find and load plugin, or return error. */
-int
+void
plugin_opt_plugin (const char *plugin)
{
plugin_t *newplug;
+ plugin_t *curplug = plugins_list;
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);
+ einfo (_("%P%F: %s: error loading plugin: %s\n"), plugin, dlerror ());
+
+ /* Check if plugin has been loaded already. */
+ while (curplug)
+ {
+ if (newplug->dlhandle == curplug->dlhandle)
+ {
+ einfo (_("%P: %s: duplicated plugin\n"), plugin);
+ free (newplug);
+ return;
+ }
+ curplug = curplug->next;
+ }
/* Chain on end, so when we run list it is in command-line order. */
*plugins_tail_chain_ptr = newplug;
/* 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
if (!last_plugin)
return set_plugin_error (_("<no plugin>"));
+ /* Ignore -pass-through= from GCC driver. */
+ if (*arg == '-')
+ {
+ const char *p = arg + 1;
+
+ if (*p == '-')
+ ++p;
+ if (strncmp (p, "pass-through=", 13) == 0)
+ return 0;
+ }
+
newarg = xmalloc (sizeof *newarg);
newarg->arg = arg;
newarg->next = NULL;
return 0;
}
-/* Create a dummy BFD. */
-bfd *
+/* Generate a dummy BFD to represent an IR file, for any callers of
+ plugin_call_claim_file to use as the handle in the ld_plugin_input_file
+ struct that they build to pass in. The BFD is initially writable, so
+ that symbols can be added to it; it must be made readable after the
+ add_symbols hook has been called so that it can be read when linking. */
+static bfd *
plugin_get_ir_dummy_bfd (const char *name, bfd *srctemplate)
{
- asection *sec;
bfd *abfd;
+ bfd_boolean bfd_plugin_target;
bfd_use_reserved_id = 1;
- 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;
+ bfd_plugin_target = bfd_plugin_target_p (srctemplate->xvec);
+ abfd = bfd_create (concat (name, IRONLY_SUFFIX, (const char *) NULL),
+ bfd_plugin_target ? link_info.output_bfd : srctemplate);
+ if (abfd != NULL)
+ {
+ abfd->flags |= BFD_LINKER_CREATED | BFD_PLUGIN;
+ if (!bfd_make_writable (abfd))
+ goto report_error;
+ if (!bfd_plugin_target)
+ {
+ bfd_set_arch_info (abfd, bfd_get_arch_info (srctemplate));
+ bfd_set_gp_size (abfd, bfd_get_gp_size (srctemplate));
+ if (!bfd_copy_private_bfd_data (srctemplate, abfd))
+ goto report_error;
+ }
+ {
+ flagword flags;
+
+ /* Create section to own the symbols. */
+ flags = (SEC_CODE | SEC_HAS_CONTENTS | SEC_READONLY
+ | SEC_ALLOC | SEC_LOAD | SEC_KEEP | SEC_EXCLUDE);
+ if (bfd_make_section_anyway_with_flags (abfd, ".text", flags))
+ return abfd;
+ }
+ }
+report_error:
+ einfo (_("could not create dummy IR bfd: %F%E\n"));
+ return NULL;
}
/* Check if the BFD passed in is an IR dummy object file. */
-static bfd_boolean
+static inline bfd_boolean
is_ir_dummy_bfd (const bfd *abfd)
{
- size_t namlen;
-
- if (abfd == NULL)
- return FALSE;
- namlen = strlen (abfd->filename);
- if (namlen < IRONLY_SUFFIX_LEN)
- return FALSE;
- return !strcmp (abfd->filename + namlen - IRONLY_SUFFIX_LEN, IRONLY_SUFFIX);
+ /* ABFD can sometimes legitimately be NULL, e.g. when called from one
+ of the linker callbacks for a symbol in the *ABS* or *UND* sections. */
+ return abfd != NULL && (abfd->flags & BFD_PLUGIN) != 0;
}
/* Helpers to convert between BFD and GOLD symbol formats. */
asym->the_bfd = abfd;
asym->name = (ldsym->version
- ? concat (ldsym->name, "@", ldsym->version, NULL)
+ ? concat (ldsym->name, "@", ldsym->version, (const char *) NULL)
: ldsym->name);
asym->value = 0;
switch (ldsym->def)
/* FALLTHRU */
case LDPK_DEF:
flags |= BSF_GLOBAL;
- section = bfd_get_section_by_name (abfd, ".text");
+ if (ldsym->comdat_key)
+ {
+ char *name = concat (".gnu.linkonce.t.", ldsym->comdat_key,
+ (const char *) NULL);
+ section = bfd_get_section_by_name (abfd, name);
+ if (section != NULL)
+ free (name);
+ else
+ {
+ flagword sflags;
+
+ sflags = (SEC_CODE | SEC_HAS_CONTENTS | SEC_READONLY
+ | SEC_ALLOC | SEC_LOAD | SEC_KEEP | SEC_EXCLUDE
+ | SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD);
+ section = bfd_make_section_anyway_with_flags (abfd, name, sflags);
+ if (section == NULL)
+ return LDPS_ERR;
+ }
+ }
+ else
+ section = bfd_get_section_by_name (abfd, ".text");
break;
case LDPK_WEAKUNDEF:
asym->value = ldsym->size;
/* For ELF targets, set alignment of common symbol to 1. */
if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
- ((elf_symbol_type *) asym)->internal_elf_sym.st_value = 1;
+ {
+ ((elf_symbol_type *) asym)->internal_elf_sym.st_shndx = SHN_COMMON;
+ ((elf_symbol_type *) asym)->internal_elf_sym.st_value = 1;
+ }
break;
default:
default:
einfo (_("%P%F: unknown ELF symbol visibility: %d!\n"),
ldsym->visibility);
+ return LDPS_ERR;
+
case LDPV_DEFAULT:
visibility = STV_DEFAULT;
break;
add_symbols (void *handle, int nsyms, const struct ld_plugin_symbol *syms)
{
asymbol **symptrs;
- bfd *abfd = handle;
+ plugin_input_file_t *input = handle;
+ bfd *abfd = input->abfd;
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);
+ asymbol *bfdsym;
+
+ bfdsym = bfd_make_empty_symbol (abfd);
symptrs[n] = bfdsym;
rv = asymbol_from_plugin_symbol (abfd, bfdsym, syms + n);
if (rv != LDPS_OK)
static enum ld_plugin_status
get_input_file (const void *handle, struct ld_plugin_input_file *file)
{
+ const plugin_input_file_t *input = handle;
+
ASSERT (called_plugin);
- handle = handle;
- file = file;
- return LDPS_ERR;
+
+ file->name = input->name;
+ file->offset = input->offset;
+ file->filesize = input->filesize;
+ file->handle = (void *) handle;
+
+ return LDPS_OK;
+}
+
+/* Get view of the input file. */
+static enum ld_plugin_status
+get_view (const void *handle, const void **viewp)
+{
+ plugin_input_file_t *input = (plugin_input_file_t *) handle;
+ char *buffer;
+ size_t size = input->filesize;
+ off_t offset = input->offset;
+#if HAVE_MMAP && HAVE_GETPAGESIZE
+ off_t bias;
+#endif
+
+ ASSERT (called_plugin);
+
+ /* FIXME: einfo should support %lld. */
+ if ((off_t) size != input->filesize)
+ einfo (_("%P%F: unsupported input file size: %s (%ld bytes)\n"),
+ input->name, (long) input->filesize);
+
+ /* Check the cached view buffer. */
+ if (input->view_buffer.addr != NULL
+ && input->view_buffer.filesize == size
+ && input->view_buffer.offset == offset)
+ {
+ *viewp = input->view_buffer.addr;
+ return LDPS_OK;
+ }
+
+ input->view_buffer.filesize = size;
+ input->view_buffer.offset = offset;
+
+#if HAVE_MMAP
+# if HAVE_GETPAGESIZE
+ bias = offset % plugin_pagesize;
+ offset -= bias;
+ size += bias;
+# endif
+ buffer = mmap (NULL, size, PROT_READ, MAP_PRIVATE, input->fd, offset);
+ if (buffer != MAP_FAILED)
+ {
+ input->use_mmap = TRUE;
+# if HAVE_GETPAGESIZE
+ buffer += bias;
+# endif
+ }
+ else
+#endif
+ {
+ char *p;
+
+ input->use_mmap = FALSE;
+
+ if (lseek (input->fd, offset, SEEK_SET) < 0)
+ return LDPS_ERR;
+
+ buffer = bfd_alloc (input->abfd, size);
+ if (buffer == NULL)
+ return LDPS_ERR;
+
+ p = buffer;
+ do
+ {
+ ssize_t got = read (input->fd, p, size);
+ if (got == 0)
+ break;
+ else if (got > 0)
+ {
+ p += got;
+ size -= got;
+ }
+ else if (errno != EINTR)
+ return LDPS_ERR;
+ }
+ while (size > 0);
+ }
+
+ input->view_buffer.addr = buffer;
+ *viewp = buffer;
+
+ return LDPS_OK;
}
/* Release the input file. */
static enum ld_plugin_status
release_input_file (const void *handle)
{
+ plugin_input_file_t *input = (plugin_input_file_t *) handle;
ASSERT (called_plugin);
- handle = handle;
- return LDPS_ERR;
+ if (input->fd != -1)
+ {
+ close (input->fd);
+ input->fd = -1;
+ }
+ return LDPS_OK;
}
/* Return TRUE if a defined symbol might be reachable from outside the
universe of claimed objects. */
static inline bfd_boolean
-is_visible_from_outside (struct ld_plugin_symbol *lsym, asection *section,
+is_visible_from_outside (struct ld_plugin_symbol *lsym,
struct bfd_link_hash_entry *blhe)
{
- /* Section's owner may be NULL if it is the absolute
- section, fortunately is_ir_dummy_bfd handles that. */
- if (!is_ir_dummy_bfd (section->owner))
- return TRUE;
- if (link_info.relocatable)
+ struct bfd_sym_chain *sym;
+
+ if (bfd_link_relocatable (&link_info))
return TRUE;
- if (link_info.export_dynamic || link_info.shared)
+ if (blhe->non_ir_ref_dynamic
+ || link_info.export_dynamic
+ || bfd_link_dll (&link_info))
{
+ /* Check if symbol is hidden by version script. */
+ if (bfd_hide_sym_by_version (link_info.version_info,
+ blhe->root.string))
+ return FALSE;
/* Only ELF symbols really have visibility. */
if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour)
{
return (lsym->visibility == LDPV_DEFAULT
|| lsym->visibility == LDPV_PROTECTED);
}
+
+ for (sym = &entry_symbol; sym != NULL; sym = sym->next)
+ if (sym->name
+ && strcmp (sym->name, blhe->root.string) == 0)
+ return TRUE;
+
return FALSE;
}
/* 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)
+get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms,
+ int def_ironly_exp)
{
- const bfd *abfd = handle;
+ const plugin_input_file_t *input = handle;
+ const bfd *abfd = (const bfd *) input->abfd;
int n;
+
ASSERT (called_plugin);
for (n = 0; n < nsyms; n++)
{
struct bfd_link_hash_entry *blhe;
- bfd_boolean ironly;
asection *owner_sec;
-
- blhe = bfd_link_hash_lookup (link_info.hash, syms[n].name,
- FALSE, FALSE, TRUE);
+ int res;
+
+ if (syms[n].def != LDPK_UNDEF)
+ blhe = bfd_link_hash_lookup (link_info.hash, syms[n].name,
+ FALSE, FALSE, TRUE);
+ else
+ blhe = bfd_wrapped_link_hash_lookup (link_info.output_bfd, &link_info,
+ syms[n].name, FALSE, FALSE, TRUE);
if (!blhe)
{
- syms[n].resolution = LDPR_UNKNOWN;
- continue;
+ /* The plugin is called to claim symbols in an archive element
+ from plugin_object_p. But those symbols aren't needed to
+ create output. They are defined and referenced only within
+ IR. */
+ switch (syms[n].def)
+ {
+ default:
+ abort ();
+ case LDPK_UNDEF:
+ case LDPK_WEAKUNDEF:
+ res = LDPR_UNDEF;
+ break;
+ case LDPK_DEF:
+ case LDPK_WEAKDEF:
+ case LDPK_COMMON:
+ res = LDPR_PREVAILING_DEF_IRONLY;
+ break;
+ }
+ goto report_symbol;
}
/* 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;
+ res = LDPR_UNDEF;
+ goto report_symbol;
}
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)\n",
+ einfo (_("%P%F: %s: plugin symbol table corrupt (sym type %d)\n"),
called_plugin->name, blhe->type);
}
/* Find out which section owns the symbol. Since it's not undef,
it must have an owner; if it's not a common symbol, both defs
and weakdefs keep it in the same place. */
- owner_sec = (blhe->type == bfd_link_hash_common)
- ? blhe->u.c.p->section
- : blhe->u.def.section;
+ owner_sec = (blhe->type == bfd_link_hash_common
+ ? blhe->u.c.p->section
+ : blhe->u.def.section);
- /* We need to know if the sym is referenced from non-IR files. Or
- even potentially-referenced, perhaps in a future final link if
- this is a partial one, perhaps dynamically at load-time if the
- symbol is externally visible. */
- ironly = !is_visible_from_outside (&syms[n], owner_sec, blhe)
- && !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. */
|| syms[n].def == LDPK_COMMON)
{
if (owner_sec->owner == link_info.output_bfd)
- syms[n].resolution = LDPR_RESOLVED_EXEC;
+ res = LDPR_RESOLVED_EXEC;
else if (owner_sec->owner == abfd)
- syms[n].resolution = (ironly
- ? LDPR_PREVAILING_DEF_IRONLY
- : LDPR_PREVAILING_DEF);
+ res = LDPR_PREVAILING_DEF_IRONLY;
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;
+ res = LDPR_RESOLVED_IR;
+ else if (owner_sec->owner != NULL
+ && (owner_sec->owner->flags & DYNAMIC) != 0)
+ res = LDPR_RESOLVED_DYN;
else
- syms[n].resolution = LDPR_RESOLVED_EXEC;
- continue;
+ res = LDPR_RESOLVED_EXEC;
}
/* 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 (owner_sec->owner == link_info.output_bfd)
- syms[n].resolution = LDPR_PREEMPTED_REG;
+ else if (owner_sec->owner == link_info.output_bfd)
+ res = LDPR_PREEMPTED_REG;
else if (owner_sec->owner == abfd)
+ res = LDPR_PREVAILING_DEF_IRONLY;
+
+ /* Was originally def, weakdef, or common, but has been pre-empted. */
+ else if (is_ir_dummy_bfd (owner_sec->owner))
+ res = LDPR_PREEMPTED_IR;
+ else
+ res = LDPR_PREEMPTED_REG;
+
+ if (res == LDPR_PREVAILING_DEF_IRONLY)
{
- syms[n].resolution = (ironly
- ? LDPR_PREVAILING_DEF_IRONLY
- : LDPR_PREVAILING_DEF);
- continue;
+ /* We need to know if the sym is referenced from non-IR files. Or
+ even potentially-referenced, perhaps in a future final link if
+ this is a partial one, perhaps dynamically at load-time if the
+ symbol is externally visible. */
+ if (blhe->non_ir_ref_regular)
+ res = LDPR_PREVAILING_DEF;
+ else if (is_visible_from_outside (&syms[n], blhe))
+ res = def_ironly_exp;
}
- /* Was originally def, weakdef, or common, but has been pre-empted. */
- syms[n].resolution = is_ir_dummy_bfd (owner_sec->owner)
- ? LDPR_PREEMPTED_IR
- : LDPR_PREEMPTED_REG;
+ report_symbol:
+ syms[n].resolution = res;
+ if (report_plugin_symbols)
+ einfo (_("%P: %pB: symbol `%s' "
+ "definition: %d, visibility: %d, resolution: %d\n"),
+ abfd, syms[n].name,
+ syms[n].def, syms[n].visibility, res);
}
return LDPS_OK;
}
+static enum ld_plugin_status
+get_symbols_v1 (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
+{
+ return get_symbols (handle, nsyms, syms, LDPR_PREVAILING_DEF);
+}
+
+static enum ld_plugin_status
+get_symbols_v2 (const void *handle, int nsyms, struct ld_plugin_symbol *syms)
+{
+ return get_symbols (handle, nsyms, syms, LDPR_PREVAILING_DEF_IRONLY_EXP);
+}
+
/* Add a new (real) input file generated by a plugin. */
static enum ld_plugin_status
add_input_file (const char *pathname)
{
+ lang_input_statement_type *is;
+
ASSERT (called_plugin);
- if (!lang_add_input_file (xstrdup (pathname), lang_input_file_is_file_enum,
- NULL))
+ is = lang_add_input_file (xstrdup (pathname), lang_input_file_is_file_enum,
+ NULL);
+ if (!is)
return LDPS_ERR;
+ is->flags.lto_output = 1;
return LDPS_OK;
}
static enum ld_plugin_status
add_input_library (const char *pathname)
{
+ lang_input_statement_type *is;
+
ASSERT (called_plugin);
- if (!lang_add_input_file (xstrdup (pathname), lang_input_file_is_l_enum,
- NULL))
+ is = lang_add_input_file (xstrdup (pathname), lang_input_file_is_l_enum,
+ NULL);
+ if (!is)
return LDPS_ERR;
+ is->flags.lto_output = 1;
return LDPS_OK;
}
{
case LDPL_INFO:
vfinfo (stdout, format, args, FALSE);
+ putchar ('\n');
break;
case LDPL_WARNING:
- vfinfo (stdout, format, args, TRUE);
+ {
+ char *newfmt = concat ("%P: warning: ", format, "\n",
+ (const char *) NULL);
+ vfinfo (stdout, newfmt, args, TRUE);
+ free (newfmt);
+ }
break;
case LDPL_FATAL:
case LDPL_ERROR:
default:
- {
- char *newfmt = ACONCAT ((level == LDPL_FATAL
- ? "%P%F: " : "%P%X: ",
- format, "\n", NULL));
- vfinfo (stderr, newfmt, args, TRUE);
- }
+ {
+ char *newfmt = concat (level == LDPL_FATAL ? "%P%F" : "%P%X",
+ ": error: ", format, "\n",
+ (const char *) NULL);
+ fflush (stdout);
+ vfinfo (stderr, newfmt, args, TRUE);
+ fflush (stderr);
+ free (newfmt);
+ }
break;
}
- fputc('\n', stderr);
-
va_end (args);
return LDPS_OK;
}
/* Helper to size leading part of tv array and set it up. */
-static size_t
+static void
set_tv_header (struct ld_plugin_tv *tv)
{
size_t i;
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];
TVU(val) = major * 100 + minor;
break;
case LDPT_LINKER_OUTPUT:
- TVU(val) = (link_info.relocatable
- ? LDPO_REL
- : (link_info.shared ? LDPO_DYN : LDPO_EXEC));
+ TVU(val) = (bfd_link_relocatable (&link_info) ? LDPO_REL
+ : bfd_link_pde (&link_info) ? LDPO_EXEC
+ : bfd_link_pie (&link_info) ? LDPO_PIE
+ : LDPO_DYN);
break;
case LDPT_OUTPUT_NAME:
TVU(string) = output_filename;
case LDPT_GET_INPUT_FILE:
TVU(get_input_file) = get_input_file;
break;
+ case LDPT_GET_VIEW:
+ TVU(get_view) = get_view;
+ break;
case LDPT_RELEASE_INPUT_FILE:
TVU(release_input_file) = release_input_file;
break;
case LDPT_GET_SYMBOLS:
- TVU(get_symbols) = get_symbols;
+ TVU(get_symbols) = get_symbols_v1;
+ break;
+ case LDPT_GET_SYMBOLS_V2:
+ TVU(get_symbols) = get_symbols_v2;
break;
case LDPT_ADD_INPUT_FILE:
TVU(add_input_file) = add_input_file;
}
#undef TVU
}
- return tv_header_size;
}
/* Append the per-plugin args list and trailing LDPT_NULL to tv. */
tv->tv_u.tv_val = 0;
}
-/* Return true if any plugins are active this run. Only valid
- after options have been processed. */
-bfd_boolean
-plugin_active_plugins_p (void)
-{
- return plugins_list != NULL;
-}
-
/* Load up and initialise all plugins after argument parsing. */
-int
+void
plugin_load_plugins (void)
{
struct ld_plugin_tv *my_tv;
/* If there are no plugins, we need do nothing this run. */
if (!curplug)
- return 0;
+ return;
/* First pass over plugins to find max # args needed so that we
can size and allocate the tv array. */
while (curplug)
{
enum ld_plugin_status rv;
- ld_plugin_onload onloadfn = dlsym (curplug->dlhandle, "onload");
+ ld_plugin_onload onloadfn;
+
+ onloadfn = (ld_plugin_onload) dlsym (curplug->dlhandle, "onload");
if (!onloadfn)
- onloadfn = dlsym (curplug->dlhandle, "_onload");
+ onloadfn = (ld_plugin_onload) dlsym (curplug->dlhandle, "_onload");
if (!onloadfn)
- return set_plugin_error (curplug->name);
+ einfo (_("%P%F: %s: error loading plugin: %s\n"),
+ curplug->name, dlerror ());
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);
+ einfo (_("%P%F: %s: plugin error: %d\n"), curplug->name, rv);
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. */
+ orig_notice_all = link_info.notice_all;
+ orig_callbacks = link_info.callbacks;
+ plugin_callbacks = *orig_callbacks;
+ plugin_callbacks.notice = &plugin_notice;
link_info.notice_all = TRUE;
+ link_info.lto_plugin_active = TRUE;
+ link_info.callbacks = &plugin_callbacks;
- return 0;
+ register_ld_plugin_object_p (plugin_object_p);
+
+#if HAVE_MMAP && HAVE_GETPAGESIZE
+ plugin_pagesize = getpagesize ();
+#endif
}
/* Call 'claim file' hook for all plugins. */
-int
+static 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)
{
+ off_t cur_offset;
enum ld_plugin_status rv;
+
called_plugin = curplug;
+ cur_offset = lseek (file->fd, 0, SEEK_CUR);
rv = (*curplug->claim_file_handler) (file, claimed);
+ if (!*claimed)
+ lseek (file->fd, cur_offset, SEEK_SET);
called_plugin = NULL;
if (rv != LDPS_OK)
set_plugin_error (curplug->name);
return plugin_error_p () ? -1 : 0;
}
+/* Duplicates a character string with memory attached to ABFD. */
+
+static char *
+plugin_strdup (bfd *abfd, const char *str)
+{
+ size_t strlength;
+ char *copy;
+ strlength = strlen (str) + 1;
+ copy = bfd_alloc (abfd, strlength);
+ if (copy == NULL)
+ einfo (_("%P%F: plugin_strdup failed to allocate memory: %s\n"),
+ bfd_get_error ());
+ memcpy (copy, str, strlength);
+ return copy;
+}
+
+static const bfd_target *
+plugin_object_p (bfd *ibfd)
+{
+ int claimed;
+ plugin_input_file_t *input;
+ struct ld_plugin_input_file file;
+ bfd *abfd;
+
+ /* Don't try the dummy object file. */
+ if ((ibfd->flags & BFD_PLUGIN) != 0)
+ return NULL;
+
+ if (ibfd->plugin_format != bfd_plugin_unknown)
+ {
+ if (ibfd->plugin_format == bfd_plugin_yes)
+ return ibfd->plugin_dummy_bfd->xvec;
+ else
+ return NULL;
+ }
+
+ /* We create a dummy BFD, initially empty, to house whatever symbols
+ the plugin may want to add. */
+ abfd = plugin_get_ir_dummy_bfd (ibfd->filename, ibfd);
+
+ input = bfd_alloc (abfd, sizeof (*input));
+ if (input == NULL)
+ einfo (_("%P%F: plugin failed to allocate memory for input: %s\n"),
+ bfd_get_error ());
+
+ if (!bfd_plugin_open_input (ibfd, &file))
+ return NULL;
+
+ if (file.name == ibfd->filename)
+ {
+ /* We must copy filename attached to ibfd if it is not an archive
+ member since it may be freed by bfd_close below. */
+ file.name = plugin_strdup (abfd, file.name);
+ }
+
+ file.handle = input;
+ /* The plugin API expects that the file descriptor won't be closed
+ and reused as done by the bfd file cache. So dup one. */
+ file.fd = dup (file.fd);
+ if (file.fd < 0)
+ return NULL;
+
+ input->abfd = abfd;
+ input->view_buffer.addr = NULL;
+ input->view_buffer.filesize = 0;
+ input->view_buffer.offset = 0;
+ input->fd = file.fd;
+ input->use_mmap = FALSE;
+ input->offset = file.offset;
+ input->filesize = file.filesize;
+ input->name = plugin_strdup (abfd, ibfd->filename);
+
+ claimed = 0;
+
+ if (plugin_call_claim_file (&file, &claimed))
+ einfo (_("%P%F: %s: plugin reported error claiming file\n"),
+ plugin_error_plugin ());
+
+ if (input->fd != -1 && !bfd_plugin_target_p (ibfd->xvec))
+ {
+ /* FIXME: fd belongs to us, not the plugin. GCC plugin, which
+ doesn't need fd after plugin_call_claim_file, doesn't use
+ BFD plugin target vector. Since GCC plugin doesn't call
+ release_input_file, we close it here. LLVM plugin, which
+ needs fd after plugin_call_claim_file and calls
+ release_input_file after it is done, uses BFD plugin target
+ vector. This scheme doesn't work when a plugin needs fd and
+ doesn't use BFD plugin target vector neither. */
+ close (input->fd);
+ input->fd = -1;
+ }
+
+ if (claimed)
+ {
+ ibfd->plugin_format = bfd_plugin_yes;
+ ibfd->plugin_dummy_bfd = abfd;
+ bfd_make_readable (abfd);
+ return abfd->xvec;
+ }
+ else
+ {
+#if HAVE_MMAP
+ if (input->use_mmap)
+ {
+ /* If plugin didn't claim the file, unmap the buffer. */
+ char *addr = input->view_buffer.addr;
+ off_t size = input->view_buffer.filesize;
+# if HAVE_GETPAGESIZE
+ off_t bias = input->view_buffer.offset % plugin_pagesize;
+ size += bias;
+ addr -= bias;
+# endif
+ munmap (addr, size);
+ }
+#endif
+
+ /* If plugin didn't claim the file, we don't need the dummy bfd.
+ Can't avoid speculatively creating it, alas. */
+ ibfd->plugin_format = bfd_plugin_no;
+ bfd_close_all_done (abfd);
+ return NULL;
+ }
+}
+
+void
+plugin_maybe_claim (lang_input_statement_type *entry)
+{
+ ASSERT (entry->header.type == lang_input_statement_enum);
+ if (plugin_object_p (entry->the_bfd))
+ {
+ bfd *abfd = entry->the_bfd->plugin_dummy_bfd;
+
+ /* Discard the real file's BFD and substitute the dummy one. */
+
+ /* We can't call bfd_close on archives. BFD archive handling
+ caches elements, and add_archive_element keeps pointers to
+ the_bfd and the_bfd->filename in a lang_input_statement_type
+ linker script statement. */
+ if (entry->the_bfd->my_archive == NULL)
+ bfd_close (entry->the_bfd);
+ entry->the_bfd = abfd;
+ entry->flags.claimed = 1;
+ }
+}
+
/* Call 'all symbols read' hook for all plugins. */
int
plugin_call_all_symbols_read (void)
/* 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)
return plugin_error_p () ? -1 : 0;
}
-/* Call 'cleanup' hook for all plugins. */
-int
+/* Call 'cleanup' hook for all plugins at exit. */
+void
plugin_call_cleanup (void)
{
plugin_t *curplug = plugins_list;
rv = (*curplug->cleanup_handler) ();
called_plugin = NULL;
if (rv != LDPS_OK)
- set_plugin_error (curplug->name);
+ info_msg (_("%P: %s: error in plugin cleanup: %d (ignored)\n"),
+ curplug->name, rv);
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,
+ the linker adds them to the linker hash table. Mark those
+ referenced from a non-IR file with non_ir_ref_regular or
+ non_ir_ref_dynamic as appropriate. 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\n"),
- abfd->filename, name);
- }
- else if (!is_ref && is_dummy)
+static bfd_boolean
+plugin_notice (struct bfd_link_info *info,
+ struct bfd_link_hash_entry *h,
+ struct bfd_link_hash_entry *inh,
+ bfd *abfd,
+ asection *section,
+ bfd_vma value,
+ flagword flags)
+{
+ struct bfd_link_hash_entry *orig_h = h;
+
+ if (h != NULL)
{
- /* No further processing since this is a def from an IR dummy BFD. */
- return FALSE;
+ bfd *sym_bfd;
+ bfd_boolean ref = FALSE;
+
+ if (h->type == bfd_link_hash_warning)
+ h = h->u.i.link;
+
+ /* Nothing to do here if this def/ref is from an IR dummy BFD. */
+ if (is_ir_dummy_bfd (abfd))
+ ;
+
+ /* Making an indirect symbol counts as a reference unless this
+ is a brand new symbol. */
+ else if (bfd_is_ind_section (section)
+ || (flags & BSF_INDIRECT) != 0)
+ {
+ /* ??? Some of this is questionable. See comments in
+ _bfd_generic_link_add_one_symbol for case IND. */
+ if (h->type != bfd_link_hash_new
+ || inh->type == bfd_link_hash_new)
+ {
+ if ((abfd->flags & DYNAMIC) == 0)
+ inh->non_ir_ref_regular = TRUE;
+ else
+ inh->non_ir_ref_dynamic = TRUE;
+ }
+
+ if (h->type != bfd_link_hash_new)
+ ref = TRUE;
+ }
+
+ /* Nothing to do here for warning symbols. */
+ else if ((flags & BSF_WARNING) != 0)
+ ;
+
+ /* Nothing to do here for constructor symbols. */
+ else if ((flags & BSF_CONSTRUCTOR) != 0)
+ ;
+
+ /* If this is a ref, set non_ir_ref. */
+ else if (bfd_is_und_section (section))
+ {
+ /* Replace the undefined dummy bfd with the real one. */
+ if ((h->type == bfd_link_hash_undefined
+ || h->type == bfd_link_hash_undefweak)
+ && (h->u.undef.abfd == NULL
+ || (h->u.undef.abfd->flags & BFD_PLUGIN) != 0))
+ h->u.undef.abfd = abfd;
+ ref = TRUE;
+ }
+
+ /* Otherwise, it must be a new def. */
+ else
+ {
+ /* Ensure any symbol defined in an IR dummy BFD takes on a
+ new value from a real BFD. Weak symbols are not normally
+ overridden by a new weak definition, and strong symbols
+ will normally cause multiple definition errors. Avoid
+ this by making the symbol appear to be undefined. */
+ if (((h->type == bfd_link_hash_defweak
+ || h->type == bfd_link_hash_defined)
+ && is_ir_dummy_bfd (sym_bfd = h->u.def.section->owner))
+ || (h->type == bfd_link_hash_common
+ && is_ir_dummy_bfd (sym_bfd = h->u.c.p->section->owner)))
+ {
+ h->type = bfd_link_hash_undefweak;
+ h->u.undef.abfd = sym_bfd;
+ }
+
+ /* A common symbol should be merged with other commons or
+ defs with the same name. In particular, a common ought
+ to be overridden by a def in a -flto object. In that
+ sense a common is also a ref. */
+ if (bfd_is_com_section (section))
+ ref = TRUE;
+ }
+
+ if (ref)
+ {
+ if ((abfd->flags & DYNAMIC) == 0)
+ h->non_ir_ref_regular = TRUE;
+ else
+ h->non_ir_ref_dynamic = TRUE;
+ }
}
/* Continue with cref/nocrossref/trace-sym processing. */
+ if (orig_h == NULL
+ || orig_notice_all
+ || (info->notice_hash != NULL
+ && bfd_hash_lookup (info->notice_hash, orig_h->root.string,
+ FALSE, FALSE) != NULL))
+ return (*orig_callbacks->notice) (info, orig_h, inh,
+ abfd, section, value, flags);
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'\n"), nbfd->filename,
- name);
- else if (blhe->type != bfd_link_hash_defined)
- einfo (_("%P%x: %s: bad IR symbol type %d\n"), 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;
-}