/* PowerPC-specific support for 32-bit ELF
Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
- 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+ 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Cygnus Support.
This file is part of BFD, the Binary File Descriptor library.
0, /* rightshift */
1, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
- TRUE, /* pc_relative */
+ FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
#define is_ppc_elf(bfd) \
(bfd_get_flavour (bfd) == bfd_target_elf_flavour \
- && elf_object_id (bfd) == PPC32_ELF_TDATA)
+ && elf_object_id (bfd) == PPC32_ELF_DATA)
/* Override the generic function because we store some extras. */
ppc_elf_mkobject (bfd *abfd)
{
return bfd_elf_allocate_object (abfd, sizeof (struct ppc_elf_obj_tdata),
- PPC32_ELF_TDATA);
+ PPC32_ELF_DATA);
}
/* Fix bad default arch selected for a 32 bit input bfd when the
elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
/* pr_pid */
- elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 24);
+ elf_tdata (abfd)->core_lwpid = bfd_get_32 (abfd, note->descdata + 24);
/* pr_reg */
offset = 72;
Elf_Internal_Shdr *shdr,
asection *asect)
{
- if ((asect->flags & (SEC_GROUP | SEC_EXCLUDE)) == SEC_EXCLUDE)
- shdr->sh_flags |= SHF_EXCLUDE;
-
if ((asect->flags & SEC_SORT_ENTRIES) != 0)
shdr->sh_type = SHT_ORDERED;
apuinfo_list;
static apuinfo_list *head;
-
+static bfd_boolean apuinfo_set;
static void
apuinfo_list_init (void)
{
head = NULL;
+ apuinfo_set = FALSE;
}
static void
{
bfd *ibfd;
asection *asec;
- char *buffer;
- unsigned num_input_sections;
- bfd_size_type output_section_size;
+ char *buffer = NULL;
+ bfd_size_type largest_input_size = 0;
unsigned i;
- unsigned num_entries;
- unsigned long offset;
unsigned long length;
const char *error_message = NULL;
if (link_info == NULL)
return;
- /* Scan the input bfds, looking for apuinfo sections. */
- num_input_sections = 0;
- output_section_size = 0;
-
- for (ibfd = link_info->input_bfds; ibfd; ibfd = ibfd->link_next)
- {
- asec = bfd_get_section_by_name (ibfd, APUINFO_SECTION_NAME);
- if (asec)
- {
- ++ num_input_sections;
- output_section_size += asec->size;
- }
- }
-
- /* We need at least one input sections
- in order to make merging worthwhile. */
- if (num_input_sections < 1)
- return;
-
- /* Just make sure that the output section exists as well. */
- asec = bfd_get_section_by_name (abfd, APUINFO_SECTION_NAME);
- if (asec == NULL)
- return;
-
- /* Allocate a buffer for the contents of the input sections. */
- buffer = bfd_malloc (output_section_size);
- if (buffer == NULL)
- return;
-
- offset = 0;
apuinfo_list_init ();
/* Read in the input sections contents. */
for (ibfd = link_info->input_bfds; ibfd; ibfd = ibfd->link_next)
{
unsigned long datum;
- char *ptr;
asec = bfd_get_section_by_name (ibfd, APUINFO_SECTION_NAME);
if (asec == NULL)
continue;
+ error_message = _("corrupt %s section in %B");
length = asec->size;
- if (length < 24)
+ if (length < 20)
+ goto fail;
+
+ apuinfo_set = TRUE;
+ if (largest_input_size < asec->size)
{
- error_message = _("corrupt or empty %s section in %B");
- goto fail;
+ if (buffer)
+ free (buffer);
+ largest_input_size = asec->size;
+ buffer = bfd_malloc (largest_input_size);
+ if (!buffer)
+ return;
}
if (bfd_seek (ibfd, asec->filepos, SEEK_SET) != 0
- || (bfd_bread (buffer + offset, length, ibfd) != length))
+ || (bfd_bread (buffer, length, ibfd) != length))
{
error_message = _("unable to read in %s section from %B");
goto fail;
}
- /* Process the contents of the section. */
- ptr = buffer + offset;
- error_message = _("corrupt %s section in %B");
-
/* Verify the contents of the header. Note - we have to
extract the values this way in order to allow for a
host whose endian-ness is different from the target. */
- datum = bfd_get_32 (ibfd, ptr);
+ datum = bfd_get_32 (ibfd, buffer);
if (datum != sizeof APUINFO_LABEL)
goto fail;
- datum = bfd_get_32 (ibfd, ptr + 8);
+ datum = bfd_get_32 (ibfd, buffer + 8);
if (datum != 0x2)
goto fail;
- if (strcmp (ptr + 12, APUINFO_LABEL) != 0)
+ if (strcmp (buffer + 12, APUINFO_LABEL) != 0)
goto fail;
/* Get the number of bytes used for apuinfo entries. */
- datum = bfd_get_32 (ibfd, ptr + 4);
+ datum = bfd_get_32 (ibfd, buffer + 4);
if (datum + 20 != length)
goto fail;
- /* Make sure that we do not run off the end of the section. */
- if (offset + length > output_section_size)
- goto fail;
-
/* Scan the apuinfo section, building a list of apuinfo numbers. */
for (i = 0; i < datum; i += 4)
- apuinfo_list_add (bfd_get_32 (ibfd, ptr + 20 + i));
-
- /* Update the offset. */
- offset += length;
+ apuinfo_list_add (bfd_get_32 (ibfd, buffer + 20 + i));
}
error_message = NULL;
- /* Compute the size of the output section. */
- num_entries = apuinfo_list_length ();
- output_section_size = 20 + num_entries * 4;
+ if (apuinfo_set)
+ {
+ /* Compute the size of the output section. */
+ unsigned num_entries = apuinfo_list_length ();
- asec = bfd_get_section_by_name (abfd, APUINFO_SECTION_NAME);
+ /* Set the output section size, if it exists. */
+ asec = bfd_get_section_by_name (abfd, APUINFO_SECTION_NAME);
- if (! bfd_set_section_size (abfd, asec, output_section_size))
- ibfd = abfd,
- error_message = _("warning: unable to set size of %s section in %B");
+ if (asec && ! bfd_set_section_size (abfd, asec, 20 + num_entries * 4))
+ {
+ ibfd = abfd;
+ error_message = _("warning: unable to set size of %s section in %B");
+ }
+ }
fail:
- free (buffer);
+ if (buffer)
+ free (buffer);
if (error_message)
(*_bfd_error_handler) (error_message, ibfd, APUINFO_SECTION_NAME);
asection *asec,
bfd_byte *contents ATTRIBUTE_UNUSED)
{
- return (apuinfo_list_length ()
- && strcmp (asec->name, APUINFO_SECTION_NAME) == 0);
+ return apuinfo_set && strcmp (asec->name, APUINFO_SECTION_NAME) == 0;
}
/* Finally we can generate the output section. */
if (asec == NULL)
return;
- if (apuinfo_list_length () == 0)
+ if (!apuinfo_set)
return;
length = asec->size;
/* The bfd that forced an old-style PLT. */
bfd *old_bfd;
-
+
/* TLS local dynamic got entry handling. */
union {
bfd_signed_vma refcount;
struct sym_cache sym_cache;
};
+/* Rename some of the generic section flags to better document how they
+ are used here. */
+
+/* Nonzero if this section has TLS related relocations. */
+#define has_tls_reloc sec_flg0
+
+/* Nonzero if this section has a call to __tls_get_addr. */
+#define has_tls_get_addr_call sec_flg1
+
/* Get the PPC ELF linker hash table from a link_info structure. */
#define ppc_elf_hash_table(p) \
- ((struct ppc_elf_link_hash_table *) (p)->hash)
+ (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
+ == PPC32_ELF_DATA ? ((struct ppc_elf_link_hash_table *) ((p)->hash)) : NULL)
/* Create an entry in a PPC ELF linker hash table. */
ppc_elf_hash_entry (entry)->linker_section_pointer = NULL;
ppc_elf_hash_entry (entry)->dyn_relocs = NULL;
ppc_elf_hash_entry (entry)->tls_mask = 0;
+ ppc_elf_hash_entry (entry)->has_sda_refs = 0;
}
return entry;
if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd,
ppc_elf_link_hash_newfunc,
- sizeof (struct ppc_elf_link_hash_entry)))
+ sizeof (struct ppc_elf_link_hash_entry),
+ PPC32_ELF_DATA))
{
free (ret);
return NULL;
*valp = sym->st_size;
}
- if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+ if ((abfd->flags & DYNAMIC) == 0
+ && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE;
return TRUE;
{
struct plt_entry *ent;
+ if (addend < 32768)
+ sec = NULL;
for (ent = *plist; ent != NULL; ent = ent->next)
if (ent->sec == sec && ent->addend == addend)
break;
enum elf_ppc_reloc_type r_type;
struct elf_link_hash_entry *h;
int tls_type;
- struct plt_entry **ifunc;
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
}
tls_type = 0;
- ifunc = NULL;
r_type = ELF32_R_TYPE (rel->r_info);
- if (!htab->is_vxworks)
+ if (h == NULL && !htab->is_vxworks)
{
- if (h != NULL)
- {
- if (h->type == STT_GNU_IFUNC)
- ifunc = &h->plt.plist;
- }
- else
+ Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
+ return FALSE;
+
+ if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
+ && (!info->shared
+ || is_branch_reloc (r_type)))
{
- Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache,
- abfd, r_symndx);
- if (isym == NULL)
+ struct plt_entry **ifunc;
+ bfd_vma addend;
+
+ ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
+ PLT_IFUNC);
+ if (ifunc == NULL)
return FALSE;
- if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
- && (!info->shared
- || is_branch_reloc (r_type)))
+ /* STT_GNU_IFUNC symbols must have a PLT entry;
+ In a non-pie executable even when there are
+ no plt calls. */
+ addend = 0;
+ if (r_type == R_PPC_PLTREL24)
{
- bfd_vma addend;
-
- ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
- PLT_IFUNC);
- if (ifunc == NULL)
- return FALSE;
-
- /* STT_GNU_IFUNC symbols must have a PLT entry;
- In a non-pie executable even when there are
- no plt calls. */
- addend = 0;
- if (r_type == R_PPC_PLTREL24)
- {
- ppc_elf_tdata (abfd)->makes_plt_call = 1;
- if (info->shared)
- addend = rel->r_addend;
- }
- if (!update_plt_info (abfd, ifunc,
- addend < 32768 ? NULL : got2, addend))
- return FALSE;
+ ppc_elf_tdata (abfd)->makes_plt_call = 1;
+ if (info->shared)
+ addend = rel->r_addend;
}
+ if (!update_plt_info (abfd, ifunc, got2, addend))
+ return FALSE;
}
}
addend = rel->r_addend;
}
h->needs_plt = 1;
- if (!update_plt_info (abfd, &h->plt.plist,
- addend < 32768 ? NULL : got2, addend))
+ if (!update_plt_info (abfd, &h->plt.plist, got2, addend))
return FALSE;
}
break;
case R_PPC_EMB_MRKREF:
case R_PPC_NONE:
case R_PPC_max:
- case R_PPC_RELAX32:
- case R_PPC_RELAX32PC:
- case R_PPC_RELAX32_PLT:
- case R_PPC_RELAX32PC_PLT:
+ case R_PPC_RELAX:
+ case R_PPC_RELAX_PLT:
+ case R_PPC_RELAX_PLTREL24:
break;
/* These should only appear in dynamic objects. */
|| !h->def_regular)))
{
struct ppc_elf_dyn_relocs *p;
- struct ppc_elf_dyn_relocs **head;
+ struct ppc_elf_dyn_relocs **rel_head;
#ifdef DEBUG
fprintf (stderr,
relocations we need for this symbol. */
if (h != NULL)
{
- head = &ppc_elf_hash_entry (h)->dyn_relocs;
+ rel_head = &ppc_elf_hash_entry (h)->dyn_relocs;
}
else
{
s = sec;
vpp = &elf_section_data (s)->local_dynrel;
- head = (struct ppc_elf_dyn_relocs **) vpp;
+ rel_head = (struct ppc_elf_dyn_relocs **) vpp;
}
- p = *head;
+ p = *rel_head;
if (p == NULL || p->sec != sec)
{
p = bfd_alloc (htab->elf.dynobj, sizeof *p);
if (p == NULL)
return FALSE;
- p->next = *head;
- *head = p;
+ p->next = *rel_head;
+ *rel_head = p;
p->sec = sec;
p->count = 0;
p->pc_count = 0;
(_("Warning: %B uses hard float, %B uses soft float"), obfd, ibfd);
else if (out_attr->i == 1 && in_attr->i == 3)
_bfd_error_handler
- (_("Warning: %B uses double-precision hard float, %B uses single-precision hard float"),
+ (_("Warning: %B uses double-precision hard float, %B uses single-precision hard float"),
obfd, ibfd);
else if (out_attr->i == 3 && in_attr->i == 1)
_bfd_error_handler
- (_("Warning: %B uses double-precision hard float, %B uses single-precision hard float"),
+ (_("Warning: %B uses double-precision hard float, %B uses single-precision hard float"),
ibfd, obfd);
else if (out_attr->i == 3 && in_attr->i == 2)
_bfd_error_handler
- (_("Warning: %B uses soft float, %B uses single-precision hard float"),
+ (_("Warning: %B uses soft float, %B uses single-precision hard float"),
ibfd, obfd);
else if (out_attr->i == 2 && (in_attr->i == 1 || in_attr->i == 3))
_bfd_error_handler
struct plt_entry *ent;
ent = find_plt_ent (&h->plt.plist, NULL, 0);
- if (ent->plt.refcount > 0)
+ if (ent != NULL && ent->plt.refcount > 0)
ent->plt.refcount -= 1;
}
}
if (r_type == R_PPC_PLTREL24 && info->shared)
addend = rel->r_addend;
ent = find_plt_ent (&h->plt.plist, got2, addend);
- if (ent->plt.refcount > 0)
+ if (ent != NULL && ent->plt.refcount > 0)
ent->plt.refcount -= 1;
}
break;
&& tga->root.type == bfd_link_hash_undefweak)))
{
struct plt_entry *ent;
- ent = find_plt_ent (&tga->plt.plist, NULL, 0);
- if (ent != NULL
- && ent->plt.refcount > 0)
+ for (ent = tga->plt.plist; ent != NULL; ent = ent->next)
+ if (ent->plt.refcount > 0)
+ break;
+ if (ent != NULL)
{
tga->root.type = bfd_link_hash_indirect;
tga->root.u.i.link = &opt->root;
return TRUE;
htab = ppc_elf_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
/* Make two passes through the relocs. First time check that tls
relocs involved in setting up a tls_get_addr call are indeed
- followed by such a call. If they are not, exclude them from
- the optimizations done on the second pass. */
+ followed by such a call. If they are not, don't do any tls
+ optimization. On the second pass twiddle tls_mask flags to
+ 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)
{
Elf_Internal_Sym *locsyms = NULL;
Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (ibfd);
+ asection *got2 = bfd_get_section_by_name (ibfd, ".got2");
for (sec = ibfd->sections; sec != NULL; sec = sec->next)
if (sec->has_tls_reloc && !bfd_is_abs_section (sec->output_section))
{
Elf_Internal_Rela *relstart, *rel, *relend;
+ int expecting_tls_get_addr = 0;
/* Read the relocations. */
relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
char *tls_mask;
char tls_set, tls_clear;
bfd_boolean is_local;
- int expecting_tls_get_addr;
bfd_signed_vma *got_count;
r_symndx = ELF32_R_SYM (rel->r_info);
h = (struct elf_link_hash_entry *) h->root.u.i.link;
}
- expecting_tls_get_addr = 0;
is_local = FALSE;
if (h == NULL
|| !h->def_dynamic)
is_local = TRUE;
r_type = ELF32_R_TYPE (rel->r_info);
+ /* If this section has old-style __tls_get_addr calls
+ without marker relocs, then check that each
+ __tls_get_addr call reloc is preceded by a reloc
+ that conceivably belongs to the __tls_get_addr arg
+ setup insn. If we don't find matching arg setup
+ relocs, don't do any tls optimization. */
+ if (pass == 0
+ && sec->has_tls_get_addr_call
+ && h != NULL
+ && h == htab->tls_get_addr
+ && !expecting_tls_get_addr
+ && is_branch_reloc (r_type))
+ {
+ info->callbacks->minfo ("%C __tls_get_addr lost arg, "
+ "TLS optimization disabled\n",
+ ibfd, sec, rel->r_offset);
+ if (elf_section_data (sec)->relocs != relstart)
+ free (relstart);
+ return TRUE;
+ }
+
+ expecting_tls_get_addr = 0;
switch (r_type)
{
case R_PPC_GOT_TLSLD16:
else
continue;
+ case R_PPC_TLSGD:
+ case R_PPC_TLSLD:
+ expecting_tls_get_addr = 2;
+ tls_set = 0;
+ tls_clear = 0;
+ break;
+
default:
continue;
}
if (pass == 0)
{
if (!expecting_tls_get_addr
- || !sec->has_tls_get_addr_call)
+ || (expecting_tls_get_addr == 1
+ && !sec->has_tls_get_addr_call))
continue;
if (rel + 1 < relend
/* Uh oh, we didn't find the expected call. We
could just mark this symbol to exclude it
from tls optimization but it's safer to skip
- the entire section. */
- sec->has_tls_reloc = 0;
- break;
+ the entire optimization. */
+ info->callbacks->minfo (_("%C arg lost __tls_get_addr, "
+ "TLS optimization disabled\n"),
+ ibfd, sec, rel->r_offset);
+ if (elf_section_data (sec)->relocs != relstart)
+ free (relstart);
+ return TRUE;
+ }
+
+ if (expecting_tls_get_addr)
+ {
+ struct plt_entry *ent;
+ bfd_vma addend = 0;
+
+ if (info->shared
+ && ELF32_R_TYPE (rel[1].r_info) == R_PPC_PLTREL24)
+ addend = rel[1].r_addend;
+ ent = find_plt_ent (&htab->tls_get_addr->plt.plist,
+ got2, addend);
+ if (ent != NULL && ent->plt.refcount > 0)
+ ent->plt.refcount -= 1;
+
+ if (expecting_tls_get_addr == 2)
+ continue;
}
if (h != NULL)
}
else
{
- Elf_Internal_Sym *sym;
bfd_signed_vma *lgot_refs;
struct plt_entry **local_plt;
char *lgot_masks;
return FALSE;
}
}
- sym = locsyms + r_symndx;
lgot_refs = elf_local_got_refcounts (ibfd);
if (lgot_refs == NULL)
abort ();
*got_count -= 1;
}
- if (expecting_tls_get_addr)
- {
- struct plt_entry *ent;
-
- ent = find_plt_ent (&htab->tls_get_addr->plt.plist,
- NULL, 0);
- if (ent != NULL && ent->plt.refcount > 0)
- ent->plt.refcount -= 1;
- }
-
*tls_mask |= tls_set;
*tls_mask &= ~tls_clear;
}
/* If this symbol is not defined in a regular
file, and we are not generating a shared
- library, then set the symbol to this location
+ library, then set the symbol to this location
in the .plt. This is to avoid text
relocations, and is required to make
function pointers compare as equal between
local_plt = (struct plt_entry **) end_local_got;
end_local_plt = local_plt + locsymcount;
lgot_masks = (char *) end_local_plt;
+
for (; local_got < end_local_got; ++local_got, ++lgot_masks)
if (*local_got > 0)
{
for (ent = *local_plt; ent != NULL; ent = ent->next)
if (ent->plt.refcount > 0)
{
- asection *s = htab->iplt;
+ s = htab->iplt;
if (!doneone)
{
struct ppc_elf_link_hash_table *htab;
bfd_size_type trampoff;
asection *got2;
+ bfd_boolean maybe_pasted;
*again = FALSE;
anyway. */
if (link_info->relocatable && link_info->shared)
return TRUE;
-
+
trampoff = (isec->size + 3) & (bfd_vma) -4;
+ maybe_pasted = (strcmp (isec->output_section->name, ".init") == 0
+ || strcmp (isec->output_section->name, ".fini") == 0);
/* Space for a branch around any trampolines. */
- trampoff += 4;
+ if (maybe_pasted)
+ trampoff += 4;
symtab_hdr = &elf_symtab_hdr (abfd);
for (irel = internal_relocs; irel < irelend; irel++)
{
unsigned long r_type = ELF32_R_TYPE (irel->r_info);
- bfd_vma reladdr, toff, roff;
+ bfd_vma toff, roff;
asection *tsec;
struct one_fixup *f;
size_t insn_offset = 0;
continue;
roff = irel->r_offset;
- reladdr = isec->output_section->vma + isec->output_offset + roff;
/* If the branch is in range, no need to do anything. */
if (tsec != bfd_und_section_ptr
{
size = 4 * ARRAY_SIZE (shared_stub_entry);
insn_offset = 12;
- stub_rtype = R_PPC_RELAX32PC;
}
else
{
size = 4 * ARRAY_SIZE (stub_entry);
insn_offset = 0;
- stub_rtype = R_PPC_RELAX32;
}
-
- if (R_PPC_RELAX32_PLT - R_PPC_RELAX32
- != R_PPC_RELAX32PC_PLT - R_PPC_RELAX32PC)
- abort ();
+ stub_rtype = R_PPC_RELAX;
if (tsec == htab->plt
|| tsec == htab->glink)
- stub_rtype += R_PPC_RELAX32_PLT - R_PPC_RELAX32;
+ {
+ stub_rtype = R_PPC_RELAX_PLT;
+ if (r_type == R_PPC_PLTREL24)
+ stub_rtype = R_PPC_RELAX_PLTREL24;
+ }
/* Hijack the old relocation. Since we need two
relocations for this use a "composite" reloc. */
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
stub_rtype);
irel->r_offset = trampoff + insn_offset;
- if (r_type == R_PPC_PLTREL24)
+ if (r_type == R_PPC_PLTREL24
+ && stub_rtype != R_PPC_RELAX_PLTREL24)
irel->r_addend = 0;
/* Record the fixup so we don't do it again this section. */
{
const int *stub;
bfd_byte *dest;
- bfd_vma val;
int i, size;
do
goto error_return;
isec->size = (isec->size + 3) & (bfd_vma) -4;
- /* Branch around the trampolines. */
- val = B + trampoff - isec->size;
dest = contents + isec->size;
+ /* Branch around the trampolines. */
+ if (maybe_pasted)
+ {
+ bfd_vma val = B + trampoff - isec->size;
+ bfd_put_32 (abfd, val, dest);
+ dest += 4;
+ }
isec->size = trampoff;
- bfd_put_32 (abfd, val, dest);
- dest += 4;
if (link_info->shared)
{
{
/* Append sufficient NOP relocs so we can write out relocation
information for the trampolines. */
+ Elf_Internal_Shdr *rel_hdr;
Elf_Internal_Rela *new_relocs = bfd_malloc ((changes + isec->reloc_count)
* sizeof (*new_relocs));
unsigned ix;
-
+
if (!new_relocs)
goto error_return;
memcpy (new_relocs, internal_relocs,
free (internal_relocs);
elf_section_data (isec)->relocs = new_relocs;
isec->reloc_count += changes;
- elf_section_data (isec)->rel_hdr.sh_size
- += changes * elf_section_data (isec)->rel_hdr.sh_entsize;
+ rel_hdr = _bfd_elf_single_rel_hdr (isec);
+ rel_hdr->sh_size += changes * rel_hdr->sh_entsize;
}
else if (elf_section_data (isec)->relocs != internal_relocs)
free (internal_relocs);
{
/* Convert the internal relax relocs to external form. */
for (irel = internal_relocs; irel < irelend; irel++)
- if (ELF32_R_TYPE (irel->r_info) == R_PPC_RELAX32)
+ if (ELF32_R_TYPE (irel->r_info) == R_PPC_RELAX)
{
unsigned long r_symndx = ELF32_R_SYM (irel->r_info);
irel++;
}
}
-
+
return TRUE;
error_return:
return insn;
}
+/* If INSN is an opcode that may be used with an @tprel operand, return
+ the transformed insn for an undefined weak symbol, ie. with the
+ thread pointer REG operand removed. Otherwise return 0. */
+
+unsigned int
+_bfd_elf_ppc_at_tprel_transform (unsigned int insn, unsigned int reg)
+{
+ if ((insn & (0x1f << 16)) == reg << 16
+ && ((insn & (0x3f << 26)) == 14u << 26 /* addi */
+ || (insn & (0x3f << 26)) == 15u << 26 /* addis */
+ || (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))))
+ {
+ insn &= ~(0x1f << 16);
+ }
+ else if ((insn & (0x1f << 21)) == reg << 21
+ && ((insn & (0x3e << 26)) == 24u << 26 /* ori, oris */
+ || (insn & (0x3e << 26)) == 26u << 26 /* xori,xoris */
+ || (insn & (0x3e << 26)) == 28u << 26 /* andi,andis */))
+ {
+ insn &= ~(0x1f << 21);
+ insn |= (insn & (0x1f << 16)) << 5;
+ if ((insn & (0x3e << 26)) == 26 << 26 /* xori,xoris */)
+ insn -= 2 >> 26; /* convert to ori,oris */
+ }
+ else
+ insn = 0;
+ return insn;
+}
+
/* The RELOCATE_SECTION function is called by the ELF backend linker
to handle the relocations for a section.
Elf_Internal_Rela *rel;
Elf_Internal_Rela *relend;
Elf_Internal_Rela outrel;
- bfd_byte *loc;
asection *got2, *sreloc = NULL;
bfd_vma *local_got_offsets;
bfd_boolean ret = TRUE;
reloc_howto_type *howto;
unsigned long r_symndx;
bfd_vma relocation;
- bfd_vma branch_bit, insn, from;
+ bfd_vma branch_bit, from;
bfd_boolean unresolved_reloc;
bfd_boolean warned;
unsigned int tls_type, tls_mask, tls_gd;
howto = NULL;
if (r_type < R_PPC_max)
howto = ppc_elf_howto_table[r_type];
- _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset);
- rel->r_info = 0;
- rel->r_addend = 0;
- continue;
+ RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
+ rel, relend, howto, contents);
}
if (info->relocatable)
&& (tls_mask & TLS_TPREL) == 0)
{
bfd_vma insn;
+
insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset);
insn &= 31 << 21;
insn |= 0x3c020000; /* addis 0,2,0 */
if (local_sections[r_symndx] == sec)
break;
if (r_symndx >= symtab_hdr->sh_info)
- r_symndx = 0;
+ r_symndx = STN_UNDEF;
rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
- if (r_symndx != 0)
+ if (r_symndx != STN_UNDEF)
rel->r_addend -= (local_syms[r_symndx].st_value
+ sec->output_offset
+ sec->output_section->vma);
if (local_sections[r_symndx] == sec)
break;
if (r_symndx >= symtab_hdr->sh_info)
- r_symndx = 0;
+ r_symndx = STN_UNDEF;
rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
- if (r_symndx != 0)
+ if (r_symndx != STN_UNDEF)
rel->r_addend -= (local_syms[r_symndx].st_value
+ sec->output_offset
+ sec->output_section->vma);
/* Branch not taken prediction relocations. */
case R_PPC_ADDR14_BRNTAKEN:
case R_PPC_REL14_BRNTAKEN:
- insn = bfd_get_32 (output_bfd, contents + rel->r_offset);
- insn &= ~BRANCH_PREDICT_BIT;
- insn |= branch_bit;
+ {
+ bfd_vma insn;
- from = (rel->r_offset
- + input_section->output_offset
- + input_section->output_section->vma);
+ insn = bfd_get_32 (output_bfd, contents + rel->r_offset);
+ insn &= ~BRANCH_PREDICT_BIT;
+ insn |= branch_bit;
- /* Invert 'y' bit if not the default. */
- if ((bfd_signed_vma) (relocation + rel->r_addend - from) < 0)
- insn ^= BRANCH_PREDICT_BIT;
+ from = (rel->r_offset
+ + input_section->output_offset
+ + input_section->output_section->vma);
- bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
- break;
+ /* Invert 'y' bit if not the default. */
+ if ((bfd_signed_vma) (relocation + rel->r_addend - from) < 0)
+ insn ^= BRANCH_PREDICT_BIT;
+
+ bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
+ break;
+ }
}
ifunc = NULL;
|| h->root.type != bfd_link_hash_undefweak))
{
asection *rsec = htab->relgot;
+ bfd_byte * loc;
outrel.r_offset = (htab->got->output_section->vma
+ htab->got->output_offset
case R_PPC_TPREL16_LO:
case R_PPC_TPREL16_HI:
case R_PPC_TPREL16_HA:
+ if (h != NULL
+ && h->root.type == bfd_link_hash_undefweak
+ && h->dynindx == -1)
+ {
+ /* Make this relocation against an undefined weak symbol
+ resolve to zero. This is really just a tweak, since
+ code using weak externs ought to check that they are
+ defined before using them. */
+ bfd_byte *p = contents + rel->r_offset - d_offset;
+ unsigned int insn = bfd_get_32 (output_bfd, p);
+ insn = _bfd_elf_ppc_at_tprel_transform (insn, 2);
+ if (insn != 0)
+ bfd_put_32 (output_bfd, insn, p);
+ break;
+ }
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
&& !h->def_regular))
{
int skip;
-
+ bfd_byte * loc;
#ifdef DEBUG
fprintf (stderr, "ppc_elf_relocate_section needs to "
"create relocation for %s\n",
}
skip = 0;
- outrel.r_offset =
- _bfd_elf_section_offset (output_bfd, info, input_section,
- rel->r_offset);
+ outrel.r_offset = _bfd_elf_section_offset (output_bfd, info,
+ input_section,
+ rel->r_offset);
if (outrel.r_offset == (bfd_vma) -1
|| outrel.r_offset == (bfd_vma) -2)
skip = (int) outrel.r_offset;
sym_name);
ret = FALSE;
}
- else if (r_symndx == 0 || bfd_is_abs_section (sec))
+ else if (r_symndx == STN_UNDEF || bfd_is_abs_section (sec))
;
else if (sec == NULL || sec->owner == NULL)
{
}
break;
- case R_PPC_RELAX32PC_PLT:
- case R_PPC_RELAX32_PLT:
+ case R_PPC_RELAX_PLT:
+ case R_PPC_RELAX_PLTREL24:
if (h != NULL)
{
- struct plt_entry *ent = find_plt_ent (&h->plt.plist, got2,
- info->shared ? addend : 0);
+ struct plt_entry *ent;
+ bfd_vma got2_addend = 0;
+
+ if (r_type == R_PPC_RELAX_PLTREL24)
+ {
+ if (info->shared)
+ got2_addend = addend;
+ addend = 0;
+ }
+ ent = find_plt_ent (&h->plt.plist, got2, got2_addend);
if (htab->plt_type == PLT_NEW)
relocation = (htab->glink->output_section->vma
+ htab->glink->output_offset
+ htab->plt->output_offset
+ ent->plt.offset);
}
- if (r_type == R_PPC_RELAX32_PLT)
- goto relax32;
/* Fall thru */
- case R_PPC_RELAX32PC:
- relocation -= (input_section->output_section->vma
- + input_section->output_offset
- + rel->r_offset - 4);
- /* Fall thru */
+ case R_PPC_RELAX:
+ if (info->shared)
+ relocation -= (input_section->output_section->vma
+ + input_section->output_offset
+ + rel->r_offset - 4);
- case R_PPC_RELAX32:
- relax32:
{
unsigned long t0;
unsigned long t1;
}
if (r_type == R_PPC_EMB_SDA21)
- { /* fill in register field */
+ {
+ bfd_vma insn; /* Fill in register field. */
+
insn = bfd_get_32 (output_bfd, contents + rel->r_offset);
insn = (insn & ~RA_REGISTER_MASK) | (reg << RA_REGISTER_SHIFT);
bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
{
bfd_vma got_offset;
const bfd_vma *plt_entry;
-
+
/* The first three entries in .got.plt are reserved. */
got_offset = (reloc_index + 3) * 4;
low-order 16 bits of the load instruction. */
/* NOTE: It appears that this is now an index rather than a
prescaled offset. */
- bfd_put_32 (output_bfd,
+ bfd_put_32 (output_bfd,
plt_entry[4] | reloc_index,
htab->plt->contents + ent->plt.offset + 16);
/* This instruction is a PC-relative branch whose target is
The address is encoded in bits 6-29, inclusive. The value
stored is right-shifted by two bits, permitting a 26-bit
offset. */
- bfd_put_32 (output_bfd,
- (plt_entry[5]
+ bfd_put_32 (output_bfd,
+ (plt_entry[5]
| (-(ent->plt.offset + 20) & 0x03fffffc)),
htab->plt->contents + ent->plt.offset + 20);
bfd_put_32 (output_bfd, plt_entry[6],
relocation. */
sym->st_shndx = (_bfd_elf_section_from_bfd_section
(output_bfd, htab->glink->output_section));
- sym->st_value = (ent->glink_offset +
- htab->glink->output_offset
+ sym->st_value = (ent->glink_offset
+ + htab->glink->output_offset
+ htab->glink->output_section->vma);
}
doneone = TRUE;
dynobj = elf_hash_table (info)->dynobj;
sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
if (htab->is_vxworks)
- splt = bfd_get_section_by_name (dynobj, ".plt");
+ splt = bfd_get_section_by_name (dynobj, ".plt");
else
splt = NULL;
if (splt && splt->size > 0)
{
/* Use the right PLT. */
- static const bfd_vma *plt_entry = NULL;
- plt_entry = info->shared ?
- ppc_elf_vxworks_pic_plt0_entry : ppc_elf_vxworks_plt0_entry;
+ const bfd_vma *plt_entry = (info->shared
+ ? ppc_elf_vxworks_pic_plt0_entry
+ : ppc_elf_vxworks_plt0_entry);
if (!info->shared)
{
rela.r_addend = 0;
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
loc += sizeof (Elf32_External_Rela);
-
+
/* Output the @l relocation for the second instruction. */
rela.r_offset = (htab->plt->output_section->vma
+ htab->plt->output_offset
#define TARGET_BIG_SYM bfd_elf32_powerpc_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