1041 lines · 29.0 KB · C
RawDownload
/* Plugin control for the GNU linker.   Copyright 2010, 2011, 2012, 2013 Free Software Foundation, Inc.    This file is part of the GNU Binutils.    This program is free software; you can redistribute it and/or modify   it under the terms of the GNU General Public License as published by   the Free Software Foundation; either version 3 of the License, or   (at your option) any later version.    This program is distributed in the hope that it will be useful,   but WITHOUT ANY WARRANTY; without even the implied warranty of   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   GNU General Public License for more details.    You should have received a copy of the GNU General Public License   along with this program; if not, write to the Free Software   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,   MA 02110-1301, USA.  */ #include "sysdep.h"#include "libiberty.h"#include "bfd.h"#include "bfdlink.h"#include "bfdver.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 "elf-bfd.h"#if !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H)#include <windows.h>#endif /* Report plugin symbols.  */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.  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{  struct plugin_arg *next;  const char *arg;} plugin_arg_t; /* Holds all details of a single plugin.  */typedef struct plugin{  /* Next on the list of plugins, or NULL at end of chain.  */  struct plugin *next;  /* The argument string given to --plugin.  */  const char *name;  /* The shared library handle returned by dlopen.  */  void *dlhandle;  /* The list of argument string given to --plugin-opt.  */  plugin_arg_t *args;  /* Number of args in the list, for convenience.  */  size_t n_args;  /* The plugin's event handlers.  */  ld_plugin_claim_file_handler claim_file_handler;  ld_plugin_all_symbols_read_handler all_symbols_read_handler;  ld_plugin_cleanup_handler cleanup_handler;  /* TRUE if the cleanup handlers have been called.  */  bfd_boolean cleanup_done;} plugin_t; /* The master list of all plugins.  */static plugin_t *plugins_list = NULL; /* We keep a tail pointer for easy linking on the end.  */static plugin_t **plugins_tail_chain_ptr = &plugins_list; /* The last plugin added to the list, for receiving args.  */static plugin_t *last_plugin = NULL; /* The tail of the arg chain of the last plugin added to the list.  */static plugin_arg_t **last_plugin_args_tail_chain_ptr = NULL; /* The plugin which is currently having a callback executed.  */static plugin_t *called_plugin = NULL; /* Last plugin to cause an error, if any.  */static const char *error_plugin = 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.  */bfd_boolean no_more_claiming = FALSE; /* List of tags to set in the constant leading part of the tv array. */static const enum ld_plugin_tag tv_header_tags[] ={  LDPT_MESSAGE,  LDPT_API_VERSION,  LDPT_GNU_LD_VERSION,  LDPT_LINKER_OUTPUT,  LDPT_OUTPUT_NAME,  LDPT_REGISTER_CLAIM_FILE_HOOK,  LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,  LDPT_REGISTER_CLEANUP_HOOK,  LDPT_ADD_SYMBOLS,  LDPT_GET_INPUT_FILE,  LDPT_RELEASE_INPUT_FILE,  LDPT_GET_SYMBOLS,  LDPT_GET_SYMBOLS_V2,  LDPT_ADD_INPUT_FILE,  LDPT_ADD_INPUT_LIBRARY,  LDPT_SET_EXTRA_LIBRARY_PATH}; /* 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 *, bfd *,				  asection *, bfd_vma, flagword, const char *); #if !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H) #define RTLD_NOW 0	/* Dummy value.  */ static void *dlopen (const char *file, int mode ATTRIBUTE_UNUSED){  return LoadLibrary (file);} static void *dlsym (void *handle, const char *name){  return GetProcAddress (handle, name);} static intdlclose (void *handle){  FreeLibrary (handle);  return 0;} #endif /* !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H)  */ #ifndef HAVE_DLFCN_Hstatic const char *dlerror (void){  return "";}#endif /* Helper function for exiting with error status.  */static intset_plugin_error (const char *plugin){  error_plugin = plugin;  return -1;} /* Test if an error occurred.  */static bfd_booleanplugin_error_p (void){  return error_plugin != NULL;} /* Return name of plugin which caused an error if any.  */const char *plugin_error_plugin (void){  return error_plugin ? error_plugin : _("<no plugin>");} /* Handle -plugin arg: find and load plugin, or return error.  */voidplugin_opt_plugin (const char *plugin){  plugin_t *newplug;   newplug = xmalloc (sizeof *newplug);  memset (newplug, 0, sizeof *newplug);  newplug->name = plugin;  newplug->dlhandle = dlopen (plugin, RTLD_NOW);  if (!newplug->dlhandle)    einfo (_("%P%F: %s: error loading plugin: %s\n"), plugin, dlerror ());   /* 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;   /* Record it as current plugin for receiving args.  */  last_plugin = newplug;  last_plugin_args_tail_chain_ptr = &newplug->args;} /* Accumulate option arguments for last-loaded plugin, or return   error if none.  */intplugin_opt_plugin_arg (const char *arg){  plugin_arg_t *newarg;   if (!last_plugin)    return set_plugin_error (_("<no plugin>"));   newarg = xmalloc (sizeof *newarg);  newarg->arg = arg;  newarg->next = NULL;   /* Chain on end to preserve command-line order.  */  *last_plugin_args_tail_chain_ptr = newarg;  last_plugin_args_tail_chain_ptr = &newarg->next;  last_plugin->n_args++;  return 0;} /* Create a dummy BFD.  */bfd *plugin_get_ir_dummy_bfd (const char *name, bfd *srctemplate){  bfd *abfd;   bfd_use_reserved_id = 1;  abfd = bfd_create (concat (name, IRONLY_SUFFIX, (const char *) NULL),		     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))	{	  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;	}    }  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_booleanis_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);} /* Helpers to convert between BFD and GOLD symbol formats.  */static enum ld_plugin_statusasymbol_from_plugin_symbol (bfd *abfd, asymbol *asym,			    const struct ld_plugin_symbol *ldsym){  flagword flags = BSF_NO_FLAGS;  struct bfd_section *section;   asym->the_bfd = abfd;  asym->name = (ldsym->version		? concat (ldsym->name, "@", ldsym->version, (const char *) NULL)		: ldsym->name);  asym->value = 0;  switch (ldsym->def)    {    case LDPK_WEAKDEF:      flags = BSF_WEAK;      /* FALLTHRU */    case LDPK_DEF:      flags |= BSF_GLOBAL;      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_WEAK;      /* FALLTHRU */    case LDPK_UNDEF:      section = bfd_und_section_ptr;      break;     case LDPK_COMMON:      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:      return LDPS_ERR;    }  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);      switch (ldsym->visibility)	{	default:	  einfo (_("%P%F: unknown ELF symbol visibility: %d!\n"),		 ldsym->visibility);	case LDPV_DEFAULT:	  visibility = STV_DEFAULT;	  break;	case LDPV_PROTECTED:	  visibility = STV_PROTECTED;	  break;	case LDPV_INTERNAL:	  visibility = STV_INTERNAL;	  break;	case LDPV_HIDDEN:	  visibility = STV_HIDDEN;	  break;	}      elfsym->internal_elf_sym.st_other	= (visibility | (elfsym->internal_elf_sym.st_other			 & ~ELF_ST_VISIBILITY (-1)));    }   return LDPS_OK;} /* Register a claim-file handler.  */static enum ld_plugin_statusregister_claim_file (ld_plugin_claim_file_handler handler){  ASSERT (called_plugin);  called_plugin->claim_file_handler = handler;  return LDPS_OK;} /* Register an all-symbols-read handler.  */static enum ld_plugin_statusregister_all_symbols_read (ld_plugin_all_symbols_read_handler handler){  ASSERT (called_plugin);  called_plugin->all_symbols_read_handler = handler;  return LDPS_OK;} /* Register a cleanup handler.  */static enum ld_plugin_statusregister_cleanup (ld_plugin_cleanup_handler handler){  ASSERT (called_plugin);  called_plugin->cleanup_handler = handler;  return LDPS_OK;} /* Add symbols from a plugin-claimed input file.  */static enum ld_plugin_statusadd_symbols (void *handle, int nsyms, const struct ld_plugin_symbol *syms){  asymbol **symptrs;  bfd *abfd = handle;  int n;   ASSERT (called_plugin);  symptrs = xmalloc (nsyms * sizeof *symptrs);  for (n = 0; n < nsyms; n++)    {      enum ld_plugin_status rv;      asymbol *bfdsym;       bfdsym = bfd_make_empty_symbol (abfd);      symptrs[n] = bfdsym;      rv = asymbol_from_plugin_symbol (abfd, bfdsym, syms + n);      if (rv != LDPS_OK)	return rv;    }  bfd_set_symtab (abfd, symptrs, nsyms);  return LDPS_OK;} /* Get the input file information with an open (possibly re-opened)   file descriptor.  */static enum ld_plugin_statusget_input_file (const void *handle ATTRIBUTE_UNUSED,                struct ld_plugin_input_file *file ATTRIBUTE_UNUSED){  ASSERT (called_plugin);  return LDPS_ERR;} /* Release the input file.  */static enum ld_plugin_statusrelease_input_file (const void *handle ATTRIBUTE_UNUSED){  ASSERT (called_plugin);  return LDPS_ERR;} /* Return TRUE if a defined symbol might be reachable from outside the   universe of claimed objects.  */static inline bfd_booleanis_visible_from_outside (struct ld_plugin_symbol *lsym,			 struct bfd_link_hash_entry *blhe){  struct bfd_sym_chain *sym;   if (link_info.relocatable)    return TRUE;  if (link_info.export_dynamic || !link_info.executable)    {      /* 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)	{	  struct elf_link_hash_entry *el = (struct elf_link_hash_entry *)blhe;	  int vis = ELF_ST_VISIBILITY (el->other);	  return vis == STV_DEFAULT || vis == STV_PROTECTED;	}      /* On non-ELF targets, we can safely make inferences by considering	 what visibility the plugin would have liked to apply when it first	 sent us the symbol.  During ELF symbol processing, visibility only	 ever becomes more restrictive, not less, when symbols are merged,	 so this is a conservative estimate; it may give false positives,	 declaring something visible from outside when it in fact would	 not have been, but this will only lead to missed optimisation	 opportunities during LTRANS at worst; it will not give false	 negatives, which can lead to the disastrous conclusion that the	 related symbol is IRONLY.  (See GCC PR46319 for an example.)  */      return (lsym->visibility == LDPV_DEFAULT	      || 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;} /* Get the symbol resolution info for a plugin-claimed input file.  */static enum ld_plugin_statusget_symbols (const void *handle, int nsyms, struct ld_plugin_symbol *syms,	     int def_ironly_exp){  const bfd *abfd = handle;  int n;   ASSERT (called_plugin);

Lines 1–500 of 1041.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t