/* 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.
#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
{
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;
+
+ 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;
- 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 (num_entries)
+ {
+ /* Set the output section size, if it exists. */
+ asec = bfd_get_section_by_name (abfd, APUINFO_SECTION_NAME);
+ 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);
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. */
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;
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, 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;
}
}
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.
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