/* Plugin control for the GNU linker.
- Copyright (C) 2010-2015 Free Software Foundation, Inc.
+ Copyright (C) 2010-2016 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 "../bfd/plugin.h"
#include "plugin.h"
#include "plugin-api.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;
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;
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[] =
{
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_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;
}
static enum ld_plugin_status
get_view (const void *handle, const void **viewp)
{
- const plugin_input_file_t *input = handle;
+ plugin_input_file_t *input = (plugin_input_file_t *) handle;
char *buffer;
- size_t size;
+ size_t size = input->filesize;
+ off_t offset = input->offset;
+#if HAVE_MMAP && HAVE_GETPAGESIZE
+ off_t bias;
+#endif
ASSERT (called_plugin);
- if (lseek (input->fd, input->offset, SEEK_SET) < 0)
- return LDPS_ERR;
+ /* 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);
- size = input->filesize;
- buffer = bfd_alloc (input->abfd, size);
- if (buffer == NULL)
- return LDPS_ERR;
- *viewp = buffer;
+ /* 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;
+ }
- do
+ 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)
{
- ssize_t got = read (input->fd, buffer, size);
- if (got == 0)
- break;
- else if (got > 0)
+ 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
{
- buffer += got;
- size -= got;
+ 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;
}
- else if (errno != EINTR)
- return LDPS_ERR;
+ while (size > 0);
}
- while (size > 0);
+
+ input->view_buffer.addr = buffer;
+ *viewp = buffer;
return LDPS_OK;
}
static enum ld_plugin_status
release_input_file (const void *handle)
{
- const plugin_input_file_t *input = handle;
+ plugin_input_file_t *input = (plugin_input_file_t *) handle;
ASSERT (called_plugin);
if (input->fd != -1)
- close (input->fd);
+ {
+ close (input->fd);
+ input->fd = -1;
+ }
return LDPS_OK;
}
{
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 (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,
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 = ACONCAT (("%P: warning: ", format, "\n",
+ (const char *) NULL));
+ vfinfo (stdout, newfmt, args, TRUE);
+ }
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 = ACONCAT ((level == LDPL_FATAL ? "%P%F" : "%P%X",
+ ": error: ", format, "\n",
+ (const char *) NULL));
fflush (stdout);
vfinfo (stderr, newfmt, args, TRUE);
fflush (stderr);
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.notice_all = TRUE;
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 ();
+#endif
}
/* Call 'claim file' hook for all plugins. */
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)
{
- int claimed = 0;
+ 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;
- size_t namelength;
+ off_t offset, filesize;
+ struct ld_plugin_input_file file;
+ bfd *abfd;
+ bfd_boolean inarchive;
+ const char *name;
+ int fd;
+
+ /* Don't try the dummy object file. */
+ if ((ibfd->flags & BFD_PLUGIN) != 0)
+ return NULL;
+
+ if (ibfd->plugin_format != bfd_plugin_uknown)
+ {
+ if (ibfd->plugin_format == bfd_plugin_yes)
+ return ibfd->plugin_dummy_bfd->xvec;
+ else
+ return NULL;
+ }
+
+ inarchive = bfd_my_archive (ibfd) != NULL;
+ name = inarchive ? bfd_my_archive (ibfd)->filename : ibfd->filename;
+ fd = open (name, O_RDONLY | O_BINARY);
+
+ if (fd < 0)
+ return NULL;
/* We create a dummy BFD, initially empty, to house whatever symbols
the plugin may want to add. */
- bfd *abfd = plugin_get_ir_dummy_bfd (entry->the_bfd->filename,
- entry->the_bfd);
+ 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 ());
- input->abfd = abfd;
- input->fd = file->fd;
- input->offset = file->offset;
- input->filesize = file->filesize;
- namelength = strlen (entry->the_bfd->filename) + 1;
- input->name = bfd_alloc (abfd, namelength);
- if (input->name == NULL)
- einfo (_("%P%F: plugin failed to allocate memory for input filename: %s\n"),
- bfd_get_error ());
- memcpy (input->name, entry->the_bfd->filename, namelength);
+ 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);
+
+ /* 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->handle = input;
+ file.name = name;
+ file.offset = offset;
+ file.filesize = filesize;
+ file.fd = fd;
+ file.handle = input;
- if (plugin_call_claim_file (file, &claimed))
+ input->abfd = abfd;
+ input->view_buffer.addr = NULL;
+ input->view_buffer.filesize = 0;
+ input->view_buffer.offset = 0;
+ input->fd = fd;
+ input->use_mmap = FALSE;
+ input->offset = offset;
+ input->filesize = 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 (bfd_check_format (entry->the_bfd, 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 (file->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 (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)
+{
+ 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. */
/* BFD archive handling caches elements so we can't call
if (entry->the_bfd->my_archive == NULL)
bfd_close (entry->the_bfd);
entry->the_bfd = abfd;
- entry->flags.claimed = TRUE;
- bfd_make_readable (entry->the_bfd);
- }
- else
- {
- /* 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 (abfd);
- entry->flags.claimed = FALSE;
+ entry->flags.claimed = 1;
}
}