X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=ld%2Fplugin.c;h=ebde25ffe70aee6b002ea4a554f61545799c7155;hb=8b8c7c9f49992750f66f81b4601d593a3858d98c;hp=2a6d7c5f545b50eac7a0d4bc992fc9b61db0b7b2;hpb=4b95cf5c0c75d6efc1b2f96af72317aecca079f1;p=deliverable%2Fbinutils-gdb.git diff --git a/ld/plugin.c b/ld/plugin.c index 2a6d7c5f54..ebde25ffe7 100644 --- a/ld/plugin.c +++ b/ld/plugin.c @@ -1,5 +1,5 @@ /* Plugin control for the GNU linker. - Copyright (C) 2010-2014 Free Software Foundation, Inc. + Copyright (C) 2010-2016 Free Software Foundation, Inc. This file is part of the GNU Binutils. @@ -21,6 +21,7 @@ #include "sysdep.h" #include "libiberty.h" #include "bfd.h" +#include "libbfd.h" #include "bfdlink.h" #include "bfdver.h" #include "ld.h" @@ -29,9 +30,26 @@ #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 +# 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 +#if !(defined(errno) || defined(_MSC_VER) && defined(_INC_ERRNO)) +extern int errno; +#endif #if !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H) #include #endif @@ -72,6 +90,26 @@ typedef struct plugin 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; @@ -101,6 +139,11 @@ static struct bfd_link_callbacks plugin_callbacks; 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[] = { @@ -114,6 +157,7 @@ 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, @@ -127,8 +171,11 @@ 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 *, 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) @@ -217,6 +264,17 @@ plugin_opt_plugin_arg (const char *arg) if (!last_plugin) return set_plugin_error (_("")); + /* 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; @@ -228,22 +286,33 @@ plugin_opt_plugin_arg (const char *arg) 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; @@ -254,22 +323,18 @@ plugin_get_ir_dummy_bfd (const char *name, bfd *srctemplate) 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) { /* 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))->flags.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. */ @@ -406,7 +471,8 @@ static enum ld_plugin_status 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); @@ -429,19 +495,114 @@ add_symbols (void *handle, int nsyms, const struct ld_plugin_symbol *syms) /* Get the input file information with an open (possibly re-opened) file descriptor. */ static enum ld_plugin_status -get_input_file (const void *handle ATTRIBUTE_UNUSED, - struct ld_plugin_input_file *file ATTRIBUTE_UNUSED) +get_input_file (const void *handle, struct ld_plugin_input_file *file) { + const plugin_input_file_t *input = handle; + ASSERT (called_plugin); - 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 ATTRIBUTE_UNUSED) +release_input_file (const void *handle) { + plugin_input_file_t *input = (plugin_input_file_t *) handle; ASSERT (called_plugin); - 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 @@ -452,9 +613,9 @@ is_visible_from_outside (struct ld_plugin_symbol *lsym, { 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, @@ -494,7 +655,8 @@ static enum ld_plugin_status 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); @@ -512,7 +674,24 @@ get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms, 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; } @@ -612,10 +791,14 @@ get_symbols_v2 (const void *handle, int nsyms, struct ld_plugin_symbol *syms) 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; } @@ -623,10 +806,14 @@ add_input_file (const char *pathname) 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; } @@ -654,18 +841,24 @@ message (int level, const char *format, ...) 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; } @@ -700,11 +893,10 @@ set_tv_header (struct ld_plugin_tv *tv) 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; @@ -724,6 +916,9 @@ set_tv_header (struct ld_plugin_tv *tv) 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; @@ -767,14 +962,6 @@ set_tv_plugin_args (plugin_t *plugin, struct ld_plugin_tv *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. */ void plugin_load_plugins (void) @@ -830,7 +1017,14 @@ plugin_load_plugins (void) 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; + + register_ld_plugin_object_p (plugin_object_p); + +#if HAVE_MMAP && HAVE_GETPAGESIZE + plugin_pagesize = getpagesize (); +#endif } /* Call 'claim file' hook for all plugins. */ @@ -839,8 +1033,6 @@ 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) @@ -857,39 +1049,167 @@ plugin_call_claim_file (const struct ld_plugin_input_file *file, int *claimed) 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 (_("%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 = 0; + int claimed; + plugin_input_file_t *input; + 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 = (ibfd->my_archive != NULL + && !bfd_is_thin_archive (ibfd->my_archive)); + name = inarchive ? ibfd->my_archive->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. */ - file->handle = plugin_get_ir_dummy_bfd (entry->the_bfd->filename, - entry->the_bfd); - if (plugin_call_claim_file (file, &claimed)) + 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 (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.name = name; + file.offset = offset; + file.filesize = filesize; + file.fd = fd; + file.handle = input; + + 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 ()); - /* 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 (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->flags.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->flags.claimed = FALSE; + 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. */ + + /* 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; } } @@ -950,16 +1270,21 @@ plugin_call_cleanup (void) 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; + 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)) ; @@ -969,16 +1294,15 @@ plugin_notice (struct bfd_link_info *info, 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) { - 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; + inh->non_ir_ref = TRUE; } + else if (inh->type == bfd_link_hash_new) + inh->non_ir_ref = TRUE; } /* Nothing to do here for warning symbols. */ @@ -1019,23 +1343,12 @@ plugin_notice (struct bfd_link_info *info, } /* 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; } - -/* Return true if bfd is a dynamic library that should be reloaded. */ - -bfd_boolean -plugin_should_reload (bfd *abfd) -{ - return ((abfd->flags & DYNAMIC) != 0 - && bfd_get_flavour (abfd) == bfd_target_elf_flavour - && bfd_get_format (abfd) == bfd_object - && (elf_dyn_lib_class (abfd) & DYN_AS_NEEDED) != 0); -}