/* 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.
&& (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;
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. */
+ the versioned symbol point to this one. */
bed = get_elf_backend_data (output_bfd);
hv = h;
while (hv->root.type == bfd_link_hash_indirect
(*_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;
}
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
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, htab->root.creator))
{
asection *o;
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. */
/* Check if 2 sections define the same set of local and global
symbols. */
-bfd_boolean
+static bfd_boolean
bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
struct bfd_link_info *info)
{
bfd1 = sec1->owner;
bfd2 = sec2->owner;
- /* If both are .gnu.linkonce sections, they have to have the same
- section name. */
- if (CONST_STRNEQ (sec1->name, ".gnu.linkonce")
- && CONST_STRNEQ (sec2->name, ".gnu.linkonce"))
- return strcmp (sec1->name + sizeof ".gnu.linkonce",
- sec2->name + sizeof ".gnu.linkonce") == 0;
-
/* Both sections have to be in ELF. */
if (bfd_get_flavour (bfd1) != bfd_target_elf_flavour
|| bfd_get_flavour (bfd2) != bfd_target_elf_flavour)
if (elf_section_type (sec1) != elf_section_type (sec2))
return FALSE;
- if ((elf_section_flags (sec1) & SHF_GROUP) != 0
- && (elf_section_flags (sec2) & SHF_GROUP) != 0)
- {
- /* If both are members of section groups, they have to have the
- same group name. */
- if (strcmp (elf_group_name (sec1), elf_group_name (sec2)) != 0)
- return FALSE;
- }
-
shndx1 = _bfd_elf_section_from_bfd_section (bfd1, sec1);
shndx2 = _bfd_elf_section_from_bfd_section (bfd2, sec2);
if (shndx1 == -1 || shndx2 == -1)
Complex relocations are generalized, self-describing relocations. The
implementation of them consists of two parts: complex symbols, and the
- relocations themselves.
+ 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
<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)
+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;
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;
-
+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)
}
/* 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.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,
- 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)
-{
- int len;
- int symlen;
- bfd_vma a;
- bfd_vma b;
- const int bufsz = 4096;
- char symbuf [bufsz];
+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;
+ 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 '.':
case 'S':
symbol_is_section = TRUE;
- case 's':
+ 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';
+ symbuf[symlen] = '\0';
*symp = sym + symlen;
-
- /* Is it always possible, with complex symbols, that gas "mis-guessed"
+
+ /* 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)
&& !resolve_symbol (symbuf, input_bfd, finfo, result,
undefined_reference ("section", symbuf);
return FALSE;
}
- }
- else
+ }
+ else
{
if (!resolve_symbol (symbuf, input_bfd, finfo, result,
isymbuf, locsymcount)
}
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 (*sym == ':') \
+ ++sym; \
*symp = sym; \
if (!eval_symbol (&a, symp, input_bfd, finfo, dot, \
isymbuf, locsymcount, signed_p)) \
- return FALSE; \
- if (signed_p) \
+ return FALSE; \
+ if (signed_p) \
*result = op ((bfd_signed_vma) a); \
- else \
- * result = op a; \
+ else \
+ *result = op a; \
return TRUE; \
}
if (strncmp (sym, #op, strlen (#op)) == 0) \
{ \
sym += strlen (#op); \
- if (* sym == ':') \
- ++ sym; \
+ if (*sym == ':') \
+ ++sym; \
*symp = sym; \
if (!eval_symbol (&a, symp, input_bfd, finfo, dot, \
isymbuf, locsymcount, signed_p)) \
- return FALSE; \
+ return FALSE; \
++*symp; \
if (!eval_symbol (&b, symp, input_bfd, finfo, dot, \
isymbuf, locsymcount, signed_p)) \
- return FALSE; \
- if (signed_p) \
+ return FALSE; \
+ if (signed_p) \
*result = ((bfd_signed_vma) a) op ((bfd_signed_vma) b); \
- else \
- * result = a op b; \
+ else \
+ *result = a op b; \
return TRUE; \
}
}
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_reloc_status_type
bfd_elf_perform_complex_relocation (bfd *input_bfd,
- asection *input_section,
+ 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.). */
+ word size, etc) encoded within it.). */
- 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
if (sym->st_shndx > SHN_HIRESERVE)
{
/* 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);
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);
+ }
}
}
}
{
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;
}
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_link_input_bfd ignores this section. */
input_section->flags &= ~SEC_HAS_CONTENTS;
}
-
+
attr_size = bfd_elf_obj_attr_size (abfd);
if (attr_size)
{
if (dyn.d_tag == DT_TEXTREL)
{
- info->callbacks->einfo
+ info->callbacks->einfo
(_("%P: warning: creating a DT_TEXTREL in a shared object.\n"));
break;
}
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);
{
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