/* Plugin control for the GNU linker.
- Copyright 2010, 2011 Free Software Foundation, Inc.
+ Copyright (C) 2010-2020 Free Software Foundation, Inc.
This file is part of the GNU Binutils.
#include "bfd.h"
#include "bfdlink.h"
#include "bfdver.h"
+#include "ctf-api.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 "../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>
#endif
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;
its own newly-added input files and libs to claim. */
bfd_boolean no_more_claiming = 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
/* Forward references. */
static bfd_boolean plugin_notice (struct bfd_link_info *,
- struct bfd_link_hash_entry *, bfd *,
- asection *, bfd_vma, flagword, const char *);
+ 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)
#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 (_("%F%P: %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)
{
bfd *abfd;
+ bfd_boolean bfd_plugin_target;
bfd_use_reserved_id = 1;
+ bfd_plugin_target = bfd_plugin_target_p (srctemplate->xvec);
abfd = bfd_create (concat (name, IRONLY_SUFFIX, (const char *) NULL),
- srctemplate);
+ bfd_plugin_target ? link_info.output_bfd : srctemplate);
if (abfd != NULL)
{
abfd->flags |= BFD_LINKER_CREATED | BFD_PLUGIN;
- bfd_set_arch_info (abfd, bfd_get_arch_info (srctemplate));
- bfd_set_gp_size (abfd, bfd_get_gp_size (srctemplate));
- if (bfd_make_writable (abfd)
- && bfd_copy_private_bfd_data (srctemplate, abfd))
+ 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 sections to own the symbols. */
+ /* 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;
}
}
- einfo (_("could not create dummy IR bfd: %F%E\n"));
+report_error:
+ einfo (_("%F%P: could not create dummy IR bfd: %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)
{
/* 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.
- Likewise, the usrdata field may be NULL if ABFD was added by the
- backend without a corresponding input statement, as happens e.g.
- when processing DT_NEEDED dependencies. */
- return (abfd
- && abfd->usrdata
- && ((lang_input_statement_type *)(abfd->usrdata))->claimed);
+ 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. */
/* 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:
flags = BSF_GLOBAL;
section = bfd_com_section_ptr;
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_shndx = SHN_COMMON;
- ((elf_symbol_type *) asym)->internal_elf_sym.st_value = 1;
- }
break;
default:
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);
unsigned char visibility;
if (!elfsym)
- einfo (_("%P%F: %s: non-ELF symbol in ELF BFD!\n"), asym->name);
+ einfo (_("%F%P: %s: non-ELF symbol in ELF BFD!\n"), asym->name);
+
+ if (ldsym->def == LDPK_COMMON)
+ {
+ elfsym->internal_elf_sym.st_shndx = SHN_COMMON;
+ elfsym->internal_elf_sym.st_value = 1;
+ }
+
switch (ldsym->visibility)
{
default:
- einfo (_("%P%F: unknown ELF symbol visibility: %d!\n"),
+ einfo (_("%F%P: unknown ELF symbol visibility: %d!\n"),
ldsym->visibility);
+ return LDPS_ERR;
+
case LDPV_DEFAULT:
visibility = STV_DEFAULT;
break;
visibility = STV_HIDDEN;
break;
}
- elfsym->internal_elf_sym.st_other
- = (visibility | (elfsym->internal_elf_sym.st_other
- & ~ELF_ST_VISIBILITY (-1)));
+ elfsym->internal_elf_sym.st_other |= visibility;
}
return LDPS_OK;
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 (_("%F%P: 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)
{
- struct bfd_sym_chain *sym;
-
- /* 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))
+ if (bfd_link_relocatable (&link_info))
return TRUE;
- if (link_info.relocatable)
- 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)
{
|| 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;
}
+/* Return LTO kind string name that corresponds to IDX enum value. */
+static const char *
+get_lto_kind (unsigned int idx)
+{
+ static char buffer[64];
+ const char *lto_kind_str[5] =
+ {
+ "DEF",
+ "WEAKDEF",
+ "UNDEF",
+ "WEAKUNDEF",
+ "COMMON"
+ };
+
+ if (idx < ARRAY_SIZE (lto_kind_str))
+ return lto_kind_str [idx];
+
+ sprintf (buffer, _("unknown LTO kind value %x"), idx);
+ return buffer;
+}
+
+/* Return LTO resolution string name that corresponds to IDX enum value. */
+static const char *
+get_lto_resolution (unsigned int idx)
+{
+ static char buffer[64];
+ static const char *lto_resolution_str[10] =
+ {
+ "UNKNOWN",
+ "UNDEF",
+ "PREVAILING_DEF",
+ "PREVAILING_DEF_IRONLY",
+ "PREEMPTED_REG",
+ "PREEMPTED_IR",
+ "RESOLVED_IR",
+ "RESOLVED_EXEC",
+ "RESOLVED_DYN",
+ "PREVAILING_DEF_IRONLY_EXP",
+ };
+
+ if (idx < ARRAY_SIZE (lto_resolution_str))
+ return lto_resolution_str [idx];
+
+ sprintf (buffer, _("unknown LTO resolution value %x"), idx);
+ return buffer;
+}
+
+/* Return LTO visibility string name that corresponds to IDX enum value. */
+static const char *
+get_lto_visibility (unsigned int idx)
+{
+ static char buffer[64];
+ const char *lto_visibility_str[4] =
+ {
+ "DEFAULT",
+ "PROTECTED",
+ "INTERNAL",
+ "HIDDEN"
+ };
+
+ if (idx < ARRAY_SIZE (lto_visibility_str))
+ return lto_visibility_str [idx];
+
+ sprintf (buffer, _("unknown LTO visibility value %x"), idx);
+ return buffer;
+}
+
/* 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;
- if (syms[n].def != LDPK_UNDEF)
- blhe = bfd_link_hash_lookup (link_info.hash, syms[n].name,
- FALSE, FALSE, TRUE);
+ int res;
+ struct bfd_link_hash_entry *h
+ = bfd_link_hash_lookup (link_info.hash, syms[n].name,
+ FALSE, FALSE, TRUE);
+ enum { wrap_none, wrapper, wrapped } wrap_status = wrap_none;
+
+ if (syms[n].def != LDPK_UNDEF && syms[n].def != LDPK_WEAKUNDEF)
+ {
+ blhe = h;
+ if (blhe && link_info.wrap_hash != NULL)
+ {
+ /* Check if a symbol is a wrapper symbol. */
+ struct bfd_link_hash_entry *unwrap
+ = unwrap_hash_lookup (&link_info, (bfd *) abfd, blhe);
+ if (unwrap && unwrap != h)
+ wrap_status = wrapper;
+ }
+ }
else
- blhe = bfd_wrapped_link_hash_lookup (link_info.output_bfd, &link_info,
- syms[n].name, FALSE, FALSE, TRUE);
+ {
+ blhe = bfd_wrapped_link_hash_lookup (link_info.output_bfd,
+ &link_info, syms[n].name,
+ FALSE, FALSE, TRUE);
+ /* Check if a symbol is a wrapped symbol. */
+ if (blhe && blhe != h)
+ wrap_status = wrapped;
+ }
if (!blhe)
{
- syms[n].resolution = LDPR_UNKNOWN;
+ /* 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;
}
if (blhe->type == bfd_link_hash_undefined
|| blhe->type == bfd_link_hash_undefweak)
{
- syms[n].resolution = LDPR_UNDEF;
+ res = LDPR_UNDEF;
goto report_symbol;
}
if (blhe->type != bfd_link_hash_defined
&& 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 (_("%F%P: %s: plugin symbol table corrupt (sym type %d)\n"),
called_plugin->name, blhe->type);
}
? 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 = !(blhe->non_ir_ref
- || is_visible_from_outside (&syms[n], owner_sec, blhe));
/* 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;
+ res = LDPR_RESOLVED_IR;
else if (owner_sec->owner != NULL
&& (owner_sec->owner->flags & DYNAMIC) != 0)
- syms[n].resolution = LDPR_RESOLVED_DYN;
+ res = LDPR_RESOLVED_DYN;
else
- syms[n].resolution = LDPR_RESOLVED_EXEC;
- goto report_symbol;
+ 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)
- {
- syms[n].resolution = (ironly
- ? LDPR_PREVAILING_DEF_IRONLY
- : LDPR_PREVAILING_DEF);
- goto report_symbol;
- }
+ res = LDPR_PREVAILING_DEF_IRONLY;
/* 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);
+ else if (is_ir_dummy_bfd (owner_sec->owner))
+ res = LDPR_PREEMPTED_IR;
+ else
+ res = LDPR_PREEMPTED_REG;
+
+ if (res == LDPR_PREVAILING_DEF_IRONLY)
+ {
+ /* 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. Also check for wrapper symbol. */
+ if (blhe->non_ir_ref_regular || wrap_status == wrapper)
+ res = LDPR_PREVAILING_DEF;
+ else if (wrap_status == wrapped)
+ res = LDPR_RESOLVED_IR;
+ else if (is_visible_from_outside (&syms[n], blhe))
+ res = def_ironly_exp;
+ }
report_symbol:
+ syms[n].resolution = res;
if (report_plugin_symbols)
- einfo (_("%P: %B: symbol `%s' "
- "definition: %d, visibility: %d, resolution: %d\n"),
+ einfo (_("%P: %pB: symbol `%s' "
+ "definition: %s, visibility: %s, resolution: %s\n"),
abfd, syms[n].name,
- syms[n].def, syms[n].visibility, syms[n].resolution);
+ get_lto_kind (syms[n].def),
+ get_lto_visibility (syms[n].visibility),
+ get_lto_resolution (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;
}
putchar ('\n');
break;
case LDPL_WARNING:
- vfinfo (stdout, format, args, TRUE);
- putchar ('\n');
+ {
+ 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", (const char *) NULL));
+ char *newfmt = concat (level == LDPL_FATAL ? "%F" : "%X",
+ _("%P: error: "), format, "\n",
+ (const char *) NULL);
fflush (stdout);
vfinfo (stderr, newfmt, args, TRUE);
fflush (stderr);
+ free (newfmt);
}
break;
}
}
/* 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 (_("%F%P: %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 (_("%F%P: %s: plugin error: %d\n"), curplug->name, rv);
curplug = curplug->next;
}
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. */
{
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;
return plugin_error_p () ? -1 : 0;
}
-void
-plugin_maybe_claim (struct ld_plugin_input_file *file,
- lang_input_statement_type *entry)
+/* 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 (_("%F%P: 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 = 0;
+ 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. */
- file->handle = plugin_get_ir_dummy_bfd (entry->the_bfd->filename,
- entry->the_bfd);
- if (plugin_call_claim_file (file, &claimed))
- einfo (_("%P%F: %s: plugin reported error claiming file\n"),
+ abfd = plugin_get_ir_dummy_bfd (ibfd->filename, ibfd);
+
+ input = bfd_alloc (abfd, sizeof (*input));
+ if (input == NULL)
+ einfo (_("%F%P: 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;
+ 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 (_("%F%P: %s: plugin reported error claiming file\n"),
plugin_error_plugin ());
- /* fd belongs to us, not the plugin; but we don't need it. */
- close (file->fd);
- if (claimed)
+
+ if (input->fd != -1 && !bfd_plugin_target_p (ibfd->xvec))
{
- /* Discard the real file's BFD and substitute the dummy one. */
+ /* 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;
+ }
- /* BFD archive handling caches elements so we can't call
- bfd_close for archives. */
- if (entry->the_bfd->my_archive == NULL)
- bfd_close (entry->the_bfd);
- entry->the_bfd = file->handle;
- entry->claimed = TRUE;
- bfd_make_readable (entry->the_bfd);
+ 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. */
- bfd_close_all_done (file->handle);
- entry->claimed = FALSE;
+ 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;
}
}
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;
}
- if (plugin_error_p ())
- info_msg (_("%P: %s: error in plugin cleanup (ignored)\n"),
- plugin_error_plugin ());
}
/* 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. Mark those
- referenced from a non-IR file with non_ir_ref. We have to
- notice_all symbols, because we won't necessarily know until later
- which ones will be contributed by IR files. */
+ 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. */
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,
- const char *string)
+ flagword flags)
{
+ struct bfd_link_hash_entry *orig_h = h;
+
if (h != NULL)
{
bfd *sym_bfd;
+ bfd_boolean ref = FALSE;
+
+ if (h->type == bfd_link_hash_warning)
+ h = h->u.i.link;
- /* No further processing if this def/ref is from an IR dummy BFD. */
+ /* Nothing to do here if this def/ref is from an IR dummy BFD. */
if (is_ir_dummy_bfd (abfd))
- return TRUE;
+ ;
/* Making an indirect symbol counts as a reference unless this
is a brand new symbol. */
- if (bfd_is_ind_section (section)
- || (flags & BSF_INDIRECT) != 0)
+ else if (bfd_is_ind_section (section)
+ || (flags & BSF_INDIRECT) != 0)
{
- if (h->type != bfd_link_hash_new)
+ /* ??? 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)
{
- struct bfd_link_hash_entry *inh;
-
- h->non_ir_ref = TRUE;
- inh = bfd_wrapped_link_hash_lookup (abfd, info, string, FALSE,
- FALSE, FALSE);
- if (inh != NULL)
- inh->non_ir_ref = TRUE;
+ 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. */
/* If this is a ref, set non_ir_ref. */
else if (bfd_is_und_section (section))
- h->non_ir_ref = TRUE;
-
- /* Otherwise, it must be a new def. 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. */
+ {
+ /* 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;
+ }
+
+
+ /* 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. */
+ else if (bfd_is_com_section (section))
+ {
+ if (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;
+ }
+ ref = TRUE;
+ }
+
+ /* Otherwise, it must be a new def.
+ 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. */
else 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_undefweak;
h->u.undef.abfd = sym_bfd;
}
+
+ 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 (h == NULL
+ if (orig_h == NULL
|| orig_notice_all
|| (info->notice_hash != NULL
- && bfd_hash_lookup (info->notice_hash, h->root.string,
+ && bfd_hash_lookup (info->notice_hash, orig_h->root.string,
FALSE, FALSE) != NULL))
- return (*orig_callbacks->notice) (info, h,
- abfd, section, value, flags, string);
+ return (*orig_callbacks->notice) (info, orig_h, inh,
+ abfd, section, value, flags);
return TRUE;
}