diff options
author | Alan Modra <amodra@gmail.com> | 2018-01-13 18:53:41 +1030 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2018-01-17 18:51:04 +1030 |
commit | 9e390558cef76767a98123994c422d0642d86cf8 (patch) | |
tree | fa46b2f2c764e30be6e6e512fe29a01de0b4ad04 /bfd/elf32-ppc.c | |
parent | 78742b93a5fef4b88b1391817e7bcc6738000044 (diff) | |
download | gdb-9e390558cef76767a98123994c422d0642d86cf8.zip gdb-9e390558cef76767a98123994c422d0642d86cf8.tar.gz gdb-9e390558cef76767a98123994c422d0642d86cf8.tar.bz2 |
PowerPC PLT stub tidy
This is in preparation for the next patch adding Spectre variant 2
mitigation for PowerPC and PowerPC64. Besides tidying code involved
in stub output (to reduce the number of places where bctr is output),
the patch adds some user visible features:
1) PowerPC64 ELFv2 global entry stubs now are aligned under the
control of --plt-align, with a default alignment of 32 bytes.
2) PowerPC64 __glink_PLTresolve is no longer padded out with nops.
3) PowerPC32 PLT stubs are aligned under the control of --plt-align,
with the default alignment being 16 bytes as before.
4) The PowerPC32 branch/nop table emitted before __glink_PLTresolve
is now smaller in many cases. It was sized incorrectly when the
__tls_get_addr_opt stub was used, and unnecessarily included space
for local ifuncs.
bfd/
* elf32-ppc.c (GLINK_ENTRY_SIZE): Add parameters, handle
__tls_get_addr_opt, and alignment sizing.
(TLS_GET_ADDR_GLINK_SIZE): Delete.
(is_nonpic_glink_stub): Don't use GLINK_ENTRY_SIZE.
(ppc_elf_get_synthetic_symtab): Recognize stubs spaced at 4, 6,
or 8 insns.
(ppc_elf_link_hash_table_create): Init new ppc_elf_params field.
(allocate_dynrelocs): Use new GLINK_ENTRY_SIZE.
(ppc_elf_size_dynamic_sections): Likewise. Size branch table
by PLT reloc count.
(write_glink_stub): Handle __tls_get_addr_opt stub.
Pad out to size given by GLINK_ENTRY_SIZE.
(ppc_elf_relocate_section): Adjust write_glink_stub call.
(ppc_elf_finish_dynamic_symbol): Likewise.
(ppc_elf_finish_dynamic_sections): Write PLTresolve without using
insn array since so many need rewriting.
* elf32-ppc.h (struct ppc_elf_params): Add plt_stub_align.
* elf64-ppc.c (GLINK_PLTRESOLVE_SIZE): Rename from
GLINK_CALL_STUB_SIZE. Add htab param and evaluate to size without
nops. Adjust all uses.
(ppc64_elf_get_synthetic_symtab): Don't use GLINK_CALL_STUB_SIZE
in glink_vma calculation.
(struct ppc_link_hash_table): Add global_entry section pointer.
(create_linkage_sections): Create separate section for global
entry stubs.
(PPC_LO, PPC_HI, PPC_HA): Move earlier.
(size_global_entry_stubs): Handle sizing for aligned stubs.
(ppc64_elf_size_dynamic_sections): Handle global_entry alloc,
and don't stash end of glink branch table in rawsize.
(ppc_build_one_stub): Rewrite stub size calculations.
(build_global_entry_stubs): Use new section.
(ppc64_elf_build_stubs): Don't pad __glink_PLTresolve with nops.
Build lazy link stubs out to end of section. Build global entry
stubs in new section.
gold/
* options.h (plt_align): Support for PowerPC32 too.
* powerpc.cc (Stub_table::stub_align): Heed --plt-align for 32-bit.
(Stub_table::plt_call_size, branch_stub_size): Tidy.
(Stub_table::plt_call_align): Implement using stub_align.
(Output_data_glink::global_entry_align): New function.
(Output_data_glink::global_entry_off): New function.
(Output_data_glink::global_entry_address): Use global_entry_off.
(Output_data_glink::pltresolve_size): New function, replacing
pltresolve_size_ constant. Update all uses.
(Output_data_glink::add_global_entry): Align offset.
(Output_data_glink::set_final_data_size): Use global_entry_align.
(Stub_table::do_write): Don't pad __glink_PLTrelsolve with nops.
Tidy stub output. Use global_entry_off.
ld/
* emultempl/ppc32elf.em (params): Init new field.
(enum ppc32_opt): New enum to define OPTION_* values. Add
OPTION_PLT_ALIGN and OPTION_NO_PLT_ALIGN.
(PARSE_AND_LIST_LONGOPTS): Handle new options.
(PARSE_AND_LIST_ARGS_CASES): Likewise.
(PARSE_AND_LIST_OPTIONS): Likewise. Break up help output.
* emultempl/ppc64elf.em (ppc_add_stub_section): Init alignment
correctly for negative --plt-stub-align.
* testsuite/ld-powerpc/elfv2exe.d,
* testsuite/ld-powerpc/elfv2so.d,
* testsuite/ld-powerpc/relbrlt.d,
* testsuite/ld-powerpc/relbrlt.s,
* testsuite/ld-powerpc/tlsexe.d,
* testsuite/ld-powerpc/tlsexe.r,
* testsuite/ld-powerpc/tlsexe32.d,
* testsuite/ld-powerpc/tlsexe32.g,
* testsuite/ld-powerpc/tlsexe32.r,
* testsuite/ld-powerpc/tlsexetoc.d,
* testsuite/ld-powerpc/tlsexetoc.r,
* testsuite/ld-powerpc/tlsopt5_32.d,
* testsuite/ld-powerpc/tlsso.d,
* testsuite/ld-powerpc/tlstocso.d: Update for changed stub order.
Diffstat (limited to 'bfd/elf32-ppc.c')
-rw-r--r-- | bfd/elf32-ppc.c | 295 |
1 files changed, 127 insertions, 168 deletions
diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c index 3e40962..efdaf04 100644 --- a/bfd/elf32-ppc.c +++ b/bfd/elf32-ppc.c @@ -68,8 +68,13 @@ static bfd_reloc_status_type ppc_elf_unhandled_reloc /* For new-style .glink and .plt. */ #define GLINK_PLTRESOLVE 16*4 -#define GLINK_ENTRY_SIZE 4*4 -#define TLS_GET_ADDR_GLINK_SIZE 12*4 +#define GLINK_ENTRY_SIZE(htab, h) \ + ((4*4 \ + + (h != NULL \ + && h == htab->tls_get_addr \ + && !htab->params->no_tls_get_addr_opt ? 8*4 : 0) \ + + (1u << htab->params->plt_stub_align) - 1) \ + & -(1u << htab->params->plt_stub_align)) /* VxWorks uses its own plt layout, filled in by the static linker. */ @@ -2873,9 +2878,9 @@ ppc_elf_final_write_processing (bfd *abfd, bfd_boolean linker ATTRIBUTE_UNUSED) static bfd_boolean is_nonpic_glink_stub (bfd *abfd, asection *glink, bfd_vma off) { - bfd_byte buf[GLINK_ENTRY_SIZE]; + bfd_byte buf[4 * 4]; - if (!bfd_get_section_contents (abfd, glink, buf, off, GLINK_ENTRY_SIZE)) + if (!bfd_get_section_contents (abfd, glink, buf, off, sizeof buf)) return FALSE; return ((bfd_get_32 (abfd, buf + 0) & 0xffff0000) == LIS_11 @@ -2902,10 +2907,10 @@ ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms, asection *plt, *relplt, *dynamic, *glink; bfd_vma glink_vma = 0; bfd_vma resolv_vma = 0; - bfd_vma stub_vma; + bfd_vma stub_off; asymbol *s; arelent *p; - long count, i; + long count, i, stub_delta; size_t size; char *names; bfd_byte buf[4]; @@ -3016,9 +3021,14 @@ ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms, /* 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 - of figuring out the GOT pointer value used in the stub. */ - if (!is_nonpic_glink_stub (abfd, glink, - glink_vma - GLINK_ENTRY_SIZE - glink->vma)) + of figuring out the GOT pointer value used in the stub. + The offsets tested here need to cover all possible values of + GLINK_ENTRY_SIZE for other than __tls_get_addr_opt. */ + stub_off = glink_vma - glink->vma; + for (stub_delta = 16; stub_delta <= 32; stub_delta += 8) + if (is_nonpic_glink_stub (abfd, glink, stub_off - stub_delta)) + break; + if (stub_delta > 32) return 0; slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table; @@ -3043,13 +3053,16 @@ ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms, if (s == NULL) return -1; - stub_vma = glink_vma; + stub_off = glink_vma - glink->vma; names = (char *) (s + count + 1 + (resolv_vma != 0)); p = relplt->relocation + count - 1; for (i = 0; i < count; i++) { size_t len; + stub_off -= stub_delta; + if (strcmp ((*p->sym_ptr_ptr)->name, "__tls_get_addr_opt") == 0) + stub_off -= 32; *s = **p->sym_ptr_ptr; /* Undefined syms won't have BSF_LOCAL or BSF_GLOBAL set. Since we are defining a symbol, ensure one of them is set. */ @@ -3057,10 +3070,7 @@ ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms, 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->value = stub_off; s->name = names; s->udata.p = NULL; len = strlen ((*p->sym_ptr_ptr)->name); @@ -3355,7 +3365,7 @@ 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, 0, 0, 0 }; + = { PLT_OLD, 0, 0, 1, 0, 0, 12, 0, 0, 0 }; ret = bfd_zmalloc (sizeof (struct ppc_elf_link_hash_table)); if (ret == NULL) @@ -6015,10 +6025,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) if (!doneone || bfd_link_pic (info)) { glink_offset = s->size; - s->size += GLINK_ENTRY_SIZE; - if (h == htab->tls_get_addr - && !htab->params->no_tls_get_addr_opt) - s->size += TLS_GET_ADDR_GLINK_SIZE - GLINK_ENTRY_SIZE; + s->size += GLINK_ENTRY_SIZE (htab, h); } if (!doneone && !bfd_link_pic (info) @@ -6333,7 +6340,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd, if (!doneone || bfd_link_pic (info)) { glink_offset = s->size; - s->size += GLINK_ENTRY_SIZE; + s->size += GLINK_ENTRY_SIZE (htab, NULL); } ent->glink_offset = glink_offset; @@ -6401,10 +6408,9 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd, && htab->elf.dynamic_sections_created) { htab->glink_pltresolve = htab->glink->size; - /* Space for the branch table. ??? We don't need entries for - non-dynamic symbols in this table. This case can arise with - static ifuncs or forced local ifuncs. */ - htab->glink->size += htab->glink->size / (GLINK_ENTRY_SIZE / 4) - 4; + /* Space for the branch table. */ + htab->glink->size + += htab->elf.srelplt->size / (sizeof (Elf32_External_Rela) / 4) - 4; /* Pad out to align the start of PLTresolve. */ htab->glink->size += -htab->glink->size & (htab->params->ppc476_workaround ? 63 : 15); @@ -7443,12 +7449,36 @@ elf_finish_pointer_linker_section (bfd *input_bfd, #define PPC_HA(v) PPC_HI ((v) + 0x8000) static void -write_glink_stub (struct plt_entry *ent, asection *plt_sec, unsigned char *p, +write_glink_stub (struct elf_link_hash_entry *h, struct plt_entry *ent, + asection *plt_sec, unsigned char *p, struct bfd_link_info *info) { struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info); bfd *output_bfd = info->output_bfd; bfd_vma plt; + unsigned char *end = p + GLINK_ENTRY_SIZE (htab, h); + + if (h != NULL + && h == htab->tls_get_addr + && !htab->params->no_tls_get_addr_opt) + { + bfd_put_32 (output_bfd, LWZ_11_3, p); + p += 4; + bfd_put_32 (output_bfd, LWZ_12_3 + 4, p); + p += 4; + bfd_put_32 (output_bfd, MR_0_3, p); + p += 4; + bfd_put_32 (output_bfd, CMPWI_11_0, p); + p += 4; + bfd_put_32 (output_bfd, ADD_3_12_2, p); + p += 4; + bfd_put_32 (output_bfd, BEQLR, p); + p += 4; + bfd_put_32 (output_bfd, MR_3_0, p); + p += 4; + bfd_put_32 (output_bfd, NOP, p); + p += 4; + } plt = ((ent->plt.offset & ~1) + plt_sec->output_section->vma @@ -7468,26 +7498,12 @@ write_glink_stub (struct plt_entry *ent, asection *plt_sec, unsigned char *p, plt -= got; if (plt + 0x8000 < 0x10000) - { - bfd_put_32 (output_bfd, LWZ_11_30 + PPC_LO (plt), p); - p += 4; - bfd_put_32 (output_bfd, MTCTR_11, p); - p += 4; - bfd_put_32 (output_bfd, BCTR, p); - p += 4; - bfd_put_32 (output_bfd, htab->params->ppc476_workaround ? BA : NOP, p); - p += 4; - } + bfd_put_32 (output_bfd, LWZ_11_30 + PPC_LO (plt), p); else { bfd_put_32 (output_bfd, ADDIS_11_30 + PPC_HA (plt), p); p += 4; bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p); - p += 4; - bfd_put_32 (output_bfd, MTCTR_11, p); - p += 4; - bfd_put_32 (output_bfd, BCTR, p); - p += 4; } } else @@ -7495,10 +7511,15 @@ write_glink_stub (struct plt_entry *ent, asection *plt_sec, unsigned char *p, bfd_put_32 (output_bfd, LIS_11 + PPC_HA (plt), p); p += 4; bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p); - p += 4; - bfd_put_32 (output_bfd, MTCTR_11, p); - p += 4; - bfd_put_32 (output_bfd, BCTR, p); + } + p += 4; + bfd_put_32 (output_bfd, MTCTR_11, p); + p += 4; + bfd_put_32 (output_bfd, BCTR, p); + p += 4; + while (p < end) + { + bfd_put_32 (output_bfd, htab->params->ppc476_workaround ? BA : NOP, p); p += 4; } } @@ -8253,7 +8274,8 @@ ppc_elf_relocate_section (bfd *output_bfd, { unsigned char *p = ((unsigned char *) htab->glink->contents + ent->glink_offset); - write_glink_stub (ent, htab->elf.iplt, p, info); + + write_glink_stub (NULL, ent, htab->elf.iplt, p, info); ent->glink_offset |= 1; } @@ -10191,33 +10213,13 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, { unsigned char *p; asection *splt = htab->elf.splt; + if (!htab->elf.dynamic_sections_created || h->dynindx == -1) splt = htab->elf.iplt; p = (unsigned char *) htab->glink->contents + ent->glink_offset; - - if (h == htab->tls_get_addr && !htab->params->no_tls_get_addr_opt) - { - bfd_put_32 (output_bfd, LWZ_11_3, p); - p += 4; - bfd_put_32 (output_bfd, LWZ_12_3 + 4, p); - p += 4; - bfd_put_32 (output_bfd, MR_0_3, p); - p += 4; - bfd_put_32 (output_bfd, CMPWI_11_0, p); - p += 4; - bfd_put_32 (output_bfd, ADD_3_12_2, p); - p += 4; - bfd_put_32 (output_bfd, BEQLR, p); - p += 4; - bfd_put_32 (output_bfd, MR_3_0, p); - p += 4; - bfd_put_32 (output_bfd, NOP, p); - p += 4; - } - - write_glink_stub (ent, splt, p, info); + write_glink_stub (h, ent, splt, p, info); if (!bfd_link_pic (info)) /* We only need one non-PIC glink stub. */ @@ -10502,7 +10504,6 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd, unsigned char *p; unsigned char *endp; bfd_vma res0; - unsigned int i; /* * PIC glink code is the following: @@ -10539,28 +10540,7 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd, * add 0,11,11 * add 11,0,11 # r11 = index * 12 = reloc offset. * bctr - */ - static const unsigned int pic_plt_resolve[] = - { - ADDIS_11_11, - MFLR_0, - BCL_20_31, - ADDI_11_11, - MFLR_12, - MTLR_0, - SUB_11_11_12, - ADDIS_12_12, - LWZ_0_12, - LWZ_12_12, - MTCTR_0, - ADD_0_11_11, - ADD_11_0_11, - BCTR, - NOP, - NOP - }; - - /* + * * Non-PIC glink code is a little simpler. * * # ith PLT code stub. @@ -10582,30 +10562,6 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd, * add 11,0,11 # r11 = index * 12 = reloc offset. * bctr */ - static const unsigned int plt_resolve[] = - { - LIS_12, - ADDIS_11_11, - LWZ_0_12, - ADDI_11_11, - MTCTR_0, - ADD_0_11_11, - LWZ_12_12, - ADD_11_0_11, - BCTR, - NOP, - NOP, - NOP, - NOP, - NOP, - NOP, - NOP - }; - - if (ARRAY_SIZE (pic_plt_resolve) != GLINK_PLTRESOLVE / 4) - abort (); - if (ARRAY_SIZE (plt_resolve) != GLINK_PLTRESOLVE / 4) - abort (); /* Build the branch table, one for each plt entry (less one), and perhaps some padding. */ @@ -10662,80 +10618,83 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd, } /* Last comes the PLTresolve stub. */ + endp = p + GLINK_PLTRESOLVE; if (bfd_link_pic (info)) { bfd_vma bcl; - for (i = 0; i < ARRAY_SIZE (pic_plt_resolve); i++) - { - unsigned int insn = pic_plt_resolve[i]; - - if (htab->params->ppc476_workaround && insn == NOP) - insn = BA + 0; - bfd_put_32 (output_bfd, insn, p); - p += 4; - } - p -= 4 * ARRAY_SIZE (pic_plt_resolve); - bcl = (htab->glink->size - GLINK_PLTRESOLVE + 3*4 + htab->glink->output_section->vma + htab->glink->output_offset); - bfd_put_32 (output_bfd, - ADDIS_11_11 + PPC_HA (bcl - res0), p + 0*4); - bfd_put_32 (output_bfd, - ADDI_11_11 + PPC_LO (bcl - res0), p + 3*4); - bfd_put_32 (output_bfd, - ADDIS_12_12 + PPC_HA (got + 4 - bcl), p + 7*4); + bfd_put_32 (output_bfd, ADDIS_11_11 + PPC_HA (bcl - res0), p); + p += 4; + bfd_put_32 (output_bfd, MFLR_0, p); + p += 4; + bfd_put_32 (output_bfd, BCL_20_31, p); + p += 4; + bfd_put_32 (output_bfd, ADDI_11_11 + PPC_LO (bcl - res0), p); + p += 4; + bfd_put_32 (output_bfd, MFLR_12, p); + p += 4; + bfd_put_32 (output_bfd, MTLR_0, p); + p += 4; + bfd_put_32 (output_bfd, SUB_11_11_12, p); + p += 4; + bfd_put_32 (output_bfd, ADDIS_12_12 + PPC_HA (got + 4 - bcl), p); + p += 4; if (PPC_HA (got + 4 - bcl) == PPC_HA (got + 8 - bcl)) { - bfd_put_32 (output_bfd, - LWZ_0_12 + PPC_LO (got + 4 - bcl), p + 8*4); - bfd_put_32 (output_bfd, - LWZ_12_12 + PPC_LO (got + 8 - bcl), p + 9*4); + bfd_put_32 (output_bfd, LWZ_0_12 + PPC_LO (got + 4 - bcl), p); + p += 4; + bfd_put_32 (output_bfd, LWZ_12_12 + PPC_LO (got + 8 - bcl), p); + p += 4; } else { - bfd_put_32 (output_bfd, - LWZU_0_12 + PPC_LO (got + 4 - bcl), p + 8*4); - bfd_put_32 (output_bfd, - LWZ_12_12 + 4, p + 9*4); + bfd_put_32 (output_bfd, LWZU_0_12 + PPC_LO (got + 4 - bcl), p); + p += 4; + bfd_put_32 (output_bfd, LWZ_12_12 + 4, p); + p += 4; } + bfd_put_32 (output_bfd, MTCTR_0, p); + p += 4; + bfd_put_32 (output_bfd, ADD_0_11_11, p); } else { - for (i = 0; i < ARRAY_SIZE (plt_resolve); i++) - { - unsigned int insn = plt_resolve[i]; - - if (htab->params->ppc476_workaround && insn == NOP) - insn = BA + 0; - bfd_put_32 (output_bfd, insn, p); - p += 4; - } - p -= 4 * ARRAY_SIZE (plt_resolve); - - bfd_put_32 (output_bfd, - LIS_12 + PPC_HA (got + 4), p + 0*4); - bfd_put_32 (output_bfd, - ADDIS_11_11 + PPC_HA (-res0), p + 1*4); - bfd_put_32 (output_bfd, - ADDI_11_11 + PPC_LO (-res0), p + 3*4); + bfd_put_32 (output_bfd, LIS_12 + PPC_HA (got + 4), p); + p += 4; + bfd_put_32 (output_bfd, ADDIS_11_11 + PPC_HA (-res0), p); + p += 4; if (PPC_HA (got + 4) == PPC_HA (got + 8)) - { - bfd_put_32 (output_bfd, - LWZ_0_12 + PPC_LO (got + 4), p + 2*4); - bfd_put_32 (output_bfd, - LWZ_12_12 + PPC_LO (got + 8), p + 6*4); - } + bfd_put_32 (output_bfd, LWZ_0_12 + PPC_LO (got + 4), p); else - { - bfd_put_32 (output_bfd, - LWZU_0_12 + PPC_LO (got + 4), p + 2*4); - bfd_put_32 (output_bfd, - LWZ_12_12 + 4, p + 6*4); - } + bfd_put_32 (output_bfd, LWZU_0_12 + PPC_LO (got + 4), p); + p += 4; + bfd_put_32 (output_bfd, ADDI_11_11 + PPC_LO (-res0), p); + p += 4; + bfd_put_32 (output_bfd, MTCTR_0, p); + p += 4; + bfd_put_32 (output_bfd, ADD_0_11_11, p); + p += 4; + if (PPC_HA (got + 4) == PPC_HA (got + 8)) + bfd_put_32 (output_bfd, LWZ_12_12 + PPC_LO (got + 8), p); + else + bfd_put_32 (output_bfd, LWZ_12_12 + 4, p); + } + p += 4; + bfd_put_32 (output_bfd, ADD_11_0_11, p); + p += 4; + bfd_put_32 (output_bfd, BCTR, p); + p += 4; + while (p < endp) + { + bfd_put_32 (output_bfd, + htab->params->ppc476_workaround ? BA : NOP, p); + p += 4; } + BFD_ASSERT (p == endp); } if (htab->glink_eh_frame != NULL |