/* PowerPC-specific support for 32-bit ELF
- Copyright (C) 1994-2014 Free Software Foundation, Inc.
+ Copyright (C) 1994-2015 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Cygnus Support.
This file is part of BFD, the Binary File Descriptor library.
/* This reloc does nothing. */
HOWTO (R_PPC_NONE, /* type */
0, /* rightshift */
- 2, /* size (0 = byte, 1 = short, 2 = long) */
- 32, /* bitsize */
+ 3, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
arelent *cache_ptr,
Elf_Internal_Rela *dst)
{
+ unsigned int r_type;
+
/* Initialize howto table if not already done. */
if (!ppc_elf_howto_table[R_PPC_ADDR32])
ppc_elf_howto_init ();
- BFD_ASSERT (ELF32_R_TYPE (dst->r_info) < (unsigned int) R_PPC_max);
- cache_ptr->howto = ppc_elf_howto_table[ELF32_R_TYPE (dst->r_info)];
+ r_type = ELF32_R_TYPE (dst->r_info);
+ if (r_type >= R_PPC_max)
+ {
+ (*_bfd_error_handler) (_("%B: unrecognised PPC reloc number: %d"),
+ abfd, r_type);
+ bfd_set_error (bfd_error_bad_value);
+ r_type = R_PPC_NONE;
+ }
+ cache_ptr->howto = ppc_elf_howto_table[r_type];
/* Just because the above assert didn't trigger doesn't mean that
ELF32_R_TYPE (dst->r_info) is necessarily a valid relocation. */
if (!cache_ptr->howto)
{
(*_bfd_error_handler) (_("%B: invalid relocation type %d"),
- abfd, ELF32_R_TYPE (dst->r_info));
+ abfd, r_type);
bfd_set_error (bfd_error_bad_value);
cache_ptr->howto = ppc_elf_howto_table[R_PPC_NONE];
return bfd_reloc_ok;
}
- if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
- return bfd_reloc_outofrange;
-
if (bfd_is_com_section (symbol->section))
relocation = 0;
else
apuinfo_list_init ();
/* Read in the input sections contents. */
- for (ibfd = link_info->input_bfds; ibfd; ibfd = ibfd->link_next)
+ for (ibfd = link_info->input_bfds; ibfd; ibfd = ibfd->link.next)
{
unsigned long datum;
}
count = relplt->size / sizeof (Elf32_External_Rela);
- stub_vma = glink_vma - (bfd_vma) count * 16;
/* If the stubs are those for -shared/-pie then we might have
multiple stubs for each plt entry. If that is the case then
there is no way to associate stubs with their plt entries short
if (s == NULL)
return -1;
+ stub_vma = glink_vma;
names = (char *) (s + count + 1 + (resolv_vma != 0));
- p = relplt->relocation;
- for (i = 0; i < count; i++, p++)
+ p = relplt->relocation + count - 1;
+ for (i = 0; i < count; i++)
{
size_t len;
s->flags |= BSF_GLOBAL;
s->flags |= BSF_SYNTHETIC;
s->section = glink;
+ stub_vma -= 16;
+ if (strcmp ((*p->sym_ptr_ptr)->name, "__tls_get_addr_opt") == 0)
+ stub_vma -= 32;
s->value = stub_vma - glink->vma;
s->name = names;
s->udata.p = NULL;
memcpy (names, "@plt", sizeof ("@plt"));
names += sizeof ("@plt");
++s;
- stub_vma += 16;
+ --p;
}
/* Add a symbol at the start of the glink branch table. */
/* Nonzero if we have seen a small data relocation referring to this
symbol. */
- unsigned char has_sda_refs;
+ unsigned char has_sda_refs : 1;
+
+ /* Flag use of given relocations. */
+ unsigned char has_addr16_ha : 1;
+ unsigned char has_addr16_lo : 1;
};
#define ppc_elf_hash_entry(ent) ((struct ppc_elf_link_hash_entry *) (ent))
ppc_elf_link_hash_table_create (bfd *abfd)
{
struct ppc_elf_link_hash_table *ret;
- static struct ppc_elf_params default_params = { PLT_OLD, 0, 1, 0, 0, 12 };
+ static struct ppc_elf_params default_params = { PLT_OLD, 0, 1, 0, 0, 12, 0 };
ret = bfd_zmalloc (sizeof (struct ppc_elf_link_hash_table));
if (ret == NULL)
return TRUE;
}
+/* Create a special linker section, used for R_PPC_EMB_SDAI16 and
+ R_PPC_EMB_SDA2I16 pointers. These sections become part of .sdata
+ and .sdata2. Create _SDA_BASE_ and _SDA2_BASE too. */
+
+static bfd_boolean
+ppc_elf_create_linker_section (bfd *abfd,
+ struct bfd_link_info *info,
+ flagword flags,
+ elf_linker_section_t *lsect)
+{
+ asection *s;
+
+ flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED);
+
+ s = bfd_make_section_anyway_with_flags (abfd, lsect->name, flags);
+ if (s == NULL)
+ return FALSE;
+ lsect->section = s;
+
+ /* Define the sym on the first section of this name. */
+ s = bfd_get_section_by_name (abfd, lsect->name);
+
+ lsect->sym = _bfd_elf_define_linkage_sym (abfd, info, s, lsect->sym_name);
+ if (lsect->sym == NULL)
+ return FALSE;
+ lsect->sym->root.u.def.value = 0x8000;
+ return TRUE;
+}
+
static bfd_boolean
ppc_elf_create_glink (bfd *abfd, struct bfd_link_info *info)
{
if (s == NULL
|| ! bfd_set_section_alignment (abfd, s, 2))
return FALSE;
+
+ if (!ppc_elf_create_linker_section (abfd, info, 0,
+ &htab->sdata[0]))
+ return FALSE;
+
+ if (!ppc_elf_create_linker_section (abfd, info, SEC_READONLY,
+ &htab->sdata[1]))
+ return FALSE;
+
return TRUE;
}
*valp = sym->st_size;
}
- if ((abfd->flags & DYNAMIC) == 0
- && (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
- || ELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE))
+ if ((ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
+ || ELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE)
+ && (abfd->flags & DYNAMIC) == 0
+ && bfd_get_flavour (info->output_bfd) == bfd_target_elf_flavour)
elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
return TRUE;
}
\f
-static bfd_boolean
-create_sdata_sym (struct bfd_link_info *info, elf_linker_section_t *lsect)
-{
- struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
-
- lsect->sym = elf_link_hash_lookup (&htab->elf, lsect->sym_name,
- TRUE, FALSE, TRUE);
- if (lsect->sym == NULL)
- return FALSE;
- if (lsect->sym->root.type == bfd_link_hash_new)
- lsect->sym->non_elf = 0;
- lsect->sym->ref_regular = 1;
- _bfd_elf_link_hash_hide_symbol (info, lsect->sym, TRUE);
- return TRUE;
-}
-
-/* Create a special linker section. */
-
-static bfd_boolean
-ppc_elf_create_linker_section (bfd *abfd,
- struct bfd_link_info *info,
- flagword flags,
- elf_linker_section_t *lsect)
-{
- struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
- asection *s;
-
- flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
- | SEC_LINKER_CREATED);
-
- /* Record the first bfd that needs the special sections. */
- if (!htab->elf.dynobj)
- htab->elf.dynobj = abfd;
-
- s = bfd_make_section_anyway_with_flags (htab->elf.dynobj,
- lsect->name,
- flags);
- if (s == NULL
- || !bfd_set_section_alignment (htab->elf.dynobj, s, 2))
- return FALSE;
- lsect->section = s;
-
- return create_sdata_sym (info, lsect);
-}
-
/* Find a linker generated pointer with a given addend and type. */
static elf_linker_section_pointers_t *
/* Allocate a pointer to live in a linker created section. */
static bfd_boolean
-elf_create_pointer_linker_section (bfd *abfd,
- elf_linker_section_t *lsect,
- struct elf_link_hash_entry *h,
- const Elf_Internal_Rela *rel)
+elf_allocate_pointer_linker_section (bfd *abfd,
+ elf_linker_section_t *lsect,
+ struct elf_link_hash_entry *h,
+ const Elf_Internal_Rela *rel)
{
elf_linker_section_pointers_t **ptr_linker_section_ptr = NULL;
elf_linker_section_pointers_t *linker_section_ptr;
linker_section_ptr->lsect = lsect;
*ptr_linker_section_ptr = linker_section_ptr;
+ if (!bfd_set_section_alignment (lsect->section->owner, lsect->section, 2))
+ return FALSE;
linker_section_ptr->offset = lsect->section->size;
lsect->section->size += 4;
case R_PPC_GOT_TPREL16_LO:
case R_PPC_GOT_TPREL16_HI:
case R_PPC_GOT_TPREL16_HA:
- if (!info->executable)
+ if (info->shared)
info->flags |= DF_STATIC_TLS;
tls_type = TLS_TLS | TLS_TPREL;
goto dogottls;
bad_shared_reloc (abfd, r_type);
return FALSE;
}
- if (htab->sdata[0].section == NULL
- && !ppc_elf_create_linker_section (abfd, info, 0,
- &htab->sdata[0]))
- return FALSE;
- if (!elf_create_pointer_linker_section (abfd, &htab->sdata[0],
- h, rel))
+ htab->sdata[0].sym->ref_regular = 1;
+ if (!elf_allocate_pointer_linker_section (abfd, &htab->sdata[0],
+ h, rel))
return FALSE;
if (h != NULL)
{
bad_shared_reloc (abfd, r_type);
return FALSE;
}
- if (htab->sdata[1].section == NULL
- && !ppc_elf_create_linker_section (abfd, info, SEC_READONLY,
- &htab->sdata[1]))
- return FALSE;
- if (!elf_create_pointer_linker_section (abfd, &htab->sdata[1],
- h, rel))
+ htab->sdata[1].sym->ref_regular = 1;
+ if (!elf_allocate_pointer_linker_section (abfd, &htab->sdata[1],
+ h, rel))
return FALSE;
if (h != NULL)
{
}
break;
+ case R_PPC_SDAREL16:
+ htab->sdata[0].sym->ref_regular = 1;
+ /* Fall thru */
+
case R_PPC_VLE_SDAREL_LO16A:
case R_PPC_VLE_SDAREL_LO16D:
case R_PPC_VLE_SDAREL_HI16A:
case R_PPC_VLE_SDAREL_HI16D:
case R_PPC_VLE_SDAREL_HA16A:
case R_PPC_VLE_SDAREL_HA16D:
- case R_PPC_SDAREL16:
- if (htab->sdata[0].sym == NULL
- && !create_sdata_sym (info, &htab->sdata[0]))
- return FALSE;
-
- if (htab->sdata[1].sym == NULL
- && !create_sdata_sym (info, &htab->sdata[1]))
- return FALSE;
-
if (h != NULL)
{
ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
bad_shared_reloc (abfd, r_type);
return FALSE;
}
- if (htab->sdata[1].sym == NULL
- && !create_sdata_sym (info, &htab->sdata[1]))
- return FALSE;
+ htab->sdata[1].sym->ref_regular = 1;
if (h != NULL)
{
ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
bad_shared_reloc (abfd, r_type);
return FALSE;
}
- if (htab->sdata[0].sym == NULL
- && !create_sdata_sym (info, &htab->sdata[0]))
- return FALSE;
- if (htab->sdata[1].sym == NULL
- && !create_sdata_sym (info, &htab->sdata[1]))
- return FALSE;
if (h != NULL)
{
ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
case R_PPC_TPREL16_LO:
case R_PPC_TPREL16_HI:
case R_PPC_TPREL16_HA:
- if (!info->executable)
+ if (info->shared)
info->flags |= DF_STATIC_TLS;
goto dodyn;
/* We may need a copy reloc too. */
h->non_got_ref = 1;
h->pointer_equality_needed = 1;
+ if (r_type == R_PPC_ADDR16_HA)
+ ppc_elf_hash_entry (h)->has_addr16_ha = 1;
+ if (r_type == R_PPC_ADDR16_LO)
+ ppc_elf_hash_entry (h)->has_addr16_lo = 1;
}
goto dodyn;
--secure-plt and we never see REL16 relocs. */
if (plt_type == PLT_UNSET)
plt_type = PLT_OLD;
- for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link.next)
if (is_ppc_elf (ibfd))
{
if (ppc_elf_tdata (ibfd)->has_rel16)
htab = ppc_elf_hash_table (info);
htab->tls_get_addr = elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
FALSE, FALSE, TRUE);
+ if (htab->plt_type != PLT_NEW)
+ htab->params->no_tls_get_addr_opt = TRUE;
+
if (!htab->params->no_tls_get_addr_opt)
{
struct elf_link_hash_entry *opt, *tga;
notify relocate_section that optimization can be done, and
adjust got and plt refcounts. */
for (pass = 0; pass < 2; ++pass)
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
Elf_Internal_Sym *locsyms = NULL;
Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (ibfd);
will go to this object, or will remain undefined. */
h->plt.plist = NULL;
h->needs_plt = 0;
+ h->pointer_equality_needed = 0;
}
else
{
+ /* Taking a function's address in a read/write section
+ doesn't require us to define the function symbol in the
+ executable on a plt call stub. A dynamic reloc can
+ be used instead. */
+ if (h->pointer_equality_needed
+ && h->type != STT_GNU_IFUNC
+ && !htab->is_vxworks
+ && !ppc_elf_hash_entry (h)->has_sda_refs
+ && !readonly_dynrelocs (h))
+ {
+ h->pointer_equality_needed = 0;
+ h->non_got_ref = 0;
+ }
+
/* After adjust_dynamic_symbol, non_got_ref set in the
non-shared case means that we have allocated space in
.dynbss for the symbol and thus dyn_relocs for this
relocations against this symbol to the PLT entry. Allow
dynamic relocs if the reference is weak, and the dynamic
relocs will not cause text relocation. */
- if (!h->ref_regular_nonweak
- && h->non_got_ref
- && h->type != STT_GNU_IFUNC
- && !htab->is_vxworks
- && !ppc_elf_hash_entry (h)->has_sda_refs
- && !readonly_dynrelocs (h))
+ else if (!h->ref_regular_nonweak
+ && h->non_got_ref
+ && h->type != STT_GNU_IFUNC
+ && !htab->is_vxworks
+ && !ppc_elf_hash_entry (h)->has_sda_refs
+ && !readonly_dynrelocs (h))
h->non_got_ref = 0;
}
+ h->protected_def = 0;
return TRUE;
}
else
For such cases we need not do anything here; the relocations will
be handled correctly by relocate_section. */
if (info->shared)
- return TRUE;
+ {
+ h->protected_def = 0;
+ return TRUE;
+ }
/* If there are no references to this symbol that do not use the
GOT, we don't need to generate a copy reloc. */
if (!h->non_got_ref)
- return TRUE;
+ {
+ h->protected_def = 0;
+ return TRUE;
+ }
+
+ /* Protected variables do not work with .dynbss. The copy in
+ .dynbss won't be used by the shared library with the protected
+ definition for the variable. Editing to PIC, or text relocations
+ are preferable to an incorrect program. */
+ if (h->protected_def)
+ {
+ if (ELIMINATE_COPY_RELOCS
+ && ppc_elf_hash_entry (h)->has_addr16_ha
+ && ppc_elf_hash_entry (h)->has_addr16_lo
+ && htab->params->pic_fixup == 0
+ && info->disable_target_specific_optimizations <= 1)
+ htab->params->pic_fixup = 1;
+ h->non_got_ref = 0;
+ return TRUE;
+ }
+
+ /* If -z nocopyreloc was given, we won't generate them either. */
+ if (info->nocopyreloc)
+ {
+ h->non_got_ref = 0;
+ return TRUE;
+ }
/* If we didn't find any dynamic relocs in read-only sections, then
we'll be keeping the dynamic relocs and avoiding the copy reloc.
h->needs_copy = 1;
}
- return _bfd_elf_adjust_dynamic_copy (h, s);
+ return _bfd_elf_adjust_dynamic_copy (info, h, s);
}
\f
/* Generate a symbol to mark plt call stubs. For non-PIC code the sym is
sh->ref_regular_nonweak = 1;
sh->forced_local = 1;
sh->non_elf = 0;
+ sh->root.linker_def = 1;
}
return TRUE;
}
}
eh = (struct ppc_elf_link_hash_entry *) h;
- if (eh->elf.got.refcount > 0)
+ if (eh->elf.got.refcount > 0
+ || (ELIMINATE_COPY_RELOCS
+ && !eh->elf.def_regular
+ && eh->elf.protected_def
+ && eh->has_addr16_ha
+ && eh->has_addr16_lo
+ && htab->params->pic_fixup > 0))
{
bfd_boolean dyn;
unsigned int need;
dynamic. */
if (!h->non_got_ref
- && !h->def_regular)
+ && !h->def_regular
+ && !(h->protected_def
+ && eh->has_addr16_ha
+ && eh->has_addr16_lo
+ && htab->params->pic_fixup > 0))
{
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
/* Set the sizes of the dynamic sections. */
static bfd_boolean
-ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
+ppc_elf_size_dynamic_sections (bfd *output_bfd,
struct bfd_link_info *info)
{
struct ppc_elf_link_hash_table *htab;
/* Set up .got offsets for local syms, and space for local dynamic
relocs. */
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
bfd_signed_vma *local_got;
bfd_signed_vma *end_local_got;
if (info->shared)
{
struct elf_link_hash_entry *sda = htab->sdata[0].sym;
- if (sda != NULL
- && !(sda->root.type == bfd_link_hash_defined
- || sda->root.type == bfd_link_hash_defweak))
- {
- sda->root.type = bfd_link_hash_defined;
- sda->root.u.def.section = htab->elf.hgot->root.u.def.section;
- sda->root.u.def.value = htab->elf.hgot->root.u.def.value;
- }
+
+ sda->root.u.def.section = htab->elf.hgot->root.u.def.section;
+ sda->root.u.def.value = htab->elf.hgot->root.u.def.value;
+ }
+ if (info->emitrelocations)
+ {
+ struct elf_link_hash_entry *sda = htab->sdata[0].sym;
+
+ if (sda != NULL && sda->ref_regular)
+ sda->root.u.def.section->flags |= SEC_KEEP;
+ sda = htab->sdata[1].sym;
+ if (sda != NULL && sda->ref_regular)
+ sda->root.u.def.section->flags |= SEC_KEEP;
}
if (htab->glink != NULL
sh->ref_regular_nonweak = 1;
sh->forced_local = 1;
sh->non_elf = 0;
+ sh->root.linker_def = 1;
}
sh = elf_link_hash_lookup (&htab->elf, "__glink_PLTresolve",
TRUE, FALSE, FALSE);
sh->ref_regular_nonweak = 1;
sh->forced_local = 1;
sh->non_elf = 0;
+ sh->root.linker_def = 1;
}
}
}
|| s == htab->sgotplt
|| s == htab->sbss
|| s == htab->dynbss
- || s == htab->dynsbss
- || s == htab->sdata[0].section
- || s == htab->sdata[1].section)
+ || s == htab->dynsbss)
{
/* Strip these too. */
}
+ else if (s == htab->sdata[0].section
+ || s == htab->sdata[1].section)
+ {
+ strip_section = (s->flags & SEC_KEEP) == 0;
+ }
else if (CONST_STRNEQ (bfd_get_section_name (htab->elf.dynobj, s),
".rela"))
{
return TRUE;
}
+/* Arrange to have _SDA_BASE_ or _SDA2_BASE_ stripped from the output
+ if it looks like nothing is using them. */
+
+static void
+maybe_strip_sdasym (bfd *output_bfd, elf_linker_section_t *lsect)
+{
+ struct elf_link_hash_entry *sda = lsect->sym;
+
+ if (sda != NULL && !sda->ref_regular && sda->dynindx == -1)
+ {
+ asection *s;
+
+ s = bfd_get_section_by_name (output_bfd, lsect->name);
+ if (s == NULL || bfd_section_removed_from_list (output_bfd, s))
+ {
+ s = bfd_get_section_by_name (output_bfd, lsect->bss_name);
+ if (s == NULL || bfd_section_removed_from_list (output_bfd, s))
+ {
+ sda->def_regular = 0;
+ /* This is somewhat magic. See elf_link_output_extsym. */
+ sda->ref_dynamic = 1;
+ sda->forced_local = 0;
+ }
+ }
+ }
+}
+
+void
+ppc_elf_maybe_strip_sdata_syms (struct bfd_link_info *info)
+{
+ struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+
+ if (htab != NULL)
+ {
+ maybe_strip_sdasym (info->output_bfd, &htab->sdata[0]);
+ maybe_strip_sdasym (info->output_bfd, &htab->sdata[1]);
+ }
+}
+
+
/* Return TRUE if symbol should be hashed in the `.gnu.hash' section. */
static bfd_boolean
struct ppc_elf_relax_info
{
unsigned int workaround_size;
+ unsigned int picfixup_size;
};
/* This function implements long branch trampolines, and the ppc476
struct bfd_link_info *link_info,
bfd_boolean *again)
{
- struct one_fixup
+ struct one_branch_fixup
{
- struct one_fixup *next;
+ struct one_branch_fixup *next;
asection *tsec;
/* Final link, can use the symbol offset. For a
relocatable link we use the symbol's index. */
Elf_Internal_Sym *isymbuf = NULL;
Elf_Internal_Rela *internal_relocs = NULL;
Elf_Internal_Rela *irel, *irelend = NULL;
- struct one_fixup *fixups = NULL;
+ struct one_branch_fixup *branch_fixups = NULL;
struct ppc_elf_relax_info *relax_info = NULL;
unsigned changes = 0;
bfd_boolean workaround_change;
struct ppc_elf_link_hash_table *htab;
- bfd_size_type trampbase, trampoff, newsize;
+ bfd_size_type trampbase, trampoff, newsize, picfixup_size;
asection *got2;
bfd_boolean maybe_pasted;
|| isec->sec_info_type == SEC_INFO_TYPE_TARGET);
isec->sec_info_type = SEC_INFO_TYPE_TARGET;
- if (htab->params->ppc476_workaround)
+ if (htab->params->ppc476_workaround
+ || htab->params->pic_fixup > 0)
{
if (elf_section_data (isec)->sec_info == NULL)
{
trampoff += 4;
symtab_hdr = &elf_symtab_hdr (abfd);
-
- if (htab->params->branch_trampolines)
+ picfixup_size = 0;
+ if (htab->params->branch_trampolines
+ || htab->params->pic_fixup > 0)
{
/* Get a copy of the native relocations. */
if (isec->reloc_count != 0)
unsigned long r_type = ELF32_R_TYPE (irel->r_info);
bfd_vma toff, roff;
asection *tsec;
- struct one_fixup *f;
+ struct one_branch_fixup *f;
size_t insn_offset = 0;
- bfd_vma max_branch_offset, val;
+ bfd_vma max_branch_offset = 0, val;
bfd_byte *hit_addr;
unsigned long t0;
struct elf_link_hash_entry *h;
max_branch_offset = 1 << 15;
break;
+ case R_PPC_ADDR16_HA:
+ if (htab->params->pic_fixup > 0)
+ break;
+ continue;
+
default:
continue;
}
sym_type = h->type;
}
+ if (r_type == R_PPC_ADDR16_HA)
+ {
+ if (h != NULL
+ && !h->def_regular
+ && h->protected_def
+ && ppc_elf_hash_entry (h)->has_addr16_ha
+ && ppc_elf_hash_entry (h)->has_addr16_lo)
+ picfixup_size += 12;
+ continue;
+ }
+
/* The condition here under which we call find_plt_ent must
match that in relocate_section. If we call find_plt_ent here
but not in relocate_section, or vice versa, then the branch
}
/* Look for an existing fixup to this address. */
- for (f = fixups; f ; f = f->next)
+ for (f = branch_fixups; f ; f = f->next)
if (f->tsec == tsec && f->toff == toff)
break;
/* Record the fixup so we don't do it again this section. */
f = bfd_malloc (sizeof (*f));
- f->next = fixups;
+ f->next = branch_fixups;
f->tsec = tsec;
f->toff = toff;
f->trampoff = trampoff;
- fixups = f;
+ branch_fixups = f;
trampoff += size;
changes++;
}
}
- while (fixups != NULL)
+ while (branch_fixups != NULL)
{
- struct one_fixup *f = fixups;
- fixups = fixups->next;
+ struct one_branch_fixup *f = branch_fixups;
+ branch_fixups = branch_fixups->next;
free (f);
}
}
bfd_vma pagesize = (bfd_vma) 1 << htab->params->pagesize_p2;
addr = isec->output_section->vma + isec->output_offset;
- end_addr = addr + trampoff - 1;
+ end_addr = addr + trampoff;
addr &= -pagesize;
crossings = ((end_addr & -pagesize) - addr) >> htab->params->pagesize_p2;
if (crossings != 0)
/* Keep space aligned, to ensure the patch code itself does
not cross a page. Don't decrease size calculated on a
previous pass as otherwise we might never settle on a layout. */
- newsize = 15 - (end_addr & 15);
+ newsize = 15 - ((end_addr - 1) & 15);
newsize += crossings * 16;
if (relax_info->workaround_size < newsize)
{
newsize = trampoff + relax_info->workaround_size;
}
- if (changes || workaround_change)
+ if (htab->params->pic_fixup > 0)
+ {
+ picfixup_size -= relax_info->picfixup_size;
+ if (picfixup_size != 0)
+ relax_info->picfixup_size += picfixup_size;
+ newsize += relax_info->picfixup_size;
+ }
+
+ if (changes != 0 || picfixup_size != 0 || workaround_change)
isec->size = newsize;
if (isymbuf != NULL
}
}
+ changes += picfixup_size;
if (changes != 0)
{
/* Append sufficient NOP relocs so we can write out relocation
return TRUE;
error_return:
- while (fixups != NULL)
+ while (branch_fixups != NULL)
{
- struct one_fixup *f = fixups;
- fixups = fixups->next;
+ struct one_branch_fixup *f = branch_fixups;
+ branch_fixups = branch_fixups->next;
free (f);
}
if (isymbuf != NULL && (unsigned char *) isymbuf != symtab_hdr->contents)
bfd_boolean ret = TRUE;
bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
bfd_boolean is_vxworks_tls;
+ unsigned int picfixup_size = 0;
+ struct ppc_elf_relax_info *relax_info = NULL;
#ifdef DEBUG
_bfd_error_handler ("ppc_elf_relocate_section called for %B section %A, "
is_vxworks_tls = (htab->is_vxworks && info->shared
&& !strcmp (input_section->output_section->name,
".tls_vars"));
+ if (input_section->sec_info_type == SEC_INFO_TYPE_TARGET)
+ relax_info = elf_section_data (input_section)->sec_info;
rel = relocs;
relend = relocs + input_section->reloc_count;
for (; rel < relend; rel++)
+ R_PPC_GOT_TPREL16);
else
{
- bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
rel->r_offset -= d_offset;
+ bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
r_type = R_PPC_NONE;
}
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
&& branch_reloc_hash_match (input_bfd, rel + 1,
htab->tls_get_addr))
offset = rel[1].r_offset;
+ /* We read the low GOT_TLS insn because we need to keep
+ the destination reg. It may be something other than
+ the usual r3, and moved to r3 before the call by
+ intervening code. */
+ insn1 = bfd_get_32 (output_bfd,
+ contents + rel->r_offset - d_offset);
if ((tls_mask & tls_gd) != 0)
{
/* IE */
- insn1 = bfd_get_32 (output_bfd,
- contents + rel->r_offset - d_offset);
- insn1 &= (1 << 26) - 1;
+ insn1 &= (0x1f << 21) | (0x1f << 16);
insn1 |= 32 << 26; /* lwz */
if (offset != (bfd_vma) -1)
{
else
{
/* LE */
- insn1 = 0x3c620000; /* addis 3,2,0 */
+ insn1 &= 0x1f << 21;
+ insn1 |= 0x3c020000; /* addis r,2,0 */
if (tls_gd == 0)
{
/* Was an LD reloc. */
}
}
+ if (ELIMINATE_COPY_RELOCS
+ && h != NULL
+ && !h->def_regular
+ && h->protected_def
+ && ppc_elf_hash_entry (h)->has_addr16_ha
+ && ppc_elf_hash_entry (h)->has_addr16_lo
+ && htab->params->pic_fixup > 0)
+ {
+ /* Convert lis;addi or lis;load/store accessing a protected
+ variable defined in a shared library to PIC. */
+ unsigned int insn;
+
+ if (r_type == R_PPC_ADDR16_HA)
+ {
+ insn = bfd_get_32 (output_bfd,
+ contents + rel->r_offset - d_offset);
+ if ((insn & (0x3f << 26)) == (15u << 26)
+ && (insn & (0x1f << 16)) == 0 /* lis */)
+ {
+ bfd_byte *p;
+ bfd_vma off;
+ bfd_vma got_addr;
+
+ p = (contents + input_section->size
+ - relax_info->workaround_size
+ - relax_info->picfixup_size
+ + picfixup_size);
+ off = (p - contents) - (rel->r_offset - d_offset);
+ if (off > 0x1fffffc || (off & 3) != 0)
+ info->callbacks->einfo
+ (_("%P: %H: fixup branch overflow\n"),
+ input_bfd, input_section, rel->r_offset);
+
+ bfd_put_32 (output_bfd, B | off,
+ contents + rel->r_offset - d_offset);
+ got_addr = (htab->got->output_section->vma
+ + htab->got->output_offset
+ + (h->got.offset & ~1));
+ rel->r_info = ELF32_R_INFO (0, R_PPC_ADDR16_HA);
+ rel->r_addend = got_addr;
+ rel->r_offset = (p - contents) + d_offset;
+ insn &= ~0xffff;
+ insn |= ((unsigned int )(got_addr + 0x8000) >> 16) & 0xffff;
+ bfd_put_32 (output_bfd, insn, p);
+
+ /* Convert lis to lwz, loading address from GOT. */
+ insn &= ~0xffff;
+ insn ^= (32u ^ 15u) << 26;
+ insn |= (insn & (0x1f << 21)) >> 5;
+ insn |= got_addr & 0xffff;
+ bfd_put_32 (output_bfd, insn, p + 4);
+
+ bfd_put_32 (output_bfd, B | ((-4 - off) & 0x3ffffff), p + 8);
+ picfixup_size += 12;
+
+ /* Use one of the spare relocs, so --emit-relocs
+ output is reasonable. */
+ memmove (rel + 1, rel, (relend - rel - 1) * sizeof (*rel));
+ rel++;
+ rel->r_info = ELF32_R_INFO (0, R_PPC_ADDR16_LO);
+ rel->r_offset += 4;
+
+ /* Continue on as if we had a got reloc, to output
+ dynamic reloc. */
+ r_type = R_PPC_GOT16_LO;
+ }
+ else
+ info->callbacks->einfo
+ (_("%P: %H: error: %s with unexpected instruction %x\n"),
+ input_bfd, input_section, rel->r_offset,
+ "R_PPC_ADDR16_HA", insn);
+ }
+ else if (r_type == R_PPC_ADDR16_LO)
+ {
+ insn = bfd_get_32 (output_bfd,
+ contents + rel->r_offset - d_offset);
+ if ((insn & (0x3f << 26)) == 14u << 26 /* addi */
+ || (insn & (0x3f << 26)) == 32u << 26 /* lwz */
+ || (insn & (0x3f << 26)) == 34u << 26 /* lbz */
+ || (insn & (0x3f << 26)) == 36u << 26 /* stw */
+ || (insn & (0x3f << 26)) == 38u << 26 /* stb */
+ || (insn & (0x3f << 26)) == 40u << 26 /* lhz */
+ || (insn & (0x3f << 26)) == 42u << 26 /* lha */
+ || (insn & (0x3f << 26)) == 44u << 26 /* sth */
+ || (insn & (0x3f << 26)) == 46u << 26 /* lmw */
+ || (insn & (0x3f << 26)) == 47u << 26 /* stmw */
+ || (insn & (0x3f << 26)) == 48u << 26 /* lfs */
+ || (insn & (0x3f << 26)) == 50u << 26 /* lfd */
+ || (insn & (0x3f << 26)) == 52u << 26 /* stfs */
+ || (insn & (0x3f << 26)) == 54u << 26 /* stfd */
+ || ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
+ && (insn & 3) != 1)
+ || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
+ && ((insn & 3) == 0 || (insn & 3) == 3)))
+ {
+ /* Arrange to apply the reloc addend, if any. */
+ relocation = 0;
+ unresolved_reloc = FALSE;
+ rel->r_info = ELF32_R_INFO (0, r_type);
+ }
+ else
+ info->callbacks->einfo
+ (_("%P: %H: error: %s with unexpected instruction %x\n"),
+ input_bfd, input_section, rel->r_offset,
+ "R_PPC_ADDR16_LO", insn);
+ }
+ }
+
ifunc = NULL;
if (!htab->is_vxworks)
{
{
outrel.r_addend += relocation;
if (tls_ty & (TLS_GD | TLS_DTPREL | TLS_TPREL))
- outrel.r_addend -= htab->elf.tls_sec->vma;
+ {
+ if (htab->elf.tls_sec == NULL)
+ outrel.r_addend = 0;
+ else
+ outrel.r_addend -= htab->elf.tls_sec->vma;
+ }
}
loc = rsec->contents;
loc += (rsec->reloc_count++
value = 1;
else if (tls_ty != 0)
{
- value -= htab->elf.tls_sec->vma + DTP_OFFSET;
- if (tls_ty == (TLS_TLS | TLS_TPREL))
- value += DTP_OFFSET - TP_OFFSET;
+ if (htab->elf.tls_sec == NULL)
+ value = 0;
+ else
+ {
+ value -= htab->elf.tls_sec->vma + DTP_OFFSET;
+ if (tls_ty == (TLS_TLS | TLS_TPREL))
+ value += DTP_OFFSET - TP_OFFSET;
+ }
if (tls_ty == (TLS_TLS | TLS_GD))
{
}
}
+ /* If here for a picfixup, we're done. */
+ if (r_type != ELF32_R_TYPE (rel->r_info))
+ continue;
+
relocation = (htab->got->output_section->vma
+ htab->got->output_offset
+ off
case R_PPC_DTPREL16_LO:
case R_PPC_DTPREL16_HI:
case R_PPC_DTPREL16_HA:
- addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
+ if (htab->elf.tls_sec != NULL)
+ addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
break;
/* Relocations that may need to be propagated if this is a shared
bfd_put_32 (output_bfd, insn, p);
break;
}
- addend -= htab->elf.tls_sec->vma + TP_OFFSET;
+ if (htab->elf.tls_sec != NULL)
+ addend -= htab->elf.tls_sec->vma + TP_OFFSET;
/* The TPREL16 relocs shouldn't really be used in shared
libs as they will result in DT_TEXTREL being set, but
support them anyway. */
goto dodyn;
case R_PPC_TPREL32:
- addend -= htab->elf.tls_sec->vma + TP_OFFSET;
+ if (htab->elf.tls_sec != NULL)
+ addend -= htab->elf.tls_sec->vma + TP_OFFSET;
goto dodyn;
case R_PPC_DTPREL32:
- addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
+ if (htab->elf.tls_sec != NULL)
+ addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
goto dodyn;
case R_PPC_DTPMOD32:
&& h != NULL
&& h->dynindx != -1
&& !h->non_got_ref
- && !h->def_regular))
+ && !h->def_regular
+ && !(h->protected_def
+ && ppc_elf_hash_entry (h)->has_addr16_ha
+ && ppc_elf_hash_entry (h)->has_addr16_lo
+ && htab->params->pic_fixup > 0)))
{
int skip;
bfd_byte *loc;
addend -= SYM_VAL (sda);
name = bfd_get_section_name (output_bfd, sec->output_section);
- if (! ((CONST_STRNEQ (name, ".sdata")
- && (name[6] == 0 || name[6] == '.'))
- || (CONST_STRNEQ (name, ".sbss")
- && (name[5] == 0 || name[5] == '.'))))
+ if (!(strcmp (name, ".sdata") == 0
+ || strcmp (name, ".sbss") == 0))
{
info->callbacks->einfo
(_("%P: %B: the target (%s) of a %s relocation is "
addend -= SYM_VAL (sda);
name = bfd_get_section_name (output_bfd, sec->output_section);
- if (! (CONST_STRNEQ (name, ".sdata2")
- || CONST_STRNEQ (name, ".sbss2")))
+ if (!(strcmp (name, ".sdata2") == 0
+ || strcmp (name, ".sbss2") == 0))
{
info->callbacks->einfo
(_("%P: %B: the target (%s) of a %s relocation is "
}
name = bfd_get_section_name (output_bfd, sec->output_section);
- if (((CONST_STRNEQ (name, ".sdata")
- && (name[6] == 0 || name[6] == '.'))
- || (CONST_STRNEQ (name, ".sbss")
- && (name[5] == 0 || name[5] == '.'))))
+ if (strcmp (name, ".sdata") == 0
+ || strcmp (name, ".sbss") == 0)
{
reg = 13;
sda = htab->sdata[0].sym;
}
- else if (CONST_STRNEQ (name, ".sdata2")
- || CONST_STRNEQ (name, ".sbss2"))
+ else if (strcmp (name, ".sdata2") == 0
+ || strcmp (name, ".sbss2") == 0)
{
reg = 2;
sda = htab->sdata[1].sym;
}
name = bfd_get_section_name (output_bfd, sec->output_section);
- if (((CONST_STRNEQ (name, ".sdata")
- && (name[6] == 0 || name[6] == '.'))
- || (CONST_STRNEQ (name, ".sbss")
- && (name[5] == 0 || name[5] == '.'))))
+ if (strcmp (name, ".sdata") == 0
+ || strcmp (name, ".sbss") == 0)
{
//reg = 13;
sda = htab->sdata[0].sym;
}
- else if (CONST_STRNEQ (name, ".sdata2")
- || CONST_STRNEQ (name, ".sbss2"))
+ else if (strcmp (name, ".sdata2") == 0
+ || strcmp (name, ".sbss2") == 0)
{
//reg = 2;
sda = htab->sdata[1].sym;
unsigned int insn;
insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
- if ((insn & (0x3f << 26)) == 28u << 26 /* andi */
- || (insn & (0x3f << 26)) == 24u << 26 /* ori */
- || (insn & (0x3f << 26)) == 26u << 26 /* xori */
- || (insn & (0x3f << 26)) == 10u << 26 /* cmpli */)
+ if ((insn & (0x3f << 26)) == 10u << 26 /* cmpli */)
+ complain = complain_overflow_bitfield;
+ else if ((insn & (0x3f << 26)) == 28u << 26 /* andi */
+ || (insn & (0x3f << 26)) == 24u << 26 /* ori */
+ || (insn & (0x3f << 26)) == 26u << 26 /* xori */)
complain = complain_overflow_unsigned;
}
if (howto->complain_on_overflow != complain)
if (r == bfd_reloc_overflow)
{
overflow:
- if (warned)
- continue;
- if (h != NULL
- && h->root.type == bfd_link_hash_undefweak
- && howto->pc_relative)
+ /* On code like "if (foo) foo();" don't report overflow
+ on a branch to zero when foo is undefined. */
+ if (!warned
+ && !(h != NULL
+ && (h->root.type == bfd_link_hash_undefweak
+ || h->root.type == bfd_link_hash_undefined)
+ && is_branch_reloc (r_type)))
{
- /* Assume this is a call protected by other code that
- detect the symbol is undefined. If this is the case,
- we can safely ignore the overflow. If not, the
- program is hosed anyway, and a little warning isn't
- going to help. */
-
- continue;
+ if (!((*info->callbacks->reloc_overflow)
+ (info, (h ? &h->root : NULL), sym_name,
+ howto->name, rel->r_addend,
+ input_bfd, input_section, rel->r_offset)))
+ return FALSE;
}
-
- if (! (*info->callbacks->reloc_overflow) (info,
- (h ? &h->root : NULL),
- sym_name,
- howto->name,
- rel->r_addend,
- input_bfd,
- input_section,
- rel->r_offset))
- return FALSE;
}
else
{
|| (input_section->output_section->alignment_power
>= htab->params->pagesize_p2)))
{
- struct ppc_elf_relax_info *relax_info;
bfd_vma start_addr, end_addr, addr;
bfd_vma pagesize = (bfd_vma) 1 << htab->params->pagesize_p2;
- relax_info = elf_section_data (input_section)->sec_info;
if (relax_info->workaround_size != 0)
- memset (contents + input_section->size - relax_info->workaround_size,
- 0, relax_info->workaround_size);
+ {
+ bfd_byte *p;
+ unsigned int n;
+ bfd_byte fill[4];
+
+ bfd_put_32 (input_bfd, BA, fill);
+ p = contents + input_section->size - relax_info->workaround_size;
+ n = relax_info->workaround_size >> 2;
+ while (n--)
+ {
+ memcpy (p, fill, 4);
+ p += 4;
+ }
+ }
/* The idea is: Replace the last instruction on a page with a
branch to a patch area. Put the insn there followed by a
return ret;
}
\f
-#define TARGET_LITTLE_SYM bfd_elf32_powerpcle_vec
+#define TARGET_LITTLE_SYM powerpc_elf32_le_vec
#define TARGET_LITTLE_NAME "elf32-powerpcle"
-#define TARGET_BIG_SYM bfd_elf32_powerpc_vec
+#define TARGET_BIG_SYM powerpc_elf32_vec
#define TARGET_BIG_NAME "elf32-powerpc"
#define ELF_ARCH bfd_arch_powerpc
#define ELF_TARGET_ID PPC32_ELF_DATA
#define ELF_MACHINE_CODE EM_PPC
#ifdef __QNXTARGET__
#define ELF_MAXPAGESIZE 0x1000
+#define ELF_COMMONPAGESIZE 0x1000
#else
#define ELF_MAXPAGESIZE 0x10000
+#define ELF_COMMONPAGESIZE 0x10000
#endif
#define ELF_MINPAGESIZE 0x1000
-#define ELF_COMMONPAGESIZE 0x1000
#define elf_info_to_howto ppc_elf_info_to_howto
#ifdef EM_CYGNUS_POWERPC
#undef TARGET_LITTLE_NAME
#undef TARGET_BIG_SYM
-#define TARGET_BIG_SYM bfd_elf32_powerpc_freebsd_vec
+#define TARGET_BIG_SYM powerpc_elf32_fbsd_vec
#undef TARGET_BIG_NAME
#define TARGET_BIG_NAME "elf32-powerpc-freebsd"
#undef TARGET_LITTLE_NAME
#undef TARGET_BIG_SYM
-#define TARGET_BIG_SYM bfd_elf32_powerpc_vxworks_vec
+#define TARGET_BIG_SYM powerpc_elf32_vxworks_vec
#undef TARGET_BIG_NAME
#define TARGET_BIG_NAME "elf32-powerpc-vxworks"