/* ELF linking support for BFD.
Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- 2005, 2006, 2007 Free Software Foundation, Inc.
+ 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
This file is part of BFD, the Binary File Descriptor library.
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 2 of the License, or
+ 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,
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. */
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
#include "sysdep.h"
#include "bfd.h"
}
s = bfd_make_section_with_flags (abfd,
- (bed->default_use_rela_p
+ (bed->rela_plts_and_copies_p
? ".rela.plt" : ".rel.plt"),
flags | SEC_READONLY);
if (s == NULL
if (! info->shared)
{
s = bfd_make_section_with_flags (abfd,
- (bed->default_use_rela_p
+ (bed->rela_plts_and_copies_p
? ".rela.bss" : ".rel.bss"),
flags | SEC_READONLY);
if (s == NULL
&& (h->type == STT_OBJECT
|| (sym != NULL
&& ELF_ST_TYPE (sym->st_info) == STT_OBJECT)))
- || (d != NULL
+ || (d != NULL
&& h->root.type == bfd_link_hash_new
&& (*d->match) (&d->head, NULL, h->root.root.string)))
h->dynamic = 1;
bfd_boolean provide,
bfd_boolean hidden)
{
- struct elf_link_hash_entry *h;
+ struct elf_link_hash_entry *h, *hv;
struct elf_link_hash_table *htab;
+ const struct elf_backend_data *bed;
if (!is_elf_hash_table (info->hash))
return TRUE;
if (h == NULL)
return provide;
- /* Since we're defining the symbol, don't let it seem to have not
- been defined. record_dynamic_symbol and size_dynamic_sections
- may depend on this. */
- if (h->root.type == bfd_link_hash_undefweak
- || h->root.type == bfd_link_hash_undefined)
+ switch (h->root.type)
{
+ case bfd_link_hash_defined:
+ case bfd_link_hash_defweak:
+ case bfd_link_hash_common:
+ break;
+ case bfd_link_hash_undefweak:
+ case bfd_link_hash_undefined:
+ /* Since we're defining the symbol, don't let it seem to have not
+ been defined. record_dynamic_symbol and size_dynamic_sections
+ may depend on this. */
h->root.type = bfd_link_hash_new;
if (h->root.u.undef.next != NULL || htab->root.undefs_tail == &h->root)
bfd_link_repair_undef_list (&htab->root);
- }
-
- if (h->root.type == bfd_link_hash_new)
- {
+ break;
+ case bfd_link_hash_new:
bfd_elf_link_mark_dynamic_symbol (info, h, NULL);
h->non_elf = 0;
+ break;
+ case bfd_link_hash_indirect:
+ /* We had a versioned symbol in a dynamic library. We make the
+ the versioned symbol point to this one. */
+ bed = get_elf_backend_data (output_bfd);
+ hv = h;
+ while (hv->root.type == bfd_link_hash_indirect
+ || hv->root.type == bfd_link_hash_warning)
+ hv = (struct elf_link_hash_entry *) hv->root.u.i.link;
+ /* We don't need to update h->root.u since linker will set them
+ later. */
+ h->root.type = bfd_link_hash_undefined;
+ hv->root.type = bfd_link_hash_indirect;
+ hv->root.u.i.link = (struct bfd_link_hash_entry *) h;
+ (*bed->elf_backend_copy_indirect_symbol) (info, h, hv);
+ break;
+ case bfd_link_hash_warning:
+ abort ();
+ break;
}
/* If this symbol is being provided by the linker script, and it is
}
if (entry->isym.st_shndx != SHN_UNDEF
- && (entry->isym.st_shndx < SHN_LORESERVE
- || entry->isym.st_shndx > SHN_HIRESERVE))
+ && entry->isym.st_shndx < SHN_LORESERVE)
{
asection *s;
return FALSE;
*sym_hash = h;
+ bed = get_elf_backend_data (abfd);
+
/* This code is for coping with dynamic objects, and is only useful
if we are doing an ELF link. */
- if (info->hash->creator != abfd->xvec)
+ if (!(*bed->relocs_compatible) (abfd->xvec, info->output_bfd->xvec))
return TRUE;
/* For merging, we only care about real symbols. */
&& h->root.type != bfd_link_hash_undefweak
&& h->root.type != bfd_link_hash_common);
- bed = get_elf_backend_data (abfd);
/* When we try to create a default indirect symbol from the dynamic
definition with the default version, we skip it if its type and
the type of existing regular definition mismatch. We only do it
case, we make the versioned symbol point to the normal one. */
const struct elf_backend_data *bed = get_elf_backend_data (abfd);
flip->root.type = h->root.type;
+ flip->root.u.undef.abfd = h->root.u.undef.abfd;
h->root.type = bfd_link_hash_indirect;
h->root.u.i.link = (struct bfd_link_hash_entry *) flip;
(*bed->elf_backend_copy_indirect_symbol) (info, flip, h);
- flip->root.u.undef.abfd = h->root.u.undef.abfd;
if (h->def_dynamic)
{
h->def_dynamic = 0;
amt = sizeof *a;
a = bfd_zalloc (rinfo->output_bfd, amt);
+ if (a == NULL)
+ {
+ rinfo->failed = TRUE;
+ return FALSE;
+ }
/* Note that we are copying a string pointer here, and testing it
above. If bfd_elf_string_from_elf_section is ever changed to
len = p - h->root.root.string;
alc = bfd_malloc (len);
if (alc == NULL)
- return FALSE;
+ {
+ sinfo->failed = TRUE;
+ return FALSE;
+ }
memcpy (alc, h->root.root.string, len - 1);
alc[len - 1] = '\0';
if (alc[len - 2] == ELF_VER_CHR)
size = o->reloc_count;
size *= bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rela);
if (keep_memory)
- internal_relocs = bfd_alloc (abfd, size);
+ internal_relocs = alloc2 = bfd_alloc (abfd, size);
else
internal_relocs = alloc2 = bfd_malloc (size);
if (internal_relocs == NULL)
if (alloc1 != NULL)
free (alloc1);
if (alloc2 != NULL)
- free (alloc2);
+ {
+ if (keep_memory)
+ bfd_release (abfd, alloc2);
+ else
+ free (alloc2);
+ }
return NULL;
}
(*_bfd_error_handler)
(_("%B: relocation size mismatch in %B section %A"),
output_bfd, input_section->owner, input_section);
- bfd_set_error (bfd_error_wrong_object_format);
+ bfd_set_error (bfd_error_wrong_format);
return FALSE;
}
_bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h,
struct elf_info_failed *eif)
{
- const struct elf_backend_data *bed = NULL;
+ const struct elf_backend_data *bed;
/* If this symbol was mentioned in a non-ELF file, try to set
DEF_REGULAR and REF_REGULAR correctly. This is the only way to
}
/* Backend specific symbol fixup. */
- if (elf_hash_table (eif->info)->dynobj)
- {
- bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj);
- if (bed->elf_backend_fixup_symbol
- && !(*bed->elf_backend_fixup_symbol) (eif->info, h))
- return FALSE;
- }
+ bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj);
+ if (bed->elf_backend_fixup_symbol
+ && !(*bed->elf_backend_fixup_symbol) (eif->info, h))
+ return FALSE;
/* If this is a final link, and the symbol was defined as a common
symbol in a regular object file, and there was no definition in
hide it from the dynamic linker. */
if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
&& h->root.type == bfd_link_hash_undefweak)
- {
- const struct elf_backend_data *bed;
- bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj);
- (*bed->elf_backend_hide_symbol) (eif->info, h, TRUE);
- }
+ (*bed->elf_backend_hide_symbol) (eif->info, h, TRUE);
/* If this is a weak defined symbol in a dynamic object, and we know
the real definition in the dynamic object, copy interesting flags
BFD_ASSERT (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak);
- BFD_ASSERT (weakdef->root.type == bfd_link_hash_defined
- || weakdef->root.type == bfd_link_hash_defweak);
BFD_ASSERT (weakdef->def_dynamic);
/* If the real definition is defined by a regular object file,
if (weakdef->def_regular)
h->u.weakdef = NULL;
else
- (*bed->elf_backend_copy_indirect_symbol) (eif->info, weakdef,
- h);
+ {
+ BFD_ASSERT (weakdef->root.type == bfd_link_hash_defined
+ || weakdef->root.type == bfd_link_hash_defweak);
+ (*bed->elf_backend_copy_indirect_symbol) (eif->info, weakdef, h);
+ }
}
return TRUE;
if (h == NULL)
return TRUE;
+ /* STV_HIDDEN or STV_INTERNAL ones must be local. */
+ if (ELF_ST_VISIBILITY (h->other) == STV_HIDDEN
+ || ELF_ST_VISIBILITY (h->other) == STV_INTERNAL)
+ return TRUE;
+
/* Common symbols that become definitions don't get the DEF_REGULAR
flag set, so test it first, and don't bail out. */
if (ELF_COMMON_DEF_P (h))
if (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
return FALSE;
- /* However, STV_HIDDEN or STV_INTERNAL ones must be local. */
- if (ELF_ST_VISIBILITY (h->other) != STV_PROTECTED)
- return TRUE;
-
hash_table = elf_hash_table (info);
if (!is_elf_hash_table (hash_table))
return TRUE;
return TRUE;
}
\f
+/* Return TRUE iff relocations for INPUT are compatible with OUTPUT.
+ The default is to only match when the INPUT and OUTPUT are exactly
+ the same target. */
+
+bfd_boolean
+_bfd_elf_default_relocs_compatible (const bfd_target *input,
+ const bfd_target *output)
+{
+ return input == output;
+}
+
+/* Return TRUE iff relocations for INPUT are compatible with OUTPUT.
+ This version is used when different targets for the same architecture
+ are virtually identical. */
+
+bfd_boolean
+_bfd_elf_relocs_compatible (const bfd_target *input,
+ const bfd_target *output)
+{
+ const struct elf_backend_data *obed, *ibed;
+
+ if (input == output)
+ return TRUE;
+
+ ibed = xvec_get_elf_backend_data (input);
+ obed = xvec_get_elf_backend_data (output);
+
+ if (ibed->arch != obed->arch)
+ return FALSE;
+
+ /* If both backends are using this function, deem them compatible. */
+ return ibed->relocs_compatible == obed->relocs_compatible;
+}
+
/* Add symbols from an ELF object file to the linker hash table. */
static bfd_boolean
the format of the output file. */
if (info->relocatable
|| !is_elf_hash_table (htab)
- || htab->root.creator != abfd->xvec)
+ || info->output_bfd->xvec != abfd->xvec)
{
if (info->relocatable)
bfd_set_error (bfd_error_invalid_operation);
format as the output, we can't make a shared library. */
if (info->shared
&& is_elf_hash_table (htab)
- && htab->root.creator == abfd->xvec
+ && info->output_bfd->xvec == abfd->xvec
&& !htab->dynamic_sections_created)
{
if (! _bfd_elf_link_create_dynamic_sections (abfd, info))
{
bfd_byte *dynbuf;
bfd_byte *extdyn;
- int elfsec;
+ unsigned int elfsec;
unsigned long shlink;
if (!bfd_malloc_and_get_section (abfd, s, &dynbuf))
goto error_free_dyn;
elfsec = _bfd_elf_section_from_bfd_section (abfd, s);
- if (elfsec == -1)
+ if (elfsec == SHN_BAD)
goto error_free_dyn;
shlink = elf_elfsections (abfd)[elfsec]->sh_link;
tell it that we are about to handle an as-needed lib. */
if (!(*info->callbacks->notice) (info, NULL, abfd, NULL,
notice_as_needed))
- return FALSE;
-
+ goto error_free_vers;
/* Clone the symbol table and sym hashes. Remember some
pointers into the symbol table, and dynamic symbol count. */
if (isym->st_shndx == SHN_UNDEF)
sec = bfd_und_section_ptr;
- else if (isym->st_shndx < SHN_LORESERVE
- || isym->st_shndx > SHN_HIRESERVE)
+ else if (isym->st_shndx == SHN_ABS)
+ sec = bfd_abs_section_ptr;
+ else if (isym->st_shndx == SHN_COMMON)
+ {
+ sec = bfd_com_section_ptr;
+ /* What ELF calls the size we call the value. What ELF
+ calls the value we call the alignment. */
+ value = isym->st_size;
+ }
+ else
{
sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
if (sec == NULL)
else if ((abfd->flags & (EXEC_P | DYNAMIC)) != 0)
value -= sec->vma;
}
- else if (isym->st_shndx == SHN_ABS)
- sec = bfd_abs_section_ptr;
- else if (isym->st_shndx == SHN_COMMON)
- {
- sec = bfd_com_section_ptr;
- /* What ELF calls the size we call the value. What ELF
- calls the value we call the alignment. */
- value = isym->st_size;
- }
- else
- {
- /* Leave it up to the processor backend. */
- }
name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
isym->st_name);
dynsym = TRUE;
}
- if (definition && (sec->flags & SEC_DEBUGGING))
+ if (definition && (sec->flags & SEC_DEBUGGING) && !info->relocatable)
{
/* We don't want to make debug symbol dynamic. */
(*bed->elf_backend_hide_symbol) (info, h, TRUE);
amt = ((isymend - isym + 1)
* sizeof (struct elf_link_hash_entry *));
nondeflt_vers = bfd_malloc (amt);
+ if (!nondeflt_vers)
+ goto error_free_vers;
}
nondeflt_vers[nondeflt_vers_cnt++] = h;
}
tell it that symbols added for crefs may need to be removed. */
if (!(*info->callbacks->notice) (info, NULL, abfd, NULL,
notice_not_needed))
- return FALSE;
+ goto error_free_vers;
free (old_tab);
objalloc_free_block ((struct objalloc *) htab->root.table.memory,
{
if (!(*info->callbacks->notice) (info, NULL, abfd, NULL,
notice_needed))
- return FALSE;
+ goto error_free_vers;
free (old_tab);
old_tab = NULL;
}
amt = p - h->root.root.string;
shortname = bfd_malloc (amt + 1);
+ if (!shortname)
+ goto error_free_vers;
memcpy (shortname, h->root.root.string, amt);
shortname[amt] = '\0';
if (hlook->dynindx != -1 && h->dynindx == -1)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
- goto error_return;
+ {
+ err_free_sym_hash:
+ free (sorted_sym_hash);
+ goto error_return;
+ }
}
/* If the real definition is in the list of dynamic
if (h->dynindx != -1 && hlook->dynindx == -1)
{
if (! bfd_elf_link_record_dynamic_symbol (info, hlook))
- goto error_return;
+ goto err_free_sym_hash;
}
break;
}
different format. It probably can't be done. */
if (! dynamic
&& is_elf_hash_table (htab)
- && htab->root.creator == abfd->xvec
- && bed->check_relocs != NULL)
+ && bed->check_relocs != NULL
+ && (*bed->relocs_compatible) (abfd->xvec, info->output_bfd->xvec))
{
asection *o;
}
}
\f
+struct hash_codes_info
+{
+ unsigned long *hashcodes;
+ bfd_boolean error;
+};
+
/* This function will be called though elf_link_hash_traverse to store
all hash value of the exported symbols in an array. */
static bfd_boolean
elf_collect_hash_codes (struct elf_link_hash_entry *h, void *data)
{
- unsigned long **valuep = data;
+ struct hash_codes_info *inf = data;
const char *name;
char *p;
unsigned long ha;
if (p != NULL)
{
alc = bfd_malloc (p - name + 1);
+ if (alc == NULL)
+ {
+ inf->error = TRUE;
+ return FALSE;
+ }
memcpy (alc, name, p - name);
alc[p - name] = '\0';
name = alc;
ha = bfd_elf_hash (name);
/* Store the found hash value in the array given as the argument. */
- *(*valuep)++ = ha;
+ *(inf->hashcodes)++ = ha;
/* And store it in the struct so that we can put it in the hash table
later. */
long int local_indx;
long int shift1, shift2;
unsigned long int mask;
+ bfd_boolean error;
};
/* This function will be called though elf_link_hash_traverse to store
if (p != NULL)
{
alc = bfd_malloc (p - name + 1);
+ if (alc == NULL)
+ {
+ s->error = TRUE;
+ return FALSE;
+ }
memcpy (alc, name, p - name);
alc[p - name] = '\0';
name = alc;
Therefore the result is always a good payoff between few collisions
(= short chain lengths) and table size. */
static size_t
-compute_bucket_count (struct bfd_link_info *info, unsigned long int *hashcodes,
- unsigned long int nsyms, int gnu_hash)
+compute_bucket_count (struct bfd_link_info *info,
+ unsigned long int *hashcodes ATTRIBUTE_UNUSED,
+ unsigned long int nsyms,
+ int gnu_hash)
{
- size_t dynsymcount = elf_hash_table (info)->dynsymcount;
size_t best_size = 0;
unsigned long int i;
- bfd_size_type amt;
/* We have a problem here. The following code to optimize the table
size requires an integer type with more the 32 bits. If
size_t maxsize;
BFD_HOST_U_64_BIT best_chlen = ~((BFD_HOST_U_64_BIT) 0);
bfd *dynobj = elf_hash_table (info)->dynobj;
+ size_t dynsymcount = elf_hash_table (info)->dynsymcount;
const struct elf_backend_data *bed = get_elf_backend_data (dynobj);
unsigned long int *counts;
+ bfd_size_type amt;
/* Possible optimization parameters: if we have NSYMS symbols we say
that the hashing table must at least have NSYMS/4 and at most
return TRUE;
bed = get_elf_backend_data (output_bfd);
- elf_tdata (output_bfd)->relro = info->relro;
if (info->execstack)
elf_tdata (output_bfd)->stack_flags = PF_R | PF_W | PF_X;
else if (info->noexecstack)
{
asection *s;
- if (inputobj->flags & (DYNAMIC | BFD_LINKER_CREATED))
+ if (inputobj->flags & (DYNAMIC | EXEC_P | BFD_LINKER_CREATED))
continue;
s = bfd_get_section_by_name (inputobj, ".note.GNU-stack");
if (s)
elf_link_hash_traverse (elf_hash_table (info),
_bfd_elf_link_find_version_dependencies,
&sinfo);
+ if (sinfo.failed)
+ return FALSE;
if (elf_tdata (output_bfd)->verref == NULL)
s->flags |= SEC_EXCLUDE;
{
asection *s;
+ /* Data first, since setting text_index_section changes
+ _bfd_elf_link_omit_section_dynsym. */
for (s = output_bfd->sections; s != NULL; s = s->next)
- if (((s->flags & (SEC_EXCLUDE | SEC_ALLOC | SEC_READONLY))
- == (SEC_ALLOC | SEC_READONLY))
+ if (((s->flags & (SEC_EXCLUDE | SEC_ALLOC | SEC_READONLY)) == SEC_ALLOC)
&& !_bfd_elf_link_omit_section_dynsym (output_bfd, info, s))
{
- elf_hash_table (info)->text_index_section = s;
+ elf_hash_table (info)->data_index_section = s;
break;
}
for (s = output_bfd->sections; s != NULL; s = s->next)
- if (((s->flags & (SEC_EXCLUDE | SEC_ALLOC | SEC_READONLY)) == SEC_ALLOC)
+ if (((s->flags & (SEC_EXCLUDE | SEC_ALLOC | SEC_READONLY))
+ == (SEC_ALLOC | SEC_READONLY))
&& !_bfd_elf_link_omit_section_dynsym (output_bfd, info, s))
{
- elf_hash_table (info)->data_index_section = s;
+ elf_hash_table (info)->text_index_section = s;
break;
}
if (info->emit_hash)
{
unsigned long int *hashcodes;
- unsigned long int *hashcodesp;
+ struct hash_codes_info hashinf;
bfd_size_type amt;
unsigned long int nsyms;
size_t bucketcount;
hashcodes = bfd_malloc (amt);
if (hashcodes == NULL)
return FALSE;
- hashcodesp = hashcodes;
+ hashinf.hashcodes = hashcodes;
+ hashinf.error = FALSE;
/* Put all hash values in HASHCODES. */
elf_link_hash_traverse (elf_hash_table (info),
- elf_collect_hash_codes, &hashcodesp);
+ elf_collect_hash_codes, &hashinf);
+ if (hashinf.error)
+ {
+ free (hashcodes);
+ return FALSE;
+ }
- nsyms = hashcodesp - hashcodes;
+ nsyms = hashinf.hashcodes - hashcodes;
bucketcount
= compute_bucket_count (info, hashcodes, nsyms, 0);
free (hashcodes);
/* Put all hash values in HASHCODES. */
elf_link_hash_traverse (elf_hash_table (info),
elf_collect_gnu_hash_codes, &cinfo);
+ if (cinfo.error)
+ {
+ free (cinfo.hashcodes);
+ return FALSE;
+ }
bucketcount
= compute_bucket_count (info, cinfo.hashcodes, cinfo.nsyms, 1);
return TRUE;
}
+\f
+/* Indicate that we are only retrieving symbol values from this
+ section. */
-/* Final phase of ELF linker. */
+void
+_bfd_elf_link_just_syms (asection *sec, struct bfd_link_info *info)
+{
+ if (is_elf_hash_table (info->hash))
+ sec->sec_info_type = ELF_INFO_TYPE_JUST_SYMS;
+ _bfd_generic_link_just_syms (sec, info);
+}
-/* A structure we use to avoid passing large numbers of arguments. */
+/* Make sure sec_info_type is cleared if sec_info is cleared too. */
-struct elf_final_link_info
+static void
+merge_sections_remove_hook (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec)
{
- /* General link information. */
- struct bfd_link_info *info;
- /* Output BFD. */
- bfd *output_bfd;
- /* Symbol string table. */
- struct bfd_strtab_hash *symstrtab;
- /* .dynsym section. */
- asection *dynsym_sec;
- /* .hash section. */
- asection *hash_sec;
- /* symbol version section (.gnu.version). */
- asection *symver_sec;
- /* Buffer large enough to hold contents of any section. */
- bfd_byte *contents;
- /* Buffer large enough to hold external relocs of any section. */
- void *external_relocs;
- /* Buffer large enough to hold internal relocs of any section. */
- Elf_Internal_Rela *internal_relocs;
- /* Buffer large enough to hold external local symbols of any input
- BFD. */
- bfd_byte *external_syms;
- /* And a buffer for symbol section indices. */
- Elf_External_Sym_Shndx *locsym_shndx;
- /* Buffer large enough to hold internal local symbols of any input
- BFD. */
- Elf_Internal_Sym *internal_syms;
- /* Array large enough to hold a symbol index for each local symbol
- of any input BFD. */
- long *indices;
- /* Array large enough to hold a section pointer for each local
- symbol of any input BFD. */
- asection **sections;
- /* Buffer to hold swapped out symbols. */
- bfd_byte *symbuf;
- /* And one for symbol section indices. */
- Elf_External_Sym_Shndx *symshndxbuf;
- /* Number of swapped out symbols in buffer. */
- size_t symbuf_count;
- /* Number of symbols which fit in symbuf. */
- size_t symbuf_size;
- /* And same for symshndxbuf. */
- size_t shndxbuf_size;
-};
+ BFD_ASSERT (sec->sec_info_type == ELF_INFO_TYPE_MERGE);
+ sec->sec_info_type = ELF_INFO_TYPE_NONE;
+}
-/* This struct is used to pass information to elf_link_output_extsym. */
+/* Finish SHF_MERGE section merging. */
-struct elf_outext_info
+bfd_boolean
+_bfd_elf_merge_sections (bfd *abfd, struct bfd_link_info *info)
{
- bfd_boolean failed;
- bfd_boolean localsyms;
- struct elf_final_link_info *finfo;
-};
+ bfd *ibfd;
+ asection *sec;
+ if (!is_elf_hash_table (info->hash))
+ return FALSE;
-/* Support for evaluating a complex relocation.
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ if ((ibfd->flags & DYNAMIC) == 0)
+ for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+ if ((sec->flags & SEC_MERGE) != 0
+ && !bfd_is_abs_section (sec->output_section))
+ {
+ struct bfd_elf_section_data *secdata;
- Complex relocations are generalized, self-describing relocations. The
- implementation of them consists of two parts: complex symbols, and the
- relocations themselves.
+ secdata = elf_section_data (sec);
+ if (! _bfd_add_merge_section (abfd,
+ &elf_hash_table (info)->merge_info,
+ sec, &secdata->sec_info))
+ return FALSE;
+ else if (secdata->sec_info)
+ sec->sec_info_type = ELF_INFO_TYPE_MERGE;
+ }
- The relocations are use a reserved elf-wide relocation type code (R_RELC
- external / BFD_RELOC_RELC internal) and an encoding of relocation field
- information (start bit, end bit, word width, etc) into the addend. This
- information is extracted from CGEN-generated operand tables within gas.
+ if (elf_hash_table (info)->merge_info != NULL)
+ _bfd_merge_sections (abfd, info, elf_hash_table (info)->merge_info,
+ merge_sections_remove_hook);
+ return TRUE;
+}
- Complex symbols are mangled symbols (BSF_RELC external / STT_RELC
- internal) representing prefix-notation expressions, including but not
- limited to those sorts of expressions normally encoded as addends in the
- addend field. The symbol mangling format is:
+/* Create an entry in an ELF linker hash table. */
- <node> := <literal>
- | <unary-operator> ':' <node>
- | <binary-operator> ':' <node> ':' <node>
- ;
+struct bfd_hash_entry *
+_bfd_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
+ struct bfd_hash_table *table,
+ const char *string)
+{
+ /* Allocate the structure if it has not already been allocated by a
+ subclass. */
+ if (entry == NULL)
+ {
+ entry = bfd_hash_allocate (table, sizeof (struct elf_link_hash_entry));
+ if (entry == NULL)
+ return entry;
+ }
- <literal> := 's' <digits=N> ':' <N character symbol name>
- | 'S' <digits=N> ':' <N character section name>
- | '#' <hexdigits>
- ;
+ /* Call the allocation method of the superclass. */
+ entry = _bfd_link_hash_newfunc (entry, table, string);
+ if (entry != NULL)
+ {
+ struct elf_link_hash_entry *ret = (struct elf_link_hash_entry *) entry;
+ struct elf_link_hash_table *htab = (struct elf_link_hash_table *) table;
- <binary-operator> := as in C
- <unary-operator> := as in C, plus "0-" for unambiguous negation. */
+ /* Set local fields. */
+ ret->indx = -1;
+ ret->dynindx = -1;
+ ret->got = htab->init_got_refcount;
+ ret->plt = htab->init_plt_refcount;
+ memset (&ret->size, 0, (sizeof (struct elf_link_hash_entry)
+ - offsetof (struct elf_link_hash_entry, size)));
+ /* Assume that we have been called by a non-ELF symbol reader.
+ This flag is then reset by the code which reads an ELF input
+ file. This ensures that a symbol created by a non-ELF symbol
+ reader will have the flag set correctly. */
+ ret->non_elf = 1;
+ }
-static void
-set_symbol_value (bfd * bfd_with_globals,
- struct elf_final_link_info * finfo,
- int symidx,
- bfd_vma val)
+ return entry;
+}
+
+/* Copy data from an indirect symbol to its direct symbol, hiding the
+ old indirect symbol. Also used for copying flags to a weakdef. */
+
+void
+_bfd_elf_link_hash_copy_indirect (struct bfd_link_info *info,
+ struct elf_link_hash_entry *dir,
+ struct elf_link_hash_entry *ind)
{
- bfd_boolean is_local;
- Elf_Internal_Sym * sym;
- struct elf_link_hash_entry ** sym_hashes;
- struct elf_link_hash_entry * h;
+ struct elf_link_hash_table *htab;
- sym_hashes = elf_sym_hashes (bfd_with_globals);
- sym = finfo->internal_syms + symidx;
- is_local = ELF_ST_BIND(sym->st_info) == STB_LOCAL;
-
- if (is_local)
+ /* Copy down any references that we may have already seen to the
+ symbol which just became indirect. */
+
+ dir->ref_dynamic |= ind->ref_dynamic;
+ dir->ref_regular |= ind->ref_regular;
+ dir->ref_regular_nonweak |= ind->ref_regular_nonweak;
+ dir->non_got_ref |= ind->non_got_ref;
+ dir->needs_plt |= ind->needs_plt;
+ dir->pointer_equality_needed |= ind->pointer_equality_needed;
+
+ if (ind->root.type != bfd_link_hash_indirect)
+ return;
+
+ /* Copy over the global and procedure linkage table refcount entries.
+ These may have been already set up by a check_relocs routine. */
+ htab = elf_hash_table (info);
+ if (ind->got.refcount > htab->init_got_refcount.refcount)
{
- /* It is a local symbol: move it to the
- "absolute" section and give it a value. */
- sym->st_shndx = SHN_ABS;
- sym->st_value = val;
+ if (dir->got.refcount < 0)
+ dir->got.refcount = 0;
+ dir->got.refcount += ind->got.refcount;
+ ind->got.refcount = htab->init_got_refcount.refcount;
}
- else
+
+ if (ind->plt.refcount > htab->init_plt_refcount.refcount)
{
- /* It is a global symbol: set its link type
- to "defined" and give it a value. */
- h = sym_hashes [symidx];
- while (h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
- h->root.type = bfd_link_hash_defined;
- h->root.u.def.value = val;
- h->root.u.def.section = bfd_abs_section_ptr;
- }
-}
-
-static bfd_boolean
-resolve_symbol (const char * name,
- bfd * input_bfd,
- struct elf_final_link_info * finfo,
- bfd_vma * result,
- size_t locsymcount)
-{
- Elf_Internal_Sym * sym;
- struct bfd_link_hash_entry * global_entry;
- const char * candidate = NULL;
- Elf_Internal_Shdr * symtab_hdr;
- asection * sec = NULL;
- size_t i;
-
- symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+ if (dir->plt.refcount < 0)
+ dir->plt.refcount = 0;
+ dir->plt.refcount += ind->plt.refcount;
+ ind->plt.refcount = htab->init_plt_refcount.refcount;
+ }
- for (i = 0; i < locsymcount; ++ i)
+ if (ind->dynindx != -1)
{
- sym = finfo->internal_syms + i;
- sec = finfo->sections [i];
+ if (dir->dynindx != -1)
+ _bfd_elf_strtab_delref (htab->dynstr, dir->dynstr_index);
+ dir->dynindx = ind->dynindx;
+ dir->dynstr_index = ind->dynstr_index;
+ ind->dynindx = -1;
+ ind->dynstr_index = 0;
+ }
+}
- if (ELF_ST_BIND (sym->st_info) != STB_LOCAL)
- continue;
+void
+_bfd_elf_link_hash_hide_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *h,
+ bfd_boolean force_local)
+{
+ h->plt = elf_hash_table (info)->init_plt_offset;
+ h->needs_plt = 0;
+ if (force_local)
+ {
+ h->forced_local = 1;
+ if (h->dynindx != -1)
+ {
+ h->dynindx = -1;
+ _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
+ h->dynstr_index);
+ }
+ }
+}
+
+/* Initialize an ELF linker hash table. */
+
+bfd_boolean
+_bfd_elf_link_hash_table_init
+ (struct elf_link_hash_table *table,
+ bfd *abfd,
+ struct bfd_hash_entry *(*newfunc) (struct bfd_hash_entry *,
+ struct bfd_hash_table *,
+ const char *),
+ unsigned int entsize)
+{
+ bfd_boolean ret;
+ int can_refcount = get_elf_backend_data (abfd)->can_refcount;
+
+ memset (table, 0, sizeof * table);
+ table->init_got_refcount.refcount = can_refcount - 1;
+ table->init_plt_refcount.refcount = can_refcount - 1;
+ table->init_got_offset.offset = -(bfd_vma) 1;
+ table->init_plt_offset.offset = -(bfd_vma) 1;
+ /* The first dynamic symbol is a dummy. */
+ table->dynsymcount = 1;
+
+ ret = _bfd_link_hash_table_init (&table->root, abfd, newfunc, entsize);
+ table->root.type = bfd_link_elf_hash_table;
+
+ return ret;
+}
+
+/* Create an ELF linker hash table. */
+
+struct bfd_link_hash_table *
+_bfd_elf_link_hash_table_create (bfd *abfd)
+{
+ struct elf_link_hash_table *ret;
+ bfd_size_type amt = sizeof (struct elf_link_hash_table);
+
+ ret = bfd_malloc (amt);
+ if (ret == NULL)
+ return NULL;
+
+ if (! _bfd_elf_link_hash_table_init (ret, abfd, _bfd_elf_link_hash_newfunc,
+ sizeof (struct elf_link_hash_entry)))
+ {
+ free (ret);
+ return NULL;
+ }
+
+ return &ret->root;
+}
+
+/* This is a hook for the ELF emulation code in the generic linker to
+ tell the backend linker what file name to use for the DT_NEEDED
+ entry for a dynamic object. */
+
+void
+bfd_elf_set_dt_needed_name (bfd *abfd, const char *name)
+{
+ if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+ && bfd_get_format (abfd) == bfd_object)
+ elf_dt_name (abfd) = name;
+}
+
+int
+bfd_elf_get_dyn_lib_class (bfd *abfd)
+{
+ int lib_class;
+ if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+ && bfd_get_format (abfd) == bfd_object)
+ lib_class = elf_dyn_lib_class (abfd);
+ else
+ lib_class = 0;
+ return lib_class;
+}
+
+void
+bfd_elf_set_dyn_lib_class (bfd *abfd, enum dynamic_lib_link_class lib_class)
+{
+ if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+ && bfd_get_format (abfd) == bfd_object)
+ elf_dyn_lib_class (abfd) = lib_class;
+}
+
+/* Get the list of DT_NEEDED entries for a link. This is a hook for
+ the linker ELF emulation code. */
+
+struct bfd_link_needed_list *
+bfd_elf_get_needed_list (bfd *abfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info)
+{
+ if (! is_elf_hash_table (info->hash))
+ return NULL;
+ return elf_hash_table (info)->needed;
+}
+
+/* Get the list of DT_RPATH/DT_RUNPATH entries for a link. This is a
+ hook for the linker ELF emulation code. */
+
+struct bfd_link_needed_list *
+bfd_elf_get_runpath_list (bfd *abfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info)
+{
+ if (! is_elf_hash_table (info->hash))
+ return NULL;
+ return elf_hash_table (info)->runpath;
+}
+
+/* Get the name actually used for a dynamic object for a link. This
+ is the SONAME entry if there is one. Otherwise, it is the string
+ passed to bfd_elf_set_dt_needed_name, or it is the filename. */
+
+const char *
+bfd_elf_get_dt_soname (bfd *abfd)
+{
+ if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+ && bfd_get_format (abfd) == bfd_object)
+ return elf_dt_name (abfd);
+ return NULL;
+}
+
+/* Get the list of DT_NEEDED entries from a BFD. This is a hook for
+ the ELF linker emulation code. */
+
+bfd_boolean
+bfd_elf_get_bfd_needed_list (bfd *abfd,
+ struct bfd_link_needed_list **pneeded)
+{
+ asection *s;
+ bfd_byte *dynbuf = NULL;
+ unsigned int elfsec;
+ unsigned long shlink;
+ bfd_byte *extdyn, *extdynend;
+ size_t extdynsize;
+ void (*swap_dyn_in) (bfd *, const void *, Elf_Internal_Dyn *);
+
+ *pneeded = NULL;
+
+ if (bfd_get_flavour (abfd) != bfd_target_elf_flavour
+ || bfd_get_format (abfd) != bfd_object)
+ return TRUE;
+
+ s = bfd_get_section_by_name (abfd, ".dynamic");
+ if (s == NULL || s->size == 0)
+ return TRUE;
+
+ if (!bfd_malloc_and_get_section (abfd, s, &dynbuf))
+ goto error_return;
+
+ elfsec = _bfd_elf_section_from_bfd_section (abfd, s);
+ if (elfsec == SHN_BAD)
+ goto error_return;
+
+ shlink = elf_elfsections (abfd)[elfsec]->sh_link;
+
+ extdynsize = get_elf_backend_data (abfd)->s->sizeof_dyn;
+ swap_dyn_in = get_elf_backend_data (abfd)->s->swap_dyn_in;
+
+ extdyn = dynbuf;
+ extdynend = extdyn + s->size;
+ for (; extdyn < extdynend; extdyn += extdynsize)
+ {
+ Elf_Internal_Dyn dyn;
+
+ (*swap_dyn_in) (abfd, extdyn, &dyn);
+
+ if (dyn.d_tag == DT_NULL)
+ break;
+
+ if (dyn.d_tag == DT_NEEDED)
+ {
+ const char *string;
+ struct bfd_link_needed_list *l;
+ unsigned int tagv = dyn.d_un.d_val;
+ bfd_size_type amt;
+
+ string = bfd_elf_string_from_elf_section (abfd, shlink, tagv);
+ if (string == NULL)
+ goto error_return;
+
+ amt = sizeof *l;
+ l = bfd_alloc (abfd, amt);
+ if (l == NULL)
+ goto error_return;
+
+ l->by = abfd;
+ l->name = string;
+ l->next = *pneeded;
+ *pneeded = l;
+ }
+ }
+
+ free (dynbuf);
+
+ return TRUE;
+
+ error_return:
+ if (dynbuf != NULL)
+ free (dynbuf);
+ return FALSE;
+}
+
+struct elf_symbuf_symbol
+{
+ unsigned long st_name; /* Symbol name, index in string tbl */
+ unsigned char st_info; /* Type and binding attributes */
+ unsigned char st_other; /* Visibilty, and target specific */
+};
+
+struct elf_symbuf_head
+{
+ struct elf_symbuf_symbol *ssym;
+ bfd_size_type count;
+ unsigned int st_shndx;
+};
+
+struct elf_symbol
+{
+ union
+ {
+ Elf_Internal_Sym *isym;
+ struct elf_symbuf_symbol *ssym;
+ } u;
+ const char *name;
+};
+
+/* Sort references to symbols by ascending section number. */
+
+static int
+elf_sort_elf_symbol (const void *arg1, const void *arg2)
+{
+ const Elf_Internal_Sym *s1 = *(const Elf_Internal_Sym **) arg1;
+ const Elf_Internal_Sym *s2 = *(const Elf_Internal_Sym **) arg2;
+
+ return s1->st_shndx - s2->st_shndx;
+}
+
+static int
+elf_sym_name_compare (const void *arg1, const void *arg2)
+{
+ const struct elf_symbol *s1 = (const struct elf_symbol *) arg1;
+ const struct elf_symbol *s2 = (const struct elf_symbol *) arg2;
+ return strcmp (s1->name, s2->name);
+}
+
+static struct elf_symbuf_head *
+elf_create_symbuf (bfd_size_type symcount, Elf_Internal_Sym *isymbuf)
+{
+ Elf_Internal_Sym **ind, **indbufend, **indbuf;
+ struct elf_symbuf_symbol *ssym;
+ struct elf_symbuf_head *ssymbuf, *ssymhead;
+ bfd_size_type i, shndx_count, total_size;
+
+ indbuf = bfd_malloc2 (symcount, sizeof (*indbuf));
+ if (indbuf == NULL)
+ return NULL;
+
+ for (ind = indbuf, i = 0; i < symcount; i++)
+ if (isymbuf[i].st_shndx != SHN_UNDEF)
+ *ind++ = &isymbuf[i];
+ indbufend = ind;
+
+ qsort (indbuf, indbufend - indbuf, sizeof (Elf_Internal_Sym *),
+ elf_sort_elf_symbol);
+
+ shndx_count = 0;
+ if (indbufend > indbuf)
+ for (ind = indbuf, shndx_count++; ind < indbufend - 1; ind++)
+ if (ind[0]->st_shndx != ind[1]->st_shndx)
+ shndx_count++;
+
+ total_size = ((shndx_count + 1) * sizeof (*ssymbuf)
+ + (indbufend - indbuf) * sizeof (*ssym));
+ ssymbuf = bfd_malloc (total_size);
+ if (ssymbuf == NULL)
+ {
+ free (indbuf);
+ return NULL;
+ }
+
+ ssym = (struct elf_symbuf_symbol *) (ssymbuf + shndx_count + 1);
+ ssymbuf->ssym = NULL;
+ ssymbuf->count = shndx_count;
+ ssymbuf->st_shndx = 0;
+ for (ssymhead = ssymbuf, ind = indbuf; ind < indbufend; ssym++, ind++)
+ {
+ if (ind == indbuf || ssymhead->st_shndx != (*ind)->st_shndx)
+ {
+ ssymhead++;
+ ssymhead->ssym = ssym;
+ ssymhead->count = 0;
+ ssymhead->st_shndx = (*ind)->st_shndx;
+ }
+ ssym->st_name = (*ind)->st_name;
+ ssym->st_info = (*ind)->st_info;
+ ssym->st_other = (*ind)->st_other;
+ ssymhead->count++;
+ }
+ BFD_ASSERT ((bfd_size_type) (ssymhead - ssymbuf) == shndx_count
+ && (((bfd_hostptr_t) ssym - (bfd_hostptr_t) ssymbuf)
+ == total_size));
+
+ free (indbuf);
+ return ssymbuf;
+}
+
+/* Check if 2 sections define the same set of local and global
+ symbols. */
+
+static bfd_boolean
+bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
+ struct bfd_link_info *info)
+{
+ bfd *bfd1, *bfd2;
+ const struct elf_backend_data *bed1, *bed2;
+ Elf_Internal_Shdr *hdr1, *hdr2;
+ bfd_size_type symcount1, symcount2;
+ Elf_Internal_Sym *isymbuf1, *isymbuf2;
+ struct elf_symbuf_head *ssymbuf1, *ssymbuf2;
+ Elf_Internal_Sym *isym, *isymend;
+ struct elf_symbol *symtable1 = NULL, *symtable2 = NULL;
+ bfd_size_type count1, count2, i;
+ unsigned int shndx1, shndx2;
+ bfd_boolean result;
+
+ bfd1 = sec1->owner;
+ bfd2 = sec2->owner;
+
+ /* Both sections have to be in ELF. */
+ if (bfd_get_flavour (bfd1) != bfd_target_elf_flavour
+ || bfd_get_flavour (bfd2) != bfd_target_elf_flavour)
+ return FALSE;
+
+ if (elf_section_type (sec1) != elf_section_type (sec2))
+ return FALSE;
+
+ shndx1 = _bfd_elf_section_from_bfd_section (bfd1, sec1);
+ shndx2 = _bfd_elf_section_from_bfd_section (bfd2, sec2);
+ if (shndx1 == SHN_BAD || shndx2 == SHN_BAD)
+ return FALSE;
+
+ bed1 = get_elf_backend_data (bfd1);
+ bed2 = get_elf_backend_data (bfd2);
+ hdr1 = &elf_tdata (bfd1)->symtab_hdr;
+ symcount1 = hdr1->sh_size / bed1->s->sizeof_sym;
+ hdr2 = &elf_tdata (bfd2)->symtab_hdr;
+ symcount2 = hdr2->sh_size / bed2->s->sizeof_sym;
+
+ if (symcount1 == 0 || symcount2 == 0)
+ return FALSE;
+
+ result = FALSE;
+ isymbuf1 = NULL;
+ isymbuf2 = NULL;
+ ssymbuf1 = elf_tdata (bfd1)->symbuf;
+ ssymbuf2 = elf_tdata (bfd2)->symbuf;
+
+ if (ssymbuf1 == NULL)
+ {
+ isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, symcount1, 0,
+ NULL, NULL, NULL);
+ if (isymbuf1 == NULL)
+ goto done;
+
+ if (!info->reduce_memory_overheads)
+ elf_tdata (bfd1)->symbuf = ssymbuf1
+ = elf_create_symbuf (symcount1, isymbuf1);
+ }
+
+ if (ssymbuf1 == NULL || ssymbuf2 == NULL)
+ {
+ isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, symcount2, 0,
+ NULL, NULL, NULL);
+ if (isymbuf2 == NULL)
+ goto done;
+
+ if (ssymbuf1 != NULL && !info->reduce_memory_overheads)
+ elf_tdata (bfd2)->symbuf = ssymbuf2
+ = elf_create_symbuf (symcount2, isymbuf2);
+ }
+
+ if (ssymbuf1 != NULL && ssymbuf2 != NULL)
+ {
+ /* Optimized faster version. */
+ bfd_size_type lo, hi, mid;
+ struct elf_symbol *symp;
+ struct elf_symbuf_symbol *ssym, *ssymend;
+
+ lo = 0;
+ hi = ssymbuf1->count;
+ ssymbuf1++;
+ count1 = 0;
+ while (lo < hi)
+ {
+ mid = (lo + hi) / 2;
+ if (shndx1 < ssymbuf1[mid].st_shndx)
+ hi = mid;
+ else if (shndx1 > ssymbuf1[mid].st_shndx)
+ lo = mid + 1;
+ else
+ {
+ count1 = ssymbuf1[mid].count;
+ ssymbuf1 += mid;
+ break;
+ }
+ }
+
+ lo = 0;
+ hi = ssymbuf2->count;
+ ssymbuf2++;
+ count2 = 0;
+ while (lo < hi)
+ {
+ mid = (lo + hi) / 2;
+ if (shndx2 < ssymbuf2[mid].st_shndx)
+ hi = mid;
+ else if (shndx2 > ssymbuf2[mid].st_shndx)
+ lo = mid + 1;
+ else
+ {
+ count2 = ssymbuf2[mid].count;
+ ssymbuf2 += mid;
+ break;
+ }
+ }
+
+ if (count1 == 0 || count2 == 0 || count1 != count2)
+ goto done;
+
+ symtable1 = bfd_malloc (count1 * sizeof (struct elf_symbol));
+ symtable2 = bfd_malloc (count2 * sizeof (struct elf_symbol));
+ if (symtable1 == NULL || symtable2 == NULL)
+ goto done;
+
+ symp = symtable1;
+ for (ssym = ssymbuf1->ssym, ssymend = ssym + count1;
+ ssym < ssymend; ssym++, symp++)
+ {
+ symp->u.ssym = ssym;
+ symp->name = bfd_elf_string_from_elf_section (bfd1,
+ hdr1->sh_link,
+ ssym->st_name);
+ }
+
+ symp = symtable2;
+ for (ssym = ssymbuf2->ssym, ssymend = ssym + count2;
+ ssym < ssymend; ssym++, symp++)
+ {
+ symp->u.ssym = ssym;
+ symp->name = bfd_elf_string_from_elf_section (bfd2,
+ hdr2->sh_link,
+ ssym->st_name);
+ }
+
+ /* Sort symbol by name. */
+ qsort (symtable1, count1, sizeof (struct elf_symbol),
+ elf_sym_name_compare);
+ qsort (symtable2, count1, sizeof (struct elf_symbol),
+ elf_sym_name_compare);
+
+ for (i = 0; i < count1; i++)
+ /* Two symbols must have the same binding, type and name. */
+ if (symtable1 [i].u.ssym->st_info != symtable2 [i].u.ssym->st_info
+ || symtable1 [i].u.ssym->st_other != symtable2 [i].u.ssym->st_other
+ || strcmp (symtable1 [i].name, symtable2 [i].name) != 0)
+ goto done;
+
+ result = TRUE;
+ goto done;
+ }
+
+ symtable1 = bfd_malloc (symcount1 * sizeof (struct elf_symbol));
+ symtable2 = bfd_malloc (symcount2 * sizeof (struct elf_symbol));
+ if (symtable1 == NULL || symtable2 == NULL)
+ goto done;
+
+ /* Count definitions in the section. */
+ count1 = 0;
+ for (isym = isymbuf1, isymend = isym + symcount1; isym < isymend; isym++)
+ if (isym->st_shndx == shndx1)
+ symtable1[count1++].u.isym = isym;
+
+ count2 = 0;
+ for (isym = isymbuf2, isymend = isym + symcount2; isym < isymend; isym++)
+ if (isym->st_shndx == shndx2)
+ symtable2[count2++].u.isym = isym;
+
+ if (count1 == 0 || count2 == 0 || count1 != count2)
+ goto done;
+
+ for (i = 0; i < count1; i++)
+ symtable1[i].name
+ = bfd_elf_string_from_elf_section (bfd1, hdr1->sh_link,
+ symtable1[i].u.isym->st_name);
+
+ for (i = 0; i < count2; i++)
+ symtable2[i].name
+ = bfd_elf_string_from_elf_section (bfd2, hdr2->sh_link,
+ symtable2[i].u.isym->st_name);
+
+ /* Sort symbol by name. */
+ qsort (symtable1, count1, sizeof (struct elf_symbol),
+ elf_sym_name_compare);
+ qsort (symtable2, count1, sizeof (struct elf_symbol),
+ elf_sym_name_compare);
+
+ for (i = 0; i < count1; i++)
+ /* Two symbols must have the same binding, type and name. */
+ if (symtable1 [i].u.isym->st_info != symtable2 [i].u.isym->st_info
+ || symtable1 [i].u.isym->st_other != symtable2 [i].u.isym->st_other
+ || strcmp (symtable1 [i].name, symtable2 [i].name) != 0)
+ goto done;
+
+ result = TRUE;
+
+done:
+ if (symtable1)
+ free (symtable1);
+ if (symtable2)
+ free (symtable2);
+ if (isymbuf1)
+ free (isymbuf1);
+ if (isymbuf2)
+ free (isymbuf2);
+
+ return result;
+}
+
+/* Return TRUE if 2 section types are compatible. */
+
+bfd_boolean
+_bfd_elf_match_sections_by_type (bfd *abfd, const asection *asec,
+ bfd *bbfd, const asection *bsec)
+{
+ if (asec == NULL
+ || bsec == NULL
+ || abfd->xvec->flavour != bfd_target_elf_flavour
+ || bbfd->xvec->flavour != bfd_target_elf_flavour)
+ return TRUE;
+
+ return elf_section_type (asec) == elf_section_type (bsec);
+}
+\f
+/* Final phase of ELF linker. */
+
+/* A structure we use to avoid passing large numbers of arguments. */
+
+struct elf_final_link_info
+{
+ /* General link information. */
+ struct bfd_link_info *info;
+ /* Output BFD. */
+ bfd *output_bfd;
+ /* Symbol string table. */
+ struct bfd_strtab_hash *symstrtab;
+ /* .dynsym section. */
+ asection *dynsym_sec;
+ /* .hash section. */
+ asection *hash_sec;
+ /* symbol version section (.gnu.version). */
+ asection *symver_sec;
+ /* Buffer large enough to hold contents of any section. */
+ bfd_byte *contents;
+ /* Buffer large enough to hold external relocs of any section. */
+ void *external_relocs;
+ /* Buffer large enough to hold internal relocs of any section. */
+ Elf_Internal_Rela *internal_relocs;
+ /* Buffer large enough to hold external local symbols of any input
+ BFD. */
+ bfd_byte *external_syms;
+ /* And a buffer for symbol section indices. */
+ Elf_External_Sym_Shndx *locsym_shndx;
+ /* Buffer large enough to hold internal local symbols of any input
+ BFD. */
+ Elf_Internal_Sym *internal_syms;
+ /* Array large enough to hold a symbol index for each local symbol
+ of any input BFD. */
+ long *indices;
+ /* Array large enough to hold a section pointer for each local
+ symbol of any input BFD. */
+ asection **sections;
+ /* Buffer to hold swapped out symbols. */
+ bfd_byte *symbuf;
+ /* And one for symbol section indices. */
+ Elf_External_Sym_Shndx *symshndxbuf;
+ /* Number of swapped out symbols in buffer. */
+ size_t symbuf_count;
+ /* Number of symbols which fit in symbuf. */
+ size_t symbuf_size;
+ /* And same for symshndxbuf. */
+ size_t shndxbuf_size;
+};
+
+/* This struct is used to pass information to elf_link_output_extsym. */
+
+struct elf_outext_info
+{
+ bfd_boolean failed;
+ bfd_boolean localsyms;
+ struct elf_final_link_info *finfo;
+};
+
+
+/* Support for evaluating a complex relocation.
+
+ Complex relocations are generalized, self-describing relocations. The
+ implementation of them consists of two parts: complex symbols, and the
+ relocations themselves.
+
+ The relocations are use a reserved elf-wide relocation type code (R_RELC
+ external / BFD_RELOC_RELC internal) and an encoding of relocation field
+ information (start bit, end bit, word width, etc) into the addend. This
+ information is extracted from CGEN-generated operand tables within gas.
+
+ Complex symbols are mangled symbols (BSF_RELC external / STT_RELC
+ internal) representing prefix-notation expressions, including but not
+ limited to those sorts of expressions normally encoded as addends in the
+ addend field. The symbol mangling format is:
+
+ <node> := <literal>
+ | <unary-operator> ':' <node>
+ | <binary-operator> ':' <node> ':' <node>
+ ;
+
+ <literal> := 's' <digits=N> ':' <N character symbol name>
+ | 'S' <digits=N> ':' <N character section name>
+ | '#' <hexdigits>
+ ;
+
+ <binary-operator> := as in C
+ <unary-operator> := as in C, plus "0-" for unambiguous negation. */
+
+static void
+set_symbol_value (bfd *bfd_with_globals,
+ Elf_Internal_Sym *isymbuf,
+ size_t locsymcount,
+ size_t symidx,
+ bfd_vma val)
+{
+ struct elf_link_hash_entry **sym_hashes;
+ struct elf_link_hash_entry *h;
+ size_t extsymoff = locsymcount;
+
+ if (symidx < locsymcount)
+ {
+ Elf_Internal_Sym *sym;
+
+ sym = isymbuf + symidx;
+ if (ELF_ST_BIND (sym->st_info) == STB_LOCAL)
+ {
+ /* It is a local symbol: move it to the
+ "absolute" section and give it a value. */
+ sym->st_shndx = SHN_ABS;
+ sym->st_value = val;
+ return;
+ }
+ BFD_ASSERT (elf_bad_symtab (bfd_with_globals));
+ extsymoff = 0;
+ }
+
+ /* It is a global symbol: set its link type
+ to "defined" and give it a value. */
+
+ sym_hashes = elf_sym_hashes (bfd_with_globals);
+ h = sym_hashes [symidx - extsymoff];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ h->root.type = bfd_link_hash_defined;
+ h->root.u.def.value = val;
+ h->root.u.def.section = bfd_abs_section_ptr;
+}
+
+static bfd_boolean
+resolve_symbol (const char *name,
+ bfd *input_bfd,
+ struct elf_final_link_info *finfo,
+ bfd_vma *result,
+ Elf_Internal_Sym *isymbuf,
+ size_t locsymcount)
+{
+ Elf_Internal_Sym *sym;
+ struct bfd_link_hash_entry *global_entry;
+ const char *candidate = NULL;
+ Elf_Internal_Shdr *symtab_hdr;
+ size_t i;
+
+ symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+
+ for (i = 0; i < locsymcount; ++ i)
+ {
+ sym = isymbuf + i;
+
+ if (ELF_ST_BIND (sym->st_info) != STB_LOCAL)
+ continue;
candidate = bfd_elf_string_from_elf_section (input_bfd,
symtab_hdr->sh_link,
sym->st_name);
#ifdef DEBUG
- printf ("Comparing string: '%s' vs. '%s' = 0x%x\n",
- name, candidate, (unsigned int)sym->st_value);
+ printf ("Comparing string: '%s' vs. '%s' = 0x%lx\n",
+ name, candidate, (unsigned long) sym->st_value);
#endif
if (candidate && strcmp (candidate, name) == 0)
{
- * result = sym->st_value;
+ asection *sec = finfo->sections [i];
- if (sym->st_shndx > SHN_UNDEF &&
- sym->st_shndx < SHN_LORESERVE)
- {
-#ifdef DEBUG
- printf ("adjusting for sec '%s' @ 0x%x + 0x%x\n",
- sec->output_section->name,
- (unsigned int)sec->output_section->vma,
- (unsigned int)sec->output_offset);
-#endif
- * result += sec->output_offset + sec->output_section->vma;
- }
+ *result = _bfd_elf_rel_local_sym (input_bfd, sym, &sec, 0);
+ *result += sec->output_offset + sec->output_section->vma;
#ifdef DEBUG
- printf ("Found symbol with effective value %8.8x\n", (unsigned int)* result);
+ printf ("Found symbol with value %8.8lx\n",
+ (unsigned long) *result);
#endif
return TRUE;
}
}
/* Hmm, haven't found it yet. perhaps it is a global. */
- global_entry = bfd_link_hash_lookup (finfo->info->hash, name, FALSE, FALSE, TRUE);
+ global_entry = bfd_link_hash_lookup (finfo->info->hash, name,
+ FALSE, FALSE, TRUE);
if (!global_entry)
return FALSE;
-
+
if (global_entry->type == bfd_link_hash_defined
|| global_entry->type == bfd_link_hash_defweak)
{
- * result = global_entry->u.def.value
- + global_entry->u.def.section->output_section->vma
- + global_entry->u.def.section->output_offset;
+ *result = (global_entry->u.def.value
+ + global_entry->u.def.section->output_section->vma
+ + global_entry->u.def.section->output_offset);
#ifdef DEBUG
- printf ("Found GLOBAL symbol '%s' with value %8.8x\n",
- global_entry->root.string, (unsigned int)*result);
-#endif
- return TRUE;
- }
-
- if (global_entry->type == bfd_link_hash_common)
- {
- *result = global_entry->u.def.value +
- bfd_com_section_ptr->output_section->vma +
- bfd_com_section_ptr->output_offset;
-#ifdef DEBUG
- printf ("Found COMMON symbol '%s' with value %8.8x\n",
- global_entry->root.string, (unsigned int)*result);
+ printf ("Found GLOBAL symbol '%s' with value %8.8lx\n",
+ global_entry->root.string, (unsigned long) *result);
#endif
return TRUE;
}
-
+
return FALSE;
}
static bfd_boolean
-resolve_section (const char * name,
- asection * sections,
- bfd_vma * result)
+resolve_section (const char *name,
+ asection *sections,
+ bfd_vma *result)
{
- asection * curr;
- unsigned int len;
+ asection *curr;
+ unsigned int len;
- for (curr = sections; curr; curr = curr->next)
+ for (curr = sections; curr; curr = curr->next)
if (strcmp (curr->name, name) == 0)
{
*result = curr->vma;
}
/* Hmm. still haven't found it. try pseudo-section names. */
- for (curr = sections; curr; curr = curr->next)
+ for (curr = sections; curr; curr = curr->next)
{
len = strlen (curr->name);
- if (len > strlen (name))
+ if (len > strlen (name))
continue;
if (strncmp (curr->name, name, len) == 0)
/* Insert more pseudo-section names here, if you like. */
}
}
-
+
return FALSE;
}
static void
-undefined_reference (const char * reftype,
- const char * name)
+undefined_reference (const char *reftype, const char *name)
{
- _bfd_error_handler (_("undefined %s reference in complex symbol: %s"), reftype, name);
+ _bfd_error_handler (_("undefined %s reference in complex symbol: %s"),
+ reftype, name);
}
static bfd_boolean
-eval_symbol (bfd_vma * result,
- char * sym,
- char ** advanced,
- bfd * input_bfd,
- struct elf_final_link_info * finfo,
- bfd_vma addr,
- bfd_vma section_offset,
- size_t locsymcount,
- int signed_p)
-{
- int len;
- int symlen;
- bfd_vma a;
- bfd_vma b;
- const int bufsz = 4096;
- char symbuf [bufsz];
- const char * symend;
- bfd_boolean symbol_is_section = FALSE;
+eval_symbol (bfd_vma *result,
+ const char **symp,
+ bfd *input_bfd,
+ struct elf_final_link_info *finfo,
+ bfd_vma dot,
+ Elf_Internal_Sym *isymbuf,
+ size_t locsymcount,
+ int signed_p)
+{
+ size_t len;
+ size_t symlen;
+ bfd_vma a;
+ bfd_vma b;
+ char symbuf[4096];
+ const char *sym = *symp;
+ const char *symend;
+ bfd_boolean symbol_is_section = FALSE;
len = strlen (sym);
symend = sym + len;
- if (len < 1 || len > bufsz)
+ if (len < 1 || len > sizeof (symbuf))
{
bfd_set_error (bfd_error_invalid_operation);
return FALSE;
}
-
+
switch (* sym)
{
case '.':
- * result = addr + section_offset;
- * advanced = sym + 1;
+ *result = dot;
+ *symp = sym + 1;
return TRUE;
case '#':
- ++ sym;
- * result = strtoul (sym, advanced, 16);
+ ++sym;
+ *result = strtoul (sym, (char **) symp, 16);
return TRUE;
case 'S':
symbol_is_section = TRUE;
- case 's':
- ++ sym;
- symlen = strtol (sym, &sym, 10);
- ++ sym; /* Skip the trailing ':'. */
+ case 's':
+ ++sym;
+ symlen = strtol (sym, (char **) symp, 10);
+ sym = *symp + 1; /* Skip the trailing ':'. */
- if ((symend < sym) || ((symlen + 1) > bufsz))
+ if (symend < sym || symlen + 1 > sizeof (symbuf))
{
bfd_set_error (bfd_error_invalid_operation);
return FALSE;
}
memcpy (symbuf, sym, symlen);
- symbuf [symlen] = '\0';
- * advanced = sym + symlen;
-
- /* Is it always possible, with complex symbols, that gas "mis-guessed"
+ symbuf[symlen] = '\0';
+ *symp = sym + symlen;
+
+ /* Is it always possible, with complex symbols, that gas "mis-guessed"
the symbol as a section, or vice-versa. so we're pretty liberal in our
interpretation here; section means "try section first", not "must be a
section", and likewise with symbol. */
- if (symbol_is_section)
+ if (symbol_is_section)
{
- if ((resolve_section (symbuf, finfo->output_bfd->sections, result) != TRUE)
- && (resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE))
+ if (!resolve_section (symbuf, finfo->output_bfd->sections, result)
+ && !resolve_symbol (symbuf, input_bfd, finfo, result,
+ isymbuf, locsymcount))
{
undefined_reference ("section", symbuf);
return FALSE;
}
- }
- else
+ }
+ else
{
- if ((resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE)
- && (resolve_section (symbuf, finfo->output_bfd->sections,
- result) != TRUE))
+ if (!resolve_symbol (symbuf, input_bfd, finfo, result,
+ isymbuf, locsymcount)
+ && !resolve_section (symbuf, finfo->output_bfd->sections,
+ result))
{
undefined_reference ("symbol", symbuf);
return FALSE;
}
return TRUE;
-
+
/* All that remains are operators. */
#define UNARY_OP(op) \
if (strncmp (sym, #op, strlen (#op)) == 0) \
{ \
sym += strlen (#op); \
- if (* sym == ':') \
- ++ sym; \
- if (eval_symbol (& a, sym, & sym, input_bfd, finfo, addr, \
- section_offset, locsymcount, signed_p) \
- != TRUE) \
- return FALSE; \
- if (signed_p) \
- * result = op ((signed)a); \
- else \
- * result = op a; \
- * advanced = sym; \
+ if (*sym == ':') \
+ ++sym; \
+ *symp = sym; \
+ if (!eval_symbol (&a, symp, input_bfd, finfo, dot, \
+ isymbuf, locsymcount, signed_p)) \
+ return FALSE; \
+ if (signed_p) \
+ *result = op ((bfd_signed_vma) a); \
+ else \
+ *result = op a; \
return TRUE; \
}
if (strncmp (sym, #op, strlen (#op)) == 0) \
{ \
sym += strlen (#op); \
- if (* sym == ':') \
- ++ sym; \
- if (eval_symbol (& a, sym, & sym, input_bfd, finfo, addr, \
- section_offset, locsymcount, signed_p) \
- != TRUE) \
- return FALSE; \
- ++ sym; \
- if (eval_symbol (& b, sym, & sym, input_bfd, finfo, addr, \
- section_offset, locsymcount, signed_p) \
- != TRUE) \
- return FALSE; \
- if (signed_p) \
- * result = ((signed) a) op ((signed) b); \
- else \
- * result = a op b; \
- * advanced = sym; \
+ if (*sym == ':') \
+ ++sym; \
+ *symp = sym; \
+ if (!eval_symbol (&a, symp, input_bfd, finfo, dot, \
+ isymbuf, locsymcount, signed_p)) \
+ return FALSE; \
+ ++*symp; \
+ if (!eval_symbol (&b, symp, input_bfd, finfo, dot, \
+ isymbuf, locsymcount, signed_p)) \
+ return FALSE; \
+ if (signed_p) \
+ *result = ((bfd_signed_vma) a) op ((bfd_signed_vma) b); \
+ else \
+ *result = a op b; \
return TRUE; \
}
}
}
-/* Entry point to evaluator, called from elf_link_input_bfd. */
-
-static bfd_boolean
-evaluate_complex_relocation_symbols (bfd * input_bfd,
- struct elf_final_link_info * finfo,
- size_t locsymcount)
-{
- const struct elf_backend_data * bed;
- Elf_Internal_Shdr * symtab_hdr;
- struct elf_link_hash_entry ** sym_hashes;
- asection * reloc_sec;
- bfd_boolean result = TRUE;
-
- /* For each section, we're going to check and see if it has any
- complex relocations, and we're going to evaluate any of them
- we can. */
-
- if (finfo->info->relocatable)
- return TRUE;
-
- symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
- sym_hashes = elf_sym_hashes (input_bfd);
- bed = get_elf_backend_data (input_bfd);
-
- for (reloc_sec = input_bfd->sections; reloc_sec; reloc_sec = reloc_sec->next)
- {
- Elf_Internal_Rela * internal_relocs;
- unsigned long i;
-
- /* This section was omitted from the link. */
- if (! reloc_sec->linker_mark)
- continue;
-
- /* Only process sections containing relocs. */
- if ((reloc_sec->flags & SEC_RELOC) == 0)
- continue;
-
- if (reloc_sec->reloc_count == 0)
- continue;
-
- /* Read in the relocs for this section. */
- internal_relocs
- = _bfd_elf_link_read_relocs (input_bfd, reloc_sec, NULL,
- (Elf_Internal_Rela *) NULL,
- FALSE);
- if (internal_relocs == NULL)
- continue;
-
- for (i = reloc_sec->reloc_count; i--;)
- {
- Elf_Internal_Rela * rel;
- char * sym_name;
- bfd_vma index;
- Elf_Internal_Sym * sym;
- bfd_vma result;
- bfd_vma section_offset;
- bfd_vma addr;
- int signed_p = 0;
-
- rel = internal_relocs + i;
- section_offset = reloc_sec->output_section->vma
- + reloc_sec->output_offset;
- addr = rel->r_offset;
-
- index = ELF32_R_SYM (rel->r_info);
- if (bed->s->arch_size == 64)
- index >>= 24;
-
- if (index == STN_UNDEF)
- continue;
-
- if (index < locsymcount)
- {
- /* The symbol is local. */
- sym = finfo->internal_syms + index;
-
- /* We're only processing STT_RELC or STT_SRELC type symbols. */
- if ((ELF_ST_TYPE (sym->st_info) != STT_RELC) &&
- (ELF_ST_TYPE (sym->st_info) != STT_SRELC))
- continue;
-
- sym_name = bfd_elf_string_from_elf_section
- (input_bfd, symtab_hdr->sh_link, sym->st_name);
-
- signed_p = (ELF_ST_TYPE (sym->st_info) == STT_SRELC);
- }
- else
- {
- /* The symbol is global. */
- struct elf_link_hash_entry * h;
-
- if (elf_bad_symtab (input_bfd))
- continue;
-
- h = sym_hashes [index - locsymcount];
- while ( h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
- if (h->type != STT_RELC && h->type != STT_SRELC)
- continue;
-
- signed_p = (h->type == STT_SRELC);
- sym_name = (char *) h->root.root.string;
- }
-#ifdef DEBUG
- printf ("Encountered a complex symbol!");
- printf (" (input_bfd %s, section %s, reloc %ld\n",
- input_bfd->filename, reloc_sec->name, i);
- printf (" symbol: idx %8.8lx, name %s\n",
- index, sym_name);
- printf (" reloc : info %8.8lx, addr %8.8lx\n",
- rel->r_info, addr);
- printf (" Evaluating '%s' ...\n ", sym_name);
-#endif
- if (eval_symbol (& result, sym_name, & sym_name, input_bfd,
- finfo, addr, section_offset, locsymcount,
- signed_p))
- /* Symbol evaluated OK. Update to absolute value. */
- set_symbol_value (input_bfd, finfo, index, result);
-
- else
- result = FALSE;
- }
-
- if (internal_relocs != elf_section_data (reloc_sec)->relocs)
- free (internal_relocs);
- }
-
- /* If nothing went wrong, then we adjusted
- everything we wanted to adjust. */
- return result;
-}
-
static void
-put_value (bfd_vma size,
- unsigned long chunksz,
- bfd * input_bfd,
- bfd_vma x,
- bfd_byte * location)
+put_value (bfd_vma size,
+ unsigned long chunksz,
+ bfd *input_bfd,
+ bfd_vma x,
+ bfd_byte *location)
{
location += (size - chunksz);
- for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8))
+ for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8))
{
switch (chunksz)
{
}
}
-static bfd_vma
-get_value (bfd_vma size,
- unsigned long chunksz,
- bfd * input_bfd,
- bfd_byte * location)
+static bfd_vma
+get_value (bfd_vma size,
+ unsigned long chunksz,
+ bfd *input_bfd,
+ bfd_byte *location)
{
bfd_vma x = 0;
- for (; size; size -= chunksz, location += chunksz)
+ for (; size; size -= chunksz, location += chunksz)
{
switch (chunksz)
{
return x;
}
-static void
-decode_complex_addend
- (unsigned long * start, /* in bits */
- unsigned long * oplen, /* in bits */
- unsigned long * len, /* in bits */
- unsigned long * wordsz, /* in bytes */
- unsigned long * chunksz, /* in bytes */
- unsigned long * lsb0_p,
- unsigned long * signed_p,
- unsigned long * trunc_p,
- unsigned long encoded)
+static void
+decode_complex_addend (unsigned long *start, /* in bits */
+ unsigned long *oplen, /* in bits */
+ unsigned long *len, /* in bits */
+ unsigned long *wordsz, /* in bytes */
+ unsigned long *chunksz, /* in bytes */
+ unsigned long *lsb0_p,
+ unsigned long *signed_p,
+ unsigned long *trunc_p,
+ unsigned long encoded)
{
* start = encoded & 0x3F;
* len = (encoded >> 6) & 0x3F;
* trunc_p = (encoded >> 29) & 1;
}
-void
-bfd_elf_perform_complex_relocation
- (bfd * output_bfd ATTRIBUTE_UNUSED,
- struct bfd_link_info * info,
- bfd * input_bfd,
- asection * input_section,
- bfd_byte * contents,
- Elf_Internal_Rela * rel,
- Elf_Internal_Sym * local_syms,
- asection ** local_sections)
-{
- const struct elf_backend_data * bed;
- Elf_Internal_Shdr * symtab_hdr;
- asection * sec;
- bfd_vma relocation = 0, shift, x;
- bfd_vma r_symndx;
- bfd_vma mask;
- unsigned long start, oplen, len, wordsz,
- chunksz, lsb0_p, signed_p, trunc_p;
+bfd_reloc_status_type
+bfd_elf_perform_complex_relocation (bfd *input_bfd,
+ asection *input_section ATTRIBUTE_UNUSED,
+ bfd_byte *contents,
+ Elf_Internal_Rela *rel,
+ bfd_vma relocation)
+{
+ bfd_vma shift, x, mask;
+ unsigned long start, oplen, len, wordsz, chunksz, lsb0_p, signed_p, trunc_p;
+ bfd_reloc_status_type r;
/* Perform this reloc, since it is complex.
(this is not to say that it necessarily refers to a complex
symbol; merely that it is a self-describing CGEN based reloc.
i.e. the addend has the complete reloc information (bit start, end,
- word size, etc) encoded within it.). */
- r_symndx = ELF32_R_SYM (rel->r_info);
- bed = get_elf_backend_data (input_bfd);
- if (bed->s->arch_size == 64)
- r_symndx >>= 24;
-
-#ifdef DEBUG
- printf ("Performing complex relocation %ld...\n", r_symndx);
-#endif
-
- symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
- if (r_symndx < symtab_hdr->sh_info)
- {
- /* The symbol is local. */
- Elf_Internal_Sym * sym;
+ word size, etc) encoded within it.). */
- sym = local_syms + r_symndx;
- sec = local_sections [r_symndx];
- relocation = sym->st_value;
- if (sym->st_shndx > SHN_UNDEF &&
- sym->st_shndx < SHN_LORESERVE)
- relocation += (sec->output_offset +
- sec->output_section->vma);
- }
- else
- {
- /* The symbol is global. */
- struct elf_link_hash_entry **sym_hashes;
- struct elf_link_hash_entry * h;
-
- sym_hashes = elf_sym_hashes (input_bfd);
- h = sym_hashes [r_symndx];
-
- while (h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
- if (h->root.type == bfd_link_hash_defined
- || h->root.type == bfd_link_hash_defweak)
- {
- sec = h->root.u.def.section;
- relocation = h->root.u.def.value;
-
- if (! bfd_is_abs_section (sec))
- relocation += (sec->output_section->vma
- + sec->output_offset);
- }
- if (h->root.type == bfd_link_hash_undefined
- && !((*info->callbacks->undefined_symbol)
- (info, h->root.root.string, input_bfd,
- input_section, rel->r_offset,
- info->unresolved_syms_in_objects == RM_GENERATE_ERROR
- || ELF_ST_VISIBILITY (h->other))))
- return;
- }
-
- decode_complex_addend (& start, & oplen, & len, & wordsz,
- & chunksz, & lsb0_p, & signed_p,
- & trunc_p, rel->r_addend);
+ decode_complex_addend (&start, &oplen, &len, &wordsz,
+ &chunksz, &lsb0_p, &signed_p,
+ &trunc_p, rel->r_addend);
mask = (((1L << (len - 1)) - 1) << 1) | 1;
else
shift = (8 * wordsz) - (start + len);
- x = get_value (wordsz, chunksz, input_bfd, contents + rel->r_offset);
+ x = get_value (wordsz, chunksz, input_bfd, contents + rel->r_offset);
#ifdef DEBUG
printf ("Doing complex reloc: "
oplen, x, mask, relocation);
#endif
+ r = bfd_reloc_ok;
if (! trunc_p)
- {
- /* Now do an overflow check. */
- if (bfd_check_overflow ((signed_p ?
- complain_overflow_signed :
- complain_overflow_unsigned),
- len, 0, (8 * wordsz),
- relocation) == bfd_reloc_overflow)
- (*_bfd_error_handler)
- ("%s (%s + 0x%lx): relocation overflow: 0x%lx %sdoes not fit "
- "within 0x%lx",
- input_bfd->filename, input_section->name, rel->r_offset,
- relocation, (signed_p ? "(signed) " : ""), mask);
- }
-
+ /* Now do an overflow check. */
+ r = bfd_check_overflow ((signed_p
+ ? complain_overflow_signed
+ : complain_overflow_unsigned),
+ len, 0, (8 * wordsz),
+ relocation);
+
/* Do the deed. */
x = (x & ~(mask << shift)) | ((relocation & mask) << shift);
" shifted mask: %8.8lx\n"
" shifted/masked reloc: %8.8lx\n"
" result: %8.8lx\n",
- relocation, (mask << shift),
+ relocation, (mask << shift),
((relocation & mask) << shift), x);
#endif
put_value (wordsz, chunksz, input_bfd, x, contents + rel->r_offset);
+ return r;
}
/* When performing a relocatable link, the input relocations are
bfd_size_type amt;
amt = finfo->shndxbuf_size * sizeof (Elf_External_Sym_Shndx);
- finfo->symshndxbuf = destshndx = bfd_realloc (destshndx, amt * 2);
+ destshndx = bfd_realloc (destshndx, amt * 2);
if (destshndx == NULL)
return FALSE;
+ finfo->symshndxbuf = destshndx;
memset ((char *) destshndx + amt, 0, amt);
finfo->shndxbuf_size *= 2;
}
static bfd_boolean
check_dynsym (bfd *abfd, Elf_Internal_Sym *sym)
{
- if (sym->st_shndx > SHN_HIRESERVE)
+ if (sym->st_shndx >= (SHN_LORESERVE & 0xffff)
+ && sym->st_shndx < SHN_LORESERVE)
{
/* The gABI doesn't support dynamic symbols in output sections
- beyond 64k. */
+ beyond 64k. */
(*_bfd_error_handler)
(_("%B: Too many sections: %d (>= %d)"),
- abfd, bfd_count_sections (abfd), SHN_LORESERVE);
+ abfd, bfd_count_sections (abfd), SHN_LORESERVE & 0xffff);
bfd_set_error (bfd_error_nonrepresentable_section);
return FALSE;
}
sym.st_value += input_sec->output_section->vma;
if (h->type == STT_TLS)
{
- /* STT_TLS symbols are relative to PT_TLS segment
- base. */
- BFD_ASSERT (elf_hash_table (finfo->info)->tls_sec != NULL);
- sym.st_value -= elf_hash_table (finfo->info)->tls_sec->vma;
+ asection *tls_sec = elf_hash_table (finfo->info)->tls_sec;
+ if (tls_sec != NULL)
+ sym.st_value -= tls_sec->vma;
+ else
+ {
+ /* The TLS section may have been garbage collected. */
+ BFD_ASSERT (finfo->info->gc_sections
+ && !input_sec->gc_mark);
+ }
}
}
}
sym.st_info = ELF_ST_INFO (bindtype, ELF_ST_TYPE (sym.st_info));
}
+ /* If this is a symbol defined in a dynamic library, don't use the
+ symbol size from the dynamic library. Relinking an executable
+ against a new library may introduce gratuitous changes in the
+ executable's symbols if we keep the size. */
+ if (sym.st_shndx == SHN_UNDEF
+ && !h->def_regular
+ && h->def_dynamic)
+ sym.st_size = 0;
+
/* If a non-weak symbol with non-default visibility is not defined
locally, it is a fatal error. */
if (! finfo->info->relocatable
{
if ((kept->flags & SEC_GROUP) != 0)
kept = match_group_member (sec, kept, info);
- if (kept != NULL && sec->size != kept->size)
+ if (kept != NULL
+ && ((sec->rawsize != 0 ? sec->rawsize : sec->size)
+ != (kept->rawsize != 0 ? kept->rawsize : kept->size)))
kept = NULL;
sec->kept_section = kept;
}
if (isymbuf == NULL)
return FALSE;
}
- /* evaluate_complex_relocation_symbols looks for symbols in
- finfo->internal_syms. */
- else if (isymbuf != NULL && locsymcount != 0)
- {
- bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0,
- finfo->internal_syms,
- finfo->external_syms,
- finfo->locsym_shndx);
- }
/* Find local symbol sections and adjust values of symbols in
SEC_MERGE sections. Write out those local symbols we know are
if (isym->st_shndx == SHN_UNDEF)
isec = bfd_und_section_ptr;
- else if (isym->st_shndx < SHN_LORESERVE
- || isym->st_shndx > SHN_HIRESERVE)
- {
- isec = bfd_section_from_elf_index (input_bfd, isym->st_shndx);
- if (isec
- && isec->sec_info_type == ELF_INFO_TYPE_MERGE
- && ELF_ST_TYPE (isym->st_info) != STT_SECTION)
- isym->st_value =
- _bfd_merged_section_offset (output_bfd, &isec,
- elf_section_data (isec)->sec_info,
- isym->st_value);
- }
else if (isym->st_shndx == SHN_ABS)
isec = bfd_abs_section_ptr;
else if (isym->st_shndx == SHN_COMMON)
isec = bfd_com_section_ptr;
else
{
- /* Don't attempt to output symbols with st_shnx in the
- reserved range other than SHN_ABS and SHN_COMMON. */
- *ppsection = NULL;
- continue;
+ isec = bfd_section_from_elf_index (input_bfd, isym->st_shndx);
+ if (isec == NULL)
+ {
+ /* Don't attempt to output symbols with st_shnx in the
+ reserved range other than SHN_ABS and SHN_COMMON. */
+ *ppsection = NULL;
+ continue;
+ }
+ else if (isec->sec_info_type == ELF_INFO_TYPE_MERGE
+ && ELF_ST_TYPE (isym->st_info) != STT_SECTION)
+ isym->st_value =
+ _bfd_merged_section_offset (output_bfd, &isec,
+ elf_section_data (isec)->sec_info,
+ isym->st_value);
}
*ppsection = isec;
/* If this symbol is defined in a section which we are
discarding, we don't need to keep it. */
if (isym->st_shndx != SHN_UNDEF
- && (isym->st_shndx < SHN_LORESERVE || isym->st_shndx > SHN_HIRESERVE)
- && (isec == NULL
- || bfd_section_removed_from_list (output_bfd,
- isec->output_section)))
+ && isym->st_shndx < SHN_LORESERVE
+ && bfd_section_removed_from_list (output_bfd,
+ isec->output_section))
continue;
/* Get the name of the symbol. */
return FALSE;
}
- if (! evaluate_complex_relocation_symbols (input_bfd, finfo, locsymcount))
- return FALSE;
-
/* Relocate the contents of each section. */
sym_hashes = elf_sym_hashes (input_bfd);
for (o = input_bfd->sections; o != NULL; o = o->next)
if ((o->flags & SEC_RELOC) != 0)
{
Elf_Internal_Rela *internal_relocs;
+ Elf_Internal_Rela *rel, *relend;
bfd_vma r_type_mask;
int r_sym_shift;
+ int action_discarded;
int ret;
/* Get the swapped relocs. */
r_sym_shift = 32;
}
- /* Run through the relocs looking for any against symbols
- from discarded sections and section symbols from
- removed link-once sections. Complain about relocs
- against discarded sections. Zero relocs against removed
- link-once sections. */
+ action_discarded = -1;
if (!elf_section_ignore_discarded_relocs (o))
+ action_discarded = (*bed->action_discarded) (o);
+
+ /* Run through the relocs evaluating complex reloc symbols and
+ looking for relocs against symbols from discarded sections
+ or section symbols from removed link-once sections.
+ Complain about relocs against discarded sections. Zero
+ relocs against removed link-once sections. */
+
+ rel = internal_relocs;
+ relend = rel + o->reloc_count * bed->s->int_rels_per_ext_rel;
+ for ( ; rel < relend; rel++)
{
- Elf_Internal_Rela *rel, *relend;
- unsigned int action = (*bed->action_discarded) (o);
+ unsigned long r_symndx = rel->r_info >> r_sym_shift;
+ unsigned int s_type;
+ asection **ps, *sec;
+ struct elf_link_hash_entry *h = NULL;
+ const char *sym_name;
+
+ if (r_symndx == STN_UNDEF)
+ continue;
- rel = internal_relocs;
- relend = rel + o->reloc_count * bed->s->int_rels_per_ext_rel;
- for ( ; rel < relend; rel++)
+ if (r_symndx >= locsymcount
+ || (elf_bad_symtab (input_bfd)
+ && finfo->sections[r_symndx] == NULL))
{
- unsigned long r_symndx = rel->r_info >> r_sym_shift;
- asection **ps, *sec;
- struct elf_link_hash_entry *h = NULL;
- const char *sym_name;
-
- if (r_symndx == STN_UNDEF)
- continue;
+ h = sym_hashes[r_symndx - extsymoff];
- if (r_symndx >= locsymcount
- || (elf_bad_symtab (input_bfd)
- && finfo->sections[r_symndx] == NULL))
+ /* Badly formatted input files can contain relocs that
+ reference non-existant symbols. Check here so that
+ we do not seg fault. */
+ if (h == NULL)
{
- h = sym_hashes[r_symndx - extsymoff];
+ char buffer [32];
- /* Badly formatted input files can contain relocs that
- reference non-existant symbols. Check here so that
- we do not seg fault. */
- if (h == NULL)
- {
- char buffer [32];
+ sprintf_vma (buffer, rel->r_info);
+ (*_bfd_error_handler)
+ (_("error: %B contains a reloc (0x%s) for section %A "
+ "that references a non-existent global symbol"),
+ input_bfd, o, buffer);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
- sprintf_vma (buffer, rel->r_info);
- (*_bfd_error_handler)
- (_("error: %B contains a reloc (0x%s) for section %A "
- "that references a non-existent global symbol"),
- input_bfd, o, buffer);
- bfd_set_error (bfd_error_bad_value);
- return FALSE;
- }
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
- while (h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ s_type = h->type;
- if (h->root.type != bfd_link_hash_defined
- && h->root.type != bfd_link_hash_defweak)
- continue;
+ ps = NULL;
+ if (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ ps = &h->root.u.def.section;
- ps = &h->root.u.def.section;
- sym_name = h->root.root.string;
- }
- else
- {
- Elf_Internal_Sym *sym = isymbuf + r_symndx;
- ps = &finfo->sections[r_symndx];
- sym_name = bfd_elf_sym_name (input_bfd,
- symtab_hdr,
- sym, *ps);
- }
+ sym_name = h->root.root.string;
+ }
+ else
+ {
+ Elf_Internal_Sym *sym = isymbuf + r_symndx;
+
+ s_type = ELF_ST_TYPE (sym->st_info);
+ ps = &finfo->sections[r_symndx];
+ sym_name = bfd_elf_sym_name (input_bfd, symtab_hdr,
+ sym, *ps);
+ }
+
+ if (s_type == STT_RELC || s_type == STT_SRELC)
+ {
+ bfd_vma val;
+ bfd_vma dot = (rel->r_offset
+ + o->output_offset + o->output_section->vma);
+#ifdef DEBUG
+ printf ("Encountered a complex symbol!");
+ printf (" (input_bfd %s, section %s, reloc %ld\n",
+ input_bfd->filename, o->name, rel - internal_relocs);
+ printf (" symbol: idx %8.8lx, name %s\n",
+ r_symndx, sym_name);
+ printf (" reloc : info %8.8lx, addr %8.8lx\n",
+ (unsigned long) rel->r_info,
+ (unsigned long) rel->r_offset);
+#endif
+ if (!eval_symbol (&val, &sym_name, input_bfd, finfo, dot,
+ isymbuf, locsymcount, s_type == STT_SRELC))
+ return FALSE;
+
+ /* Symbol evaluated OK. Update to absolute value. */
+ set_symbol_value (input_bfd, isymbuf, locsymcount,
+ r_symndx, val);
+ continue;
+ }
+ if (action_discarded != -1 && ps != NULL)
+ {
/* Complain if the definition comes from a
discarded section. */
if ((sec = *ps) != NULL && elf_discarded_section (sec))
{
BFD_ASSERT (r_symndx != 0);
- if (action & COMPLAIN)
+ if (action_discarded & COMPLAIN)
(*finfo->info->callbacks->einfo)
(_("%X`%s' referenced in section `%A' of %B: "
"defined in discarded section `%A' of %B\n"),
FIXME: This is quite broken. Modifying the
symbol here means we will be changing all later
uses of the symbol, not just in this section. */
- if (action & PRETEND)
+ if (action_discarded & PRETEND)
{
asection *kept;
default:
{
if (! (o->flags & SEC_EXCLUDE)
+ && ! (o->output_section->flags & SEC_NEVER_LOAD)
&& ! bfd_set_section_contents (output_bfd, o->output_section,
contents,
(file_ptr) o->output_offset,
&& elf_elfheader (sub)->e_ident[EI_CLASS] == bed->s->elfclass
&& (elfsec = _bfd_elf_section_from_bfd_section (sub, s))
&& elfsec < elf_numsections (sub)
- && elf_elfsections (sub)[elfsec]->sh_flags & SHF_LINK_ORDER)
+ && elf_elfsections (sub)[elfsec]->sh_flags & SHF_LINK_ORDER
+ && elf_elfsections (sub)[elfsec]->sh_link < elf_numsections (sub))
{
seen_linkorder++;
linkorder_sec = s;
return TRUE;
sections = (struct bfd_link_order **)
- xmalloc (seen_linkorder * sizeof (struct bfd_link_order *));
+ bfd_malloc (seen_linkorder * sizeof (struct bfd_link_order *));
+ if (sections == NULL)
+ return FALSE;
seen_linkorder = 0;
for (p = o->map_head.link_order; p != NULL; p = p->next)
for (n = 0; n < seen_linkorder; n++)
{
s = sections[n]->u.indirect.section;
- offset &= ~(bfd_vma)((1 << s->alignment_power) - 1);
+ offset &= ~(bfd_vma) 0 << s->alignment_power;
s->output_offset = offset;
sections[n]->offset = offset;
offset += sections[n]->size;
}
+ free (sections);
return TRUE;
}
size_t relativecount = 0;
asection *reldyn = 0;
bfd_size_type amt;
+ asection *attr_section = NULL;
+ bfd_vma attr_size = 0;
+ const char *std_attrs_section;
if (! is_elf_hash_table (info->hash))
return FALSE;
finfo.symbuf_count = 0;
finfo.shndxbuf_size = 0;
+ /* The object attributes have been merged. Remove the input
+ sections from the link, and set the contents of the output
+ secton. */
+ std_attrs_section = get_elf_backend_data (abfd)->obj_attrs_section;
+ for (o = abfd->sections; o != NULL; o = o->next)
+ {
+ if ((std_attrs_section && strcmp (o->name, std_attrs_section) == 0)
+ || strcmp (o->name, ".gnu.attributes") == 0)
+ {
+ for (p = o->map_head.link_order; p != NULL; p = p->next)
+ {
+ asection *input_section;
+
+ if (p->type != bfd_indirect_link_order)
+ continue;
+ input_section = p->u.indirect.section;
+ /* Hack: reset the SEC_HAS_CONTENTS flag so that
+ elf_link_input_bfd ignores this section. */
+ input_section->flags &= ~SEC_HAS_CONTENTS;
+ }
+
+ attr_size = bfd_elf_obj_attr_size (abfd);
+ if (attr_size)
+ {
+ bfd_set_section_size (abfd, o, attr_size);
+ attr_section = o;
+ /* Skip this section later on. */
+ o->map_head.link_order = NULL;
+ }
+ else
+ o->flags |= SEC_EXCLUDE;
+ }
+ }
+
/* Count up the number of relocations we will output for each output
section, so that we know the sizes of the reloc sections. We
also figure out some maximum sizes. */
/* sh_link is set in assign_section_numbers. */
/* sh_info is set below. */
/* sh_offset is set just below. */
- symtab_hdr->sh_addralign = 1 << bed->s->log_file_align;
+ symtab_hdr->sh_addralign = (bfd_vma) 1 << bed->s->log_file_align;
off = elf_tdata (abfd)->next_file_pos;
off = _bfd_elf_assign_file_position_for_section (symtab_hdr, off, TRUE);
finfo.symbuf = bfd_malloc (amt);
if (finfo.symbuf == NULL)
goto error_return;
- if (elf_numsections (abfd) > SHN_LORESERVE)
+ if (elf_numsections (abfd) > (SHN_LORESERVE & 0xFFFF))
{
/* Wild guess at number of output symbols. realloc'd as needed. */
amt = 2 * max_sym_count + elf_numsections (abfd) + 1000;
if (!elf_link_output_sym (&finfo, NULL, &elfsym, o, NULL))
goto error_return;
}
- if (i == SHN_LORESERVE - 1)
- i += SHN_HIRESERVE + 1 - SHN_LORESERVE;
}
}
the original st_name with the dynstr_index. */
sym = e->isym;
- if (e->isym.st_shndx != SHN_UNDEF
- && (e->isym.st_shndx < SHN_LORESERVE
- || e->isym.st_shndx > SHN_HIRESERVE))
+ s = bfd_section_from_elf_index (e->input_bfd,
+ e->isym.st_shndx);
+ if (s != NULL)
{
- s = bfd_section_from_elf_index (e->input_bfd,
- e->isym.st_shndx);
-
sym.st_shndx =
elf_section_data (s->output_section)->this_idx;
if (! check_dynsym (abfd, &sym))
if (dyn.d_tag == DT_TEXTREL)
{
- info->callbacks->einfo
+ info->callbacks->einfo
(_("%P: warning: creating a DT_TEXTREL in a shared object.\n"));
break;
}
elf_tdata (abfd)->linker = TRUE;
+ if (attr_section)
+ {
+ bfd_byte *contents = bfd_malloc (attr_size);
+ if (contents == NULL)
+ return FALSE; /* Bail out and fail. */
+ bfd_elf_set_obj_attr_contents (abfd, contents, attr_size);
+ bfd_set_section_contents (abfd, attr_section, contents, 0, attr_size);
+ free (contents);
+ }
+
return TRUE;
error_return:
return FALSE;
}
\f
+/* Initialize COOKIE for input bfd ABFD. */
+
+static bfd_boolean
+init_reloc_cookie (struct elf_reloc_cookie *cookie,
+ struct bfd_link_info *info, bfd *abfd)
+{
+ Elf_Internal_Shdr *symtab_hdr;
+ const struct elf_backend_data *bed;
+
+ bed = get_elf_backend_data (abfd);
+ symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+
+ cookie->abfd = abfd;
+ cookie->sym_hashes = elf_sym_hashes (abfd);
+ cookie->bad_symtab = elf_bad_symtab (abfd);
+ if (cookie->bad_symtab)
+ {
+ cookie->locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
+ cookie->extsymoff = 0;
+ }
+ else
+ {
+ cookie->locsymcount = symtab_hdr->sh_info;
+ cookie->extsymoff = symtab_hdr->sh_info;
+ }
+
+ if (bed->s->arch_size == 32)
+ cookie->r_sym_shift = 8;
+ else
+ cookie->r_sym_shift = 32;
+
+ cookie->locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (cookie->locsyms == NULL && cookie->locsymcount != 0)
+ {
+ cookie->locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+ cookie->locsymcount, 0,
+ NULL, NULL, NULL);
+ if (cookie->locsyms == NULL)
+ {
+ info->callbacks->einfo (_("%P%X: can not read symbols: %E\n"));
+ return FALSE;
+ }
+ if (info->keep_memory)
+ symtab_hdr->contents = (bfd_byte *) cookie->locsyms;
+ }
+ return TRUE;
+}
+
+/* Free the memory allocated by init_reloc_cookie, if appropriate. */
+
+static void
+fini_reloc_cookie (struct elf_reloc_cookie *cookie, bfd *abfd)
+{
+ Elf_Internal_Shdr *symtab_hdr;
+
+ symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ if (cookie->locsyms != NULL
+ && symtab_hdr->contents != (unsigned char *) cookie->locsyms)
+ free (cookie->locsyms);
+}
+
+/* Initialize the relocation information in COOKIE for input section SEC
+ of input bfd ABFD. */
+
+static bfd_boolean
+init_reloc_cookie_rels (struct elf_reloc_cookie *cookie,
+ struct bfd_link_info *info, bfd *abfd,
+ asection *sec)
+{
+ const struct elf_backend_data *bed;
+
+ if (sec->reloc_count == 0)
+ {
+ cookie->rels = NULL;
+ cookie->relend = NULL;
+ }
+ else
+ {
+ bed = get_elf_backend_data (abfd);
+
+ cookie->rels = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
+ info->keep_memory);
+ if (cookie->rels == NULL)
+ return FALSE;
+ cookie->rel = cookie->rels;
+ cookie->relend = (cookie->rels
+ + sec->reloc_count * bed->s->int_rels_per_ext_rel);
+ }
+ cookie->rel = cookie->rels;
+ return TRUE;
+}
+
+/* Free the memory allocated by init_reloc_cookie_rels,
+ if appropriate. */
+
+static void
+fini_reloc_cookie_rels (struct elf_reloc_cookie *cookie,
+ asection *sec)
+{
+ if (cookie->rels && elf_section_data (sec)->relocs != cookie->rels)
+ free (cookie->rels);
+}
+
+/* Initialize the whole of COOKIE for input section SEC. */
+
+static bfd_boolean
+init_reloc_cookie_for_section (struct elf_reloc_cookie *cookie,
+ struct bfd_link_info *info,
+ asection *sec)
+{
+ if (!init_reloc_cookie (cookie, info, sec->owner))
+ goto error1;
+ if (!init_reloc_cookie_rels (cookie, info, sec->owner, sec))
+ goto error2;
+ return TRUE;
+
+ error2:
+ fini_reloc_cookie (cookie, sec->owner);
+ error1:
+ return FALSE;
+}
+
+/* Free the memory allocated by init_reloc_cookie_for_section,
+ if appropriate. */
+
+static void
+fini_reloc_cookie_for_section (struct elf_reloc_cookie *cookie,
+ asection *sec)
+{
+ fini_reloc_cookie_rels (cookie, sec);
+ fini_reloc_cookie (cookie, sec->owner);
+}
+\f
/* Garbage collect unused sections. */
/* Default gc_mark_hook. */
return NULL;
}
+/* COOKIE->rel describes a relocation against section SEC, which is
+ a section we've decided to keep. Return the section that contains
+ the relocation symbol, or NULL if no section contains it. */
+
+asection *
+_bfd_elf_gc_mark_rsec (struct bfd_link_info *info, asection *sec,
+ elf_gc_mark_hook_fn gc_mark_hook,
+ struct elf_reloc_cookie *cookie)
+{
+ unsigned long r_symndx;
+ struct elf_link_hash_entry *h;
+
+ r_symndx = cookie->rel->r_info >> cookie->r_sym_shift;
+ if (r_symndx == 0)
+ return NULL;
+
+ if (r_symndx >= cookie->locsymcount
+ || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
+ {
+ h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ return (*gc_mark_hook) (sec, info, cookie->rel, h, NULL);
+ }
+
+ return (*gc_mark_hook) (sec, info, cookie->rel, NULL,
+ &cookie->locsyms[r_symndx]);
+}
+
+/* COOKIE->rel describes a relocation against section SEC, which is
+ a section we've decided to keep. Mark the section that contains
+ the relocation symbol. */
+
+bfd_boolean
+_bfd_elf_gc_mark_reloc (struct bfd_link_info *info,
+ asection *sec,
+ elf_gc_mark_hook_fn gc_mark_hook,
+ struct elf_reloc_cookie *cookie)
+{
+ asection *rsec;
+
+ rsec = _bfd_elf_gc_mark_rsec (info, sec, gc_mark_hook, cookie);
+ if (rsec && !rsec->gc_mark)
+ {
+ if (bfd_get_flavour (rsec->owner) != bfd_target_elf_flavour)
+ rsec->gc_mark = 1;
+ else if (!_bfd_elf_gc_mark (info, rsec, gc_mark_hook))
+ return FALSE;
+ }
+ return TRUE;
+}
+
/* The mark phase of garbage collection. For a given section, mark
it and any sections in this section's group, and all the sections
which define symbols to which it refers. */
elf_gc_mark_hook_fn gc_mark_hook)
{
bfd_boolean ret;
- bfd_boolean is_eh;
- asection *group_sec;
+ asection *group_sec, *eh_frame;
sec->gc_mark = 1;
/* Look through the section relocs. */
ret = TRUE;
- is_eh = strcmp (sec->name, ".eh_frame") == 0;
- if ((sec->flags & SEC_RELOC) != 0 && sec->reloc_count > 0)
+ eh_frame = elf_eh_frame_section (sec->owner);
+ if ((sec->flags & SEC_RELOC) != 0
+ && sec->reloc_count > 0
+ && sec != eh_frame)
{
- Elf_Internal_Rela *relstart, *rel, *relend;
- Elf_Internal_Shdr *symtab_hdr;
- struct elf_link_hash_entry **sym_hashes;
- size_t nlocsyms;
- size_t extsymoff;
- bfd *input_bfd = sec->owner;
- const struct elf_backend_data *bed = get_elf_backend_data (input_bfd);
- Elf_Internal_Sym *isym = NULL;
- int r_sym_shift;
-
- symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
- sym_hashes = elf_sym_hashes (input_bfd);
-
- /* Read the local symbols. */
- if (elf_bad_symtab (input_bfd))
- {
- nlocsyms = symtab_hdr->sh_size / bed->s->sizeof_sym;
- extsymoff = 0;
- }
- else
- extsymoff = nlocsyms = symtab_hdr->sh_info;
+ struct elf_reloc_cookie cookie;
- isym = (Elf_Internal_Sym *) symtab_hdr->contents;
- if (isym == NULL && nlocsyms != 0)
+ if (!init_reloc_cookie_for_section (&cookie, info, sec))
+ ret = FALSE;
+ else
{
- isym = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, nlocsyms, 0,
- NULL, NULL, NULL);
- if (isym == NULL)
- return FALSE;
+ for (; cookie.rel < cookie.relend; cookie.rel++)
+ if (!_bfd_elf_gc_mark_reloc (info, sec, gc_mark_hook, &cookie))
+ {
+ ret = FALSE;
+ break;
+ }
+ fini_reloc_cookie_for_section (&cookie, sec);
}
+ }
- /* Read the relocations. */
- relstart = _bfd_elf_link_read_relocs (input_bfd, sec, NULL, NULL,
- info->keep_memory);
- if (relstart == NULL)
- {
- ret = FALSE;
- goto out1;
- }
- relend = relstart + sec->reloc_count * bed->s->int_rels_per_ext_rel;
+ if (ret && eh_frame && elf_fde_list (sec))
+ {
+ struct elf_reloc_cookie cookie;
- if (bed->s->arch_size == 32)
- r_sym_shift = 8;
+ if (!init_reloc_cookie_for_section (&cookie, info, eh_frame))
+ ret = FALSE;
else
- r_sym_shift = 32;
-
- for (rel = relstart; rel < relend; rel++)
- {
- unsigned long r_symndx;
- asection *rsec;
- struct elf_link_hash_entry *h;
-
- r_symndx = rel->r_info >> r_sym_shift;
- if (r_symndx == 0)
- continue;
-
- if (r_symndx >= nlocsyms
- || ELF_ST_BIND (isym[r_symndx].st_info) != STB_LOCAL)
- {
- h = sym_hashes[r_symndx - extsymoff];
- while (h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
- rsec = (*gc_mark_hook) (sec, info, rel, h, NULL);
- }
- else
- {
- rsec = (*gc_mark_hook) (sec, info, rel, NULL, &isym[r_symndx]);
- }
-
- if (rsec && !rsec->gc_mark)
- {
- if (bfd_get_flavour (rsec->owner) != bfd_target_elf_flavour)
- rsec->gc_mark = 1;
- else if (is_eh)
- rsec->gc_mark_from_eh = 1;
- else if (!_bfd_elf_gc_mark (info, rsec, gc_mark_hook))
- {
- ret = FALSE;
- goto out2;
- }
- }
- }
-
- out2:
- if (elf_section_data (sec)->relocs != relstart)
- free (relstart);
- out1:
- if (isym != NULL && symtab_hdr->contents != (unsigned char *) isym)
{
- if (! info->keep_memory)
- free (isym);
- else
- symtab_hdr->contents = (unsigned char *) isym;
+ if (!_bfd_elf_gc_mark_fdes (info, sec, eh_frame,
+ gc_mark_hook, &cookie))
+ ret = FALSE;
+ fini_reloc_cookie_for_section (&cookie, eh_frame);
}
}
return TRUE;
}
+/* Keep all sections containing symbols undefined on the command-line,
+ and the section containing the entry symbol. */
+
+void
+_bfd_elf_gc_keep (struct bfd_link_info *info)
+{
+ struct bfd_sym_chain *sym;
+
+ for (sym = info->gc_sym_list; sym != NULL; sym = sym->next)
+ {
+ struct elf_link_hash_entry *h;
+
+ h = elf_link_hash_lookup (elf_hash_table (info), sym->name,
+ FALSE, FALSE, FALSE);
+
+ if (h != NULL
+ && (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && !bfd_is_abs_section (h->root.u.def.section))
+ h->root.u.def.section->flags |= SEC_KEEP;
+ }
+}
+
/* Do mark and sweep of unused sections. */
bfd_boolean
const struct elf_backend_data *bed = get_elf_backend_data (abfd);
if (!bed->can_gc_sections
- || info->relocatable
- || info->emitrelocations
|| !is_elf_hash_table (info->hash))
{
(*_bfd_error_handler)(_("Warning: gc-sections option ignored"));
return TRUE;
}
+ bed->gc_keep (info);
+
+ /* Try to parse each bfd's .eh_frame section. Point elf_eh_frame_section
+ at the .eh_frame section if we can mark the FDEs individually. */
+ _bfd_elf_begin_eh_frame_parsing (info);
+ for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
+ {
+ asection *sec;
+ struct elf_reloc_cookie cookie;
+
+ sec = bfd_get_section_by_name (sub, ".eh_frame");
+ if (sec && init_reloc_cookie_for_section (&cookie, info, sec))
+ {
+ _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
+ if (elf_section_data (sec)->sec_info)
+ elf_eh_frame_section (sub) = sec;
+ fini_reloc_cookie_for_section (&cookie, sec);
+ }
+ }
+ _bfd_elf_end_eh_frame_parsing (info);
+
/* Apply transitive closure to the vtable entry usage info. */
elf_link_hash_traverse (elf_hash_table (info),
elf_gc_propagate_vtable_entries_used,
/* Allow the backend to mark additional target specific sections. */
if (bed->gc_mark_extra_sections)
- bed->gc_mark_extra_sections(info, gc_mark_hook);
-
- /* ... again for sections marked from eh_frame. */
- for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
- {
- asection *o;
-
- if (bfd_get_flavour (sub) != bfd_target_elf_flavour)
- continue;
-
- /* Keep .gcc_except_table.* if the associated .text.* (or the
- associated .gnu.linkonce.t.* if .text.* doesn't exist) is
- marked. This isn't very nice, but the proper solution,
- splitting .eh_frame up and using comdat doesn't pan out
- easily due to needing special relocs to handle the
- difference of two symbols in separate sections.
- Don't keep code sections referenced by .eh_frame. */
-#define TEXT_PREFIX ".text."
-#define TEXT_PREFIX2 ".gnu.linkonce.t."
-#define GCC_EXCEPT_TABLE_PREFIX ".gcc_except_table."
- for (o = sub->sections; o != NULL; o = o->next)
- if (!o->gc_mark && o->gc_mark_from_eh && (o->flags & SEC_CODE) == 0)
- {
- if (CONST_STRNEQ (o->name, GCC_EXCEPT_TABLE_PREFIX))
- {
- char *fn_name;
- const char *sec_name;
- asection *fn_text;
- unsigned o_name_prefix_len , fn_name_prefix_len, tmp;
-
- o_name_prefix_len = strlen (GCC_EXCEPT_TABLE_PREFIX);
- sec_name = o->name + o_name_prefix_len;
- fn_name_prefix_len = strlen (TEXT_PREFIX);
- tmp = strlen (TEXT_PREFIX2);
- if (tmp > fn_name_prefix_len)
- fn_name_prefix_len = tmp;
- fn_name
- = bfd_malloc (fn_name_prefix_len + strlen (sec_name) + 1);
- if (fn_name == NULL)
- return FALSE;
-
- /* Try the first prefix. */
- sprintf (fn_name, "%s%s", TEXT_PREFIX, sec_name);
- fn_text = bfd_get_section_by_name (sub, fn_name);
-
- /* Try the second prefix. */
- if (fn_text == NULL)
- {
- sprintf (fn_name, "%s%s", TEXT_PREFIX2, sec_name);
- fn_text = bfd_get_section_by_name (sub, fn_name);
- }
-
- free (fn_name);
- if (fn_text == NULL || !fn_text->gc_mark)
- continue;
- }
-
- /* If not using specially named exception table section,
- then keep whatever we are using. */
- if (!_bfd_elf_gc_mark (info, o, gc_mark_hook))
- return FALSE;
- }
- }
+ bed->gc_mark_extra_sections (info, gc_mark_hook);
/* ... and mark SEC_EXCLUDE for those that go. */
return elf_gc_sweep (abfd, info);
/* Need to: get the symbol; get the section. */
isym = &rcookie->locsyms[r_symndx];
- if (isym->st_shndx < SHN_LORESERVE || isym->st_shndx > SHN_HIRESERVE)
- {
- isec = bfd_section_from_elf_index (rcookie->abfd, isym->st_shndx);
- if (isec != NULL && elf_discarded_section (isec))
- return TRUE;
- }
+ isec = bfd_section_from_elf_index (rcookie->abfd, isym->st_shndx);
+ if (isec != NULL && elf_discarded_section (isec))
+ return TRUE;
}
return FALSE;
}
{
struct elf_reloc_cookie cookie;
asection *stab, *eh;
- Elf_Internal_Shdr *symtab_hdr;
const struct elf_backend_data *bed;
bfd *abfd;
- unsigned int count;
bfd_boolean ret = FALSE;
if (info->traditional_format
|| !is_elf_hash_table (info->hash))
return FALSE;
+ _bfd_elf_begin_eh_frame_parsing (info);
for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
{
if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
&& bed->elf_backend_discard_info == NULL)
continue;
- symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
- cookie.abfd = abfd;
- cookie.sym_hashes = elf_sym_hashes (abfd);
- cookie.bad_symtab = elf_bad_symtab (abfd);
- if (cookie.bad_symtab)
- {
- cookie.locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
- cookie.extsymoff = 0;
- }
- else
- {
- cookie.locsymcount = symtab_hdr->sh_info;
- cookie.extsymoff = symtab_hdr->sh_info;
- }
-
- if (bed->s->arch_size == 32)
- cookie.r_sym_shift = 8;
- else
- cookie.r_sym_shift = 32;
-
- cookie.locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
- if (cookie.locsyms == NULL && cookie.locsymcount != 0)
- {
- cookie.locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
- cookie.locsymcount, 0,
- NULL, NULL, NULL);
- if (cookie.locsyms == NULL)
- {
- info->callbacks->einfo (_("%P%X: can not read symbols: %E\n"));
- return FALSE;
- }
- }
+ if (!init_reloc_cookie (&cookie, info, abfd))
+ return FALSE;
- if (stab != NULL)
+ if (stab != NULL
+ && stab->reloc_count > 0
+ && init_reloc_cookie_rels (&cookie, info, abfd, stab))
{
- cookie.rels = NULL;
- count = stab->reloc_count;
- if (count != 0)
- cookie.rels = _bfd_elf_link_read_relocs (abfd, stab, NULL, NULL,
- info->keep_memory);
- if (cookie.rels != NULL)
- {
- cookie.rel = cookie.rels;
- cookie.relend = cookie.rels;
- cookie.relend += count * bed->s->int_rels_per_ext_rel;
- if (_bfd_discard_section_stabs (abfd, stab,
- elf_section_data (stab)->sec_info,
- bfd_elf_reloc_symbol_deleted_p,
- &cookie))
- ret = TRUE;
- if (elf_section_data (stab)->relocs != cookie.rels)
- free (cookie.rels);
- }
+ if (_bfd_discard_section_stabs (abfd, stab,
+ elf_section_data (stab)->sec_info,
+ bfd_elf_reloc_symbol_deleted_p,
+ &cookie))
+ ret = TRUE;
+ fini_reloc_cookie_rels (&cookie, stab);
}
- if (eh != NULL)
+ if (eh != NULL
+ && init_reloc_cookie_rels (&cookie, info, abfd, eh))
{
- cookie.rels = NULL;
- count = eh->reloc_count;
- if (count != 0)
- cookie.rels = _bfd_elf_link_read_relocs (abfd, eh, NULL, NULL,
- info->keep_memory);
- cookie.rel = cookie.rels;
- cookie.relend = cookie.rels;
- if (cookie.rels != NULL)
- cookie.relend += count * bed->s->int_rels_per_ext_rel;
-
+ _bfd_elf_parse_eh_frame (abfd, info, eh, &cookie);
if (_bfd_elf_discard_section_eh_frame (abfd, info, eh,
bfd_elf_reloc_symbol_deleted_p,
&cookie))
ret = TRUE;
-
- if (cookie.rels != NULL
- && elf_section_data (eh)->relocs != cookie.rels)
- free (cookie.rels);
+ fini_reloc_cookie_rels (&cookie, eh);
}
if (bed->elf_backend_discard_info != NULL
&& (*bed->elf_backend_discard_info) (abfd, &cookie, info))
ret = TRUE;
- if (cookie.locsyms != NULL
- && symtab_hdr->contents != (unsigned char *) cookie.locsyms)
- {
- if (! info->keep_memory)
- free (cookie.locsyms);
- else
- symtab_hdr->contents = (unsigned char *) cookie.locsyms;
- }
+ fini_reloc_cookie (&cookie, abfd);
}
+ _bfd_elf_end_eh_frame_parsing (info);
if (info->eh_frame_hdr
&& !info->relocatable
}
/* This is the first section with this name. Record it. */
- bfd_section_already_linked_table_insert (already_linked_list, sec);
+ if (! bfd_section_already_linked_table_insert (already_linked_list, sec))
+ info->callbacks->einfo (_("%F%P: already_linked_table: %E"));
}
bfd_boolean