/* Plugin control for the GNU linker.
- Copyright (C) 2010-2015 Free Software Foundation, Inc.
+ Copyright (C) 2010-2017 Free Software Foundation, Inc.
This file is part of the GNU Binutils.
#include "sysdep.h"
#include "libiberty.h"
#include "bfd.h"
-#include "libbfd.h"
#include "bfdlink.h"
#include "bfdver.h"
#include "ld.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>
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. */
plugin_opt_plugin (const char *plugin)
{
plugin_t *newplug;
+ plugin_t *curplug = plugins_list;
newplug = xmalloc (sizeof *newplug);
memset (newplug, 0, sizeof *newplug);
if (!newplug->dlhandle)
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;
plugins_tail_chain_ptr = &newplug->next;
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;
return abfd;
}
}
+report_error:
einfo (_("could not create dummy IR bfd: %F%E\n"));
return NULL;
}
default:
einfo (_("%P%F: unknown ELF symbol visibility: %d!\n"),
ldsym->visibility);
+ return LDPS_ERR;
+
case LDPV_DEFAULT:
visibility = STV_DEFAULT;
break;
{
struct bfd_sym_chain *sym;
- if (link_info.relocatable)
+ if (bfd_link_relocatable (&link_info))
return TRUE;
- if (link_info.export_dynamic || !link_info.executable)
+ 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,
syms[n].name, FALSE, FALSE, TRUE);
if (!blhe)
{
- res = 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;
}
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)
+ if (blhe->non_ir_ref_regular)
res = LDPR_PREVAILING_DEF;
else if (is_visible_from_outside (&syms[n], blhe))
res = def_ironly_exp;
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 ? "%P%F" : "%P%X",
+ ": error: ", format, "\n",
+ (const char *) NULL);
fflush (stdout);
vfinfo (stderr, newfmt, args, TRUE);
fflush (stderr);
+ free (newfmt);
}
break;
}
TVU(val) = major * 100 + minor;
break;
case LDPT_LINKER_OUTPUT:
- TVU(val) = (link_info.relocatable
- ? LDPO_REL
- : (link_info.executable
- ? (link_info.pie ? LDPO_PIE : LDPO_EXEC)
- : LDPO_DYN));
+ 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;
link_info.lto_plugin_active = TRUE;
link_info.callbacks = &plugin_callbacks;
+ register_ld_plugin_object_p (plugin_object_p);
+
#if HAVE_MMAP && HAVE_GETPAGESIZE
- plugin_pagesize = getpagesize ();;
+ plugin_pagesize = getpagesize ();
#endif
}
{
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 copy;
}
-void
-plugin_maybe_claim (lang_input_statement_type *entry)
+static const bfd_target *
+plugin_object_p (bfd *ibfd)
{
- int claimed = 0;
+ int claimed;
plugin_input_file_t *input;
- off_t offset, filesize;
struct ld_plugin_input_file file;
bfd *abfd;
- bfd *ibfd = entry->the_bfd;
- bfd_boolean inarchive = bfd_my_archive (ibfd) != NULL;
- const char *name
- = inarchive ? bfd_my_archive (ibfd)->filename : ibfd->filename;
- int fd = open (name, O_RDONLY | O_BINARY);
- if (fd < 0)
- return;
+ /* 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. */
einfo (_("%P%F: plugin failed to allocate memory for input: %s\n"),
bfd_get_error ());
- if (inarchive)
- {
- /* Offset and filesize must refer to the individual archive
- member, not the whole file, and must exclude the header.
- Fortunately for us, that is how the data is stored in the
- origin field of the bfd and in the arelt_data. */
- offset = ibfd->origin;
- filesize = arelt_size (ibfd);
- }
- else
- {
- offset = 0;
- filesize = lseek (fd, 0, SEEK_END);
+ 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. */
- name = plugin_strdup (abfd, name);
+ file.name = plugin_strdup (abfd, file.name);
}
- file.name = name;
- file.offset = offset;
- file.filesize = filesize;
- file.fd = fd;
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 = fd;
+ input->fd = file.fd;
input->use_mmap = FALSE;
- input->offset = offset;
- input->filesize = filesize;
+ 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 && ibfd->format == bfd_object)
+ if (input->fd != -1 && !bfd_plugin_target_p (ibfd->xvec))
{
- /* FIXME: fd belongs to us, not the plugin. IR for GCC plugin,
- which doesn't need fd after plugin_call_claim_file, is
- stored in bfd_object file. Since GCC plugin before GCC 5
- doesn't call release_input_file, we close it here. IR for
- LLVM plugin, which needs fd after plugin_call_claim_file and
- calls release_input_file after it is done, is stored in
- non-bfd_object file. This scheme doesn't work when a plugin
- needs fd and its IR is stored in bfd_object file. */
- close (fd);
+ /* 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)
{
- /* Discard the real file's BFD and substitute the dummy one. */
-
- /* BFD archive handling caches elements so we can't call
- bfd_close for archives. */
- if (!inarchive)
- bfd_close (ibfd);
+ ibfd->plugin_format = bfd_plugin_yes;
+ ibfd->plugin_dummy_bfd = abfd;
bfd_make_readable (abfd);
- entry->the_bfd = abfd;
- entry->flags.claimed = TRUE;
+ return abfd->xvec;
}
else
{
/* 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);
- entry->flags.claimed = FALSE;
+ 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;
}
}
/* 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,
if (h != NULL)
{
bfd *sym_bfd;
+ bfd_boolean ref = FALSE;
if (h->type == bfd_link_hash_warning)
h = h->u.i.link;
{
/* ??? Some of this is questionable. See comments in
_bfd_generic_link_add_one_symbol for case IND. */
- if (h->type != bfd_link_hash_new)
+ if (h->type != bfd_link_hash_new
+ || inh->type == bfd_link_hash_new)
{
- h->non_ir_ref = TRUE;
- inh->non_ir_ref = TRUE;
+ if ((abfd->flags & DYNAMIC) == 0)
+ inh->non_ir_ref_regular = TRUE;
+ else
+ inh->non_ir_ref_dynamic = TRUE;
}
- else if (inh->type == bfd_link_hash_new)
- inh->non_ir_ref = TRUE;
+
+ if (h->type != bfd_link_hash_new)
+ ref = TRUE;
}
/* Nothing to do here for warning symbols. */
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;
- h->non_ir_ref = TRUE;
+ 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. 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_common
- && is_ir_dummy_bfd (sym_bfd = h->u.c.p->section->owner)))
+ /* 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)
{
- h->type = bfd_link_hash_undefweak;
- h->u.undef.abfd = sym_bfd;
+ if ((abfd->flags & DYNAMIC) == 0)
+ h->non_ir_ref_regular = TRUE;
+ else
+ h->non_ir_ref_dynamic = TRUE;
}
}