diff options
author | Alan Modra <amodra@gmail.com> | 2020-09-22 22:51:42 +0930 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2020-09-24 07:54:05 +0930 |
commit | 294338867c268b6da43327b6cbe70e746bff1a04 (patch) | |
tree | 5a92aab3be8a1fcc30d2778739111b09d2146a2c /bfd | |
parent | c94053440e29421dd8846530da73f09c9ede2e0f (diff) | |
download | gdb-294338867c268b6da43327b6cbe70e746bff1a04.zip gdb-294338867c268b6da43327b6cbe70e746bff1a04.tar.gz gdb-294338867c268b6da43327b6cbe70e746bff1a04.tar.bz2 |
PR26656, power10 libstdc++.so segfault in __cxxabiv1::__cxa_throw
This adds missing support for a power10 version of the __tls_get_addr
call stub implementing DT_PPC64_OPT PPC64_OPT_TLS. Without this,
power10 code using __tls_get_addr fails miserably at runtime unless
the --no-tls-get-addr-optimize option is given.
PR 26656
* elf64-ppc.c (plt_stub_size): Add "odd" param. Use it with
size_power10_offset rather than calculating from start of stub.
Add size for notoc tls_get_addr_opt stub.
(plt_stub_pad): Add "odd" param, pass to plt_stub_size.
(build_tls_get_addr_head, build_tls_get_addr_tail): New functions.
(build_tls_get_addr_stub): Delete.
(ppc_build_one_stub): Use a temp for htab->params->stub_bfd.
Emit notoc tls_get_addr_opt stub. Move eh_frame code to
suit. Adjust code to use bfd_tls_get_addr_head/tail in place
of build_tls_get_addr_stub.
(ppc_size_one_stub): Size notoc tls_get_addr_opt stub.
Adjust plt_stub_size and plt_stub_pad calls. Correct "odd"
when padding stub. Size eh_frame for notoc stub too.
Correct lr_restore value.
(ppc64_elf_relocate_section): Don't skip over first insn of
notoc tls_get_addr_opt stub.
Diffstat (limited to 'bfd')
-rw-r--r-- | bfd/ChangeLog | 20 | ||||
-rw-r--r-- | bfd/elf64-ppc.c | 424 |
2 files changed, 259 insertions, 185 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 39e5ead..f642403 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,25 @@ 2020-09-24 Alan Modra <amodra@gmail.com> + PR 26656 + * elf64-ppc.c (plt_stub_size): Add "odd" param. Use it with + size_power10_offset rather than calculating from start of stub. + Add size for notoc tls_get_addr_opt stub. + (plt_stub_pad): Add "odd" param, pass to plt_stub_size. + (build_tls_get_addr_head, build_tls_get_addr_tail): New functions. + (build_tls_get_addr_stub): Delete. + (ppc_build_one_stub): Use a temp for htab->params->stub_bfd. + Emit notoc tls_get_addr_opt stub. Move eh_frame code to + suit. Adjust code to use bfd_tls_get_addr_head/tail in place + of build_tls_get_addr_stub. + (ppc_size_one_stub): Size notoc tls_get_addr_opt stub. + Adjust plt_stub_size and plt_stub_pad calls. Correct "odd" + when padding stub. Size eh_frame for notoc stub too. + Correct lr_restore value. + (ppc64_elf_relocate_section): Don't skip over first insn of + notoc tls_get_addr_opt stub. + +2020-09-24 Alan Modra <amodra@gmail.com> + PR 26655 * elf64-ppc.c (ppc64_elf_func_desc_adjust): Rename to.. (ppc64_elf_edit): Call params->edit. diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index 4d16dc3..320717c 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -10842,62 +10842,60 @@ eh_advance_size (unsigned int delta) static inline unsigned int plt_stub_size (struct ppc_link_hash_table *htab, struct ppc_stub_hash_entry *stub_entry, - bfd_vma off) + bfd_vma off, + unsigned int odd) { unsigned size; if (stub_entry->stub_type >= ppc_stub_plt_call_notoc) { if (htab->params->power10_stubs != 0) - { - 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_power10_offset (off, start & 4); - } + size = 8 + size_power10_offset (off, odd); else size = 8 + size_offset (off - 8); if (stub_entry->stub_type > ppc_stub_plt_call_notoc) size += 4; - return size; } - - size = 12; - if (ALWAYS_EMIT_R2SAVE - || stub_entry->stub_type == ppc_stub_plt_call_r2save) - size += 4; - if (PPC_HA (off) != 0) - size += 4; - if (htab->opd_abi) + else { - size += 4; - if (htab->params->plt_static_chain) + size = 12; + if (ALWAYS_EMIT_R2SAVE + || stub_entry->stub_type == ppc_stub_plt_call_r2save) size += 4; - if (htab->params->plt_thread_safe - && htab->elf.dynamic_sections_created - && stub_entry->h != NULL - && stub_entry->h->elf.dynindx != -1) - size += 8; - if (PPC_HA (off + 8 + 8 * htab->params->plt_static_chain) != PPC_HA (off)) + if (PPC_HA (off) != 0) size += 4; + if (htab->opd_abi) + { + size += 4; + if (htab->params->plt_static_chain) + size += 4; + if (htab->params->plt_thread_safe + && htab->elf.dynamic_sections_created + && stub_entry->h != NULL + && stub_entry->h->elf.dynindx != -1) + size += 8; + if (PPC_HA (off + 8 + 8 * htab->params->plt_static_chain) + != PPC_HA (off)) + size += 4; + } } if (stub_entry->h != NULL && is_tls_get_addr (&stub_entry->h->elf, htab) && htab->params->tls_get_addr_opt) { - if (htab->params->no_tls_get_addr_regsave) + if (!htab->params->no_tls_get_addr_regsave) { - size += 7 * 4; - if (stub_entry->stub_type == ppc_stub_plt_call_r2save) - size += 6 * 4; + size += 30 * 4; + if (stub_entry->stub_type == ppc_stub_plt_call_r2save + || stub_entry->stub_type == ppc_stub_plt_call_both) + size += 4; } else { - size += 30 * 4; - if (stub_entry->stub_type == ppc_stub_plt_call_r2save) - size += 4; + size += 7 * 4; + if (stub_entry->stub_type == ppc_stub_plt_call_r2save + || stub_entry->stub_type == ppc_stub_plt_call_both) + size += 6 * 4; } } return size; @@ -10912,7 +10910,8 @@ plt_stub_size (struct ppc_link_hash_table *htab, static inline unsigned int plt_stub_pad (struct ppc_link_hash_table *htab, struct ppc_stub_hash_entry *stub_entry, - bfd_vma plt_off) + bfd_vma plt_off, + unsigned int odd) { int stub_align; unsigned stub_size; @@ -10927,7 +10926,7 @@ plt_stub_pad (struct ppc_link_hash_table *htab, } stub_align = 1 << -htab->params->plt_stub_align; - stub_size = plt_stub_size (htab, stub_entry, plt_off); + stub_size = plt_stub_size (htab, stub_entry, plt_off, odd); if (((stub_off + stub_size - 1) & -stub_align) - (stub_off & -stub_align) > ((stub_size - 1) & -stub_align)) return stub_align - (stub_off & (stub_align - 1)); @@ -11123,14 +11122,12 @@ build_plt_stub (struct ppc_link_hash_table *htab, #define MR_R3_R0 0x7c030378 #define BCTRL 0x4e800421 -static inline bfd_byte * -build_tls_get_addr_stub (struct ppc_link_hash_table *htab, +static bfd_byte * +build_tls_get_addr_head (struct ppc_link_hash_table *htab, struct ppc_stub_hash_entry *stub_entry, - bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r) + bfd_byte *p) { bfd *obfd = htab->params->stub_bfd; - bfd_byte *loc = p; - unsigned int i; bfd_put_32 (obfd, LD_R0_0R3 + 0, p), p += 4; bfd_put_32 (obfd, LD_R12_0R3 + 8, p), p += 4; @@ -11139,21 +11136,43 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab, bfd_put_32 (obfd, ADD_R3_R12_R13, p), p += 4; bfd_put_32 (obfd, BEQLR, p), p += 4; bfd_put_32 (obfd, MR_R3_R0, p), p += 4; - if (htab->params->no_tls_get_addr_regsave) - { - if (r != NULL) - r[0].r_offset += 7 * 4; - if (stub_entry->stub_type != ppc_stub_plt_call_r2save) - return build_plt_stub (htab, stub_entry, p, offset, r); + if (!htab->params->no_tls_get_addr_regsave) + p = tls_get_addr_prologue (obfd, p, htab); + else if (stub_entry->stub_type == ppc_stub_plt_call_r2save + || stub_entry->stub_type == ppc_stub_plt_call_both) + { bfd_put_32 (obfd, MFLR_R0, p); p += 4; bfd_put_32 (obfd, STD_R0_0R1 + STK_LINKER (htab), p); p += 4; + } + return p; +} - if (r != NULL) - r[0].r_offset += 2 * 4; - p = build_plt_stub (htab, stub_entry, p, offset, r); +static bfd_byte * +build_tls_get_addr_tail (struct ppc_link_hash_table *htab, + struct ppc_stub_hash_entry *stub_entry, + bfd_byte *p, + bfd_byte *loc) +{ + bfd *obfd = htab->params->stub_bfd; + + if (!htab->params->no_tls_get_addr_regsave) + { + bfd_put_32 (obfd, BCTRL, p - 4); + + if (stub_entry->stub_type == ppc_stub_plt_call_r2save + || stub_entry->stub_type == ppc_stub_plt_call_both) + { + bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p); + p += 4; + } + p = tls_get_addr_epilogue (obfd, p, htab); + } + else if (stub_entry->stub_type == ppc_stub_plt_call_r2save + || stub_entry->stub_type == ppc_stub_plt_call_both) + { bfd_put_32 (obfd, BCTRL, p - 4); bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p); @@ -11165,24 +11184,6 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab, bfd_put_32 (obfd, BLR, p); p += 4; } - else - { - p = tls_get_addr_prologue (obfd, p, htab); - - if (r != NULL) - r[0].r_offset += 18 * 4; - - p = build_plt_stub (htab, stub_entry, p, offset, r); - bfd_put_32 (obfd, BCTRL, p - 4); - - if (stub_entry->stub_type == ppc_stub_plt_call_r2save) - { - bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p); - p += 4; - } - - p = tls_get_addr_epilogue (obfd, p, htab); - } if (htab->glink_eh_frame != NULL && htab->glink_eh_frame->size != 0) @@ -11191,21 +11192,11 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab, base = htab->glink_eh_frame->contents + stub_entry->group->eh_base + 17; eh = base + stub_entry->group->eh_size; - if (htab->params->no_tls_get_addr_regsave) - { - unsigned int lr_used, delta; - lr_used = stub_entry->stub_offset + (p - 20 - loc); - delta = lr_used - stub_entry->group->lr_restore; - stub_entry->group->lr_restore = lr_used + 16; - eh = eh_advance (htab->elf.dynobj, eh, delta); - *eh++ = DW_CFA_offset_extended_sf; - *eh++ = 65; - *eh++ = -(STK_LINKER (htab) / 8) & 0x7f; - *eh++ = DW_CFA_advance_loc + 4; - } - else + + if (!htab->params->no_tls_get_addr_regsave) { - unsigned int cfa_updt, delta; + unsigned int cfa_updt, delta, i; + /* After the bctrl, lr has been modified so we need to emit .eh_frame info saying the return address is on the stack. In fact we must put the EH info at or before the call rather @@ -11244,10 +11235,27 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab, for (i = 4; i < 12; i++) *eh++ = DW_CFA_restore + i; *eh++ = DW_CFA_advance_loc + 2; + *eh++ = DW_CFA_restore_extended; + *eh++ = 65; + stub_entry->group->eh_size = eh - base; + } + else if (stub_entry->stub_type == ppc_stub_plt_call_r2save + || stub_entry->stub_type == ppc_stub_plt_call_both) + { + unsigned int lr_used, delta; + + lr_used = stub_entry->stub_offset + (p - 20 - loc); + delta = lr_used - stub_entry->group->lr_restore; + stub_entry->group->lr_restore = lr_used + 16; + eh = eh_advance (htab->elf.dynobj, eh, delta); + *eh++ = DW_CFA_offset_extended_sf; + *eh++ = 65; + *eh++ = -(STK_LINKER (htab) / 8) & 0x7f; + *eh++ = DW_CFA_advance_loc + 4; + *eh++ = DW_CFA_restore_extended; + *eh++ = 65; + stub_entry->group->eh_size = eh - base; } - *eh++ = DW_CFA_restore_extended; - *eh++ = 65; - stub_entry->group->eh_size = eh - base; } return p; } @@ -11381,6 +11389,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) struct ppc_branch_hash_entry *br_entry; struct bfd_link_info *info; struct ppc_link_hash_table *htab; + bfd *obfd; bfd_byte *loc; bfd_byte *p, *relp; bfd_vma targ, off; @@ -11388,6 +11397,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) asection *plt; int num_rel; int odd; + bfd_boolean is_tga; /* Massage our args to the form they really have. */ stub_entry = (struct ppc_stub_hash_entry *) gen_entry; @@ -11437,6 +11447,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) off = targ - off; p = loc; + obfd = htab->params->stub_bfd; if (stub_entry->stub_type == ppc_stub_long_branch_r2off) { bfd_vma r2off = get_r2off (info, stub_entry); @@ -11446,23 +11457,21 @@ 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), p); + bfd_put_32 (obfd, 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), p); + bfd_put_32 (obfd, 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), p); + bfd_put_32 (obfd, ADDI_R2_R2 | PPC_LO (r2off), p); p += 4; } off -= p - loc; } - bfd_put_32 (htab->params->stub_bfd, B_DOT | (off & 0x3fffffc), p); + bfd_put_32 (obfd, B_DOT | (off & 0x3fffffc), p); p += 4; if (off + (1 << 25) >= (bfd_vma) (1 << 26)) @@ -11588,19 +11597,17 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) } p = loc; + obfd = htab->params->stub_bfd; if (stub_entry->stub_type != ppc_stub_plt_branch_r2off) { if (PPC_HA (off) != 0) { - bfd_put_32 (htab->params->stub_bfd, - ADDIS_R12_R2 | PPC_HA (off), p); + bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (off), p); p += 4; - bfd_put_32 (htab->params->stub_bfd, - LD_R12_0R12 | PPC_LO (off), p); + bfd_put_32 (obfd, LD_R12_0R12 | PPC_LO (off), p); } else - bfd_put_32 (htab->params->stub_bfd, - LD_R12_0R2 | PPC_LO (off), p); + bfd_put_32 (obfd, LD_R12_0R2 | PPC_LO (off), p); } else { @@ -11612,36 +11619,32 @@ 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), p); + bfd_put_32 (obfd, STD_R2_0R1 + STK_TOC (htab), p); p += 4; if (PPC_HA (off) != 0) { - bfd_put_32 (htab->params->stub_bfd, - ADDIS_R12_R2 | PPC_HA (off), p); + bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (off), p); p += 4; - bfd_put_32 (htab->params->stub_bfd, - LD_R12_0R12 | PPC_LO (off), p); + bfd_put_32 (obfd, LD_R12_0R12 | PPC_LO (off), p); } else - bfd_put_32 (htab->params->stub_bfd, LD_R12_0R2 | PPC_LO (off), p); + bfd_put_32 (obfd, LD_R12_0R2 | PPC_LO (off), p); if (PPC_HA (r2off) != 0) { p += 4; - bfd_put_32 (htab->params->stub_bfd, - ADDIS_R2_R2 | PPC_HA (r2off), p); + bfd_put_32 (obfd, ADDIS_R2_R2 | PPC_HA (r2off), p); } if (PPC_LO (r2off) != 0) { p += 4; - bfd_put_32 (htab->params->stub_bfd, - ADDI_R2_R2 | PPC_LO (r2off), p); + bfd_put_32 (obfd, ADDI_R2_R2 | PPC_LO (r2off), p); } } p += 4; - bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, p); + bfd_put_32 (obfd, MTCTR_R12, p); p += 4; - bfd_put_32 (htab->params->stub_bfd, BCTR, p); + bfd_put_32 (obfd, BCTR, p); p += 4; break; @@ -11655,12 +11658,22 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) off = (stub_entry->stub_offset + stub_entry->group->stub_sec->output_offset + stub_entry->group->stub_sec->output_section->vma); + obfd = htab->params->stub_bfd; + is_tga = ((stub_entry->stub_type == ppc_stub_plt_call_notoc + || stub_entry->stub_type == ppc_stub_plt_call_both) + && is_tls_get_addr (&stub_entry->h->elf, htab) + && htab->params->tls_get_addr_opt); + if (is_tga) + { + p = build_tls_get_addr_head (htab, stub_entry, p); + off += p - loc; + } if (stub_entry->stub_type == ppc_stub_long_branch_both || stub_entry->stub_type == ppc_stub_plt_branch_both || stub_entry->stub_type == ppc_stub_plt_call_both) { off += 4; - bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p); + bfd_put_32 (obfd, STD_R2_0R1 + STK_TOC (htab), p); p += 4; } if (stub_entry->stub_type >= ppc_stub_plt_call_notoc) @@ -11693,17 +11706,39 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (htab->params->power10_stubs != 0) { bfd_boolean load = stub_entry->stub_type >= ppc_stub_plt_call_notoc; - p = build_power10_offset (htab->params->stub_bfd, p, off, odd, load); + p = build_power10_offset (obfd, p, off, odd, load); } else { + if (htab->glink_eh_frame != NULL + && htab->glink_eh_frame->size != 0) + { + bfd_byte *base, *eh; + unsigned int lr_used, delta; + + base = (htab->glink_eh_frame->contents + + stub_entry->group->eh_base + 17); + eh = base + stub_entry->group->eh_size; + lr_used = stub_entry->stub_offset + (p - loc) + 8; + delta = lr_used - stub_entry->group->lr_restore; + stub_entry->group->lr_restore = lr_used + 8; + eh = eh_advance (htab->elf.dynobj, eh, delta); + *eh++ = DW_CFA_register; + *eh++ = 65; + *eh++ = 12; + *eh++ = DW_CFA_advance_loc + 2; + *eh++ = DW_CFA_restore_extended; + *eh++ = 65; + stub_entry->group->eh_size = eh - base; + } + /* 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, + p = build_offset (obfd, p, off, stub_entry->stub_type >= ppc_stub_plt_call_notoc); } @@ -11715,17 +11750,19 @@ 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 + (p - loc)); - bfd_put_32 (htab->params->stub_bfd, - B_DOT | ((targ - from) & 0x3fffffc), p); + bfd_put_32 (obfd, B_DOT | ((targ - from) & 0x3fffffc), p); } else { - bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, p); + bfd_put_32 (obfd, MTCTR_R12, p); p += 4; - bfd_put_32 (htab->params->stub_bfd, BCTR, p); + bfd_put_32 (obfd, BCTR, p); } p += 4; + if (is_tga) + p = build_tls_get_addr_tail (htab, stub_entry, p, loc); + if (info->emitrelocations) { bfd_vma roff = relp - stub_entry->group->stub_sec->contents; @@ -11756,33 +11793,6 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) return FALSE; } } - - if (htab->params->power10_stubs == 0 - && htab->glink_eh_frame != NULL - && htab->glink_eh_frame->size != 0) - { - bfd_byte *base, *eh; - unsigned int lr_used, delta; - - base = (htab->glink_eh_frame->contents - + stub_entry->group->eh_base + 17); - eh = base + stub_entry->group->eh_size; - lr_used = stub_entry->stub_offset + 8; - if (stub_entry->stub_type == ppc_stub_long_branch_both - || stub_entry->stub_type == ppc_stub_plt_branch_both - || stub_entry->stub_type == ppc_stub_plt_call_both) - lr_used += 4; - delta = lr_used - stub_entry->group->lr_restore; - stub_entry->group->lr_restore = lr_used + 8; - eh = eh_advance (htab->elf.dynobj, eh, delta); - *eh++ = DW_CFA_register; - *eh++ = 65; - *eh++ = 12; - *eh++ = DW_CFA_advance_loc + 2; - *eh++ = DW_CFA_restore_extended; - *eh++ = 65; - stub_entry->group->eh_size = eh - base; - } break; case ppc_stub_plt_call: @@ -11851,12 +11861,20 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) r[0].r_offset += 2; r[0].r_addend = targ; } - if (stub_entry->h != NULL - && is_tls_get_addr (&stub_entry->h->elf, htab) - && htab->params->tls_get_addr_opt) - p = build_tls_get_addr_stub (htab, stub_entry, loc, off, r); - else - p = build_plt_stub (htab, stub_entry, loc, off, r); + p = loc; + obfd = htab->params->stub_bfd; + is_tga = (stub_entry->h != NULL + && is_tls_get_addr (&stub_entry->h->elf, htab) + && htab->params->tls_get_addr_opt); + if (is_tga) + { + p = build_tls_get_addr_head (htab, stub_entry, p); + if (r != NULL) + r[0].r_offset += p - loc; + } + p = build_plt_stub (htab, stub_entry, p, off, r); + if (is_tga) + p = build_tls_get_addr_tail (htab, stub_entry, p, loc); break; case ppc_stub_save_res: @@ -12152,11 +12170,18 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) case ppc_stub_plt_call_notoc: case ppc_stub_plt_call_both: - off = (stub_entry->stub_offset - + stub_entry->group->stub_sec->output_offset - + stub_entry->group->stub_sec->output_section->vma); + lr_used = 0; + if (is_tls_get_addr (&stub_entry->h->elf, htab) + && htab->params->tls_get_addr_opt) + { + lr_used += 7 * 4; + if (!htab->params->no_tls_get_addr_regsave) + lr_used += 11 * 4; + else if (stub_entry->stub_type == ppc_stub_plt_call_both) + lr_used += 2 * 4; + } if (stub_entry->stub_type == ppc_stub_plt_call_both) - off += 4; + lr_used += 4; targ = stub_entry->plt_ent->plt.offset & ~1; if (targ >= (bfd_vma) -2) abort (); @@ -12172,16 +12197,21 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) plt = htab->pltlocal; } targ += plt->output_offset + plt->output_section->vma; + off = (stub_entry->stub_offset + + stub_entry->group->stub_sec->output_offset + + stub_entry->group->stub_sec->output_section->vma + + lr_used); odd = off & 4; off = targ - off; if (htab->params->plt_stub_align != 0) { - unsigned pad = plt_stub_pad (htab, stub_entry, off); + unsigned pad = plt_stub_pad (htab, stub_entry, off, odd); stub_entry->group->stub_sec->size += pad; stub_entry->stub_offset = stub_entry->group->stub_sec->size; off -= pad; + odd ^= pad & 4; } if (info->emitrelocations) @@ -12195,15 +12225,13 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) stub_entry->group->stub_sec->flags |= SEC_RELOC; } - size = plt_stub_size (htab, stub_entry, off); + size = plt_stub_size (htab, stub_entry, off, odd); if (htab->params->power10_stubs == 0) { /* 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; + lr_used += stub_entry->stub_offset + 8; /* 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. */ @@ -12211,6 +12239,29 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) stub_entry->group->eh_size += eh_advance_size (delta) + 6; stub_entry->group->lr_restore = lr_used + 8; } + if ((stub_entry->stub_type == ppc_stub_plt_call_notoc + || stub_entry->stub_type == ppc_stub_plt_call_both) + && is_tls_get_addr (&stub_entry->h->elf, htab) + && htab->params->tls_get_addr_opt) + { + if (!htab->params->no_tls_get_addr_regsave) + { + unsigned int cfa_updt = stub_entry->stub_offset + 18 * 4; + delta = cfa_updt - stub_entry->group->lr_restore; + stub_entry->group->eh_size += eh_advance_size (delta); + stub_entry->group->eh_size += htab->opd_abi ? 36 : 35; + stub_entry->group->lr_restore + = stub_entry->stub_offset + size - 4; + } + else if (stub_entry->stub_type == ppc_stub_plt_call_both) + { + lr_used = stub_entry->stub_offset + size - 20; + delta = lr_used - stub_entry->group->lr_restore; + stub_entry->group->eh_size += eh_advance_size (delta) + 6; + stub_entry->group->lr_restore + = stub_entry->stub_offset + size - 4; + } + } break; case ppc_stub_plt_call: @@ -12236,7 +12287,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (htab->params->plt_stub_align != 0) { - unsigned pad = plt_stub_pad (htab, stub_entry, off); + unsigned pad = plt_stub_pad (htab, stub_entry, off, 0); stub_entry->group->stub_sec->size += pad; stub_entry->stub_offset = stub_entry->group->stub_sec->size; @@ -12253,14 +12304,22 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) stub_entry->group->stub_sec->flags |= SEC_RELOC; } - size = plt_stub_size (htab, stub_entry, off); + size = plt_stub_size (htab, stub_entry, off, 0); if (stub_entry->h != NULL && is_tls_get_addr (&stub_entry->h->elf, htab) && htab->params->tls_get_addr_opt && stub_entry->stub_type == ppc_stub_plt_call_r2save) { - if (htab->params->no_tls_get_addr_regsave) + if (!htab->params->no_tls_get_addr_regsave) + { + /* Adjustments to r1 need to be described. */ + unsigned int cfa_updt = stub_entry->stub_offset + 18 * 4; + delta = cfa_updt - stub_entry->group->lr_restore; + stub_entry->group->eh_size += eh_advance_size (delta); + stub_entry->group->eh_size += htab->opd_abi ? 36 : 35; + } + else { lr_used = stub_entry->stub_offset + size - 20; /* The eh_frame info will consist of a DW_CFA_advance_loc @@ -12269,15 +12328,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) delta = lr_used - stub_entry->group->lr_restore; stub_entry->group->eh_size += eh_advance_size (delta) + 6; } - else - { - /* Adjustments to r1 need to be described. */ - unsigned int cfa_updt = stub_entry->stub_offset + 18 * 4; - delta = cfa_updt - stub_entry->group->lr_restore; - stub_entry->group->eh_size += eh_advance_size (delta); - stub_entry->group->eh_size += htab->opd_abi ? 36 : 35; - } - stub_entry->group->lr_restore = size - 4; + stub_entry->group->lr_restore = stub_entry->stub_offset + size - 4; } break; @@ -15890,22 +15941,25 @@ ppc64_elf_relocate_section (bfd *output_bfd, addend = 0; reloc_dest = DEST_STUB; - if (((stub_entry->stub_type == ppc_stub_plt_call - && ALWAYS_EMIT_R2SAVE) - || stub_entry->stub_type == ppc_stub_plt_call_r2save - || stub_entry->stub_type == ppc_stub_plt_call_both) - && !(h != NULL - && is_tls_get_addr (&h->elf, htab) - && htab->params->tls_get_addr_opt) - && rel + 1 < relend - && rel[1].r_offset == rel->r_offset + 4 - && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOCSAVE) - relocation += 4; - else if ((stub_entry->stub_type == ppc_stub_long_branch_both - || stub_entry->stub_type == ppc_stub_plt_branch_both - || stub_entry->stub_type == ppc_stub_plt_call_both) - && r_type == R_PPC64_REL24_NOTOC) - relocation += 4; + if ((((stub_entry->stub_type == ppc_stub_plt_call + && ALWAYS_EMIT_R2SAVE) + || stub_entry->stub_type == ppc_stub_plt_call_r2save + || stub_entry->stub_type == ppc_stub_plt_call_both) + && rel + 1 < relend + && rel[1].r_offset == rel->r_offset + 4 + && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOCSAVE) + || ((stub_entry->stub_type == ppc_stub_long_branch_both + || stub_entry->stub_type == ppc_stub_plt_branch_both + || stub_entry->stub_type == ppc_stub_plt_call_both) + && r_type == R_PPC64_REL24_NOTOC)) + { + /* Skip over the r2 store at the start of the stub. */ + if (!(stub_entry->stub_type >= ppc_stub_plt_call + && htab->params->tls_get_addr_opt + && h != NULL + && is_tls_get_addr (&h->elf, htab))) + relocation += 4; + } if (r_type == R_PPC64_REL24_NOTOC && (stub_entry->stub_type == ppc_stub_plt_call_notoc |