This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
+
/* The 64-bit PowerPC ELF ABI may be found at
http://www.linuxbase.org/spec/ELF/ppc64/PPC-elf64abi.txt, and
http://www.linuxbase.org/spec/ELF/ppc64/spec/book1.html */
+#include "sysdep.h"
#include <stdarg.h>
#include "bfd.h"
-#include "sysdep.h"
#include "bfdlink.h"
#include "libbfd.h"
#include "elf-bfd.h"
#define elf_backend_can_gc_sections 1
#define elf_backend_can_refcount 1
#define elf_backend_rela_normal 1
+#define elf_backend_default_execstack 0
#define bfd_elf64_mkobject ppc64_elf_mkobject
#define bfd_elf64_bfd_reloc_type_lookup ppc64_elf_reloc_type_lookup
+#define bfd_elf64_bfd_reloc_name_lookup ppc64_elf_reloc_name_lookup
#define bfd_elf64_bfd_merge_private_bfd_data ppc64_elf_merge_private_bfd_data
#define bfd_elf64_new_section_hook ppc64_elf_new_section_hook
#define bfd_elf64_bfd_link_hash_table_create ppc64_elf_link_hash_table_create
/* .plt call stub instructions. The normal stub is like this, but
sometimes the .plt entry crosses a 64k boundary and we need to
- insert an addis to adjust r12. */
+ insert an addi to adjust r12. */
#define PLT_CALL_STUB_SIZE (7*4)
#define ADDIS_R12_R2 0x3d820000 /* addis %r12,%r2,xxx@ha */
#define STD_R2_40R1 0xf8410028 /* std %r2,40(%r1) */
#define LD_R11_0R12 0xe96c0000 /* ld %r11,xxx+0@l(%r12) */
-#define LD_R2_0R12 0xe84c0000 /* ld %r2,xxx+8@l(%r12) */
#define MTCTR_R11 0x7d6903a6 /* mtctr %r11 */
+#define LD_R2_0R12 0xe84c0000 /* ld %r2,xxx+8@l(%r12) */
/* ld %r11,xxx+16@l(%r12) */
#define BCTR 0x4e800420 /* bctr */
#define ADDIS_R12_R12 0x3d8c0000 /* addis %r12,%r12,off@ha */
+#define ADDI_R12_R12 0x398c0000 /* addi %r12,%r12,off@l */
#define ADDIS_R2_R2 0x3c420000 /* addis %r2,%r2,off@ha */
#define ADDI_R2_R2 0x38420000 /* addi %r2,%r2,off@l */
+#define LD_R11_0R2 0xe9620000 /* ld %r11,xxx+0(%r2) */
+#define LD_R2_0R2 0xe8420000 /* ld %r2,xxx+0(%r2) */
+
#define LD_R2_40R1 0xe8410028 /* ld %r2,40(%r1) */
/* glink call stub instructions. We enter with the index in R0. */
/* Like R_PPC64_PLTGOT16, but for instructions with a DS field. */
/* FIXME: R_PPC64_PLTGOT16_DS not implemented. */
- HOWTO (R_PPC64_PLTGOT16_DS, /* type */
+ HOWTO (R_PPC64_PLTGOT16_DS, /* type */
0, /* rightshift */
1, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
return ppc64_elf_howto_table[r];
};
+static reloc_howto_type *
+ppc64_elf_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+ const char *r_name)
+{
+ unsigned int i;
+
+ for (i = 0;
+ i < sizeof (ppc64_elf_howto_raw) / sizeof (ppc64_elf_howto_raw[0]);
+ i++)
+ if (ppc64_elf_howto_raw[i].name != NULL
+ && strcasecmp (ppc64_elf_howto_raw[i].name, r_name) == 0)
+ return &ppc64_elf_howto_raw[i];
+
+ return NULL;
+}
+
/* Set the howto pointer for a PowerPC ELF reloc. */
static void
ppc_stub_plt_call:
Used to call a function in a shared library. If it so happens that
the plt entry referenced crosses a 64k boundary, then an extra
- "addis %r12,%r12,1" will be inserted before the load at xxx+8 or
- xxx+16 as appropriate.
+ "addi %r12,%r12,xxx@toc@l" will be inserted before the "mtctr".
. addis %r12,%r2,xxx@toc@ha
. std %r2,40(%r1)
. ld %r11,xxx+0@toc@l(%r12)
- . ld %r2,xxx+8@toc@l(%r12)
. mtctr %r11
+ . ld %r2,xxx+8@toc@l(%r12)
. ld %r11,xxx+16@toc@l(%r12)
. bctr
. addi %r2,%r2,off@l
. mtctr %r11
. bctr
+
+ In cases where the "addis" instruction would add zero, the "addis" is
+ omitted and following instructions modified slightly in some cases.
*/
enum ppc_stub_type {
return FALSE;
/* Create branch lookup table for plt_branch stubs. */
- if (info->shared)
- {
- flags = (SEC_ALLOC | SEC_LOAD
- | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
- htab->brlt
- = bfd_make_section_anyway_with_flags (dynobj, ".data.rel.ro.brlt",
- flags);
- }
- else
- {
- flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
- | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
- htab->brlt
- = bfd_make_section_anyway_with_flags (dynobj, ".rodata.brlt", flags);
- }
-
+ flags = (SEC_ALLOC | SEC_LOAD
+ | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ htab->brlt = bfd_make_section_anyway_with_flags (dynobj, ".branch_lt",
+ flags);
if (htab->brlt == NULL
|| ! bfd_set_section_alignment (dynobj, htab->brlt, 3))
return FALSE;
- if (info->shared)
- {
- flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
- | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
- htab->relbrlt
- = bfd_make_section_anyway_with_flags (dynobj, ".rela.data.rel.ro.brlt",
- flags);
- }
- else
+ if (!info->shared)
return TRUE;
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
+ | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ htab->relbrlt = bfd_make_section_anyway_with_flags (dynobj,
+ ".rela.branch_lt",
+ flags);
if (!htab->relbrlt
|| ! bfd_set_section_alignment (dynobj, htab->relbrlt, 3))
return FALSE;
{
struct ppc_link_hash_table *htab;
asection *s;
- unsigned int power_of_two;
htab = ppc_hash_table (info);
if (!h->non_got_ref)
return TRUE;
+ /* Don't generate a copy reloc for symbols defined in the executable. */
+ if (!h->def_dynamic || !h->ref_regular || h->def_regular)
+ return TRUE;
+
if (ELIMINATE_COPY_RELOCS)
{
struct ppc_link_hash_entry * eh;
h->needs_copy = 1;
}
- /* We need to figure out the alignment required for this symbol. I
- have no idea how ELF linkers handle this. */
- power_of_two = bfd_log2 (h->size);
- if (power_of_two > 4)
- power_of_two = 4;
-
- /* Apply the required alignment. */
s = htab->dynbss;
- s->size = BFD_ALIGN (s->size, (bfd_size_type) (1 << power_of_two));
- if (power_of_two > bfd_get_section_alignment (htab->elf.dynobj, s))
- {
- if (! bfd_set_section_alignment (htab->elf.dynobj, s, power_of_two))
- return FALSE;
- }
-
- /* Define the symbol as being at this point in the section. */
- h->root.u.def.section = s;
- h->root.u.def.value = s->size;
- /* Increment the section size to make room for the symbol. */
- s->size += h->size;
-
- return TRUE;
+ return _bfd_elf_adjust_dynamic_copy (h, s);
}
/* If given a function descriptor symbol, hide both the function code
#define PPC_HI(v) (((v) >> 16) & 0xffff)
#define PPC_HA(v) PPC_HI ((v) + 0x8000)
- bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (offset), p), p += 4;
- bfd_put_32 (obfd, STD_R2_40R1, p), p += 4;
- bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset), p), p += 4;
- if (PPC_HA (offset + 8) != PPC_HA (offset))
- bfd_put_32 (obfd, ADDIS_R12_R12 | 1, p), p += 4;
- offset += 8;
- bfd_put_32 (obfd, LD_R2_0R12 | PPC_LO (offset), p), p += 4;
- if (PPC_HA (offset + 8) != PPC_HA (offset))
- bfd_put_32 (obfd, ADDIS_R12_R12 | 1, p), p += 4;
- offset += 8;
- bfd_put_32 (obfd, MTCTR_R11, p), p += 4;
- bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset), p), p += 4;
- bfd_put_32 (obfd, BCTR, p), p += 4;
+ if (PPC_HA (offset) != 0)
+ {
+ bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (offset), p), p += 4;
+ bfd_put_32 (obfd, STD_R2_40R1, p), p += 4;
+ bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset), p), p += 4;
+ if (PPC_HA (offset + 16) != PPC_HA (offset))
+ {
+ bfd_put_32 (obfd, ADDI_R12_R12 | PPC_LO (offset), p), p += 4;
+ offset = 0;
+ }
+ bfd_put_32 (obfd, MTCTR_R11, p), p += 4;
+ bfd_put_32 (obfd, LD_R2_0R12 | PPC_LO (offset + 8), p), p += 4;
+ bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset + 16), p), p += 4;
+ bfd_put_32 (obfd, BCTR, p), p += 4;
+ }
+ else
+ {
+ bfd_put_32 (obfd, STD_R2_40R1, p), p += 4;
+ bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset), p), p += 4;
+ if (PPC_HA (offset + 16) != PPC_HA (offset))
+ {
+ bfd_put_32 (obfd, ADDI_R2_R2 | PPC_LO (offset), p), p += 4;
+ offset = 0;
+ }
+ bfd_put_32 (obfd, MTCTR_R11, p), p += 4;
+ bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset + 16), p), p += 4;
+ bfd_put_32 (obfd, LD_R2_0R2 | PPC_LO (offset + 8), p), p += 4;
+ bfd_put_32 (obfd, BCTR, p), p += 4;
+ }
return p;
}
+ stub_entry->stub_sec->output_offset
+ stub_entry->stub_sec->output_section->vma);
- if (stub_entry->stub_type != ppc_stub_long_branch_r2off)
- size = 4;
- else
+ size = 4;
+ if (stub_entry->stub_type == ppc_stub_long_branch_r2off)
{
bfd_vma r2off;
- htab->stub_group[stub_entry->id_sec->id].toc_off);
bfd_put_32 (htab->stub_bfd, STD_R2_40R1, loc);
loc += 4;
- bfd_put_32 (htab->stub_bfd, ADDIS_R2_R2 | PPC_HA (r2off), loc);
- loc += 4;
+ size = 12;
+ if (PPC_HA (r2off) != 0)
+ {
+ size = 16;
+ bfd_put_32 (htab->stub_bfd, ADDIS_R2_R2 | PPC_HA (r2off), loc);
+ loc += 4;
+ }
bfd_put_32 (htab->stub_bfd, ADDI_R2_R2 | PPC_LO (r2off), loc);
loc += 4;
- off -= 12;
- size = 16;
+ off -= size - 4;
}
bfd_put_32 (htab->stub_bfd, B_DOT | (off & 0x3fffffc), loc);
indx = off;
if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
{
- bfd_put_32 (htab->stub_bfd, ADDIS_R12_R2 | PPC_HA (indx), loc);
- loc += 4;
- bfd_put_32 (htab->stub_bfd, LD_R11_0R12 | PPC_LO (indx), loc);
- size = 16;
+ if (PPC_HA (indx) != 0)
+ {
+ size = 16;
+ bfd_put_32 (htab->stub_bfd, ADDIS_R12_R2 | PPC_HA (indx), loc);
+ loc += 4;
+ bfd_put_32 (htab->stub_bfd, LD_R11_0R12 | PPC_LO (indx), loc);
+ }
+ else
+ {
+ size = 12;
+ bfd_put_32 (htab->stub_bfd, LD_R11_0R2 | PPC_LO (indx), loc);
+ }
}
else
{
- htab->stub_group[stub_entry->id_sec->id].toc_off);
bfd_put_32 (htab->stub_bfd, STD_R2_40R1, loc);
loc += 4;
- bfd_put_32 (htab->stub_bfd, ADDIS_R12_R2 | PPC_HA (indx), loc);
- loc += 4;
- bfd_put_32 (htab->stub_bfd, LD_R11_0R12 | PPC_LO (indx), loc);
- loc += 4;
- bfd_put_32 (htab->stub_bfd, ADDIS_R2_R2 | PPC_HA (r2off), loc);
- loc += 4;
+ size = 20;
+ if (PPC_HA (indx) != 0)
+ {
+ size += 4;
+ bfd_put_32 (htab->stub_bfd, ADDIS_R12_R2 | PPC_HA (indx), loc);
+ loc += 4;
+ bfd_put_32 (htab->stub_bfd, LD_R11_0R12 | PPC_LO (indx), loc);
+ loc += 4;
+ }
+ else
+ {
+ bfd_put_32 (htab->stub_bfd, LD_R11_0R2 | PPC_LO (indx), loc);
+ loc += 4;
+ }
+
+ if (PPC_HA (r2off) != 0)
+ {
+ size += 4;
+ bfd_put_32 (htab->stub_bfd, ADDIS_R2_R2 | PPC_HA (r2off), loc);
+ loc += 4;
+ }
bfd_put_32 (htab->stub_bfd, ADDI_R2_R2 | PPC_LO (r2off), loc);
- size = 28;
}
loc += 4;
bfd_put_32 (htab->stub_bfd, MTCTR_R11, loc);
- htab->stub_group[stub_entry->id_sec->id].toc_off);
size = PLT_CALL_STUB_SIZE;
+ if (PPC_HA (off) == 0)
+ size -= 4;
if (PPC_HA (off + 16) != PPC_HA (off))
size += 4;
}
{
/* ppc_stub_long_branch or ppc_stub_plt_branch, or their r2off
variants. */
+ bfd_vma r2off = 0;
+
off = (stub_entry->target_value
+ stub_entry->target_section->output_offset
+ stub_entry->target_section->output_section->vma);
size = 4;
if (stub_entry->stub_type == ppc_stub_long_branch_r2off)
{
- off -= 12;
- size = 16;
+ r2off = (htab->stub_group[stub_entry->target_section->id].toc_off
+ - htab->stub_group[stub_entry->id_sec->id].toc_off);
+ size = 12;
+ if (PPC_HA (r2off) != 0)
+ size = 16;
+ off -= size - 4;
}
/* If the branch offset if too big, use a ppc_stub_plt_branch. */
if (off + (1 << 25) >= (bfd_vma) (1 << 26))
{
struct ppc_branch_hash_entry *br_entry;
+ unsigned int indx;
br_entry = ppc_branch_hash_lookup (&htab->branch_hash_table,
stub_entry->root.string + 9,
}
stub_entry->stub_type += ppc_stub_plt_branch - ppc_stub_long_branch;
- size = 16;
- if (stub_entry->stub_type != ppc_stub_plt_branch)
- size = 28;
+ off = (br_entry->offset
+ + htab->brlt->output_offset
+ + htab->brlt->output_section->vma
+ - elf_gp (htab->brlt->output_section->owner)
+ - htab->stub_group[stub_entry->id_sec->id].toc_off);
+
+ indx = off;
+ if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
+ {
+ size = 12;
+ if (PPC_HA (indx) != 0)
+ size = 16;
+ }
+ else
+ {
+ size = 20;
+ if (PPC_HA (indx) != 0)
+ size += 4;
+
+ if (PPC_HA (r2off) != 0)
+ size += 4;
+ }
}
else if (info->emitrelocations)
{
relocation += adjust;
}
}
- if (info->relocatable)
- continue;
}
else
{
- if (info->relocatable)
- continue;
RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
r_symndx, symtab_hdr, sym_hashes,
h_elf, sec, relocation,
}
h = (struct ppc_link_hash_entry *) h_elf;
+ if (sec != NULL && elf_discarded_section (sec))
+ {
+ /* For relocs against symbols from removed linkonce sections,
+ or sections discarded by a linker script, we just want the
+ section contents zeroed. Avoid any special processing. */
+ _bfd_clear_contents (ppc64_elf_howto_table[r_type], input_bfd,
+ contents + rel->r_offset);
+ rel->r_info = 0;
+ rel->r_addend = 0;
+ continue;
+ }
+
+ if (info->relocatable)
+ continue;
+
/* TLS optimizations. Replace instruction sequences and relocs
based on information we collected in tls_optimize. We edit
RELOCS so that --emit-relocs will output something sensible
relocation += htab->stub_group[sec->id].toc_off;
else
unresolved_reloc = TRUE;
- goto dodyn2;
+ goto dodyn;
/* TOC16 relocs. We want the offset relative to the TOC base,
which is the address of the start of the TOC plus 0x8000.
case R_PPC64_UADDR16:
case R_PPC64_UADDR32:
case R_PPC64_UADDR64:
- /* r_symndx will be zero only for relocs against symbols
- from removed linkonce sections, or sections discarded by
- a linker script. */
dodyn:
- if (r_symndx == 0)
- {
- _bfd_clear_contents (ppc64_elf_howto_table[r_type], input_bfd,
- contents + rel->r_offset);
- break;
- }
- /* Fall thru. */
-
- dodyn2:
if ((input_section->flags & SEC_ALLOC) == 0)
break;