aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf64-ppc.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf64-ppc.c')
-rw-r--r--bfd/elf64-ppc.c173
1 files changed, 98 insertions, 75 deletions
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index d4016b9..10d3fe9 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -187,8 +187,9 @@ static bfd_vma opd_entry_value
#define ADDIS_R12_R12 0x3d8c0000 /* addis %r12,%r12,xxx@ha */
#define LD_R12_0R12 0xe98c0000 /* ld %r12,xxx@l(%r12) */
-/* glink call stub instructions. We enter with the index in R0. */
-#define GLINK_CALL_STUB_SIZE (16*4)
+/* __glink_PLTresolve stub instructions. We enter with the index in R0. */
+#define GLINK_PLTRESOLVE_SIZE(htab) \
+ (8u + (htab->opd_abi ? 11 * 4 : 14 * 4))
/* 0: */
/* .quad plt0-1f */
/* __glink: */
@@ -3515,9 +3516,9 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
if (dyn.d_tag == DT_PPC64_GLINK)
{
- /* The first glink stub starts at offset 32; see
- comment in ppc64_elf_finish_dynamic_sections. */
- glink_vma = dyn.d_un.d_val + GLINK_CALL_STUB_SIZE - 8 * 4;
+ /* The first glink stub starts at DT_PPC64_GLINK plus 32.
+ See comment in ppc64_elf_finish_dynamic_sections. */
+ glink_vma = dyn.d_un.d_val + 8 * 4;
/* The .glink section usually does not survive the final
link; search for the section (usually .text) where the
glink stubs now reside. */
@@ -4092,6 +4093,7 @@ struct ppc_link_hash_table
/* Shortcuts to get to dynamic linker sections. */
asection *glink;
+ asection *global_entry;
asection *sfpr;
asection *brlt;
asection *relbrlt;
@@ -4431,6 +4433,14 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
|| ! bfd_set_section_alignment (dynobj, htab->glink, 3))
return FALSE;
+ /* The part of .glink used by global entry stubs, separate so that
+ it can be aligned appropriately without affecting htab->glink. */
+ htab->global_entry = bfd_make_section_anyway_with_flags (dynobj, ".glink",
+ flags);
+ if (htab->global_entry == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->global_entry, 2))
+ return FALSE;
+
if (!info->no_ld_generated_unwind_info)
{
flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
@@ -9793,11 +9803,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
/* Make room for the .glink code. */
s = htab->glink;
if (s->size == 0)
- s->size += GLINK_CALL_STUB_SIZE;
+ s->size += GLINK_PLTRESOLVE_SIZE (htab);
if (htab->opd_abi)
{
/* We need bigger stubs past index 32767. */
- if (s->size >= GLINK_CALL_STUB_SIZE + 32768*2*4)
+ if (s->size >= GLINK_PLTRESOLVE_SIZE (htab) + 32768*2*4)
s->size += 4;
s->size += 2*4;
}
@@ -9827,6 +9837,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
return TRUE;
}
+#define PPC_LO(v) ((v) & 0xffff)
+#define PPC_HI(v) (((v) >> 16) & 0xffff)
+#define PPC_HA(v) PPC_HI ((v) + 0x8000)
+
/* Called via elf_link_hash_traverse from ppc64_elf_size_dynamic_sections
to set up space for global entry stubs. These are put in glink,
after the branch table. */
@@ -9837,7 +9851,7 @@ size_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
struct bfd_link_info *info;
struct ppc_link_hash_table *htab;
struct plt_entry *pent;
- asection *s;
+ asection *s, *plt;
if (h->root.type == bfd_link_hash_indirect)
return TRUE;
@@ -9853,7 +9867,8 @@ size_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
if (htab == NULL)
return FALSE;
- s = htab->glink;
+ s = htab->global_entry;
+ plt = htab->elf.splt;
for (pent = h->plt.plist; pent != NULL; pent = pent->next)
if (pent->plt.offset != (bfd_vma) -1
&& pent->addend == 0)
@@ -9862,11 +9877,39 @@ size_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
and we are not generating a shared library or pie, then we
need to define the symbol in the executable on a call stub.
This is to avoid text relocations. */
- s->size = (s->size + 15) & -16;
+ bfd_vma off, stub_align, stub_off, stub_size;
+ unsigned int align_power;
+
+ stub_size = 16;
+ stub_off = s->size;
+ if (htab->params->plt_stub_align >= 0)
+ align_power = htab->params->plt_stub_align;
+ else
+ align_power = -htab->params->plt_stub_align;
+ /* Setting section alignment is delayed until we know it is
+ non-empty. Otherwise the .text output section will be
+ aligned at least to plt_stub_align even when no global
+ entry stubs are needed. */
+ if (s->alignment_power < align_power)
+ s->alignment_power = align_power;
+ stub_align = (bfd_vma) 1 << align_power;
+ if (htab->params->plt_stub_align >= 0
+ || ((((stub_off + stub_size - 1) & -stub_align)
+ - (stub_off & -stub_align))
+ > ((stub_size - 1) & -stub_align)))
+ stub_off = (stub_off + stub_align - 1) & -stub_align;
+ off = pent->plt.offset + plt->output_offset + plt->output_section->vma;
+ off -= stub_off + s->output_offset + s->output_section->vma;
+ /* Note that for --plt-stub-align negative we have a possible
+ dependency between stub offset and size. Break that
+ dependency by assuming the max stub size when calculating
+ the stub offset. */
+ if (PPC_HA (off) == 0)
+ stub_size -= 4;
h->root.type = bfd_link_hash_defined;
h->root.u.def.section = s;
- h->root.u.def.value = s->size;
- s->size += 16;
+ h->root.u.def.value = stub_off;
+ s->size = stub_off + stub_size;
break;
}
return TRUE;
@@ -10051,9 +10094,6 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
/* Allocate global sym .plt and .got entries, and space for global
sym dynamic relocs. */
elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, info);
- /* Stash the end of glink branch table. */
- if (htab->glink != NULL)
- htab->glink->rawsize = htab->glink->size;
if (!htab->opd_abi && !bfd_link_pic (info))
elf_link_hash_traverse (&htab->elf, size_global_entry_stubs, info);
@@ -10108,6 +10148,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
|| s == htab->elf.splt
|| s == htab->elf.iplt
|| s == htab->glink
+ || s == htab->global_entry
|| s == htab->elf.sdynbss
|| s == htab->elf.sdynrelro)
{
@@ -10393,10 +10434,6 @@ ppc_type_of_stub (asection *input_sec,
#define ALWAYS_USE_FAKE_DEP 0
#define ALWAYS_EMIT_R2SAVE 0
-#define PPC_LO(v) ((v) & 0xffff)
-#define PPC_HI(v) (((v) >> 16) & 0xffff)
-#define PPC_HA(v) PPC_HI ((v) + 0x8000)
-
static inline unsigned int
plt_stub_size (struct ppc_link_hash_table *htab,
struct ppc_stub_hash_entry *stub_entry,
@@ -10492,7 +10529,7 @@ build_plt_stub (struct ppc_link_hash_table *htab,
bfd_vma pltoff = stub_entry->plt_ent->plt.offset & ~1;
bfd_vma pltindex = ((pltoff - PLT_INITIAL_ENTRY_SIZE (htab))
/ PLT_ENTRY_SIZE (htab));
- bfd_vma glinkoff = GLINK_CALL_STUB_SIZE + pltindex * 8;
+ bfd_vma glinkoff = GLINK_PLTRESOLVE_SIZE (htab) + pltindex * 8;
bfd_vma to, from;
if (pltindex > 32768)
@@ -10767,7 +10804,6 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
bfd_byte *loc;
bfd_byte *p;
bfd_vma dest, off;
- int size;
Elf_Internal_Rela *r;
asection *plt;
@@ -10800,7 +10836,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
+ stub_entry->group->stub_sec->output_offset
+ stub_entry->group->stub_sec->output_section->vma);
- size = 4;
+ p = loc;
if (stub_entry->stub_type == ppc_stub_long_branch_r2off)
{
bfd_vma r2off = get_r2off (info, stub_entry);
@@ -10810,26 +10846,24 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
htab->stub_error = TRUE;
return FALSE;
}
- bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), loc);
- loc += 4;
- size = 8;
+ bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p);
+ p += 4;
if (PPC_HA (r2off) != 0)
{
bfd_put_32 (htab->params->stub_bfd,
- ADDIS_R2_R2 | PPC_HA (r2off), loc);
- loc += 4;
- size += 4;
+ ADDIS_R2_R2 | PPC_HA (r2off), p);
+ p += 4;
}
if (PPC_LO (r2off) != 0)
{
bfd_put_32 (htab->params->stub_bfd,
- ADDI_R2_R2 | PPC_LO (r2off), loc);
- loc += 4;
- size += 4;
+ ADDI_R2_R2 | PPC_LO (r2off), p);
+ p += 4;
}
- off -= size - 4;
+ off -= p - loc;
}
- bfd_put_32 (htab->params->stub_bfd, B_DOT | (off & 0x3fffffc), loc);
+ bfd_put_32 (htab->params->stub_bfd, B_DOT | (off & 0x3fffffc), p);
+ p += 4;
if (off + (1 << 25) >= (bfd_vma) (1 << 26))
{
@@ -10845,7 +10879,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
r = get_relocs (stub_entry->group->stub_sec, 1);
if (r == NULL)
return FALSE;
- r->r_offset = loc - stub_entry->group->stub_sec->contents;
+ r->r_offset = p - 4 - stub_entry->group->stub_sec->contents;
r->r_info = ELF64_R_INFO (0, R_PPC64_REL24);
r->r_addend = dest;
if (stub_entry->h != NULL)
@@ -10985,23 +11019,20 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
}
}
+ p = loc;
if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
{
if (PPC_HA (off) != 0)
{
- size = 16;
bfd_put_32 (htab->params->stub_bfd,
- ADDIS_R12_R2 | PPC_HA (off), loc);
- loc += 4;
+ ADDIS_R12_R2 | PPC_HA (off), p);
+ p += 4;
bfd_put_32 (htab->params->stub_bfd,
- LD_R12_0R12 | PPC_LO (off), loc);
+ LD_R12_0R12 | PPC_LO (off), p);
}
else
- {
- size = 12;
- bfd_put_32 (htab->params->stub_bfd,
- LD_R12_0R2 | PPC_LO (off), loc);
- }
+ bfd_put_32 (htab->params->stub_bfd,
+ LD_R12_0R2 | PPC_LO (off), p);
}
else
{
@@ -11013,40 +11044,37 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
return FALSE;
}
- bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), loc);
- loc += 4;
- size = 16;
+ bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p);
+ p += 4;
if (PPC_HA (off) != 0)
{
- size += 4;
bfd_put_32 (htab->params->stub_bfd,
- ADDIS_R12_R2 | PPC_HA (off), loc);
- loc += 4;
+ ADDIS_R12_R2 | PPC_HA (off), p);
+ p += 4;
bfd_put_32 (htab->params->stub_bfd,
- LD_R12_0R12 | PPC_LO (off), loc);
+ LD_R12_0R12 | PPC_LO (off), p);
}
else
- bfd_put_32 (htab->params->stub_bfd, LD_R12_0R2 | PPC_LO (off), loc);
+ bfd_put_32 (htab->params->stub_bfd, LD_R12_0R2 | PPC_LO (off), p);
if (PPC_HA (r2off) != 0)
{
- size += 4;
- loc += 4;
+ p += 4;
bfd_put_32 (htab->params->stub_bfd,
- ADDIS_R2_R2 | PPC_HA (r2off), loc);
+ ADDIS_R2_R2 | PPC_HA (r2off), p);
}
if (PPC_LO (r2off) != 0)
{
- size += 4;
- loc += 4;
+ p += 4;
bfd_put_32 (htab->params->stub_bfd,
- ADDI_R2_R2 | PPC_LO (r2off), loc);
+ ADDI_R2_R2 | PPC_LO (r2off), p);
}
}
- loc += 4;
- bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, loc);
- loc += 4;
- bfd_put_32 (htab->params->stub_bfd, BCTR, loc);
+ p += 4;
+ bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, p);
+ p += 4;
+ bfd_put_32 (htab->params->stub_bfd, BCTR, p);
+ p += 4;
break;
case ppc_stub_plt_call:
@@ -11150,7 +11178,6 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
p = build_tls_get_addr_stub (htab, stub_entry, loc, off, r);
else
p = build_plt_stub (htab, stub_entry, loc, off, r);
- size = p - loc;
break;
case ppc_stub_save_res:
@@ -11161,7 +11188,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
return FALSE;
}
- stub_entry->group->stub_sec->size += size;
+ stub_entry->group->stub_sec->size += p - loc;
if (htab->params->emit_stub_syms)
{
@@ -12951,7 +12978,7 @@ build_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
if (htab == NULL)
return FALSE;
- s = htab->glink;
+ s = htab->global_entry;
for (pent = h->plt.plist; pent != NULL; pent = pent->next)
if (pent->plt.offset != (bfd_vma) -1
&& pent->addend == 0)
@@ -13144,15 +13171,11 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
}
bfd_put_32 (htab->glink->owner, BCTR, p);
p += 4;
- while (p - htab->glink->contents < GLINK_CALL_STUB_SIZE)
- {
- bfd_put_32 (htab->glink->owner, NOP, p);
- p += 4;
- }
+ BFD_ASSERT (p - htab->glink->contents == GLINK_PLTRESOLVE_SIZE (htab));
/* Build the .glink lazy link call stubs. */
indx = 0;
- while (p < htab->glink->contents + htab->glink->rawsize)
+ while (p < htab->glink->contents + htab->glink->size)
{
if (htab->opd_abi)
{
@@ -13175,12 +13198,12 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
indx++;
p += 4;
}
-
- /* Build .glink global entry stubs. */
- if (htab->glink->size > htab->glink->rawsize)
- elf_link_hash_traverse (&htab->elf, build_global_entry_stubs, info);
}
+ /* Build .glink global entry stubs. */
+ if (htab->global_entry != NULL && htab->global_entry->size != 0)
+ elf_link_hash_traverse (&htab->elf, build_global_entry_stubs, info);
+
if (htab->brlt != NULL && htab->brlt->size != 0)
{
htab->brlt->contents = bfd_zalloc (htab->brlt->owner,
@@ -15557,7 +15580,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
of glink rather than the first entry point, which is
what ld.so needs, and now have a bigger stub to
support automatic multiple TOCs. */
- dyn.d_un.d_ptr += GLINK_CALL_STUB_SIZE - 8 * 4;
+ dyn.d_un.d_ptr += GLINK_PLTRESOLVE_SIZE (htab) - 8 * 4;
break;
case DT_PPC64_OPD: