diff options
Diffstat (limited to 'bfd')
-rw-r--r-- | bfd/ChangeLog | 15 | ||||
-rw-r--r-- | bfd/elf64-ppc.c | 324 |
2 files changed, 296 insertions, 43 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 0e21664..a21805f 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,20 @@ 2019-05-24 Alan Modra <amodra@gmail.com> + * elf64-ppc.c: Comment on powerxx _notoc stub variants. + (LI_R11_0, LIS_R11, ORI_R11_R11_0, SLDI_R11_R11_34): Define. + (PADDI_R12_PC, PLD_R12_PC, D34, HA34): Define. + (struct ppc_link_hash_table): Add powerxx_stubs. + (ppc64_elf_check_relocs): Set powerxx_stubs. + (build_powerxx_offset, size_powerxx_offset), + (num_relocs_for_powerxx_offset), + (emit_relocs_for_powerxx_offset): New functions. + (plt_stub_size): Size powerxx stubs. + (ppc_build_one_stub): Emit powerxx stubs. + (ppc_size_one_stub): Size powerxx stubs. Omit .eh_frame for + powerxx stubs. + +2019-05-24 Alan Modra <amodra@gmail.com> + * elf64-ppc.c (ppc64_elf_check_relocs): Set has_gotrel for R_PPC64_GOT_PCREL34. (xlate_pcrel_opt): New function. diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index 75189b6..f795e3f 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -187,18 +187,24 @@ static bfd_vma opd_entry_value #define LD_R2_0R12 0xe84c0000 /* ld %r2,0(%r12) */ #define ADD_R2_R2_R12 0x7c426214 /* add %r2,%r2,%r12 */ +#define LI_R11_0 0x39600000 /* li %r11,0 */ #define LIS_R2 0x3c400000 /* lis %r2,xxx@ha */ +#define LIS_R11 0x3d600000 /* lis %r11,xxx@ha */ #define LIS_R12 0x3d800000 /* lis %r12,xxx@ha */ #define ADDIS_R2_R12 0x3c4c0000 /* addis %r2,%r12,xxx@ha */ #define ADDIS_R12_R2 0x3d820000 /* addis %r12,%r2,xxx@ha */ #define ADDIS_R12_R11 0x3d8b0000 /* addis %r12,%r11,xxx@ha */ #define ADDIS_R12_R12 0x3d8c0000 /* addis %r12,%r12,xxx@ha */ #define ORIS_R12_R12_0 0x658c0000 /* oris %r12,%r12,xxx@hi */ +#define ORI_R11_R11_0 0x616b0000 /* ori %r11,%r11,xxx@l */ #define ORI_R12_R12_0 0x618c0000 /* ori %r12,%r12,xxx@l */ #define LD_R12_0R12 0xe98c0000 /* ld %r12,xxx@l(%r12) */ +#define SLDI_R11_R11_34 0x796b1746 /* sldi %r11,%r11,34 */ #define SLDI_R12_R12_32 0x799c07c6 /* sldi %r12,%r12,32 */ #define LDX_R12_R11_R12 0x7d8b602a /* ldx %r12,%r11,%r12 */ #define ADD_R12_R11_R12 0x7d8b6214 /* add %r12,%r11,%r12 */ +#define PADDI_R12_PC 0x0610000039800000ULL +#define PLD_R12_PC 0x04100000e5800000ULL #define PNOP 0x0700000000000000ULL /* __glink_PLTresolve stub instructions. We enter with the index in R0. */ @@ -2841,8 +2847,34 @@ must_be_dyn_reloc (struct bfd_link_info *info, . mtctr %r12 . bctr + There are also ELFv1 powerxx variants of these stubs. + ppc_stub_long_branch_notoc: + . paddi %r12,dest@pcrel + . b dest + ppc_stub_plt_branch_notoc: + . lis %r11,(dest-1f)@highesta34 + . ori %r11,%r11,(dest-1f)@highera34 + . sldi %r11,%r11,34 + . 1: paddi %r12,dest@pcrel + . add %r12,%r11,%r12 + . mtctr %r12 + . bctr + ppc_stub_plt_call_notoc: + . lis %r11,(xxx-1f)@highesta34 + . ori %r11,%r11,(xxx-1f)@highera34 + . sldi %r11,%r11,34 + . 1: paddi %r12,xxx@pcrel + . ldx %r12,%r11,%r12 + . mtctr %r12 + . bctr + In cases where the high instructions would add zero, they are omitted and following instructions modified in some cases. + For example, a powerxx ppc_stub_plt_call_notoc might simplify down + to + . pld %r12,xxx@pcrel + . mtctr %r12 + . bctr For a given stub group (a set of sections all using the same toc pointer value) there will be just one stub type used for any @@ -3132,6 +3164,9 @@ struct ppc_link_hash_table /* Whether calls are made via the PLT from NOTOC functions. */ unsigned int notoc_plt:1; + /* Whether to use powerxx instructions in linkage stubs. */ + unsigned int powerxx_stubs:1; + /* Incremented every time we size stubs. */ unsigned int stub_iteration; @@ -4476,6 +4511,13 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, r_type = ELF64_R_TYPE (rel->r_info); switch (r_type) { + case R_PPC64_D34: + case R_PPC64_D34_LO: + case R_PPC64_D34_HI30: + case R_PPC64_D34_HA30: + case R_PPC64_D28: + htab->powerxx_stubs = 1; + /* Fall through. */ default: /* Somewhat foolishly, because the ABIs don't specifically allow it, ppc64 gas and ld support GOT and PLT relocs @@ -4485,9 +4527,13 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, used to specify the pcrel offset. */ sym_addend = rel->r_addend; break; + + case R_PPC64_PCREL34: case R_PPC64_GOT_PCREL34: case R_PPC64_PLT_PCREL34: case R_PPC64_PLT_PCREL34_NOTOC: + case R_PPC64_PCREL28: + htab->powerxx_stubs = 1; sym_addend = 0; break; } @@ -9427,6 +9473,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) #define PPC_LO(v) ((v) & 0xffff) #define PPC_HI(v) (((v) >> 16) & 0xffff) #define PPC_HA(v) PPC_HI ((v) + 0x8000) +#define D34(v) \ + ((((v) & 0x3ffff0000ULL) << 16) | (v & 0xffff)) +#define HA34(v) ((v + (1ULL << 33)) >> 34) /* 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, @@ -10208,6 +10257,146 @@ emit_relocs_for_offset (struct bfd_link_info *info, Elf_Internal_Rela *r, return r; } +static bfd_byte * +build_powerxx_offset (bfd *abfd, bfd_byte *p, bfd_vma off, int odd, + bfd_boolean load) +{ + uint64_t insn; + if (off - odd + (1ULL << 33) < 1ULL << 34) + { + off -= odd; + if (odd) + { + bfd_put_32 (abfd, NOP, p); + p += 4; + } + if (load) + insn = PLD_R12_PC; + else + insn = PADDI_R12_PC; + insn |= D34 (off); + bfd_put_32 (abfd, insn >> 32, p); + p += 4; + bfd_put_32 (abfd, insn, p); + } + /* The minimum value for paddi is -0x200000000. The minimum value + for li is -0x8000, which when shifted by 34 and added gives a + minimum value of -0x2000200000000. The maximum value is + 0x1ffffffff+0x7fff<<34 which is 0x2000200000000-1. */ + else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32) + { + off -= 8 - odd; + bfd_put_32 (abfd, LI_R11_0 | (HA34 (off) & 0xffff), p); + p += 4; + if (!odd) + { + bfd_put_32 (abfd, SLDI_R11_R11_34, p); + p += 4; + } + insn = PADDI_R12_PC | D34 (off); + bfd_put_32 (abfd, insn >> 32, p); + p += 4; + bfd_put_32 (abfd, insn, p); + p += 4; + if (odd) + { + bfd_put_32 (abfd, SLDI_R11_R11_34, p); + p += 4; + } + if (load) + bfd_put_32 (abfd, LDX_R12_R11_R12, p); + else + bfd_put_32 (abfd, ADD_R12_R11_R12, p); + } + else + { + off -= odd + 8; + bfd_put_32 (abfd, LIS_R11 | ((HA34 (off) >> 16) & 0x3fff), p); + p += 4; + bfd_put_32 (abfd, ORI_R11_R11_0 | (HA34 (off) & 0xffff), p); + p += 4; + if (odd) + { + bfd_put_32 (abfd, SLDI_R11_R11_34, p); + p += 4; + } + insn = PADDI_R12_PC | D34 (off); + bfd_put_32 (abfd, insn >> 32, p); + p += 4; + bfd_put_32 (abfd, insn, p); + p += 4; + if (!odd) + { + bfd_put_32 (abfd, SLDI_R11_R11_34, p); + p += 4; + } + if (load) + bfd_put_32 (abfd, LDX_R12_R11_R12, p); + else + bfd_put_32 (abfd, ADD_R12_R11_R12, p); + } + p += 4; + return p; +} + +static unsigned int +size_powerxx_offset (bfd_vma off, int odd) +{ + if (off - odd + (1ULL << 33) < 1ULL << 34) + return odd + 8; + else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32) + return 20; + else + return 24; +} + +static unsigned int +num_relocs_for_powerxx_offset (bfd_vma off, int odd) +{ + if (off - odd + (1ULL << 33) < 1ULL << 34) + return 1; + else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32) + return 2; + else + return 3; +} + +static Elf_Internal_Rela * +emit_relocs_for_powerxx_offset (struct bfd_link_info *info, + Elf_Internal_Rela *r, bfd_vma roff, + bfd_vma targ, bfd_vma off, int odd) +{ + if (off - odd + (1ULL << 33) < 1ULL << 34) + roff += odd; + else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32) + { + int d_offset = bfd_big_endian (info->output_bfd) ? 2 : 0; + r->r_offset = roff + d_offset; + r->r_addend = targ + 8 - odd - d_offset; + r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHERA34); + ++r; + roff += 8 - odd; + } + else + { + int d_offset = bfd_big_endian (info->output_bfd) ? 2 : 0; + r->r_offset = roff + d_offset; + r->r_addend = targ + 8 + odd - d_offset; + r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHESTA34); + ++r; + roff += 4; + r->r_offset = roff + d_offset; + r->r_addend = targ + 4 + odd - d_offset; + r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHERA34); + ++r; + roff += 4 + odd; + } + r->r_offset = roff; + r->r_addend = targ; + r->r_info = ELF64_R_INFO (0, R_PPC64_PCREL34); + return r; +} + /* Emit .eh_frame opcode to advance pc by DELTA. */ static bfd_byte * @@ -10285,7 +10474,17 @@ plt_stub_size (struct ppc_link_hash_table *htab, if (stub_entry->stub_type >= ppc_stub_plt_call_notoc) { - size = 8 + size_offset (off - 8); + if (htab->powerxx_stubs) + { + bfd_vma start = (stub_entry->stub_offset + + stub_entry->group->stub_sec->output_offset + + stub_entry->group->stub_sec->output_section->vma); + if (stub_entry->stub_type > ppc_stub_plt_call_notoc) + start += 4; + size = 8 + size_powerxx_offset (off, start & 4); + } + else + size = 8 + size_offset (off - 8); if (stub_entry->stub_type > ppc_stub_plt_call_notoc) size += 4; return size; @@ -10738,6 +10937,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) Elf_Internal_Rela *r; asection *plt; int num_rel; + int odd; /* Massage our args to the form they really have. */ stub_entry = (struct ppc_stub_hash_entry *) gen_entry; @@ -11016,18 +11216,28 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) targ = (stub_entry->target_value + stub_entry->target_section->output_offset + stub_entry->target_section->output_section->vma); + odd = off & 4; off = targ - off; relp = p; num_rel = 0; - /* The notoc stubs calculate their target (either a PLT entry or - the global entry point of a function) relative to the PC - returned by the "bcl" two instructions past the start of the - sequence emitted by build_offset. The offset is therefore 8 - less than calculated from the start of the sequence. */ - off -= 8; - p = build_offset (htab->params->stub_bfd, p, off, - stub_entry->stub_type >= ppc_stub_plt_call_notoc); + if (htab->powerxx_stubs) + { + bfd_boolean load = stub_entry->stub_type >= ppc_stub_plt_call_notoc; + p = build_powerxx_offset (htab->params->stub_bfd, p, off, odd, load); + } + else + { + /* The notoc stubs calculate their target (either a PLT entry or + the global entry point of a function) relative to the PC + returned by the "bcl" two instructions past the start of the + sequence emitted by build_offset. The offset is therefore 8 + less than calculated from the start of the sequence. */ + off -= 8; + p = build_offset (htab->params->stub_bfd, p, off, + stub_entry->stub_type >= ppc_stub_plt_call_notoc); + } + if (stub_entry->stub_type <= ppc_stub_long_branch_both) { bfd_vma from; @@ -11049,13 +11259,21 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (info->emitrelocations) { - bfd_vma roff; - num_rel += num_relocs_for_offset (off); + bfd_vma roff = relp - stub_entry->group->stub_sec->contents; + if (htab->powerxx_stubs) + num_rel += num_relocs_for_powerxx_offset (off, odd); + else + { + num_rel += num_relocs_for_offset (off); + roff += 16; + } r = get_relocs (stub_entry->group->stub_sec, num_rel); if (r == NULL) return FALSE; - roff = relp + 16 - stub_entry->group->stub_sec->contents; - r = emit_relocs_for_offset (info, r, roff, targ, off); + if (htab->powerxx_stubs) + r = emit_relocs_for_powerxx_offset (info, r, roff, targ, off, odd); + else + r = emit_relocs_for_offset (info, r, roff, targ, off); if (stub_entry->stub_type == ppc_stub_long_branch_notoc || stub_entry->stub_type == ppc_stub_long_branch_both) { @@ -11070,8 +11288,9 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) } } - if (htab->glink_eh_frame != NULL - && htab->glink_eh_frame->size != 0) + if (!htab->powerxx_stubs + && htab->glink_eh_frame != NULL + && htab->glink_eh_frame->size != 0) { bfd_byte *base, *eh; unsigned int lr_used, delta; @@ -11240,7 +11459,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) struct ppc_link_hash_table *htab; asection *plt; bfd_vma targ, off, r2off; - unsigned int size, extra, lr_used, delta; + unsigned int size, extra, lr_used, delta, odd; /* Massage our args to the form they really have. */ stub_entry = (struct ppc_stub_hash_entry *) gen_entry; @@ -11393,16 +11612,24 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) targ = (stub_entry->target_value + stub_entry->target_section->output_offset + stub_entry->target_section->output_section->vma); + odd = off & 4; off = targ - off; if (info->emitrelocations) { - stub_entry->group->stub_sec->reloc_count - += num_relocs_for_offset (off); + unsigned int num_rel; + if (htab->powerxx_stubs) + num_rel = num_relocs_for_powerxx_offset (off, odd); + else + num_rel = num_relocs_for_offset (off - 8); + stub_entry->group->stub_sec->reloc_count += num_rel; stub_entry->group->stub_sec->flags |= SEC_RELOC; } - extra = size_offset (off - 8); + if (htab->powerxx_stubs) + extra = size_powerxx_offset (off, odd); + else + extra = size_offset (off - 8); /* Include branch insn plus those in the offset sequence. */ size += 4 + extra; /* The branch insn is at the end, or "extra" bytes along. So @@ -11410,17 +11637,20 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) calculated. */ off -= extra; - /* After the bcl, lr has been modified so we need to emit - .eh_frame info saying the return address is in r12. */ - lr_used = stub_entry->stub_offset + 8; - if (stub_entry->stub_type == ppc_stub_long_branch_both) - lr_used += 4; - /* The eh_frame info will consist of a DW_CFA_advance_loc or - variant, DW_CFA_register, 65, 12, DW_CFA_advance_loc+2, - DW_CFA_restore_extended 65. */ - delta = lr_used - stub_entry->group->lr_restore; - stub_entry->group->eh_size += eh_advance_size (delta) + 6; - stub_entry->group->lr_restore = lr_used + 8; + if (!htab->powerxx_stubs) + { + /* After the bcl, lr has been modified so we need to emit + .eh_frame info saying the return address is in r12. */ + lr_used = stub_entry->stub_offset + 8; + if (stub_entry->stub_type == ppc_stub_long_branch_both) + lr_used += 4; + /* The eh_frame info will consist of a DW_CFA_advance_loc or + variant, DW_CFA_register, 65, 12, DW_CFA_advance_loc+2, + DW_CFA_restore_extended 65. */ + delta = lr_used - stub_entry->group->lr_restore; + stub_entry->group->eh_size += eh_advance_size (delta) + 6; + stub_entry->group->lr_restore = lr_used + 8; + } /* If the branch can't reach, use a plt_branch. */ if (off + (1 << 25) >= (bfd_vma) (1 << 26)) @@ -11455,6 +11685,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) plt = htab->pltlocal; } targ += plt->output_offset + plt->output_section->vma; + odd = off & 4; off = targ - off; if (htab->params->plt_stub_align != 0) @@ -11468,24 +11699,31 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (info->emitrelocations) { - stub_entry->group->stub_sec->reloc_count - += num_relocs_for_offset (off - 8); + unsigned int num_rel; + if (htab->powerxx_stubs) + num_rel = num_relocs_for_powerxx_offset (off, odd); + else + num_rel = num_relocs_for_offset (off - 8); + stub_entry->group->stub_sec->reloc_count += num_rel; stub_entry->group->stub_sec->flags |= SEC_RELOC; } size = plt_stub_size (htab, stub_entry, off); - /* After the bcl, lr has been modified so we need to emit - .eh_frame info saying the return address is in r12. */ - lr_used = stub_entry->stub_offset + 8; - if (stub_entry->stub_type == ppc_stub_plt_call_both) - lr_used += 4; - /* The eh_frame info will consist of a DW_CFA_advance_loc or - variant, DW_CFA_register, 65, 12, DW_CFA_advance_loc+2, - DW_CFA_restore_extended 65. */ - delta = lr_used - stub_entry->group->lr_restore; - stub_entry->group->eh_size += eh_advance_size (delta) + 6; - stub_entry->group->lr_restore = lr_used + 8; + if (!htab->powerxx_stubs) + { + /* After the bcl, lr has been modified so we need to emit + .eh_frame info saying the return address is in r12. */ + lr_used = stub_entry->stub_offset + 8; + if (stub_entry->stub_type == ppc_stub_plt_call_both) + lr_used += 4; + /* The eh_frame info will consist of a DW_CFA_advance_loc or + variant, DW_CFA_register, 65, 12, DW_CFA_advance_loc+2, + DW_CFA_restore_extended 65. */ + delta = lr_used - stub_entry->group->lr_restore; + stub_entry->group->eh_size += eh_advance_size (delta) + 6; + stub_entry->group->lr_restore = lr_used + 8; + } break; case ppc_stub_plt_call: |