X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=ld%2Fplugin.c;h=4ef4a236650ce97b51ed6bd7edd5369a035f18f6;hb=73199c2b7a3cb81bd65778386c5b97b4f3b86534;hp=a68aea3cf7a1e107a110865ced94d8978b189ada;hpb=1715a13cc2716b05ab6715f81c5d1f852675ed20;p=deliverable%2Fbinutils-gdb.git diff --git a/ld/plugin.c b/ld/plugin.c index a68aea3cf7..4ef4a23665 100644 --- a/ld/plugin.c +++ b/ld/plugin.c @@ -1,5 +1,5 @@ /* 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. @@ -23,17 +23,35 @@ #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 +# 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 +#include #endif /* Report plugin symbols. */ @@ -41,12 +59,8 @@ 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 @@ -76,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; @@ -94,22 +128,21 @@ static plugin_t *called_plugin = 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[] = @@ -124,8 +157,10 @@ 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 @@ -134,6 +169,14 @@ static const enum ld_plugin_tag tv_header_tags[] = /* 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 bfd_cleanup plugin_object_p (bfd *); + #if !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H) #define RTLD_NOW 0 /* Dummy value. */ @@ -159,6 +202,14 @@ dlclose (void *handle) #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) @@ -182,17 +233,30 @@ plugin_error_plugin (void) } /* 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; @@ -201,7 +265,6 @@ plugin_opt_plugin (const char *plugin) /* 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 @@ -214,6 +277,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; @@ -225,42 +299,55 @@ 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) { - 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); - bfd_copy_private_bfd_data (srctemplate, abfd); - bfd_set_gp_size (abfd, bfd_get_gp_size (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 (_("%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) { - 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. */ @@ -273,7 +360,7 @@ asymbol_from_plugin_symbol (bfd *abfd, asymbol *asym, 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) @@ -283,7 +370,27 @@ asymbol_from_plugin_symbol (bfd *abfd, asymbol *asym, /* 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: @@ -297,9 +404,6 @@ asymbol_from_plugin_symbol (bfd *abfd, asymbol *asym, 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_value = 1; break; default: @@ -308,19 +412,27 @@ asymbol_from_plugin_symbol (bfd *abfd, asymbol *asym, 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; @@ -334,9 +446,7 @@ asymbol_from_plugin_symbol (bfd *abfd, asymbol *asym, 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; @@ -374,14 +484,18 @@ 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); 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) @@ -396,35 +510,130 @@ add_symbols (void *handle, int nsyms, const struct ld_plugin_symbol *syms) 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) { - /* 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) + 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) { @@ -445,27 +654,138 @@ is_visible_from_outside (struct ld_plugin_symbol *lsym, asection *section, return (lsym->visibility == LDPV_DEFAULT || lsym->visibility == LDPV_PROTECTED); } + 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; + 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; - blhe = bfd_link_hash_lookup (link_info.hash, syms[n].name, - FALSE, FALSE, TRUE); + 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); + /* 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; } @@ -473,7 +793,7 @@ get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms) 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 @@ -481,24 +801,17 @@ get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms) && 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); } /* 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; - - /* 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)); + owner_sec = (blhe->type == bfd_link_hash_common + ? blhe->u.c.p->section + : blhe->u.def.section); + /* If it was originally undefined or common, then it has been resolved; determine how. */ @@ -507,55 +820,83 @@ get_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms) || 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: + report_symbol: + syms[n].resolution = res; if (report_plugin_symbols) - einfo ("%P: %B: symbol `%s' definition: %d, resolution: %d\n", - abfd, syms[n].name, syms[n].def, syms[n].resolution); + einfo (_("%P: %pB: symbol `%s' " + "definition: %s, visibility: %s, resolution: %s\n"), + abfd, syms[n].name, + 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; } @@ -563,10 +904,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; } @@ -594,20 +939,25 @@ 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", NULL)); - fflush (stdout); - vfinfo (stderr, newfmt, args, TRUE); - fflush (stderr); - } + { + 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; } @@ -616,7 +966,7 @@ message (int level, const char *format, ...) } /* 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; @@ -625,9 +975,6 @@ set_tv_header (struct ld_plugin_tv *tv) 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]; @@ -644,9 +991,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.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; @@ -666,11 +1014,17 @@ 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; 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; @@ -688,7 +1042,6 @@ set_tv_header (struct ld_plugin_tv *tv) } #undef TVU } - return tv_header_size; } /* Append the per-plugin args list and trailing LDPT_NULL to tv. */ @@ -707,16 +1060,8 @@ 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. */ -int +void plugin_load_plugins (void) { struct ld_plugin_tv *my_tv; @@ -725,7 +1070,7 @@ plugin_load_plugins (void) /* 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. */ @@ -745,41 +1090,53 @@ plugin_load_plugins (void) 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; } /* 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) { enum ld_plugin_status rv; + called_plugin = curplug; rv = (*curplug->claim_file_handler) (file, claimed); called_plugin = NULL; @@ -791,6 +1148,151 @@ plugin_call_claim_file (const struct ld_plugin_input_file *file, int *claimed) 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 (_("%F%P: plugin_strdup failed to allocate memory: %s\n"), + bfd_get_error ()); + memcpy (copy, str, strlength); + return copy; +} + +static void +plugin_cleanup (bfd *abfd ATTRIBUTE_UNUSED) +{ +} + +static bfd_cleanup +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 plugin_cleanup; + 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 (_("%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 ()); + + 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); + abfd->no_export = ibfd->no_export; + return plugin_cleanup; + } + 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) @@ -800,13 +1302,6 @@ 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) @@ -838,105 +1333,132 @@ plugin_call_cleanup (void) 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 ()); -} - -/* Lazily init the non_ironly hash table. */ -static void -init_non_ironly_hash (void) -{ - struct bfd_sym_chain *sym; - - 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")); - - for (sym = &entry_symbol; sym != NULL; sym = sym->next) - if (sym->name - && !bfd_hash_lookup (non_ironly_hash, sym->name, TRUE, TRUE)) - einfo (_("%P%X: hash table failure adding symbol %s\n"), - sym->name); - } } /* 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; + } + + + /* 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_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; + } + + 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; -}