/* Plugin control for the GNU linker.
- Copyright 2010 Free Software Foundation, Inc.
+ Copyright 2010, 2011 Free Software Foundation, Inc.
This file is part of the GNU Binutils.
TRUE is returned from the hook. */
static bfd_boolean plugin_cached_allow_multiple_defs = FALSE;
+/* Call 'cleanup' hook for all plugins at exit. */
+static void plugin_call_cleanup (void);
+
/* List of tags to set in the constant leading part of the tv array. */
static const enum ld_plugin_tag tv_header_tags[] =
{
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,
/* Helpers to convert between BFD and GOLD symbol formats. */
static enum ld_plugin_status
asymbol_from_plugin_symbol (bfd *abfd, asymbol *asym,
- const struct ld_plugin_symbol *ldsym)
+ const struct ld_plugin_symbol *ldsym)
{
flagword flags = BSF_NO_FLAGS;
struct bfd_section *section;
asym->the_bfd = abfd;
- asym->name = ldsym->version
+ asym->name = (ldsym->version
? concat (ldsym->name, "@", ldsym->version, NULL)
- : ldsym->name;
+ : ldsym->name);
asym->value = 0;
switch (ldsym->def)
{
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:
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!"), asym->name);
- elfsym->internal_elf_sym.st_other &= ~3;
- elfsym->internal_elf_sym.st_other |= ldsym->visibility;
+ 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;
return LDPS_ERR;
}
+/* 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,
+ 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)
+ return TRUE;
+ if (link_info.export_dynamic || link_info.shared)
+ {
+ /* 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);
+ }
+ return FALSE;
+}
+
/* 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)
{
struct bfd_link_hash_entry *blhe;
bfd_boolean ironly;
+ asection *owner_sec;
blhe = bfd_link_hash_lookup (link_info.hash, syms[n].name,
- FALSE, FALSE, TRUE);
+ FALSE, FALSE, TRUE);
if (!blhe)
{
syms[n].resolution = LDPR_UNKNOWN;
/* Determine resolution from blhe type and symbol's original type. */
if (blhe->type == bfd_link_hash_undefined
- || blhe->type == bfd_link_hash_undefweak)
+ || blhe->type == bfd_link_hash_undefweak)
{
syms[n].resolution = LDPR_UNDEF;
continue;
}
if (blhe->type != bfd_link_hash_defined
- && blhe->type != bfd_link_hash_defweak
- && blhe->type != bfd_link_hash_common)
+ && blhe->type != bfd_link_hash_defweak
+ && 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)",
- called_plugin->name, blhe->type);
+ einfo ("%P%F: %s: plugin symbol table corrupt (sym type %d)\n",
+ called_plugin->name, blhe->type);
}
- /* We need to know if the sym is referenced from non-IR files. */
- ironly = !bfd_hash_lookup (non_ironly_hash, syms[n].name, FALSE, FALSE);
+ /* 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);
/* If it was originally undefined or common, then it has been
- resolved; determine how. */
- if (syms[n].def == LDPK_UNDEF || syms[n].def == LDPK_WEAKUNDEF
+ resolved; determine how. */
+ if (syms[n].def == LDPK_UNDEF
+ || syms[n].def == LDPK_WEAKUNDEF
|| syms[n].def == LDPK_COMMON)
{
- asection *owner_sec = (blhe->type == bfd_link_hash_common)
- ? blhe->u.c.p->section
- : blhe->u.def.section;
if (owner_sec->owner == link_info.output_bfd)
syms[n].resolution = LDPR_RESOLVED_EXEC;
else if (owner_sec->owner == abfd)
- syms[n].resolution = (ironly)
- ? LDPR_PREVAILING_DEF_IRONLY
- : LDPR_PREVAILING_DEF;
+ syms[n].resolution = (ironly
+ ? LDPR_PREVAILING_DEF_IRONLY
+ : LDPR_PREVAILING_DEF);
else if (is_ir_dummy_bfd (owner_sec->owner))
syms[n].resolution = LDPR_RESOLVED_IR;
- else if (owner_sec->owner->flags & DYNAMIC)
+ else if (owner_sec->owner != NULL
+ && (owner_sec->owner->flags & DYNAMIC) != 0)
syms[n].resolution = LDPR_RESOLVED_DYN;
else
syms[n].resolution = LDPR_RESOLVED_EXEC;
}
/* Was originally def, or weakdef. Does it prevail? If the
- owner is the original dummy bfd that supplied it, then this
+ owner is the original dummy bfd that supplied it, then this
is the definition that has prevailed. */
- if (blhe->u.def.section->owner == link_info.output_bfd)
+ if (owner_sec->owner == link_info.output_bfd)
syms[n].resolution = LDPR_PREEMPTED_REG;
- else if (blhe->u.def.section->owner == abfd)
+ else if (owner_sec->owner == abfd)
{
- syms[n].resolution = (ironly)
+ syms[n].resolution = (ironly
? LDPR_PREVAILING_DEF_IRONLY
- : LDPR_PREVAILING_DEF;
+ : LDPR_PREVAILING_DEF);
continue;
}
/* Was originally def, weakdef, or common, but has been pre-empted. */
- syms[n].resolution = is_ir_dummy_bfd (blhe->u.def.section->owner)
- ? LDPR_PREEMPTED_IR
- : LDPR_PREEMPTED_REG;
+ syms[n].resolution = is_ir_dummy_bfd (owner_sec->owner)
+ ? LDPR_PREEMPTED_IR
+ : LDPR_PREEMPTED_REG;
}
return LDPS_OK;
}
{
ASSERT (called_plugin);
if (!lang_add_input_file (xstrdup (pathname), lang_input_file_is_file_enum,
- NULL))
+ NULL))
return LDPS_ERR;
return LDPS_OK;
}
{
ASSERT (called_plugin);
if (!lang_add_input_file (xstrdup (pathname), lang_input_file_is_l_enum,
- NULL))
+ NULL))
return LDPS_ERR;
return LDPS_OK;
}
{
case LDPL_INFO:
vfinfo (stdout, format, args, FALSE);
+ putchar ('\n');
break;
case LDPL_WARNING:
vfinfo (stdout, format, args, TRUE);
+ putchar ('\n');
break;
case LDPL_FATAL:
case LDPL_ERROR:
default:
- {
- char *newfmt = ACONCAT ((level == LDPL_FATAL ? "%F" : "%X",
- format, NULL));
- vfinfo (stderr, newfmt, args, TRUE);
- }
+ {
+ char *newfmt = ACONCAT ((level == LDPL_FATAL
+ ? "%P%F: " : "%P%X: ",
+ format, "\n", NULL));
+ fflush (stdout);
+ vfinfo (stderr, newfmt, args, TRUE);
+ fflush (stderr);
+ }
break;
}
#define TVU(x) tv[i].tv_u.tv_ ## x
switch (tv[i].tv_tag)
{
- case LDPT_MESSAGE:
- TVU(message) = message;
- break;
- case LDPT_API_VERSION:
- TVU(val) = LD_PLUGIN_API_VERSION;
- break;
- case LDPT_GNU_LD_VERSION:
- TVU(val) = major * 100 + minor;
- break;
- case LDPT_LINKER_OUTPUT:
- TVU(val) = link_info.relocatable ? LDPO_REL
- : (link_info.shared ? LDPO_DYN : LDPO_EXEC);
- break;
- case LDPT_OUTPUT_NAME:
- TVU(string) = output_filename;
- break;
- case LDPT_REGISTER_CLAIM_FILE_HOOK:
- TVU(register_claim_file) = register_claim_file;
- break;
- case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
- TVU(register_all_symbols_read) = register_all_symbols_read;
- break;
- case LDPT_REGISTER_CLEANUP_HOOK:
- TVU(register_cleanup) = register_cleanup;
- break;
- case LDPT_ADD_SYMBOLS:
- TVU(add_symbols) = add_symbols;
- break;
- case LDPT_GET_INPUT_FILE:
- TVU(get_input_file) = get_input_file;
- break;
- case LDPT_RELEASE_INPUT_FILE:
- TVU(release_input_file) = release_input_file;
- break;
- case LDPT_GET_SYMBOLS:
- TVU(get_symbols) = get_symbols;
- break;
- case LDPT_ADD_INPUT_FILE:
- TVU(add_input_file) = add_input_file;
- break;
- case LDPT_ADD_INPUT_LIBRARY:
- TVU(add_input_library) = add_input_library;
- break;
- case LDPT_SET_EXTRA_LIBRARY_PATH:
- TVU(set_extra_library_path) = set_extra_library_path;
- break;
- default:
- /* Added a new entry to the array without adding
- a new case to set up its value is a bug. */
- FAIL ();
+ case LDPT_MESSAGE:
+ TVU(message) = message;
+ break;
+ case LDPT_API_VERSION:
+ TVU(val) = LD_PLUGIN_API_VERSION;
+ break;
+ case LDPT_GNU_LD_VERSION:
+ TVU(val) = major * 100 + minor;
+ break;
+ case LDPT_LINKER_OUTPUT:
+ TVU(val) = (link_info.relocatable
+ ? LDPO_REL
+ : (link_info.shared ? LDPO_DYN : LDPO_EXEC));
+ break;
+ case LDPT_OUTPUT_NAME:
+ TVU(string) = output_filename;
+ break;
+ case LDPT_REGISTER_CLAIM_FILE_HOOK:
+ TVU(register_claim_file) = register_claim_file;
+ break;
+ case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
+ TVU(register_all_symbols_read) = register_all_symbols_read;
+ break;
+ case LDPT_REGISTER_CLEANUP_HOOK:
+ TVU(register_cleanup) = register_cleanup;
+ break;
+ case LDPT_ADD_SYMBOLS:
+ TVU(add_symbols) = add_symbols;
+ break;
+ case LDPT_GET_INPUT_FILE:
+ TVU(get_input_file) = get_input_file;
+ break;
+ case LDPT_RELEASE_INPUT_FILE:
+ TVU(release_input_file) = release_input_file;
+ break;
+ case LDPT_GET_SYMBOLS:
+ TVU(get_symbols) = get_symbols;
+ break;
+ case LDPT_ADD_INPUT_FILE:
+ TVU(add_input_file) = add_input_file;
+ break;
+ case LDPT_ADD_INPUT_LIBRARY:
+ TVU(add_input_library) = add_input_library;
+ break;
+ case LDPT_SET_EXTRA_LIBRARY_PATH:
+ TVU(set_extra_library_path) = set_extra_library_path;
+ break;
+ default:
+ /* Added a new entry to the array without adding
+ a new case to set up its value is a bug. */
+ FAIL ();
}
#undef TVU
}
if (!curplug)
return 0;
+ xatexit (plugin_call_cleanup);
+
/* First pass over plugins to find max # args needed so that we
can size and allocate the tv array. */
while (curplug)
if (!onloadfn)
onloadfn = dlsym (curplug->dlhandle, "_onload");
if (!onloadfn)
- return set_plugin_error (curplug->name);
+ return set_plugin_error (curplug->name);
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);
+ return set_plugin_error (curplug->name);
curplug = curplug->next;
}
return plugin_error_p () ? -1 : 0;
}
-/* Call 'cleanup' hook for all plugins. */
-int
+/* Call 'cleanup' hook for all plugins at exit. */
+static void
plugin_call_cleanup (void)
{
plugin_t *curplug = plugins_list;
}
curplug = curplug->next;
}
- return plugin_error_p () ? -1 : 0;
+ if (plugin_error_p ())
+ info_msg (_("%P: %s: error in plugin cleanup (ignored)\n"),
+ plugin_error_plugin ());
}
/* Lazily init the non_ironly hash table. */
if (non_ironly_hash == NULL)
{
non_ironly_hash =
- (struct bfd_hash_table *) xmalloc (sizeof (struct bfd_hash_table));
+ (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),
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)
+ 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);
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. */
+ 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"),
- abfd->filename, name);
+ einfo (_("%P%X: %s: hash table failure adding symbol %s\n"),
+ abfd->filename, name);
}
else if (!is_ref && is_dummy)
{
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)
+ 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);
+ 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'"), nbfd->filename,
- name);
+ 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"), name, blhe->type);
+ 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;