/* ELF linking support for BFD.
Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- 2005, 2006 Free Software Foundation, Inc.
+ 2005, 2006, 2007 Free Software Foundation, Inc.
This file is part of BFD, the Binary File Descriptor library.
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
-#include "bfd.h"
#include "sysdep.h"
+#include "bfd.h"
#include "bfdlink.h"
#include "libbfd.h"
#define ARCH_SIZE 0
return FALSE;
}
- if (! info->traditional_format)
- {
- s = bfd_make_section_with_flags (abfd, ".eh_frame_hdr",
- flags | SEC_READONLY);
- if (s == NULL
- || ! bfd_set_section_alignment (abfd, s, 2))
- return FALSE;
- elf_hash_table (info)->eh_info.hdr_sec = s;
- }
-
/* Create sections to hold version informations. These are removed
if they are not needed. */
s = bfd_make_section_with_flags (abfd, ".gnu.version_d",
void
bfd_elf_link_mark_dynamic_symbol (struct bfd_link_info *info,
- struct elf_link_hash_entry *h)
+ struct elf_link_hash_entry *h,
+ Elf_Internal_Sym *sym)
{
- struct bfd_elf_dynamic_list *d = info->dynamic;
+ struct bfd_elf_dynamic_list *d = info->dynamic_list;
- if (d == NULL || info->relocatable)
+ /* It may be called more than once on the same H. */
+ if(h->dynamic || info->relocatable)
return;
- if ((*d->match) (&d->head, NULL, h->root.root.string))
+ if ((info->dynamic_data
+ && (h->type == STT_OBJECT
+ || (sym != NULL
+ && ELF_ST_TYPE (sym->st_info) == STT_OBJECT)))
+ || (d != NULL
+ && h->root.type == bfd_link_hash_new
+ && (*d->match) (&d->head, NULL, h->root.root.string)))
h->dynamic = 1;
}
if (h->root.type == bfd_link_hash_new)
{
- bfd_elf_link_mark_dynamic_symbol (info, h);
+ bfd_elf_link_mark_dynamic_symbol (info, h, NULL);
h->non_elf = 0;
}
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ /* We have to check it for every instance since the first few may be
+ refereences and not all compilers emit symbol type for undefined
+ symbols. */
+ bfd_elf_link_mark_dynamic_symbol (info, h, sym);
+
/* If we just created the symbol, mark it as being an ELF symbol.
Other than that, there is nothing to do--there is no merge issue
with a newly defined symbol--so we just return. */
if (h->root.type == bfd_link_hash_new)
{
- bfd_elf_link_mark_dynamic_symbol (info, h);
h->non_elf = 0;
return TRUE;
}
&& 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
&& (olddef || h->root.type == bfd_link_hash_common)
&& ELF_ST_TYPE (sym->st_info) != h->type
&& ELF_ST_TYPE (sym->st_info) != STT_NOTYPE
- && h->type != STT_NOTYPE)
+ && h->type != STT_NOTYPE
+ && !(bed->is_function_type (ELF_ST_TYPE (sym->st_info))
+ && bed->is_function_type (h->type)))
{
*skip = TRUE;
return TRUE;
if (olddef && newdyn)
oldweak = FALSE;
+ /* Allow changes between different types of funciton symbol. */
+ if (bed->is_function_type (ELF_ST_TYPE (sym->st_info))
+ && bed->is_function_type (h->type))
+ *type_change_ok = TRUE;
+
/* It's OK to change the type if either the existing symbol or the
new symbol is weak. A type change is also OK if the old symbol
is undefined and the new symbol is defined. */
&& (sec->flags & SEC_ALLOC) != 0
&& (sec->flags & SEC_LOAD) == 0
&& sym->st_size > 0
- && ELF_ST_TYPE (sym->st_info) != STT_FUNC)
+ && !bed->is_function_type (ELF_ST_TYPE (sym->st_info)))
newdyncommon = TRUE;
else
newdyncommon = FALSE;
&& (h->root.u.def.section->flags & SEC_ALLOC) != 0
&& (h->root.u.def.section->flags & SEC_LOAD) == 0
&& h->size > 0
- && h->type != STT_FUNC)
+ && !bed->is_function_type (h->type))
olddyncommon = TRUE;
else
olddyncommon = FALSE;
/* We now know everything about the old and new symbols. We ask the
backend to check if we can merge them. */
- bed = get_elf_backend_data (abfd);
if (bed->merge_symbol
&& !bed->merge_symbol (info, sym_hash, h, sym, psec, pvalue,
pold_alignment, skip, override,
&& (olddef
|| (h->root.type == bfd_link_hash_common
&& (newweak
- || ELF_ST_TYPE (sym->st_info) == STT_FUNC))))
+ || bed->is_function_type (ELF_ST_TYPE (sym->st_info))))))
{
*override = TRUE;
newdef = FALSE;
&& (newdef
|| (bfd_is_com_section (sec)
&& (oldweak
- || h->type == STT_FUNC)))
+ || bed->is_function_type (h->type))))
&& olddyn
&& olddef
&& h->def_dynamic)
hi = h;
}
+ /* Check if HI is a warning symbol. */
+ if (hi->root.type == bfd_link_hash_warning)
+ hi = (struct elf_link_hash_entry *) hi->root.u.i.link;
+
/* If there is a duplicate definition somewhere, then HI may not
point to an indirect symbol. We will have reported an error to
the user in that case. */
return TRUE;
}
+/* Adjust the dynamic symbol, H, for copy in the dynamic bss section,
+ DYNBSS. */
+
+bfd_boolean
+_bfd_elf_adjust_dynamic_copy (struct elf_link_hash_entry *h,
+ asection *dynbss)
+{
+ unsigned int power_of_two;
+ bfd_vma mask;
+ asection *sec = h->root.u.def.section;
+
+ /* The section aligment of definition is the maximum alignment
+ requirement of symbols defined in the section. Since we don't
+ know the symbol alignment requirement, we start with the
+ maximum alignment and check low bits of the symbol address
+ for the minimum alignment. */
+ power_of_two = bfd_get_section_alignment (sec->owner, sec);
+ mask = ((bfd_vma) 1 << power_of_two) - 1;
+ while ((h->root.u.def.value & mask) != 0)
+ {
+ mask >>= 1;
+ --power_of_two;
+ }
+
+ if (power_of_two > bfd_get_section_alignment (dynbss->owner,
+ dynbss))
+ {
+ /* Adjust the section alignment if needed. */
+ if (! bfd_set_section_alignment (dynbss->owner, dynbss,
+ power_of_two))
+ return FALSE;
+ }
+
+ /* We make sure that the symbol will be aligned properly. */
+ dynbss->size = BFD_ALIGN (dynbss->size, mask + 1);
+
+ /* Define the symbol as being at this point in DYNBSS. */
+ h->root.u.def.section = dynbss;
+ h->root.u.def.value = dynbss->size;
+
+ /* Increment the size of DYNBSS to make room for the symbol. */
+ dynbss->size += h->size;
+
+ return TRUE;
+}
+
/* Adjust all external symbols pointing into SEC_MERGE sections
to reflect the object merging within the sections. */
bfd_boolean ignore_protected)
{
bfd_boolean binding_stays_local_p;
+ const struct elf_backend_data *bed;
+ struct elf_link_hash_table *hash_table;
if (h == NULL)
return FALSE;
return FALSE;
case STV_PROTECTED:
+ hash_table = elf_hash_table (info);
+ if (!is_elf_hash_table (hash_table))
+ return FALSE;
+
+ bed = get_elf_backend_data (hash_table->dynobj);
+
/* Proper resolution for function pointer equality may require
that these symbols perhaps be resolved dynamically, even though
we should be resolving them to the current module. */
- if (!ignore_protected || h->type != STT_FUNC)
+ if (!ignore_protected || !bed->is_function_type (h->type))
binding_stays_local_p = TRUE;
break;
struct bfd_link_info *info,
bfd_boolean local_protected)
{
+ const struct elf_backend_data *bed;
+ struct elf_link_hash_table *hash_table;
+
/* If it's a local sym, of course we resolve locally. */
if (h == NULL)
return TRUE;
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;
+
+ bed = get_elf_backend_data (hash_table->dynobj);
+
/* STV_PROTECTED non-function symbols are local. */
- if (h->type != STT_FUNC)
+ if (!bed->is_function_type (h->type))
return TRUE;
/* Function pointer equality tests may require that STV_PROTECTED
&& ELF_ST_BIND (sym->st_info) < STB_LOOS)
return FALSE;
+ bed = get_elf_backend_data (abfd);
/* Function symbols do not count. */
- if (ELF_ST_TYPE (sym->st_info) == STT_FUNC)
+ if (bed->is_function_type (ELF_ST_TYPE (sym->st_info)))
return FALSE;
/* If the section is undefined, then so is the symbol. */
/* If the symbol is defined in the common section, then
it is a common definition and so does not count. */
- bed = get_elf_backend_data (abfd);
if (bed->common_definition (sym))
return FALSE;
sec = bfd_abs_section_ptr;
else if (sec->kept_section)
{
- /* Symbols from discarded section are undefined, and have
- default visibility. */
+ /* Symbols from discarded section are undefined. We keep
+ its visibility. */
sec = bfd_und_section_ptr;
isym->st_shndx = SHN_UNDEF;
- isym->st_other = (STV_DEFAULT
- | (isym->st_other & ~ ELF_ST_VISIBILITY (-1)));
}
else if ((abfd->flags & (EXEC_P | DYNAMIC)) != 0)
value -= sec->vma;
if it is not a function, because it might be the version
symbol itself. FIXME: What if it isn't? */
if ((iver.vs_vers & VERSYM_HIDDEN) != 0
- || (vernum > 1 && (! bfd_is_abs_section (sec)
- || ELF_ST_TYPE (isym->st_info) == STT_FUNC)))
+ || (vernum > 1
+ && (!bfd_is_abs_section (sec)
+ || bed->is_function_type (ELF_ST_TYPE (isym->st_info)))))
{
const char *verstr;
size_t namelen, verlen, newlen;
if (dynamic
&& definition
&& (flags & BSF_WEAK) != 0
- && ELF_ST_TYPE (isym->st_info) != STT_FUNC
+ && !bed->is_function_type (ELF_ST_TYPE (isym->st_info))
&& is_elf_hash_table (htab)
&& h->u.weakdef == NULL)
{
}
}
- /* Remember the symbol size and type. */
- if (isym->st_size != 0
+ /* Remember the symbol size if it isn't undefined. */
+ if ((isym->st_size != 0 && isym->st_shndx != SHN_UNDEF)
&& (definition || h->size == 0))
{
- if (h->size != 0 && h->size != isym->st_size && ! size_change_ok)
+ if (h->size != 0
+ && h->size != isym->st_size
+ && ! size_change_ok)
(*_bfd_error_handler)
(_("Warning: size of symbol `%s' changed"
" from %lu in %B to %lu in %B"),
to be the size of the common symbol. The code just above
won't fix the size if a common symbol becomes larger. We
don't warn about a size change here, because that is
- covered by --warn-common. */
+ covered by --warn-common. Allow changed between different
+ function types. */
if (h->root.type == bfd_link_hash_common)
h->size = h->root.u.c.size;
unsigned int i;
/* Restore the symbol table. */
+ if (bed->as_needed_cleanup)
+ (*bed->as_needed_cleanup) (abfd, info);
old_hash = (char *) old_tab + tabsize;
old_ent = (char *) old_hash + hashsize;
sym_hash = elf_sym_hashes (abfd);
h = *hpp;
if (h != NULL
&& h->root.type == bfd_link_hash_defined
- && h->type != STT_FUNC)
+ && !bed->is_function_type (h->type))
{
*sym_hash = h;
sym_hash++;
if (!is_elf_hash_table (info->hash))
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;
exec = PF_X;
notesec = s;
}
- else
+ else if (bed->default_execstack)
exec = PF_X;
}
if (notesec)
/* The backend may have to create some sections regardless of whether
we're dynamic or not. */
- bed = get_elf_backend_data (output_bfd);
if (bed->elf_backend_always_size_sections
&& ! (*bed->elf_backend_always_size_sections) (output_bfd, info))
return FALSE;
+ if (! _bfd_elf_maybe_strip_eh_frame_hdr (info))
+ return FALSE;
+
dynobj = elf_hash_table (info)->dynobj;
/* If there were no dynamic objects in the link, there is nothing to
if (dynobj == NULL)
return TRUE;
- if (! _bfd_elf_maybe_strip_eh_frame_hdr (info))
- return FALSE;
-
if (elf_hash_table (info)->dynamic_sections_created)
{
struct elf_info_failed eif;
for (sub = info->input_bfds; sub != NULL;
sub = sub->link_next)
- for (o = sub->sections; o != NULL; o = o->next)
- if (elf_section_data (o)->this_hdr.sh_type
- == SHT_PREINIT_ARRAY)
- {
- (*_bfd_error_handler)
- (_("%B: .preinit_array section is not allowed in DSO"),
- sub);
- break;
- }
+ if (bfd_get_flavour (sub) == bfd_target_elf_flavour)
+ for (o = sub->sections; o != NULL; o = o->next)
+ if (elf_section_data (o)->this_hdr.sh_type
+ == SHT_PREINIT_ARRAY)
+ {
+ (*_bfd_error_handler)
+ (_("%B: .preinit_array section is not allowed in DSO"),
+ sub);
+ break;
+ }
bfd_set_error (bfd_error_nonrepresentable_section);
return FALSE;
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,
+ struct elf_final_link_info * finfo,
+ int symidx,
+ bfd_vma val)
+{
+ bfd_boolean is_local;
+ Elf_Internal_Sym * sym;
+ struct elf_link_hash_entry ** sym_hashes;
+ struct elf_link_hash_entry * h;
+
+ 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)
+ {
+ /* 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;
+ }
+ else
+ {
+ /* 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;
+
+ for (i = 0; i < locsymcount; ++ i)
+ {
+ sym = finfo->internal_syms + i;
+ sec = finfo->sections [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);
+#endif
+ if (candidate && strcmp (candidate, name) == 0)
+ {
+ * result = sym->st_value;
+
+ 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;
+ }
+#ifdef DEBUG
+ printf ("Found symbol with effective value %8.8x\n", (unsigned int)* 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);
+ 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;
+#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);
+#endif
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static bfd_boolean
+resolve_section (const char * name,
+ asection * sections,
+ bfd_vma * result)
+{
+ asection * curr;
+ unsigned int len;
+
+ for (curr = sections; curr; curr = curr->next)
+ if (strcmp (curr->name, name) == 0)
+ {
+ *result = curr->vma;
+ return TRUE;
+ }
+
+ /* Hmm. still haven't found it. try pseudo-section names. */
+ for (curr = sections; curr; curr = curr->next)
+ {
+ len = strlen (curr->name);
+ if (len > strlen (name))
+ continue;
+
+ if (strncmp (curr->name, name, len) == 0)
+ {
+ if (strncmp (".end", name + len, 4) == 0)
+ {
+ *result = curr->vma + curr->size;
+ return TRUE;
+ }
+
+ /* Insert more pseudo-section names here, if you like. */
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+undefined_reference (const char * reftype,
+ const char * 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;
+
+ len = strlen (sym);
+ symend = sym + len;
+
+ if (len < 1 || len > bufsz)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return FALSE;
+ }
+
+ switch (* sym)
+ {
+ case '.':
+ * result = addr + section_offset;
+ * advanced = sym + 1;
+ return TRUE;
+
+ case '#':
+ ++ sym;
+ * result = strtoul (sym, advanced, 16);
+ return TRUE;
+
+ case 'S':
+ symbol_is_section = TRUE;
+ case 's':
+ ++ sym;
+ symlen = strtol (sym, &sym, 10);
+ ++ sym; /* Skip the trailing ':'. */
+
+ if ((symend < sym) || ((symlen + 1) > bufsz))
+ {
+ 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"
+ 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 ((resolve_section (symbuf, finfo->output_bfd->sections, result) != TRUE)
+ && (resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE))
+ {
+ undefined_reference ("section", symbuf);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if ((resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE)
+ && (resolve_section (symbuf, finfo->output_bfd->sections,
+ result) != TRUE))
+ {
+ 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; \
+ return TRUE; \
+ }
+
+#define BINARY_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; \
+ ++ 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; \
+ return TRUE; \
+ }
+
+ default:
+ UNARY_OP (0-);
+ BINARY_OP (<<);
+ BINARY_OP (>>);
+ BINARY_OP (==);
+ BINARY_OP (!=);
+ BINARY_OP (<=);
+ BINARY_OP (>=);
+ BINARY_OP (&&);
+ BINARY_OP (||);
+ UNARY_OP (~);
+ UNARY_OP (!);
+ BINARY_OP (*);
+ BINARY_OP (/);
+ BINARY_OP (%);
+ BINARY_OP (^);
+ BINARY_OP (|);
+ BINARY_OP (&);
+ BINARY_OP (+);
+ BINARY_OP (-);
+ BINARY_OP (<);
+ BINARY_OP (>);
+#undef UNARY_OP
+#undef BINARY_OP
+ _bfd_error_handler (_("unknown operator '%c' in complex symbol"), * sym);
+ bfd_set_error (bfd_error_invalid_operation);
+ return FALSE;
+ }
+}
+
+/* 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)
+{
+ location += (size - chunksz);
+
+ for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8))
+ {
+ switch (chunksz)
+ {
+ default:
+ case 0:
+ abort ();
+ case 1:
+ bfd_put_8 (input_bfd, x, location);
+ break;
+ case 2:
+ bfd_put_16 (input_bfd, x, location);
+ break;
+ case 4:
+ bfd_put_32 (input_bfd, x, location);
+ break;
+ case 8:
+#ifdef BFD64
+ bfd_put_64 (input_bfd, x, location);
+#else
+ abort ();
+#endif
+ break;
+ }
+ }
+}
+
+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)
+ {
+ switch (chunksz)
+ {
+ default:
+ case 0:
+ abort ();
+ case 1:
+ x = (x << (8 * chunksz)) | bfd_get_8 (input_bfd, location);
+ break;
+ case 2:
+ x = (x << (8 * chunksz)) | bfd_get_16 (input_bfd, location);
+ break;
+ case 4:
+ x = (x << (8 * chunksz)) | bfd_get_32 (input_bfd, location);
+ break;
+ case 8:
+#ifdef BFD64
+ x = (x << (8 * chunksz)) | bfd_get_64 (input_bfd, location);
+#else
+ abort ();
+#endif
+ break;
+ }
+ }
+ 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)
+{
+ * start = encoded & 0x3F;
+ * len = (encoded >> 6) & 0x3F;
+ * oplen = (encoded >> 12) & 0x3F;
+ * wordsz = (encoded >> 18) & 0xF;
+ * chunksz = (encoded >> 22) & 0xF;
+ * lsb0_p = (encoded >> 27) & 1;
+ * signed_p = (encoded >> 28) & 1;
+ * 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;
+
+ /* 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;
+
+ 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);
+
+ mask = (((1L << (len - 1)) - 1) << 1) | 1;
+
+ if (lsb0_p)
+ shift = (start + 1) - len;
+ else
+ shift = (8 * wordsz) - (start + len);
+
+ x = get_value (wordsz, chunksz, input_bfd, contents + rel->r_offset);
+
+#ifdef DEBUG
+ printf ("Doing complex reloc: "
+ "lsb0? %ld, signed? %ld, trunc? %ld, wordsz %ld, "
+ "chunksz %ld, start %ld, len %ld, oplen %ld\n"
+ " dest: %8.8lx, mask: %8.8lx, reloc: %8.8lx\n",
+ lsb0_p, signed_p, trunc_p, wordsz, chunksz, start, len,
+ oplen, x, mask, relocation);
+#endif
+
+ 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);
+ }
+
+ /* Do the deed. */
+ x = (x & ~(mask << shift)) | ((relocation & mask) << shift);
+
+#ifdef DEBUG
+ printf (" relocation: %8.8lx\n"
+ " shifted mask: %8.8lx\n"
+ " shifted/masked reloc: %8.8lx\n"
+ " result: %8.8lx\n",
+ relocation, (mask << shift),
+ ((relocation & mask) << shift), x);
+#endif
+ put_value (wordsz, chunksz, input_bfd, x, contents + rel->r_offset);
+}
+
/* When performing a relocatable link, the input relocations are
preserved. But, if they reference global symbols, the indices
referenced must be updated. Update all the relocations in
static size_t
elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
{
- asection *reldyn;
+ asection *dynamic_relocs;
+ asection *rela_dyn;
+ asection *rel_dyn;
bfd_size_type count, size;
size_t i, ret, sort_elt, ext_size;
bfd_byte *sort, *s_non_relative, *p;
void (*swap_out) (bfd *, const Elf_Internal_Rela *, bfd_byte *);
struct bfd_link_order *lo;
bfd_vma r_sym_mask;
+ bfd_boolean use_rela;
- reldyn = bfd_get_section_by_name (abfd, ".rela.dyn");
- if (reldyn == NULL || reldyn->size == 0)
+ /* Find a dynamic reloc section. */
+ rela_dyn = bfd_get_section_by_name (abfd, ".rela.dyn");
+ rel_dyn = bfd_get_section_by_name (abfd, ".rel.dyn");
+ if (rela_dyn != NULL && rela_dyn->size > 0
+ && rel_dyn != NULL && rel_dyn->size > 0)
{
- reldyn = bfd_get_section_by_name (abfd, ".rel.dyn");
- if (reldyn == NULL || reldyn->size == 0)
- return 0;
- ext_size = bed->s->sizeof_rel;
- swap_in = bed->s->swap_reloc_in;
- swap_out = bed->s->swap_reloc_out;
+ bfd_boolean use_rela_initialised = FALSE;
+
+ /* This is just here to stop gcc from complaining.
+ It's initialization checking code is not perfect. */
+ use_rela = TRUE;
+
+ /* Both sections are present. Examine the sizes
+ of the indirect sections to help us choose. */
+ for (lo = rela_dyn->map_head.link_order; lo != NULL; lo = lo->next)
+ if (lo->type == bfd_indirect_link_order)
+ {
+ asection *o = lo->u.indirect.section;
+
+ if ((o->size % bed->s->sizeof_rela) == 0)
+ {
+ if ((o->size % bed->s->sizeof_rel) == 0)
+ /* Section size is divisible by both rel and rela sizes.
+ It is of no help to us. */
+ ;
+ else
+ {
+ /* Section size is only divisible by rela. */
+ if (use_rela_initialised && (use_rela == FALSE))
+ {
+ _bfd_error_handler
+ (_("%B: Unable to sort relocs - they are in more than one size"), abfd);
+ bfd_set_error (bfd_error_invalid_operation);
+ return 0;
+ }
+ else
+ {
+ use_rela = TRUE;
+ use_rela_initialised = TRUE;
+ }
+ }
+ }
+ else if ((o->size % bed->s->sizeof_rel) == 0)
+ {
+ /* Section size is only divisible by rel. */
+ if (use_rela_initialised && (use_rela == TRUE))
+ {
+ _bfd_error_handler
+ (_("%B: Unable to sort relocs - they are in more than one size"), abfd);
+ bfd_set_error (bfd_error_invalid_operation);
+ return 0;
+ }
+ else
+ {
+ use_rela = FALSE;
+ use_rela_initialised = TRUE;
+ }
+ }
+ else
+ {
+ /* The section size is not divisible by either - something is wrong. */
+ _bfd_error_handler
+ (_("%B: Unable to sort relocs - they are of an unknown size"), abfd);
+ bfd_set_error (bfd_error_invalid_operation);
+ return 0;
+ }
+ }
+
+ for (lo = rel_dyn->map_head.link_order; lo != NULL; lo = lo->next)
+ if (lo->type == bfd_indirect_link_order)
+ {
+ asection *o = lo->u.indirect.section;
+
+ if ((o->size % bed->s->sizeof_rela) == 0)
+ {
+ if ((o->size % bed->s->sizeof_rel) == 0)
+ /* Section size is divisible by both rel and rela sizes.
+ It is of no help to us. */
+ ;
+ else
+ {
+ /* Section size is only divisible by rela. */
+ if (use_rela_initialised && (use_rela == FALSE))
+ {
+ _bfd_error_handler
+ (_("%B: Unable to sort relocs - they are in more than one size"), abfd);
+ bfd_set_error (bfd_error_invalid_operation);
+ return 0;
+ }
+ else
+ {
+ use_rela = TRUE;
+ use_rela_initialised = TRUE;
+ }
+ }
+ }
+ else if ((o->size % bed->s->sizeof_rel) == 0)
+ {
+ /* Section size is only divisible by rel. */
+ if (use_rela_initialised && (use_rela == TRUE))
+ {
+ _bfd_error_handler
+ (_("%B: Unable to sort relocs - they are in more than one size"), abfd);
+ bfd_set_error (bfd_error_invalid_operation);
+ return 0;
+ }
+ else
+ {
+ use_rela = FALSE;
+ use_rela_initialised = TRUE;
+ }
+ }
+ else
+ {
+ /* The section size is not divisible by either - something is wrong. */
+ _bfd_error_handler
+ (_("%B: Unable to sort relocs - they are of an unknown size"), abfd);
+ bfd_set_error (bfd_error_invalid_operation);
+ return 0;
+ }
+ }
+
+ if (! use_rela_initialised)
+ /* Make a guess. */
+ use_rela = TRUE;
}
+ else if (rela_dyn != NULL && rela_dyn->size > 0)
+ use_rela = TRUE;
+ else if (rel_dyn != NULL && rel_dyn->size > 0)
+ use_rela = FALSE;
else
+ return 0;
+
+ if (use_rela)
{
+ dynamic_relocs = rela_dyn;
ext_size = bed->s->sizeof_rela;
swap_in = bed->s->swap_reloca_in;
swap_out = bed->s->swap_reloca_out;
}
- count = reldyn->size / ext_size;
+ else
+ {
+ dynamic_relocs = rel_dyn;
+ ext_size = bed->s->sizeof_rel;
+ swap_in = bed->s->swap_reloc_in;
+ swap_out = bed->s->swap_reloc_out;
+ }
size = 0;
- for (lo = reldyn->map_head.link_order; lo != NULL; lo = lo->next)
+ for (lo = dynamic_relocs->map_head.link_order; lo != NULL; lo = lo->next)
if (lo->type == bfd_indirect_link_order)
- {
- asection *o = lo->u.indirect.section;
- size += o->size;
- }
+ size += lo->u.indirect.section->size;
- if (size != reldyn->size)
+ if (size != dynamic_relocs->size)
return 0;
sort_elt = (sizeof (struct elf_link_sort_rela)
+ (i2e - 1) * sizeof (Elf_Internal_Rela));
+
+ count = dynamic_relocs->size / ext_size;
sort = bfd_zmalloc (sort_elt * count);
+
if (sort == NULL)
{
(*info->callbacks->warning)
else
r_sym_mask = ~(bfd_vma) 0xffffffff;
- for (lo = reldyn->map_head.link_order; lo != NULL; lo = lo->next)
+ for (lo = dynamic_relocs->map_head.link_order; lo != NULL; lo = lo->next)
if (lo->type == bfd_indirect_link_order)
{
bfd_byte *erel, *erelend;
erel = o->contents;
erelend = o->contents + o->size;
p = sort + o->output_offset / ext_size * sort_elt;
+
while (erel < erelend)
{
struct elf_link_sort_rela *s = (struct elf_link_sort_rela *) p;
+
(*swap_in) (abfd, erel, s->rela);
s->type = (*bed->elf_backend_reloc_type_class) (s->rela);
s->u.sym_mask = r_sym_mask;
qsort (s_non_relative, count - ret, sort_elt, elf_link_sort_cmp2);
- for (lo = reldyn->map_head.link_order; lo != NULL; lo = lo->next)
+ for (lo = dynamic_relocs->map_head.link_order; lo != NULL; lo = lo->next)
if (lo->type == bfd_indirect_link_order)
{
bfd_byte *erel, *erelend;
}
free (sort);
- *psec = reldyn;
+ *psec = dynamic_relocs;
return ret;
}
static bfd_boolean
elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
{
- bfd_boolean (*relocate_section)
+ int (*relocate_section)
(bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
Elf_Internal_Rela *, Elf_Internal_Sym *, asection **);
bfd *output_bfd;
asection **ppsection;
asection *o;
const struct elf_backend_data *bed;
- bfd_boolean emit_relocs;
struct elf_link_hash_entry **sym_hashes;
output_bfd = finfo->output_bfd;
if ((input_bfd->flags & DYNAMIC) != 0)
return TRUE;
- emit_relocs = (finfo->info->relocatable
- || finfo->info->emitrelocations);
-
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
if (elf_bad_symtab (input_bfd))
{
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
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)
Elf_Internal_Rela *internal_relocs;
bfd_vma r_type_mask;
int r_sym_shift;
+ int ret;
/* Get the swapped relocs. */
internal_relocs
continue;
}
}
-
- /* Remove the symbol reference from the reloc, but
- don't kill the reloc completely. This is so that
- a zero value will be written into the section,
- which may have non-zero contents put there by the
- assembler. Zero in things like an eh_frame fde
- pc_begin allows stack unwinders to recognize the
- fde as bogus. */
- rel->r_info &= r_type_mask;
- rel->r_addend = 0;
}
}
}
corresponding to the output section, which will require
the addend to be adjusted. */
- if (! (*relocate_section) (output_bfd, finfo->info,
+ ret = (*relocate_section) (output_bfd, finfo->info,
input_bfd, o, contents,
internal_relocs,
isymbuf,
- finfo->sections))
+ finfo->sections);
+ if (!ret)
return FALSE;
- if (emit_relocs)
+ if (ret == 2
+ || finfo->info->relocatable
+ || finfo->info->emitrelocations)
{
Elf_Internal_Rela *irela;
Elf_Internal_Rela *irelaend;
/* If we have discarded a section, the output
section will be the absolute section. In
- case of discarded link-once and discarded
- SEC_MERGE sections, use the kept section. */
+ case of discarded SEC_MERGE sections, use
+ the kept section. relocate_section should
+ have already handled discarded linkonce
+ sections. */
if (bfd_is_abs_section (osec)
&& sec->kept_section != NULL
&& sec->kept_section->output_section != NULL)
/* Write out the modified section contents. */
if (bed->elf_backend_write_section
- && (*bed->elf_backend_write_section) (output_bfd, o, contents))
+ && (*bed->elf_backend_write_section) (output_bfd, finfo->info, o,
+ contents))
{
/* Section written out. */
}
{
Elf_Internal_Rela * relocs;
- relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
+ relocs = _bfd_elf_link_read_relocs (sec->owner, sec,
+ NULL, NULL,
info->keep_memory);
- reloc_count = (*bed->elf_backend_count_relocs) (sec, relocs);
+ if (relocs != NULL)
+ {
+ reloc_count
+ = (*bed->elf_backend_count_relocs) (sec, relocs);
- if (elf_section_data (o)->relocs != relocs)
- free (relocs);
+ if (elf_section_data (sec)->relocs != relocs)
+ free (relocs);
+ }
}
if (sec->rawsize > max_contents_size)
if (!info->reduce_memory_overheads)
{
for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
- if (elf_tdata (sub)->symbuf)
+ if (bfd_get_flavour (sub) == bfd_target_elf_flavour
+ && elf_tdata (sub)->symbuf)
{
free (elf_tdata (sub)->symbuf);
elf_tdata (sub)->symbuf = NULL;
if (dyn.d_tag == DT_TEXTREL)
{
- _bfd_error_handler
- (_("warning: creating a DT_TEXTREL in a shared object."));
+ info->callbacks->einfo
+ (_("%P: warning: creating a DT_TEXTREL in a shared object.\n"));
break;
}
}
\f
/* Garbage collect unused sections. */
-typedef asection * (*gc_mark_hook_fn)
- (asection *, struct bfd_link_info *, Elf_Internal_Rela *,
- struct elf_link_hash_entry *, Elf_Internal_Sym *);
-
/* Default gc_mark_hook. */
asection *
bfd_boolean
_bfd_elf_gc_mark (struct bfd_link_info *info,
asection *sec,
- gc_mark_hook_fn gc_mark_hook)
+ elf_gc_mark_hook_fn gc_mark_hook)
{
bfd_boolean ret;
bfd_boolean is_eh;
{
bfd_boolean ok = TRUE;
bfd *sub;
- asection * (*gc_mark_hook)
- (asection *, struct bfd_link_info *, Elf_Internal_Rela *,
- struct elf_link_hash_entry *h, Elf_Internal_Sym *);
+ elf_gc_mark_hook_fn gc_mark_hook;
const struct elf_backend_data *bed = get_elf_backend_data (abfd);
if (!bed->can_gc_sections
return FALSE;
}
+ /* 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)
{
if (bfd_get_flavour (sub) != bfd_target_elf_flavour)
continue;
- /* Keep .gcc_except_table.* if the associated .text.* is
+ /* 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)
char *fn_name;
const char *sec_name;
asection *fn_text;
- unsigned o_name_prefix_len = strlen (GCC_EXCEPT_TABLE_PREFIX);
- unsigned fn_name_prefix_len = strlen (TEXT_PREFIX);
+ 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 = bfd_malloc (strlen (sec_name) + fn_name_prefix_len + 1);
+ 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 ((abfd->flags & DYNAMIC) != 0)
continue;
- eh = bfd_get_section_by_name (abfd, ".eh_frame");
- if (info->relocatable
- || (eh != NULL
+ eh = NULL;
+ if (!info->relocatable)
+ {
+ eh = bfd_get_section_by_name (abfd, ".eh_frame");
+ if (eh != NULL
&& (eh->size == 0
- || bfd_is_abs_section (eh->output_section))))
- eh = NULL;
+ || bfd_is_abs_section (eh->output_section)))
+ eh = NULL;
+ }
stab = bfd_get_section_by_name (abfd, ".stab");
if (stab != NULL
cookie.locsymcount, 0,
NULL, NULL, NULL);
if (cookie.locsyms == NULL)
- return FALSE;
+ {
+ info->callbacks->einfo (_("%P%X: can not read symbols: %E\n"));
+ return FALSE;
+ }
}
if (stab != NULL)