X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=bfd%2Felf64-ppc.c;h=a451c4753ac9eb7e87ee5dda960081f384c8e998;hb=36231dfc188426d9b887af2d1c6c2cbb1c746cb3;hp=31a3edfdae749eb5724a6f730e1b0f2f5d6ec624;hpb=87469ba2d2fc953796c7bb64e535d3f283756048;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index 31a3edfdae..a451c4753a 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -1,5 +1,5 @@ /* PowerPC64-specific support for 64-bit ELF. - Copyright (C) 1999-2018 Free Software Foundation, Inc. + Copyright (C) 1999-2020 Free Software Foundation, Inc. Written by Linus Nordberg, Swox AB , based on elf32-ppc.c by Ian Lance Taylor. Largely rewritten by Alan Modra. @@ -35,6 +35,9 @@ #include "elf64-ppc.h" #include "dwarf2.h" +/* All users of this file have bfd_octets_per_byte (abfd, sec) == 1. */ +#define OCTETS_PER_BYTE(ABFD, SEC) 1 + static bfd_reloc_status_type ppc64_elf_ha_reloc (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); static bfd_reloc_status_type ppc64_elf_branch_reloc @@ -51,6 +54,8 @@ static bfd_reloc_status_type ppc64_elf_toc_ha_reloc (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); static bfd_reloc_status_type ppc64_elf_toc64_reloc (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type ppc64_elf_prefix_reloc + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); static bfd_reloc_status_type ppc64_elf_unhandled_reloc (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); static bfd_vma opd_entry_value @@ -185,18 +190,25 @@ 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. */ #define GLINK_PLTRESOLVE_SIZE(htab) \ @@ -878,6 +890,87 @@ static reloc_howto_type ppc64_elf_howto_raw[] = HOW (R_PPC64_ADDR64_LOCAL, 4, 64, 0xffffffffffffffffULL, 0, FALSE, dont, bfd_elf_generic_reloc), + HOW (R_PPC64_PLTSEQ_NOTOC, 2, 32, 0, 0, FALSE, dont, + bfd_elf_generic_reloc), + + HOW (R_PPC64_PLTCALL_NOTOC, 2, 32, 0, 0, FALSE, dont, + bfd_elf_generic_reloc), + + HOW (R_PPC64_PCREL_OPT, 2, 32, 0, 0, FALSE, dont, + bfd_elf_generic_reloc), + + HOW (R_PPC64_D34, 4, 34, 0x3ffff0000ffffULL, 0, FALSE, signed, + ppc64_elf_prefix_reloc), + + HOW (R_PPC64_D34_LO, 4, 34, 0x3ffff0000ffffULL, 0, FALSE, dont, + ppc64_elf_prefix_reloc), + + HOW (R_PPC64_D34_HI30, 4, 34, 0x3ffff0000ffffULL, 34, FALSE, dont, + ppc64_elf_prefix_reloc), + + HOW (R_PPC64_D34_HA30, 4, 34, 0x3ffff0000ffffULL, 34, FALSE, dont, + ppc64_elf_prefix_reloc), + + HOW (R_PPC64_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed, + ppc64_elf_prefix_reloc), + + HOW (R_PPC64_GOT_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed, + ppc64_elf_unhandled_reloc), + + HOW (R_PPC64_PLT_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed, + ppc64_elf_unhandled_reloc), + + HOW (R_PPC64_PLT_PCREL34_NOTOC, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed, + ppc64_elf_unhandled_reloc), + + HOW (R_PPC64_TPREL34, 4, 34, 0x3ffff0000ffffULL, 0, FALSE, signed, + ppc64_elf_unhandled_reloc), + + HOW (R_PPC64_DTPREL34, 4, 34, 0x3ffff0000ffffULL, 0, FALSE, signed, + ppc64_elf_unhandled_reloc), + + HOW (R_PPC64_GOT_TLSGD34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed, + ppc64_elf_unhandled_reloc), + + HOW (R_PPC64_GOT_TLSLD34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed, + ppc64_elf_unhandled_reloc), + + HOW (R_PPC64_GOT_TPREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed, + ppc64_elf_unhandled_reloc), + + HOW (R_PPC64_GOT_DTPREL34, 4, 34, 0x3ffff0000ffffULL, 0, TRUE, signed, + ppc64_elf_unhandled_reloc), + + HOW (R_PPC64_ADDR16_HIGHER34, 1, 16, 0xffff, 34, FALSE, dont, + bfd_elf_generic_reloc), + + HOW (R_PPC64_ADDR16_HIGHERA34, 1, 16, 0xffff, 34, FALSE, dont, + ppc64_elf_ha_reloc), + + HOW (R_PPC64_ADDR16_HIGHEST34, 1, 16, 0xffff, 50, FALSE, dont, + bfd_elf_generic_reloc), + + HOW (R_PPC64_ADDR16_HIGHESTA34, 1, 16, 0xffff, 50, FALSE, dont, + ppc64_elf_ha_reloc), + + HOW (R_PPC64_REL16_HIGHER34, 1, 16, 0xffff, 34, TRUE, dont, + bfd_elf_generic_reloc), + + HOW (R_PPC64_REL16_HIGHERA34, 1, 16, 0xffff, 34, TRUE, dont, + ppc64_elf_ha_reloc), + + HOW (R_PPC64_REL16_HIGHEST34, 1, 16, 0xffff, 50, TRUE, dont, + bfd_elf_generic_reloc), + + HOW (R_PPC64_REL16_HIGHESTA34, 1, 16, 0xffff, 50, TRUE, dont, + ppc64_elf_ha_reloc), + + HOW (R_PPC64_D28, 4, 28, 0xfff0000ffffULL, 0, FALSE, signed, + ppc64_elf_prefix_reloc), + + HOW (R_PPC64_PCREL28, 4, 28, 0xfff0000ffffULL, 0, TRUE, signed, + ppc64_elf_prefix_reloc), + /* GNU extension to record C++ vtable hierarchy. */ HOW (R_PPC64_GNU_VTINHERIT, 0, 0, 0, 0, FALSE, dont, NULL), @@ -1047,6 +1140,7 @@ ppc64_elf_reloc_type_lookup (bfd *abfd, break; case BFD_RELOC_PPC64_PLTGOT16_LO_DS: r = R_PPC64_PLTGOT16_LO_DS; break; + case BFD_RELOC_PPC64_TLS_PCREL: case BFD_RELOC_PPC_TLS: r = R_PPC64_TLS; break; case BFD_RELOC_PPC_TLSGD: r = R_PPC64_TLSGD; @@ -1167,6 +1261,52 @@ ppc64_elf_reloc_type_lookup (bfd *abfd, break; case BFD_RELOC_PPC64_ADDR64_LOCAL: r = R_PPC64_ADDR64_LOCAL; break; + case BFD_RELOC_PPC64_D34: r = R_PPC64_D34; + break; + case BFD_RELOC_PPC64_D34_LO: r = R_PPC64_D34_LO; + break; + case BFD_RELOC_PPC64_D34_HI30: r = R_PPC64_D34_HI30; + break; + case BFD_RELOC_PPC64_D34_HA30: r = R_PPC64_D34_HA30; + break; + case BFD_RELOC_PPC64_PCREL34: r = R_PPC64_PCREL34; + break; + case BFD_RELOC_PPC64_GOT_PCREL34: r = R_PPC64_GOT_PCREL34; + break; + case BFD_RELOC_PPC64_PLT_PCREL34: r = R_PPC64_PLT_PCREL34; + break; + case BFD_RELOC_PPC64_TPREL34: r = R_PPC64_TPREL34; + break; + case BFD_RELOC_PPC64_DTPREL34: r = R_PPC64_DTPREL34; + break; + case BFD_RELOC_PPC64_GOT_TLSGD34: r = R_PPC64_GOT_TLSGD34; + break; + case BFD_RELOC_PPC64_GOT_TLSLD34: r = R_PPC64_GOT_TLSLD34; + break; + case BFD_RELOC_PPC64_GOT_TPREL34: r = R_PPC64_GOT_TPREL34; + break; + case BFD_RELOC_PPC64_GOT_DTPREL34: r = R_PPC64_GOT_DTPREL34; + break; + case BFD_RELOC_PPC64_ADDR16_HIGHER34: r = R_PPC64_ADDR16_HIGHER34; + break; + case BFD_RELOC_PPC64_ADDR16_HIGHERA34: r = R_PPC64_ADDR16_HIGHERA34; + break; + case BFD_RELOC_PPC64_ADDR16_HIGHEST34: r = R_PPC64_ADDR16_HIGHEST34; + break; + case BFD_RELOC_PPC64_ADDR16_HIGHESTA34: r = R_PPC64_ADDR16_HIGHESTA34; + break; + case BFD_RELOC_PPC64_REL16_HIGHER34: r = R_PPC64_REL16_HIGHER34; + break; + case BFD_RELOC_PPC64_REL16_HIGHERA34: r = R_PPC64_REL16_HIGHERA34; + break; + case BFD_RELOC_PPC64_REL16_HIGHEST34: r = R_PPC64_REL16_HIGHEST34; + break; + case BFD_RELOC_PPC64_REL16_HIGHESTA34: r = R_PPC64_REL16_HIGHESTA34; + break; + case BFD_RELOC_PPC64_D28: r = R_PPC64_D28; + break; + case BFD_RELOC_PPC64_PCREL28: r = R_PPC64_PCREL28; + break; case BFD_RELOC_VTABLE_INHERIT: r = R_PPC64_GNU_VTINHERIT; break; case BFD_RELOC_VTABLE_ENTRY: r = R_PPC64_GNU_VTENTRY; @@ -1243,11 +1383,17 @@ ppc64_elf_ha_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, input_section, output_bfd, error_message); - /* Adjust the addend for sign extension of the low 16 bits. - We won't actually be using the low 16 bits, so trashing them + /* Adjust the addend for sign extension of the low 16 (or 34) bits. + We won't actually be using the low bits, so trashing them doesn't matter. */ - reloc_entry->addend += 0x8000; r_type = reloc_entry->howto->type; + if (r_type == R_PPC64_ADDR16_HIGHERA34 + || r_type == R_PPC64_ADDR16_HIGHESTA34 + || r_type == R_PPC64_REL16_HIGHERA34 + || r_type == R_PPC64_REL16_HIGHESTA34) + reloc_entry->addend += 1ULL << 33; + else + reloc_entry->addend += 1U << 15; if (r_type != R_PPC64_REL16DX_HA) return bfd_reloc_continue; @@ -1262,7 +1408,7 @@ ppc64_elf_ha_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + input_section->output_section->vma); value = (bfd_signed_vma) value >> 16; - octets = reloc_entry->address * bfd_octets_per_byte (abfd); + octets = reloc_entry->address * OCTETS_PER_BYTE (abfd, input_section); insn = bfd_get_32 (abfd, (bfd_byte *) data + octets); insn &= ~0x1fffc1; insn |= (value & 0xffc1) | ((value & 0x3e) << 15); @@ -1337,7 +1483,7 @@ ppc64_elf_brtaken_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, input_section, output_bfd, error_message); - octets = reloc_entry->address * bfd_octets_per_byte (abfd); + octets = reloc_entry->address * OCTETS_PER_BYTE (abfd, input_section); insn = bfd_get_32 (abfd, (bfd_byte *) data + octets); insn &= ~(0x01 << 21); r_type = reloc_entry->howto->type; @@ -1487,11 +1633,53 @@ ppc64_elf_toc64_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, if (TOCstart == 0) TOCstart = ppc64_elf_set_toc (NULL, input_section->output_section->owner); - octets = reloc_entry->address * bfd_octets_per_byte (abfd); + octets = reloc_entry->address * OCTETS_PER_BYTE (abfd, input_section); bfd_put_64 (abfd, TOCstart + TOC_BASE_OFF, (bfd_byte *) data + octets); return bfd_reloc_ok; } +static bfd_reloc_status_type +ppc64_elf_prefix_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, + bfd *output_bfd, char **error_message) +{ + uint64_t insn; + bfd_vma targ; + + if (output_bfd != NULL) + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + + insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address); + insn <<= 32; + insn |= bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address + 4); + + targ = (symbol->section->output_section->vma + + symbol->section->output_offset + + reloc_entry->addend); + if (!bfd_is_com_section (symbol->section)) + targ += symbol->value; + if (reloc_entry->howto->type == R_PPC64_D34_HA30) + targ += 1ULL << 33; + if (reloc_entry->howto->pc_relative) + { + bfd_vma from = (reloc_entry->address + + input_section->output_offset + + input_section->output_section->vma); + targ -=from; + } + targ >>= reloc_entry->howto->rightshift; + insn &= ~reloc_entry->howto->dst_mask; + insn |= ((targ << 16) | (targ & 0xffff)) & reloc_entry->howto->dst_mask; + bfd_put_32 (abfd, insn >> 32, (bfd_byte *) data + reloc_entry->address); + bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address + 4); + if (reloc_entry->howto->complain_on_overflow == complain_overflow_signed + && (targ + (1ULL << (reloc_entry->howto->bitsize - 1)) + >= 1ULL << reloc_entry->howto->bitsize)) + return bfd_reloc_overflow; + return bfd_reloc_ok; +} + static bfd_reloc_status_type ppc64_elf_unhandled_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, void *data, asection *input_section, @@ -1594,6 +1782,10 @@ struct ppc64_elf_obj_tdata /* Set if toc/got ha relocs detected not using r2, or lo reloc instruction not one we handle. */ unsigned int unexpected_toc_insn : 1; + + /* Set if PLT/GOT/TOC relocs that can be optimised are present in + this file. */ + unsigned int has_optrel : 1; }; #define ppc64_elf_tdata(bfd) \ @@ -1793,6 +1985,10 @@ struct _ppc64_elf_section_data /* Flag set when PLTCALL relocs are detected. */ unsigned int has_pltcall:1; + + /* Flag set when section has PLT/GOT/TOC relocations that can be + optimised. */ + unsigned int has_optrel:1; }; #define ppc64_elf_section_data(sec) \ @@ -1908,15 +2104,26 @@ compare_symbols (const void *ap, const void *bp) if ((a->flags & BSF_DYNAMIC) == 0 && (b->flags & BSF_DYNAMIC) != 0) return 1; - return a > b; + /* Finally, sort on where the symbol is in memory. The symbols will + be in at most two malloc'd blocks, one for static syms, one for + dynamic syms, and we distinguish the two blocks above by testing + BSF_DYNAMIC. Since we are sorting the symbol pointers which were + originally in the same order as the symbols (and we're not + sorting the symbols themselves), this ensures a stable sort. */ + if (a < b) + return -1; + if (a > b) + return 1; + return 0; } /* Search SYMS for a symbol of the given VALUE. */ static asymbol * -sym_exists_at (asymbol **syms, long lo, long hi, unsigned int id, bfd_vma value) +sym_exists_at (asymbol **syms, size_t lo, size_t hi, unsigned int id, + bfd_vma value) { - long mid; + size_t mid; if (id == (unsigned) -1) { @@ -2552,6 +2759,12 @@ must_be_dyn_reloc (struct bfd_link_info *info, case R_PPC64_REL32: case R_PPC64_REL64: case R_PPC64_REL30: + case R_PPC64_TOC16: + case R_PPC64_TOC16_DS: + case R_PPC64_TOC16_LO: + case R_PPC64_TOC16_HI: + case R_PPC64_TOC16_HA: + case R_PPC64_TOC16_LO_DS: return 0; case R_PPC64_TPREL16: @@ -2567,6 +2780,7 @@ must_be_dyn_reloc (struct bfd_link_info *info, case R_PPC64_TPREL16_HIGHEST: case R_PPC64_TPREL16_HIGHESTA: case R_PPC64_TPREL64: + case R_PPC64_TPREL34: /* These relocations are relative but in a shared library the linker doesn't know the thread pointer base. */ return bfd_link_dll (info); @@ -2687,8 +2901,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: + . pla %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: pla %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: pla %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 @@ -2853,9 +3093,7 @@ struct ppc_link_hash_entry of the other TLS bits are set. tls_optimize clears bits when optimizing to indicate the corresponding GOT entry type is not needed. If set, TLS_TLS is never cleared. tls_optimize may also - set TLS_TPRELGD when a GD reloc turns into a TPREL one. We use a - separate flag rather than setting TPREL just for convenience in - distinguishing the two cases. + set TLS_GDIE when a GD reloc turns into an IE one. These flags are also kept for local symbols. */ #define TLS_TLS 1 /* Any TLS reloc. */ #define TLS_GD 2 /* GD reloc. */ @@ -2863,8 +3101,8 @@ struct ppc_link_hash_entry #define TLS_TPREL 8 /* TPREL reloc, => IE. */ #define TLS_DTPREL 16 /* DTPREL reloc, => LD. */ #define TLS_MARK 32 /* __tls_get_addr call marked. */ -#define TLS_TPRELGD 64 /* TPREL reloc resulting from GD->IE. */ -#define TLS_EXPLICIT 128 /* Marks TOC section TLS relocs. */ +#define TLS_GDIE 64 /* GOT TPREL reloc resulting from GD->IE. */ +#define TLS_EXPLICIT 256 /* TOC section TLS reloc, not stored. */ unsigned char tls_mask; /* The above field is also used to mark function symbols. In which @@ -2975,6 +3213,12 @@ struct ppc_link_hash_table /* Whether plt calls for ELFv2 localentry:0 funcs have been optimized. */ unsigned int has_plt_localentry0:1; + /* 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; @@ -2988,8 +3232,9 @@ struct ppc_link_hash_table /* Nonzero if this section has TLS related relocations. */ #define has_tls_reloc sec_flg0 -/* Nonzero if this section has an old-style call to __tls_get_addr. */ -#define has_tls_get_addr_call sec_flg1 +/* Nonzero if this section has a call to __tls_get_addr lacking marker + relocations. */ +#define nomark_tls_get_addr sec_flg1 /* Nonzero if this section has any toc or got relocs. */ #define has_toc_reloc sec_flg2 @@ -3257,7 +3502,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info) htab->sfpr = bfd_make_section_anyway_with_flags (dynobj, ".sfpr", flags); if (htab->sfpr == NULL - || !bfd_set_section_alignment (dynobj, htab->sfpr, 2)) + || !bfd_set_section_alignment (htab->sfpr, 2)) return FALSE; } @@ -3268,7 +3513,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info) htab->glink = bfd_make_section_anyway_with_flags (dynobj, ".glink", flags); if (htab->glink == NULL - || !bfd_set_section_alignment (dynobj, htab->glink, 3)) + || !bfd_set_section_alignment (htab->glink, 3)) return FALSE; /* The part of .glink used by global entry stubs, separate so that @@ -3276,7 +3521,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info) 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)) + || !bfd_set_section_alignment (htab->global_entry, 2)) return FALSE; if (!info->no_ld_generated_unwind_info) @@ -3287,14 +3532,14 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info) ".eh_frame", flags); if (htab->glink_eh_frame == NULL - || !bfd_set_section_alignment (dynobj, htab->glink_eh_frame, 2)) + || !bfd_set_section_alignment (htab->glink_eh_frame, 2)) return FALSE; } flags = SEC_ALLOC | SEC_LINKER_CREATED; htab->elf.iplt = bfd_make_section_anyway_with_flags (dynobj, ".iplt", flags); if (htab->elf.iplt == NULL - || !bfd_set_section_alignment (dynobj, htab->elf.iplt, 3)) + || !bfd_set_section_alignment (htab->elf.iplt, 3)) return FALSE; flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY @@ -3302,7 +3547,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info) htab->elf.irelplt = bfd_make_section_anyway_with_flags (dynobj, ".rela.iplt", flags); if (htab->elf.irelplt == NULL - || !bfd_set_section_alignment (dynobj, htab->elf.irelplt, 3)) + || !bfd_set_section_alignment (htab->elf.irelplt, 3)) return FALSE; /* Create branch lookup table for plt_branch stubs. */ @@ -3311,7 +3556,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info) htab->brlt = bfd_make_section_anyway_with_flags (dynobj, ".branch_lt", flags); if (htab->brlt == NULL - || !bfd_set_section_alignment (dynobj, htab->brlt, 3)) + || !bfd_set_section_alignment (htab->brlt, 3)) return FALSE; /* Local plt entries, put in .branch_lt but a separate section for @@ -3319,7 +3564,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info) htab->pltlocal = bfd_make_section_anyway_with_flags (dynobj, ".branch_lt", flags); if (htab->pltlocal == NULL - || !bfd_set_section_alignment (dynobj, htab->pltlocal, 3)) + || !bfd_set_section_alignment (htab->pltlocal, 3)) return FALSE; if (!bfd_link_pic (info)) @@ -3330,13 +3575,13 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info) htab->relbrlt = bfd_make_section_anyway_with_flags (dynobj, ".rela.branch_lt", flags); if (htab->relbrlt == NULL - || !bfd_set_section_alignment (dynobj, htab->relbrlt, 3)) + || !bfd_set_section_alignment (htab->relbrlt, 3)) return FALSE; htab->relpltlocal = bfd_make_section_anyway_with_flags (dynobj, ".rela.branch_lt", flags); if (htab->relpltlocal == NULL - || !bfd_set_section_alignment (dynobj, htab->relpltlocal, 3)) + || !bfd_set_section_alignment (htab->relpltlocal, 3)) return FALSE; return TRUE; @@ -3532,13 +3777,13 @@ create_got_section (bfd *abfd, struct bfd_link_info *info) got = bfd_make_section_anyway_with_flags (abfd, ".got", flags); if (!got - || !bfd_set_section_alignment (abfd, got, 3)) + || !bfd_set_section_alignment (got, 3)) return FALSE; relgot = bfd_make_section_anyway_with_flags (abfd, ".rela.got", flags | SEC_READONLY); if (!relgot - || !bfd_set_section_alignment (abfd, relgot, 3)) + || !bfd_set_section_alignment (relgot, 3)) return FALSE; ppc64_elf_tdata (abfd)->got = got; @@ -4229,7 +4474,8 @@ is_branch_reloc (enum elf_ppc64_reloc_type r_type) || r_type == R_PPC64_ADDR14 || r_type == R_PPC64_ADDR14_BRTAKEN || r_type == R_PPC64_ADDR14_BRNTAKEN - || r_type == R_PPC64_PLTCALL); + || r_type == R_PPC64_PLTCALL + || r_type == R_PPC64_PLTCALL_NOTOC); } /* Relocs on inline plt call sequence insns prior to the call. */ @@ -4241,7 +4487,10 @@ is_plt_seq_reloc (enum elf_ppc64_reloc_type r_type) || r_type == R_PPC64_PLT16_HI || r_type == R_PPC64_PLT16_LO || r_type == R_PPC64_PLT16_LO_DS - || r_type == R_PPC64_PLTSEQ); + || r_type == R_PPC64_PLT_PCREL34 + || r_type == R_PPC64_PLT_PCREL34_NOTOC + || r_type == R_PPC64_PLTSEQ + || r_type == R_PPC64_PLTSEQ_NOTOC); } /* Look through the relocs for a section during the first phase, and @@ -4309,7 +4558,58 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, sec->has_toc_reloc = 1; } - tls_type = 0; + 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: + case R_PPC64_TPREL34: + case R_PPC64_DTPREL34: + case R_PPC64_PCREL34: + case R_PPC64_GOT_PCREL34: + case R_PPC64_GOT_TLSGD34: + case R_PPC64_GOT_TLSLD34: + case R_PPC64_GOT_TPREL34: + case R_PPC64_GOT_DTPREL34: + case R_PPC64_PLT_PCREL34: + case R_PPC64_PLT_PCREL34_NOTOC: + case R_PPC64_PCREL28: + htab->powerxx_stubs = 1; + break; + default: + break; + } + + switch (r_type) + { + case R_PPC64_PLT16_HA: + case R_PPC64_GOT_TLSLD16_HA: + case R_PPC64_GOT_TLSGD16_HA: + case R_PPC64_GOT_TPREL16_HA: + case R_PPC64_GOT_DTPREL16_HA: + case R_PPC64_GOT16_HA: + case R_PPC64_TOC16_HA: + case R_PPC64_PLT16_LO: + case R_PPC64_PLT16_LO_DS: + case R_PPC64_GOT_TLSLD16_LO: + case R_PPC64_GOT_TLSGD16_LO: + case R_PPC64_GOT_TPREL16_LO_DS: + case R_PPC64_GOT_DTPREL16_LO_DS: + case R_PPC64_GOT16_LO: + case R_PPC64_GOT16_LO_DS: + case R_PPC64_TOC16_LO: + case R_PPC64_TOC16_LO_DS: + case R_PPC64_GOT_PCREL34: + ppc64_elf_tdata (abfd)->has_optrel = 1; + ppc64_elf_section_data (sec)->has_optrel = 1; + break; + default: + break; + } + ifunc = NULL; if (h != NULL) { @@ -4336,7 +4636,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, } } - r_type = ELF64_R_TYPE (rel->r_info); + tls_type = 0; switch (r_type) { case R_PPC64_TLSGD: @@ -4357,6 +4657,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_GOT_TLSLD16_LO: case R_PPC64_GOT_TLSLD16_HI: case R_PPC64_GOT_TLSLD16_HA: + case R_PPC64_GOT_TLSLD34: tls_type = TLS_TLS | TLS_LD; goto dogottls; @@ -4364,6 +4665,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_GOT_TLSGD16_LO: case R_PPC64_GOT_TLSGD16_HI: case R_PPC64_GOT_TLSGD16_HA: + case R_PPC64_GOT_TLSGD34: tls_type = TLS_TLS | TLS_GD; goto dogottls; @@ -4371,6 +4673,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_TPREL16_HI: case R_PPC64_GOT_TPREL16_HA: + case R_PPC64_GOT_TPREL34: if (bfd_link_dll (info)) info->flags |= DF_STATIC_TLS; tls_type = TLS_TLS | TLS_TPREL; @@ -4380,17 +4683,20 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_GOT_DTPREL16_LO_DS: case R_PPC64_GOT_DTPREL16_HI: case R_PPC64_GOT_DTPREL16_HA: + case R_PPC64_GOT_DTPREL34: tls_type = TLS_TLS | TLS_DTPREL; dogottls: sec->has_tls_reloc = 1; - /* Fall through */ + goto dogot; case R_PPC64_GOT16: - case R_PPC64_GOT16_DS: - case R_PPC64_GOT16_HA: - case R_PPC64_GOT16_HI: case R_PPC64_GOT16_LO: + case R_PPC64_GOT16_HI: + case R_PPC64_GOT16_HA: + case R_PPC64_GOT16_DS: case R_PPC64_GOT16_LO_DS: + case R_PPC64_GOT_PCREL34: + dogot: /* This symbol requires a global offset table entry. */ sec->has_toc_reloc = 1; if (r_type == R_PPC64_GOT_TLSLD16 @@ -4455,6 +4761,8 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_PLT16_HI: case R_PPC64_PLT16_LO: case R_PPC64_PLT16_LO_DS: + case R_PPC64_PLT_PCREL34: + case R_PPC64_PLT_PCREL34_NOTOC: case R_PPC64_PLT32: case R_PPC64_PLT64: /* This symbol requires a procedure linkage table entry. */ @@ -4510,6 +4818,10 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_REL16_HIGHERA: case R_PPC64_REL16_HIGHEST: case R_PPC64_REL16_HIGHESTA: + case R_PPC64_REL16_HIGHER34: + case R_PPC64_REL16_HIGHERA34: + case R_PPC64_REL16_HIGHEST34: + case R_PPC64_REL16_HIGHESTA34: case R_PPC64_REL16DX_HA: break; @@ -4539,6 +4851,16 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_TOC16_HA: case R_PPC64_TOC16_LO_DS: sec->has_toc_reloc = 1; + if (h != NULL && bfd_link_executable (info)) + { + /* We may need a copy reloc. */ + h->non_got_ref = 1; + /* Strongly prefer a copy reloc over a dynamic reloc. + glibc ld.so as of 2019-08 will error out if one of + these relocations is emitted. */ + h->needs_copy = 1; + goto dodyn; + } break; /* Marker reloc. */ @@ -4555,9 +4877,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, /* This relocation describes which C++ vtable entries are actually used. Record for later use during GC. */ case R_PPC64_GNU_VTENTRY: - BFD_ASSERT (h != NULL); - if (h != NULL - && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) + if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) return FALSE; break; @@ -4594,6 +4914,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, goto rel24; case R_PPC64_PLTCALL: + case R_PPC64_PLTCALL_NOTOC: ppc64_elf_section_data (sec)->has_pltcall = 1; /* Fall through. */ @@ -4619,7 +4940,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, ; else /* Mark this section as having an old-style call. */ - sec->has_tls_get_addr_call = 1; + sec->nomark_tls_get_addr = 1; } plt_list = &h->plt.plist; } @@ -4667,7 +4988,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, { struct ppc_link_hash_entry *eh; eh = (struct ppc_link_hash_entry *) h; - eh->tls_mask |= tls_type; + eh->tls_mask |= tls_type & 0xff; } else if (!update_local_sym_info (abfd, symtab_hdr, r_symndx, @@ -4715,6 +5036,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_TPREL16_HIGHERA: case R_PPC64_TPREL16_HIGHEST: case R_PPC64_TPREL16_HIGHESTA: + case R_PPC64_TPREL34: if (bfd_link_dll (info)) info->flags |= DF_STATIC_TLS; goto dodyn; @@ -4741,12 +5063,21 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_ADDR16_HIGHESTA: case R_PPC64_ADDR16_LO: case R_PPC64_ADDR16_LO_DS: + case R_PPC64_D34: + case R_PPC64_D34_LO: + case R_PPC64_D34_HI30: + case R_PPC64_D34_HA30: + case R_PPC64_ADDR16_HIGHER34: + case R_PPC64_ADDR16_HIGHERA34: + case R_PPC64_ADDR16_HIGHEST34: + case R_PPC64_ADDR16_HIGHESTA34: + case R_PPC64_D28: if (h != NULL && !bfd_link_pic (info) && abiversion (abfd) != 1 && rel->r_addend == 0) { /* We may need a .plt entry if this reloc refers to a function in a shared lib. */ - if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend)) + if (!update_plt_info (abfd, &h->plt.plist, 0)) return FALSE; h->pointer_equality_needed = 1; } @@ -4760,7 +5091,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_UADDR32: case R_PPC64_UADDR64: case R_PPC64_TOC: - if (h != NULL && !bfd_link_pic (info)) + if (h != NULL && bfd_link_executable (info)) /* We may need a copy reloc. */ h->non_got_ref = 1; @@ -4790,17 +5121,14 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, dynamic library if we manage to avoid copy relocs for the symbol. */ dodyn: - if ((bfd_link_pic (info) - && (must_be_dyn_reloc (info, r_type) - || (h != NULL - && (!SYMBOLIC_BIND (info, h) - || h->root.type == bfd_link_hash_defweak - || !h->def_regular)))) - || (ELIMINATE_COPY_RELOCS - && !bfd_link_pic (info) - && h != NULL - && (h->root.type == bfd_link_hash_defweak - || !h->def_regular)) + if ((h != NULL + && (h->root.type == bfd_link_hash_defweak + || !h->def_regular)) + || (h != NULL + && !bfd_link_executable (info) + && !SYMBOLIC_BIND (info, h)) + || (bfd_link_pic (info) + && must_be_dyn_reloc (info, r_type)) || (!bfd_link_pic (info) && ifunc != NULL)) { @@ -5513,9 +5841,7 @@ sfpr_define (struct bfd_link_info *info, s = elf_link_hash_lookup (&htab->elf, buf, TRUE, TRUE, FALSE); if (s == NULL) return FALSE; - if (s->root.type == bfd_link_hash_new - || (s->root.type = bfd_link_hash_defined - && s->root.u.def.section == stub_sec)) + if (s->root.type == bfd_link_hash_new) { s->root.type = bfd_link_hash_defined; s->root.u.def.section = stub_sec; @@ -6109,7 +6435,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, only references to the symbol are via the global offset table. For such cases we need not do anything here; the relocations will be handled correctly by relocate_section. */ - if (bfd_link_pic (info)) + if (!bfd_link_executable (info)) return TRUE; /* If there are no references to this symbol that do not use the @@ -6125,7 +6451,9 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, /* If we don't find any dynamic relocs in read-only sections, then we'll be keeping the dynamic relocs and avoiding the copy reloc. */ - || (ELIMINATE_COPY_RELOCS && !alias_readonly_dynrelocs (h)) + || (ELIMINATE_COPY_RELOCS + && !h->needs_copy + && !alias_readonly_dynrelocs (h)) /* Protected variables do not work with .dynbss. The copy in .dynbss won't be used by the shared library with the protected @@ -6513,6 +6841,16 @@ dec_dynrel_count (bfd_vma r_info, default: return TRUE; + case R_PPC64_TOC16: + case R_PPC64_TOC16_DS: + case R_PPC64_TOC16_LO: + case R_PPC64_TOC16_HI: + case R_PPC64_TOC16_HA: + case R_PPC64_TOC16_LO_DS: + if (h == NULL) + return TRUE; + break; + case R_PPC64_TPREL16: case R_PPC64_TPREL16_LO: case R_PPC64_TPREL16_HI: @@ -6526,6 +6864,7 @@ dec_dynrel_count (bfd_vma r_info, case R_PPC64_TPREL16_HIGHEST: case R_PPC64_TPREL16_HIGHESTA: case R_PPC64_TPREL64: + case R_PPC64_TPREL34: case R_PPC64_DTPMOD64: case R_PPC64_DTPREL64: case R_PPC64_ADDR64: @@ -6553,6 +6892,15 @@ dec_dynrel_count (bfd_vma r_info, case R_PPC64_UADDR32: case R_PPC64_UADDR64: case R_PPC64_TOC: + case R_PPC64_D34: + case R_PPC64_D34_LO: + case R_PPC64_D34_HI30: + case R_PPC64_D34_HA30: + case R_PPC64_ADDR16_HIGHER34: + case R_PPC64_ADDR16_HIGHERA34: + case R_PPC64_ADDR16_HIGHEST34: + case R_PPC64_ADDR16_HIGHESTA34: + case R_PPC64_D28: break; } @@ -6566,17 +6914,18 @@ dec_dynrel_count (bfd_vma r_info, return FALSE; } - if ((bfd_link_pic (info) - && (must_be_dyn_reloc (info, r_type) - || (h != NULL - && (!SYMBOLIC_BIND (info, h) - || h->root.type == bfd_link_hash_defweak - || !h->def_regular)))) - || (ELIMINATE_COPY_RELOCS - && !bfd_link_pic (info) - && h != NULL - && (h->root.type == bfd_link_hash_defweak - || !h->def_regular))) + if ((h != NULL + && (h->root.type == bfd_link_hash_defweak + || !h->def_regular)) + || (h != NULL + && !bfd_link_executable (info) + && !SYMBOLIC_BIND (info, h)) + || (bfd_link_pic (info) + && must_be_dyn_reloc (info, r_type)) + || (!bfd_link_pic (info) + && (h != NULL + ? h->type == STT_GNU_IFUNC + : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC))) ; else return TRUE; @@ -7158,7 +7507,8 @@ ppc64_elf_inline_plt (struct bfd_link_info *info) unsigned char *tls_maskp; r_type = ELF64_R_TYPE (rel->r_info); - if (r_type != R_PPC64_PLTCALL) + if (r_type != R_PPC64_PLTCALL + && r_type != R_PPC64_PLTCALL_NOTOC) continue; r_symndx = ELF64_R_SYM (rel->r_info); @@ -7186,7 +7536,11 @@ ppc64_elf_inline_plt (struct bfd_link_info *info) from = (rel->r_offset + sec->output_offset + sec->output_section->vma); - if (to - from + limit < 2 * limit) + if (to - from + limit < 2 * limit + && !(r_type == R_PPC64_PLTCALL_NOTOC + && (((h ? h->other : sym->st_other) + & STO_PPC64_LOCAL_MASK) + > 1 << STO_PPC64_LOCAL_BIT))) *tls_maskp &= ~PLT_KEEP; } } @@ -7420,7 +7774,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) Elf_Internal_Sym *sym; asection *sym_sec; unsigned char *tls_mask; - unsigned char tls_set, tls_clear, tls_type = 0; + unsigned int tls_set, tls_clear, tls_type = 0; bfd_vma value; bfd_boolean ok_tprel, is_local; long toc_ref_index = 0; @@ -7462,11 +7816,9 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) value = sym->st_value; ok_tprel = FALSE; - is_local = FALSE; - if (h == NULL - || !h->def_dynamic) + is_local = SYMBOL_REFERENCES_LOCAL (info, h); + if (is_local) { - is_local = TRUE; if (h != NULL && h->root.type == bfd_link_hash_undefweak) ok_tprel = TRUE; @@ -7475,9 +7827,14 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) { value += sym_sec->output_offset; value += sym_sec->output_section->vma; - value -= htab->elf.tls_sec->vma; - ok_tprel = (value + TP_OFFSET + ((bfd_vma) 1 << 31) - < (bfd_vma) 1 << 32); + value -= htab->elf.tls_sec->vma + TP_OFFSET; + /* Note that even though the prefix insns + allow a 1<<33 offset we use the same test + as for addis;addi. There may be a mix of + pcrel and non-pcrel code and the decision + to optimise is per symbol, not per TLS + sequence. */ + ok_tprel = value + 0x80008000ULL < 1ULL << 32; } } @@ -7489,7 +7846,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) setup insn. If we don't find matching arg setup relocs, don't do any tls optimization. */ if (pass == 0 - && sec->has_tls_get_addr_call + && sec->nomark_tls_get_addr && h != NULL && (h == &htab->tls_get_addr->elf || h == &htab->tls_get_addr_fd->elf) @@ -7508,6 +7865,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) { case R_PPC64_GOT_TLSLD16: case R_PPC64_GOT_TLSLD16_LO: + case R_PPC64_GOT_TLSLD34: expecting_tls_get_addr = 1; found_tls_get_addr_arg = 1; /* Fall through. */ @@ -7528,6 +7886,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: + case R_PPC64_GOT_TLSGD34: expecting_tls_get_addr = 1; found_tls_get_addr_arg = 1; /* Fall through. */ @@ -7539,11 +7898,12 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) tls_set = 0; else /* GD -> IE */ - tls_set = TLS_TLS | TLS_TPRELGD; + tls_set = TLS_TLS | TLS_GDIE; tls_clear = TLS_GD; tls_type = TLS_TLS | TLS_GD; break; + case R_PPC64_GOT_TPREL34: case R_PPC64_GOT_TPREL16_DS: case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_TPREL16_HI: @@ -7558,14 +7918,19 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) } continue; - case R_PPC64_TLSGD: case R_PPC64_TLSLD: + if (!is_local) + continue; + /* Fall through. */ + case R_PPC64_TLSGD: if (rel + 1 < relend && is_plt_seq_reloc (ELF64_R_TYPE (rel[1].r_info))) { if (pass != 0 && (ELF64_R_TYPE (rel[1].r_info) - != R_PPC64_PLTSEQ)) + != R_PPC64_PLTSEQ) + && (ELF64_R_TYPE (rel[1].r_info) + != R_PPC64_PLTSEQ_NOTOC)) { r_symndx = ELF64_R_SYM (rel[1].r_info); if (!get_sym_h (&h, NULL, NULL, NULL, &locsyms, @@ -7664,7 +8029,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) tls_set = TLS_EXPLICIT | TLS_GD; else /* GD -> IE */ - tls_set = TLS_EXPLICIT | TLS_GD | TLS_TPRELGD; + tls_set = TLS_EXPLICIT | TLS_GD | TLS_GDIE; tls_clear = TLS_GD; } else @@ -7685,7 +8050,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) if (pass == 0) { if (!expecting_tls_get_addr - || !sec->has_tls_get_addr_call) + || !sec->nomark_tls_get_addr) continue; if (rel + 1 < relend @@ -7737,12 +8102,12 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) Disable optimization in this case. */ if ((tls_clear & (TLS_GD | TLS_LD)) != 0 && (tls_set & TLS_EXPLICIT) == 0 - && !sec->has_tls_get_addr_call + && !sec->nomark_tls_get_addr && ((*tls_mask & (TLS_TLS | TLS_MARK)) != (TLS_TLS | TLS_MARK))) continue; - if (expecting_tls_get_addr) + if (expecting_tls_get_addr == 1 + !sec->nomark_tls_get_addr) { struct plt_entry *ent = NULL; @@ -7809,7 +8174,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) } } - *tls_mask |= tls_set; + *tls_mask |= tls_set & 0xff; *tls_mask &= ~tls_clear; } @@ -7895,37 +8260,165 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf) static bfd_boolean ok_lo_toc_insn (unsigned int insn, enum elf_ppc64_reloc_type r_type) { - return ((insn & (0x3f << 26)) == 12u << 26 /* addic */ - || (insn & (0x3f << 26)) == 14u << 26 /* addi */ - || (insn & (0x3f << 26)) == 32u << 26 /* lwz */ - || (insn & (0x3f << 26)) == 34u << 26 /* lbz */ - || (insn & (0x3f << 26)) == 36u << 26 /* stw */ - || (insn & (0x3f << 26)) == 38u << 26 /* stb */ - || (insn & (0x3f << 26)) == 40u << 26 /* lhz */ - || (insn & (0x3f << 26)) == 42u << 26 /* lha */ - || (insn & (0x3f << 26)) == 44u << 26 /* sth */ - || (insn & (0x3f << 26)) == 46u << 26 /* lmw */ - || (insn & (0x3f << 26)) == 47u << 26 /* stmw */ - || (insn & (0x3f << 26)) == 48u << 26 /* lfs */ - || (insn & (0x3f << 26)) == 50u << 26 /* lfd */ - || (insn & (0x3f << 26)) == 52u << 26 /* stfs */ - || (insn & (0x3f << 26)) == 54u << 26 /* stfd */ - || (insn & (0x3f << 26)) == 56u << 26 /* lq,lfq */ - || ((insn & (0x3f << 26)) == 57u << 26 /* lxsd,lxssp,lfdp */ + return ((insn & (0x3fu << 26)) == 12u << 26 /* addic */ + || (insn & (0x3fu << 26)) == 14u << 26 /* addi */ + || (insn & (0x3fu << 26)) == 32u << 26 /* lwz */ + || (insn & (0x3fu << 26)) == 34u << 26 /* lbz */ + || (insn & (0x3fu << 26)) == 36u << 26 /* stw */ + || (insn & (0x3fu << 26)) == 38u << 26 /* stb */ + || (insn & (0x3fu << 26)) == 40u << 26 /* lhz */ + || (insn & (0x3fu << 26)) == 42u << 26 /* lha */ + || (insn & (0x3fu << 26)) == 44u << 26 /* sth */ + || (insn & (0x3fu << 26)) == 46u << 26 /* lmw */ + || (insn & (0x3fu << 26)) == 47u << 26 /* stmw */ + || (insn & (0x3fu << 26)) == 48u << 26 /* lfs */ + || (insn & (0x3fu << 26)) == 50u << 26 /* lfd */ + || (insn & (0x3fu << 26)) == 52u << 26 /* stfs */ + || (insn & (0x3fu << 26)) == 54u << 26 /* stfd */ + || (insn & (0x3fu << 26)) == 56u << 26 /* lq,lfq */ + || ((insn & (0x3fu << 26)) == 57u << 26 /* lxsd,lxssp,lfdp */ /* Exclude lfqu by testing reloc. If relocs are ever defined for the reduced D field in psq_lu then those will need testing too. */ && r_type != R_PPC64_TOC16_LO && r_type != R_PPC64_GOT16_LO) - || ((insn & (0x3f << 26)) == 58u << 26 /* ld,lwa */ + || ((insn & (0x3fu << 26)) == 58u << 26 /* ld,lwa */ && (insn & 1) == 0) - || (insn & (0x3f << 26)) == 60u << 26 /* stfq */ - || ((insn & (0x3f << 26)) == 61u << 26 /* lxv,stx{v,sd,ssp},stfdp */ + || (insn & (0x3fu << 26)) == 60u << 26 /* stfq */ + || ((insn & (0x3fu << 26)) == 61u << 26 /* lxv,stx{v,sd,ssp},stfdp */ /* Exclude stfqu. psq_stu as above for psq_lu. */ && r_type != R_PPC64_TOC16_LO && r_type != R_PPC64_GOT16_LO) - || ((insn & (0x3f << 26)) == 62u << 26 /* std,stq */ + || ((insn & (0x3fu << 26)) == 62u << 26 /* std,stq */ && (insn & 1) == 0)); } +/* PCREL_OPT in one instance flags to the linker that a pair of insns: + pld ra,symbol@got@pcrel + load/store rt,off(ra) + or + pla ra,symbol@pcrel + load/store rt,off(ra) + may be translated to + pload/pstore rt,symbol+off@pcrel + nop. + This function returns true if the optimization is possible, placing + the prefix insn in *PINSN1, a NOP in *PINSN2 and the offset in *POFF. + + On entry to this function, the linker has already determined that + the pld can be replaced with pla: *PINSN1 is that pla insn, + while *PINSN2 is the second instruction. */ + +static bfd_boolean +xlate_pcrel_opt (uint64_t *pinsn1, uint64_t *pinsn2, bfd_signed_vma *poff) +{ + uint64_t insn1 = *pinsn1; + uint64_t insn2 = *pinsn2; + bfd_signed_vma off; + + if ((insn2 & (63ULL << 58)) == 1ULL << 58) + { + /* Check that regs match. */ + if (((insn2 >> 16) & 31) != ((insn1 >> 21) & 31)) + return FALSE; + + /* P8LS or PMLS form, non-pcrel. */ + if ((insn2 & (-1ULL << 50) & ~(1ULL << 56)) != (1ULL << 58)) + return FALSE; + + *pinsn1 = (insn2 & ~(31 << 16) & ~0x3ffff0000ffffULL) | (1ULL << 52); + *pinsn2 = PNOP; + off = ((insn2 >> 16) & 0x3ffff0000ULL) | (insn2 & 0xffff); + *poff = (off ^ 0x200000000ULL) - 0x200000000ULL; + return TRUE; + } + + insn2 >>= 32; + + /* Check that regs match. */ + if (((insn2 >> 16) & 31) != ((insn1 >> 21) & 31)) + return FALSE; + + switch ((insn2 >> 26) & 63) + { + default: + return FALSE; + + case 32: /* lwz */ + case 34: /* lbz */ + case 36: /* stw */ + case 38: /* stb */ + case 40: /* lhz */ + case 42: /* lha */ + case 44: /* sth */ + case 48: /* lfs */ + case 50: /* lfd */ + case 52: /* stfs */ + case 54: /* stfd */ + /* These are the PMLS cases, where we just need to tack a prefix + on the insn. */ + insn1 = ((1ULL << 58) | (2ULL << 56) | (1ULL << 52) + | (insn2 & ((63ULL << 26) | (31ULL << 21)))); + off = insn2 & 0xffff; + break; + + case 58: /* lwa, ld */ + if ((insn2 & 1) != 0) + return FALSE; + insn1 = ((1ULL << 58) | (1ULL << 52) + | (insn2 & 2 ? 41ULL << 26 : 57ULL << 26) + | (insn2 & (31ULL << 21))); + off = insn2 & 0xfffc; + break; + + case 57: /* lxsd, lxssp */ + if ((insn2 & 3) < 2) + return FALSE; + insn1 = ((1ULL << 58) | (1ULL << 52) + | ((40ULL | (insn2 & 3)) << 26) + | (insn2 & (31ULL << 21))); + off = insn2 & 0xfffc; + break; + + case 61: /* stxsd, stxssp, lxv, stxv */ + if ((insn2 & 3) == 0) + return FALSE; + else if ((insn2 & 3) >= 2) + { + insn1 = ((1ULL << 58) | (1ULL << 52) + | ((44ULL | (insn2 & 3)) << 26) + | (insn2 & (31ULL << 21))); + off = insn2 & 0xfffc; + } + else + { + insn1 = ((1ULL << 58) | (1ULL << 52) + | ((50ULL | (insn2 & 4) | ((insn2 & 8) >> 3)) << 26) + | (insn2 & (31ULL << 21))); + off = insn2 & 0xfff0; + } + break; + + case 56: /* lq */ + insn1 = ((1ULL << 58) | (1ULL << 52) + | (insn2 & ((63ULL << 26) | (31ULL << 21)))); + off = insn2 & 0xffff; + break; + + case 62: /* std, stq */ + if ((insn2 & 1) != 0) + return FALSE; + insn1 = ((1ULL << 58) | (1ULL << 52) + | ((insn2 & 2) == 0 ? 61ULL << 26 : 60ULL << 26) + | (insn2 & (31ULL << 21))); + off = insn2 & 0xfffc; + break; + } + + *pinsn1 = insn1; + *pinsn2 = (uint64_t) NOP << 32; + *poff = (off ^ 0x8000) - 0x8000; + return TRUE; +} + /* Examine all relocs referencing .toc sections in order to remove unused .toc entries. */ @@ -8185,65 +8678,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) struct elf_link_hash_entry *h; Elf_Internal_Sym *sym; bfd_vma val; - enum {no_check, check_lo, check_ha} insn_check; r_type = ELF64_R_TYPE (rel->r_info); - switch (r_type) - { - default: - insn_check = no_check; - break; - - case R_PPC64_GOT_TLSLD16_HA: - case R_PPC64_GOT_TLSGD16_HA: - case R_PPC64_GOT_TPREL16_HA: - case R_PPC64_GOT_DTPREL16_HA: - case R_PPC64_GOT16_HA: - case R_PPC64_TOC16_HA: - insn_check = check_ha; - break; - - case R_PPC64_GOT_TLSLD16_LO: - case R_PPC64_GOT_TLSGD16_LO: - case R_PPC64_GOT_TPREL16_LO_DS: - case R_PPC64_GOT_DTPREL16_LO_DS: - case R_PPC64_GOT16_LO: - case R_PPC64_GOT16_LO_DS: - case R_PPC64_TOC16_LO: - case R_PPC64_TOC16_LO_DS: - insn_check = check_lo; - break; - } - - if (insn_check != no_check) - { - bfd_vma off = rel->r_offset & ~3; - unsigned char buf[4]; - unsigned int insn; - - if (!bfd_get_section_contents (ibfd, sec, buf, off, 4)) - { - free (used); - goto error_ret; - } - insn = bfd_get_32 (ibfd, buf); - if (insn_check == check_lo - ? !ok_lo_toc_insn (insn, r_type) - : ((insn & ((0x3f << 26) | 0x1f << 16)) - != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)) - { - char str[12]; - - ppc64_elf_tdata (ibfd)->unexpected_toc_insn = 1; - sprintf (str, "%#08x", insn); - info->callbacks->einfo - /* xgettext:c-format */ - (_("%H: toc optimization is not supported for" - " %s instruction\n"), - ibfd, sec, rel->r_offset & ~3, str); - } - } - switch (r_type) { case R_PPC64_TOC16: @@ -8581,10 +9017,248 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) free (skip); } - return TRUE; -} + /* Look for cases where we can change an indirect GOT access to + a GOT relative or PC relative access, possibly reducing the + number of GOT entries. */ + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next) + { + asection *sec; + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Sym *local_syms; + Elf_Internal_Rela *relstart, *rel; + bfd_vma got; -/* Return true iff input section I references the TOC using + if (!is_ppc64_elf (ibfd)) + continue; + + if (!ppc64_elf_tdata (ibfd)->has_optrel) + continue; + + sec = ppc64_elf_tdata (ibfd)->got; + got = 0; + if (sec != NULL) + got = sec->output_section->vma + sec->output_offset + 0x8000; + + local_syms = NULL; + symtab_hdr = &elf_symtab_hdr (ibfd); + + for (sec = ibfd->sections; sec != NULL; sec = sec->next) + { + if (sec->reloc_count == 0 + || !ppc64_elf_section_data (sec)->has_optrel + || discarded_section (sec)) + continue; + + relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL, + info->keep_memory); + if (relstart == NULL) + { + got_error_ret: + if (local_syms != NULL + && symtab_hdr->contents != (unsigned char *) local_syms) + free (local_syms); + if (sec != NULL + && relstart != NULL + && elf_section_data (sec)->relocs != relstart) + free (relstart); + return FALSE; + } + + for (rel = relstart; rel < relstart + sec->reloc_count; ++rel) + { + enum elf_ppc64_reloc_type r_type; + unsigned long r_symndx; + Elf_Internal_Sym *sym; + asection *sym_sec; + struct elf_link_hash_entry *h; + struct got_entry *ent; + bfd_vma val, pc; + unsigned char buf[8]; + unsigned int insn; + enum {no_check, check_lo, check_ha} insn_check; + + r_type = ELF64_R_TYPE (rel->r_info); + switch (r_type) + { + default: + insn_check = no_check; + break; + + case R_PPC64_PLT16_HA: + case R_PPC64_GOT_TLSLD16_HA: + case R_PPC64_GOT_TLSGD16_HA: + case R_PPC64_GOT_TPREL16_HA: + case R_PPC64_GOT_DTPREL16_HA: + case R_PPC64_GOT16_HA: + case R_PPC64_TOC16_HA: + insn_check = check_ha; + break; + + case R_PPC64_PLT16_LO: + case R_PPC64_PLT16_LO_DS: + case R_PPC64_GOT_TLSLD16_LO: + case R_PPC64_GOT_TLSGD16_LO: + case R_PPC64_GOT_TPREL16_LO_DS: + case R_PPC64_GOT_DTPREL16_LO_DS: + case R_PPC64_GOT16_LO: + case R_PPC64_GOT16_LO_DS: + case R_PPC64_TOC16_LO: + case R_PPC64_TOC16_LO_DS: + insn_check = check_lo; + break; + } + + if (insn_check != no_check) + { + bfd_vma off = rel->r_offset & ~3; + + if (!bfd_get_section_contents (ibfd, sec, buf, off, 4)) + goto got_error_ret; + + insn = bfd_get_32 (ibfd, buf); + if (insn_check == check_lo + ? !ok_lo_toc_insn (insn, r_type) + : ((insn & ((0x3fu << 26) | 0x1f << 16)) + != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)) + { + char str[12]; + + ppc64_elf_tdata (ibfd)->unexpected_toc_insn = 1; + sprintf (str, "%#08x", insn); + info->callbacks->einfo + /* xgettext:c-format */ + (_("%H: got/toc optimization is not supported for" + " %s instruction\n"), + ibfd, sec, rel->r_offset & ~3, str); + continue; + } + } + + switch (r_type) + { + /* Note that we don't delete GOT entries for + R_PPC64_GOT16_DS since we'd need a lot more + analysis. For starters, the preliminary layout is + before the GOT, PLT, dynamic sections and stubs are + laid out. Then we'd need to allow for changes in + distance between sections caused by alignment. */ + default: + continue; + + case R_PPC64_GOT16_HA: + case R_PPC64_GOT16_LO_DS: + case R_PPC64_GOT_PCREL34: + break; + } + + r_symndx = ELF64_R_SYM (rel->r_info); + if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms, + r_symndx, ibfd)) + goto got_error_ret; + + if (sym_sec == NULL + || sym_sec->output_section == NULL + || discarded_section (sym_sec)) + continue; + + if (!SYMBOL_REFERENCES_LOCAL (info, h)) + continue; + + if (h != NULL) + val = h->root.u.def.value; + else + val = sym->st_value; + val += rel->r_addend; + val += sym_sec->output_section->vma + sym_sec->output_offset; + +/* Fudge factor to allow for the fact that the preliminary layout + isn't exact. Reduce limits by this factor. */ +#define LIMIT_ADJUST(LIMIT) ((LIMIT) - (LIMIT) / 16) + + switch (r_type) + { + default: + continue; + + case R_PPC64_GOT16_HA: + if (val - got + LIMIT_ADJUST (0x80008000ULL) + >= LIMIT_ADJUST (0x100000000ULL)) + continue; + + if (!bfd_get_section_contents (ibfd, sec, buf, + rel->r_offset & ~3, 4)) + goto got_error_ret; + insn = bfd_get_32 (ibfd, buf); + if (((insn & ((0x3fu << 26) | 0x1f << 16)) + != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)) + continue; + break; + + case R_PPC64_GOT16_LO_DS: + if (val - got + LIMIT_ADJUST (0x80008000ULL) + >= LIMIT_ADJUST (0x100000000ULL)) + continue; + if (!bfd_get_section_contents (ibfd, sec, buf, + rel->r_offset & ~3, 4)) + goto got_error_ret; + insn = bfd_get_32 (ibfd, buf); + if ((insn & (0x3fu << 26 | 0x3)) != 58u << 26 /* ld */) + continue; + break; + + case R_PPC64_GOT_PCREL34: + pc = rel->r_offset; + pc += sec->output_section->vma + sec->output_offset; + if (val - pc + LIMIT_ADJUST (1ULL << 33) + >= LIMIT_ADJUST (1ULL << 34)) + continue; + if (!bfd_get_section_contents (ibfd, sec, buf, + rel->r_offset & ~3, 8)) + goto got_error_ret; + insn = bfd_get_32 (ibfd, buf); + if ((insn & (-1u << 18)) != ((1u << 26) | (1u << 20))) + continue; + insn = bfd_get_32 (ibfd, buf + 4); + if ((insn & (0x3fu << 26)) != 57u << 26) + continue; + break; + } +#undef LIMIT_ADJUST + + if (h != NULL) + ent = h->got.glist; + else + { + struct got_entry **local_got_ents = elf_local_got_ents (ibfd); + ent = local_got_ents[r_symndx]; + } + for (; ent != NULL; ent = ent->next) + if (ent->addend == rel->r_addend + && ent->owner == ibfd + && ent->tls_type == 0) + break; + BFD_ASSERT (ent && ent->got.refcount > 0); + ent->got.refcount -= 1; + } + + if (elf_section_data (sec)->relocs != relstart) + free (relstart); + } + + if (local_syms != NULL + && symtab_hdr->contents != (unsigned char *) local_syms) + { + if (!info->keep_memory) + free (local_syms); + else + symtab_hdr->contents = (unsigned char *) local_syms; + } + } + + return TRUE; +} + +/* Return true iff input section I references the TOC using instructions limited to +/-32k offsets. */ bfd_boolean @@ -8618,7 +9292,7 @@ allocate_got (struct elf_link_hash_entry *h, htab->got_reli_size += rentsize; } else if (((bfd_link_pic (info) - && !((gent->tls_type & TLS_TPREL) != 0 + && !(gent->tls_type != 0 && bfd_link_executable (info) && SYMBOL_REFERENCES_LOCAL (info, h))) || (htab->elf.dynamic_sections_created @@ -8693,7 +9367,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) eh = (struct ppc_link_hash_entry *) h; /* Run through the TLS GD got entries first if we're changing them to TPREL. */ - if ((eh->tls_mask & (TLS_TLS | TLS_TPRELGD)) == (TLS_TLS | TLS_TPRELGD)) + if ((eh->tls_mask & (TLS_TLS | TLS_GDIE)) == (TLS_TLS | TLS_GDIE)) for (gent = h->got.glist; gent != NULL; gent = gent->next) if (gent->got.refcount > 0 && (gent->tls_type & TLS_GD) != 0) @@ -8724,7 +9398,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) if (gent->got.refcount > 0) { if ((gent->tls_type & TLS_LD) != 0 - && !h->def_dynamic) + && SYMBOL_REFERENCES_LOCAL (info, h)) { ppc64_tlsld_got (gent->owner)->got.refcount += 1; *pgent = gent->next; @@ -8741,7 +9415,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) for (gent = h->got.glist; gent != NULL; gent = gent->next) if (!gent->is_indirect) { - /* Make sure this symbol is output as a dynamic symbol. */ + /* Ensure we catch all the cases where this symbol should + be made dynamic. */ if (!ensure_undef_dynamic (info, h)) return FALSE; @@ -8776,7 +9451,6 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) be defined in regular objects. For the normal shared case, discard space for relocs that have become local due to symbol visibility changes. */ - if (bfd_link_pic (info)) { /* Relocs that use pc_count are those that appear on a call @@ -8801,24 +9475,27 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) if (eh->dyn_relocs != NULL) { - /* Make sure this symbol is output as a dynamic symbol. */ + /* Ensure we catch all the cases where this symbol + should be made dynamic. */ if (!ensure_undef_dynamic (info, h)) return FALSE; } } - else if (ELIMINATE_COPY_RELOCS && h->type != STT_GNU_IFUNC) + + /* For a fixed position executable, discard space for + relocs against symbols which are not dynamic. */ + else if (h->type != STT_GNU_IFUNC) { - /* For the non-pic case, discard space for relocs against - symbols which turn out to need copy relocs or are not - dynamic. */ if (h->dynamic_adjusted && !h->def_regular && !ELF_COMMON_DEF_P (h)) { - /* Make sure this symbol is output as a dynamic symbol. */ + /* Ensure we catch all the cases where this symbol + should be made dynamic. */ if (!ensure_undef_dynamic (info, h)) return FALSE; + /* But if that didn't work out, discard dynamic relocs. */ if (h->dynindx == -1) eh->dyn_relocs = NULL; } @@ -8928,6 +9605,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, @@ -9147,7 +9827,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd, htab->got_reli_size += rel_size; } else if (bfd_link_pic (info) - && !((ent->tls_type & TLS_TPREL) != 0 + && !(ent->tls_type != 0 && bfd_link_executable (info))) { asection *srel = ppc64_elf_tdata (ibfd)->relgot; @@ -9224,7 +9904,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd, ent->got.offset = s->size; ent->owner = ibfd; s->size += 16; - if (bfd_link_pic (info)) + if (bfd_link_dll (info)) { asection *srel = ppc64_elf_tdata (ibfd)->relgot; srel->size += sizeof (Elf64_External_Rela); @@ -9709,6 +10389,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 * @@ -9786,7 +10606,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; @@ -10239,6 +11069,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; @@ -10517,18 +11348,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; @@ -10550,13 +11391,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) { @@ -10571,8 +11420,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; @@ -10741,7 +11591,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; @@ -10894,16 +11744,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 @@ -10911,17 +11769,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)) @@ -10956,6 +11817,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) @@ -10969,24 +11831,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: @@ -11330,7 +12199,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info) htab->got_reli_size += rel_size; } else if (bfd_link_pic (info) - && !((ent->tls_type & TLS_TPREL) != 0 + && !(ent->tls_type != 0 && bfd_link_executable (info))) { asection *srel = ppc64_elf_tdata (ibfd)->relgot; @@ -11356,7 +12225,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info) asection *s = ppc64_elf_tdata (ibfd)->got; ent->got.offset = s->size; s->size += 16; - if (bfd_link_pic (info)) + if (bfd_link_dll (info)) { asection *srel = ppc64_elf_tdata (ibfd)->relgot; srel->size += sizeof (Elf64_External_Rela); @@ -11467,7 +12336,8 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec) && r_type != R_PPC64_REL14 && r_type != R_PPC64_REL14_BRTAKEN && r_type != R_PPC64_REL14_BRNTAKEN - && r_type != R_PPC64_PLTCALL) + && r_type != R_PPC64_PLTCALL + && r_type != R_PPC64_PLTCALL_NOTOC) continue; r_symndx = ELF64_R_SYM (rel->r_info); @@ -12159,7 +13029,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) && code_sec->output_section != NULL && (((hash ? hash->elf.other : sym->st_other) & STO_PPC64_LOCAL_MASK) - != 1 << STO_PPC64_LOCAL_BIT))) + > 1 << STO_PPC64_LOCAL_BIT))) stub_type = ppc_stub_long_branch_notoc; } else if (stub_type != ppc_stub_plt_call) @@ -13293,6 +14163,66 @@ ppc64_elf_action_discarded (asection *sec) return _bfd_elf_default_action_discarded (sec); } +/* These are the dynamic relocations supported by glibc. */ + +static bfd_boolean +ppc64_glibc_dynamic_reloc (enum elf_ppc64_reloc_type r_type) +{ + switch (r_type) + { + case R_PPC64_RELATIVE: + case R_PPC64_NONE: + case R_PPC64_ADDR64: + case R_PPC64_GLOB_DAT: + case R_PPC64_IRELATIVE: + case R_PPC64_JMP_IREL: + case R_PPC64_JMP_SLOT: + case R_PPC64_DTPMOD64: + case R_PPC64_DTPREL64: + case R_PPC64_TPREL64: + case R_PPC64_TPREL16_LO_DS: + case R_PPC64_TPREL16_DS: + case R_PPC64_TPREL16: + case R_PPC64_TPREL16_LO: + case R_PPC64_TPREL16_HI: + case R_PPC64_TPREL16_HIGH: + case R_PPC64_TPREL16_HA: + case R_PPC64_TPREL16_HIGHA: + case R_PPC64_TPREL16_HIGHER: + case R_PPC64_TPREL16_HIGHEST: + case R_PPC64_TPREL16_HIGHERA: + case R_PPC64_TPREL16_HIGHESTA: + case R_PPC64_ADDR16_LO_DS: + case R_PPC64_ADDR16_LO: + case R_PPC64_ADDR16_HI: + case R_PPC64_ADDR16_HIGH: + case R_PPC64_ADDR16_HA: + case R_PPC64_ADDR16_HIGHA: + case R_PPC64_REL30: + case R_PPC64_COPY: + case R_PPC64_UADDR64: + case R_PPC64_UADDR32: + case R_PPC64_ADDR32: + case R_PPC64_ADDR24: + case R_PPC64_ADDR16: + case R_PPC64_UADDR16: + case R_PPC64_ADDR16_DS: + case R_PPC64_ADDR16_HIGHER: + case R_PPC64_ADDR16_HIGHEST: + case R_PPC64_ADDR16_HIGHERA: + case R_PPC64_ADDR16_HIGHESTA: + case R_PPC64_ADDR14: + case R_PPC64_ADDR14_BRTAKEN: + case R_PPC64_ADDR14_BRNTAKEN: + case R_PPC64_REL32: + case R_PPC64_REL64: + return TRUE; + + default: + return FALSE; + } +} + /* The RELOCATE_SECTION function is called by the ELF backend linker to handle the relocations for a section. @@ -13346,6 +14276,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, bfd_boolean is_opd; /* Assume 'at' branch hints. */ bfd_boolean is_isa_v2 = TRUE; + bfd_boolean warned_dynamic = FALSE; bfd_vma d_offset = (bfd_big_endian (input_bfd) ? 2 : 0); /* Initialize howto table if needed. */ @@ -13360,7 +14291,11 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (input_section->owner == htab->params->stub_bfd) return TRUE; - BFD_ASSERT (is_ppc64_elf (input_bfd)); + if (!is_ppc64_elf (input_bfd)) + { + bfd_set_error (bfd_error_wrong_format); + return FALSE; + } local_got_ents = elf_local_got_ents (input_bfd); TOCstart = elf_gp (output_bfd); @@ -13397,6 +14332,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, Elf_Internal_Rela orig_rel; reloc_howto_type *howto; struct reloc_howto_struct alt_howto; + uint64_t pinsn; + bfd_vma offset; again: orig_rel = *rel; @@ -13561,10 +14498,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, && (h == NULL || h->elf.root.type == bfd_link_hash_defined || h->elf.root.type == bfd_link_hash_defweak) - && (IS_PPC64_TLS_RELOC (r_type) - != (sym_type == STT_TLS - || (sym_type == STT_SECTION - && (sec->flags & SEC_THREAD_LOCAL) != 0)))) + && IS_PPC64_TLS_RELOC (r_type) != (sym_type == STT_TLS)) { if ((tls_mask & TLS_TLS) != 0 && (r_type == R_PPC64_TLS @@ -13604,7 +14538,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_LO_DS_OPT: insn = bfd_get_32 (input_bfd, contents + rel->r_offset - d_offset); - if ((insn & (0x3f << 26)) != 58u << 26) + if ((insn & (0x3fu << 26)) != 58u << 26) abort (); insn += (14u << 26) - (58u << 26); bfd_put_32 (input_bfd, insn, contents + rel->r_offset - d_offset); @@ -13642,7 +14576,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, doing a GD->IE transition. */ if (retval == 2) { - tls_gd = TLS_TPRELGD; + tls_gd = TLS_GDIE; if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0) goto tls_ldgd_opt; @@ -13696,35 +14630,78 @@ ppc64_elf_relocate_section (bfd *output_bfd, } break; + case R_PPC64_GOT_TPREL34: + if ((tls_mask & TLS_TLS) != 0 + && (tls_mask & TLS_TPREL) == 0) + { + /* pld ra,sym@got@tprel@pcrel -> paddi ra,r13,sym@tprel */ + pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset); + pinsn <<= 32; + pinsn |= bfd_get_32 (input_bfd, contents + rel->r_offset + 4); + pinsn += ((2ULL << 56) + (-1ULL << 52) + + (14ULL << 26) - (57ULL << 26) + (13ULL << 16)); + bfd_put_32 (input_bfd, pinsn >> 32, + contents + rel->r_offset); + bfd_put_32 (input_bfd, pinsn & 0xffffffff, + contents + rel->r_offset + 4); + r_type = R_PPC64_TPREL34; + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + } + break; + case R_PPC64_TLS: if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_TPREL) == 0) { - insn = bfd_get_32 (input_bfd, contents + rel->r_offset); + insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3)); insn = _bfd_elf_ppc_at_tls_transform (insn, 13); if (insn == 0) - abort (); - bfd_put_32 (input_bfd, insn, contents + rel->r_offset); - /* Was PPC64_TLS which sits on insn boundary, now - PPC64_TPREL16_LO which is at low-order half-word. */ - rel->r_offset += d_offset; - r_type = R_PPC64_TPREL16_LO; - if (toc_symndx != 0) + break; + if ((rel->r_offset & 3) == 0) { - rel->r_info = ELF64_R_INFO (toc_symndx, r_type); - rel->r_addend = toc_addend; - /* We changed the symbol. Start over in order to - get h, sym, sec etc. right. */ - goto again; + bfd_put_32 (input_bfd, insn, contents + rel->r_offset); + /* Was PPC64_TLS which sits on insn boundary, now + PPC64_TPREL16_LO which is at low-order half-word. */ + rel->r_offset += d_offset; + r_type = R_PPC64_TPREL16_LO; + if (toc_symndx != 0) + { + rel->r_info = ELF64_R_INFO (toc_symndx, r_type); + rel->r_addend = toc_addend; + /* We changed the symbol. Start over in order to + get h, sym, sec etc. right. */ + goto again; + } + else + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + } + else if ((rel->r_offset & 3) == 1) + { + /* For pcrel IE to LE we already have the full + offset and thus don't need an addi here. A nop + or mr will do. */ + if ((insn & (0x3fu << 26)) == 14 << 26) + { + /* Extract regs from addi rt,ra,si. */ + unsigned int rt = (insn >> 21) & 0x1f; + unsigned int ra = (insn >> 16) & 0x1f; + if (rt == ra) + insn = NOP; + else + { + /* Build or ra,rs,rb with rb==rs, ie. mr ra,rs. */ + insn = (rt << 16) | (ra << 21) | (ra << 11); + insn |= (31u << 26) | (444u << 1); + } + } + bfd_put_32 (input_bfd, insn, contents + rel->r_offset - 1); } - else - rel->r_info = ELF64_R_INFO (r_symndx, r_type); } break; case R_PPC64_GOT_TLSGD16_HI: case R_PPC64_GOT_TLSGD16_HA: - tls_gd = TLS_TPRELGD; + tls_gd = TLS_GDIE; if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0) goto tls_gdld_hi; break; @@ -13749,7 +14726,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: - tls_gd = TLS_TPRELGD; + tls_gd = TLS_GDIE; if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0) goto tls_ldgd_opt; break; @@ -13759,7 +14736,6 @@ ppc64_elf_relocate_section (bfd *output_bfd, if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0) { unsigned int insn1, insn2; - bfd_vma offset; tls_ldgd_opt: offset = (bfd_vma) -1; @@ -13768,7 +14744,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, stays with its arg setup insns, ie. that the next reloc is the __tls_get_addr call associated with the current reloc. Edit both insns. */ - if (input_section->has_tls_get_addr_call + if (input_section->nomark_tls_get_addr && rel + 1 < relend && branch_reloc_hash_match (input_bfd, rel + 1, htab->tls_get_addr, @@ -13784,15 +14760,16 @@ ppc64_elf_relocate_section (bfd *output_bfd, { /* IE */ insn1 &= (0x1f << 21) | (0x1f << 16); - insn1 |= 58 << 26; /* ld */ + insn1 |= 58u << 26; /* ld */ insn2 = 0x7c636a14; /* add 3,3,13 */ if (offset != (bfd_vma) -1) rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE); - if ((tls_mask & TLS_EXPLICIT) == 0) - r_type = (((r_type - (R_PPC64_GOT_TLSGD16 & 3)) & 3) - + R_PPC64_GOT_TPREL16_DS); - else + if (r_type == R_PPC64_TOC16 + || r_type == R_PPC64_TOC16_LO) r_type += R_PPC64_TOC16_DS - R_PPC64_TOC16; + else + r_type = (((r_type - (R_PPC64_GOT_TLSGD16 & 1)) & 1) + + R_PPC64_GOT_TPREL16_DS); rel->r_info = ELF64_R_INFO (r_symndx, r_type); } else @@ -13804,20 +14781,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (tls_gd == 0) { /* Was an LD reloc. */ - if (toc_symndx) - sec = local_sections[toc_symndx]; - for (r_symndx = 0; - r_symndx < symtab_hdr->sh_info; - r_symndx++) - if (local_sections[r_symndx] == sec) - break; - if (r_symndx >= symtab_hdr->sh_info) - r_symndx = STN_UNDEF; + r_symndx = STN_UNDEF; rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET; - if (r_symndx != STN_UNDEF) - rel->r_addend -= (local_syms[r_symndx].st_value - + sec->output_offset - + sec->output_section->vma); } else if (toc_symndx != 0) { @@ -13837,7 +14802,15 @@ ppc64_elf_relocate_section (bfd *output_bfd, bfd_put_32 (input_bfd, insn1, contents + rel->r_offset - d_offset); if (offset != (bfd_vma) -1) - bfd_put_32 (input_bfd, insn2, contents + offset); + { + bfd_put_32 (input_bfd, insn2, contents + offset); + if (offset + 8 <= input_section->size) + { + insn2 = bfd_get_32 (input_bfd, contents + offset + 4); + if (insn2 == LD_R2_0R1 + STK_TOC (htab)) + bfd_put_32 (input_bfd, NOP, contents + offset + 4); + } + } if ((tls_mask & tls_gd) == 0 && (tls_gd == 0 || toc_symndx != 0)) { @@ -13848,16 +14821,65 @@ ppc64_elf_relocate_section (bfd *output_bfd, } break; + case R_PPC64_GOT_TLSGD34: + if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0) + { + pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset); + pinsn <<= 32; + pinsn |= bfd_get_32 (input_bfd, contents + rel->r_offset + 4); + if ((tls_mask & TLS_GDIE) != 0) + { + /* IE, pla -> pld */ + pinsn += (-2ULL << 56) + (57ULL << 26) - (14ULL << 26); + r_type = R_PPC64_GOT_TPREL34; + } + else + { + /* LE, pla pcrel -> paddi r13 */ + pinsn += (-1ULL << 52) + (13ULL << 16); + r_type = R_PPC64_TPREL34; + } + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + bfd_put_32 (input_bfd, pinsn >> 32, + contents + rel->r_offset); + bfd_put_32 (input_bfd, pinsn & 0xffffffff, + contents + rel->r_offset + 4); + } + break; + + case R_PPC64_GOT_TLSLD34: + if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0) + { + pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset); + pinsn <<= 32; + pinsn |= bfd_get_32 (input_bfd, contents + rel->r_offset + 4); + pinsn += (-1ULL << 52) + (13ULL << 16); + bfd_put_32 (input_bfd, pinsn >> 32, + contents + rel->r_offset); + bfd_put_32 (input_bfd, pinsn & 0xffffffff, + contents + rel->r_offset + 4); + rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET; + r_symndx = STN_UNDEF; + r_type = R_PPC64_TPREL34; + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + goto again; + } + break; + case R_PPC64_TLSGD: if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0 && rel + 1 < relend) { unsigned int insn2; - bfd_vma offset = rel->r_offset; + enum elf_ppc64_reloc_type r_type1 = ELF64_R_TYPE (rel[1].r_info); - if (is_plt_seq_reloc (ELF64_R_TYPE (rel[1].r_info))) + offset = rel->r_offset; + if (is_plt_seq_reloc (r_type1)) { bfd_put_32 (output_bfd, NOP, contents + offset); + if (r_type1 == R_PPC64_PLT_PCREL34 + || r_type1 == R_PPC64_PLT_PCREL34_NOTOC) + bfd_put_32 (output_bfd, NOP, contents + offset + 4); rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE); break; } @@ -13865,7 +14887,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (ELF64_R_TYPE (rel[1].r_info) == R_PPC64_PLTCALL) bfd_put_32 (output_bfd, NOP, contents + offset + 4); - if ((tls_mask & TLS_TPRELGD) != 0) + if ((tls_mask & TLS_GDIE) != 0) { /* IE */ r_type = R_PPC64_NONE; @@ -13879,16 +14901,27 @@ ppc64_elf_relocate_section (bfd *output_bfd, r_symndx = toc_symndx; rel->r_addend = toc_addend; } - r_type = R_PPC64_TPREL16_LO; - rel->r_offset = offset + d_offset; - insn2 = 0x38630000; /* addi 3,3,0 */ + if (r_type1 == R_PPC64_REL24_NOTOC + || r_type1 == R_PPC64_PLTCALL_NOTOC) + { + r_type = R_PPC64_NONE; + insn2 = NOP; + } + else + { + rel->r_offset = offset + d_offset; + r_type = R_PPC64_TPREL16_LO; + insn2 = 0x38630000; /* addi 3,3,0 */ + } } rel->r_info = ELF64_R_INFO (r_symndx, r_type); /* Zap the reloc on the _tls_get_addr call too. */ BFD_ASSERT (offset == rel[1].r_offset); rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE); bfd_put_32 (input_bfd, insn2, contents + offset); - if ((tls_mask & TLS_TPRELGD) == 0 && toc_symndx != 0) + if ((tls_mask & TLS_GDIE) == 0 + && toc_symndx != 0 + && r_type != R_PPC64_NONE) goto again; } break; @@ -13898,11 +14931,15 @@ ppc64_elf_relocate_section (bfd *output_bfd, && rel + 1 < relend) { unsigned int insn2; - bfd_vma offset = rel->r_offset; + enum elf_ppc64_reloc_type r_type1 = ELF64_R_TYPE (rel[1].r_info); - if (is_plt_seq_reloc (ELF64_R_TYPE (rel[1].r_info))) + offset = rel->r_offset; + if (is_plt_seq_reloc (r_type1)) { bfd_put_32 (output_bfd, NOP, contents + offset); + if (r_type1 == R_PPC64_PLT_PCREL34 + || r_type1 == R_PPC64_PLT_PCREL34_NOTOC) + bfd_put_32 (output_bfd, NOP, contents + offset + 4); rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE); break; } @@ -13910,30 +14947,27 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (ELF64_R_TYPE (rel[1].r_info) == R_PPC64_PLTCALL) bfd_put_32 (output_bfd, NOP, contents + offset + 4); - if (toc_symndx) - sec = local_sections[toc_symndx]; - for (r_symndx = 0; - r_symndx < symtab_hdr->sh_info; - r_symndx++) - if (local_sections[r_symndx] == sec) - break; - if (r_symndx >= symtab_hdr->sh_info) - r_symndx = STN_UNDEF; - rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET; - if (r_symndx != STN_UNDEF) - rel->r_addend -= (local_syms[r_symndx].st_value - + sec->output_offset - + sec->output_section->vma); - - r_type = R_PPC64_TPREL16_LO; + if (r_type1 == R_PPC64_REL24_NOTOC + || r_type1 == R_PPC64_PLTCALL_NOTOC) + { + r_type = R_PPC64_NONE; + insn2 = NOP; + } + else + { + rel->r_offset = offset + d_offset; + r_symndx = STN_UNDEF; + r_type = R_PPC64_TPREL16_LO; + rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET; + insn2 = 0x38630000; /* addi 3,3,0 */ + } rel->r_info = ELF64_R_INFO (r_symndx, r_type); - rel->r_offset = offset + d_offset; /* Zap the reloc on the _tls_get_addr call too. */ BFD_ASSERT (offset == rel[1].r_offset); rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE); - insn2 = 0x38630000; /* addi 3,3,0 */ bfd_put_32 (input_bfd, insn2, contents + offset); - goto again; + if (r_type != R_PPC64_NONE) + goto again; } break; @@ -13945,7 +14979,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, if ((tls_mask & TLS_GD) == 0) { rel[1].r_info = ELF64_R_INFO (r_symndx, R_PPC64_NONE); - if ((tls_mask & TLS_TPRELGD) != 0) + if ((tls_mask & TLS_GDIE) != 0) r_type = R_PPC64_TPREL64; else { @@ -14040,7 +15074,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, && relocation + 0x80008000 <= 0xffffffff) { unsigned int insn1, insn2; - bfd_vma offset = rel->r_offset - d_offset; + offset = rel->r_offset - d_offset; insn1 = bfd_get_32 (input_bfd, contents + offset); insn2 = bfd_get_32 (input_bfd, contents + offset + 4); if ((insn1 & 0xffff0000) == ADDIS_R2_R12 @@ -14103,6 +15137,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_REL24: case R_PPC64_REL24_NOTOC: case R_PPC64_PLTCALL: + case R_PPC64_PLTCALL_NOTOC: /* Calls to functions with a different TOC, such as calls to shared objects, need to alter the TOC pointer. This is done using a linkage stub. A REL24 branching to these @@ -14116,7 +15151,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, fdh = ppc_follow_link (h->oh); stub_entry = ppc_get_stub_entry (input_section, sec, fdh, &orig_rel, htab); - if (r_type == R_PPC64_PLTCALL + if ((r_type == R_PPC64_PLTCALL + || r_type == R_PPC64_PLTCALL_NOTOC) && stub_entry != NULL && stub_entry->stub_type >= ppc_stub_plt_call && stub_entry->stub_type <= ppc_stub_plt_call_both) @@ -14295,7 +15331,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, || stub_entry->stub_type == ppc_stub_plt_branch_both) && (r_type != R_PPC64_REL24_NOTOC || ((fdh ? fdh->elf.other : sym->st_other) - & STO_PPC64_LOCAL_MASK) == 1 << STO_PPC64_LOCAL_BIT) + & STO_PPC64_LOCAL_MASK) <= 1 << STO_PPC64_LOCAL_BIT) && (relocation + addend - from + max_br_offset < 2 * max_br_offset)) stub_entry = NULL; @@ -14346,6 +15382,11 @@ ppc64_elf_relocate_section (bfd *output_bfd, || stub_entry->stub_type == ppc_stub_plt_call_both) && r_type == R_PPC64_REL24_NOTOC) relocation += 4; + + if (r_type == R_PPC64_REL24_NOTOC + && (stub_entry->stub_type == ppc_stub_plt_call_notoc + || stub_entry->stub_type == ppc_stub_plt_call_both)) + htab->notoc_plt = 1; } if (insn != 0) @@ -14387,9 +15428,125 @@ ppc64_elf_relocate_section (bfd *output_bfd, goto copy_reloc; } break; + + case R_PPC64_GOT16_DS: + from = TOCstart + htab->sec_info[input_section->id].toc_off; + if (relocation + addend - from + 0x8000 < 0x10000 + && SYMBOL_REFERENCES_LOCAL (info, &h->elf)) + { + insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3)); + if ((insn & (0x3fu << 26 | 0x3)) == 58u << 26 /* ld */) + { + insn += (14u << 26) - (58u << 26); + bfd_put_32 (input_bfd, insn, contents + (rel->r_offset & ~3)); + r_type = R_PPC64_TOC16; + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + } + } + break; + + case R_PPC64_GOT16_LO_DS: + case R_PPC64_GOT16_HA: + from = TOCstart + htab->sec_info[input_section->id].toc_off; + if (relocation + addend - from + 0x80008000ULL < 0x100000000ULL + && SYMBOL_REFERENCES_LOCAL (info, &h->elf)) + { + insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3)); + if ((insn & (0x3fu << 26 | 0x3)) == 58u << 26 /* ld */) + { + insn += (14u << 26) - (58u << 26); + bfd_put_32 (input_bfd, insn, contents + (rel->r_offset & ~3)); + r_type = R_PPC64_TOC16_LO; + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + } + else if ((insn & (0x3fu << 26)) == 15u << 26 /* addis */) + { + r_type = R_PPC64_TOC16_HA; + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + } + } + break; + + case R_PPC64_GOT_PCREL34: + from = (rel->r_offset + + input_section->output_section->vma + + input_section->output_offset); + if (relocation - from + (1ULL << 33) < 1ULL << 34 + && SYMBOL_REFERENCES_LOCAL (info, &h->elf)) + { + offset = rel->r_offset; + pinsn = bfd_get_32 (input_bfd, contents + offset); + pinsn <<= 32; + pinsn |= bfd_get_32 (input_bfd, contents + offset + 4); + if ((pinsn & ((-1ULL << 50) | (63ULL << 26))) + == ((1ULL << 58) | (1ULL << 52) | (57ULL << 26) /* pld */)) + { + /* Replace with paddi. */ + pinsn += (2ULL << 56) + (14ULL << 26) - (57ULL << 26); + r_type = R_PPC64_PCREL34; + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + bfd_put_32 (input_bfd, pinsn >> 32, contents + offset); + bfd_put_32 (input_bfd, pinsn, contents + offset + 4); + goto pcrelopt; + } + } + break; + + case R_PPC64_PCREL34: + if (SYMBOL_REFERENCES_LOCAL (info, &h->elf)) + { + offset = rel->r_offset; + pinsn = bfd_get_32 (input_bfd, contents + offset); + pinsn <<= 32; + pinsn |= bfd_get_32 (input_bfd, contents + offset + 4); + if ((pinsn & ((-1ULL << 50) | (63ULL << 26))) + == ((1ULL << 58) | (2ULL << 56) | (1ULL << 52) + | (14ULL << 26) /* paddi */)) + { + pcrelopt: + if (rel + 1 < relend + && rel[1].r_offset == offset + && rel[1].r_info == ELF64_R_INFO (0, R_PPC64_PCREL_OPT)) + { + bfd_vma off2 = rel[1].r_addend; + if (off2 == 0) + /* zero means next insn. */ + off2 = 8; + off2 += offset; + if (off2 + 4 <= input_section->size) + { + uint64_t pinsn2; + bfd_signed_vma addend_off; + pinsn2 = bfd_get_32 (input_bfd, contents + off2); + pinsn2 <<= 32; + if ((pinsn2 & (63ULL << 58)) == 1ULL << 58) + { + if (off2 + 8 > input_section->size) + break; + pinsn2 |= bfd_get_32 (input_bfd, + contents + off2 + 4); + } + if (xlate_pcrel_opt (&pinsn, &pinsn2, &addend_off)) + { + addend += addend_off; + rel->r_addend = addend; + bfd_put_32 (input_bfd, pinsn >> 32, + contents + offset); + bfd_put_32 (input_bfd, pinsn, + contents + offset + 4); + bfd_put_32 (input_bfd, pinsn2 >> 32, + contents + off2); + if ((pinsn2 & (63ULL << 58)) == 1ULL << 58) + bfd_put_32 (input_bfd, pinsn2, + contents + off2 + 4); + } + } + } + } + } + break; } - /* Set `addend'. */ tls_type = 0; save_unresolved_reloc = unresolved_reloc; switch (r_type) @@ -14411,6 +15568,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_GNU_VTINHERIT: case R_PPC64_GNU_VTENTRY: case R_PPC64_ENTRY: + case R_PPC64_PCREL_OPT: goto copy_reloc; /* GOT16 relocations. Like an ADDR16 using the symbol's @@ -14421,6 +15579,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_GOT_TLSGD16_LO: case R_PPC64_GOT_TLSGD16_HI: case R_PPC64_GOT_TLSGD16_HA: + case R_PPC64_GOT_TLSGD34: tls_type = TLS_TLS | TLS_GD; goto dogot; @@ -14428,6 +15587,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_GOT_TLSLD16_LO: case R_PPC64_GOT_TLSLD16_HI: case R_PPC64_GOT_TLSLD16_HA: + case R_PPC64_GOT_TLSLD34: tls_type = TLS_TLS | TLS_LD; goto dogot; @@ -14435,6 +15595,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_TPREL16_HI: case R_PPC64_GOT_TPREL16_HA: + case R_PPC64_GOT_TPREL34: tls_type = TLS_TLS | TLS_TPREL; goto dogot; @@ -14442,6 +15603,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_GOT_DTPREL16_LO_DS: case R_PPC64_GOT_DTPREL16_HI: case R_PPC64_GOT_DTPREL16_HA: + case R_PPC64_GOT_DTPREL34: tls_type = TLS_TLS | TLS_DTPREL; goto dogot; @@ -14451,6 +15613,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_GOT16_HA: case R_PPC64_GOT16_DS: case R_PPC64_GOT16_LO_DS: + case R_PPC64_GOT_PCREL34: dogot: { /* Relocation is to the entry for this symbol in the global @@ -14462,8 +15625,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, struct got_entry *ent; if (tls_type == (TLS_TLS | TLS_LD) - && (h == NULL - || !h->elf.def_dynamic)) + && SYMBOL_REFERENCES_LOCAL (info, &h->elf)) ent = ppc64_tlsld_got (input_bfd); else { @@ -14538,10 +15700,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, else if (indx != 0 || (bfd_link_pic (info) && (h == NULL - || !UNDEFWEAK_NO_DYNAMIC_RELOC (info, &h->elf) - || (tls_type == (TLS_TLS | TLS_LD) - && !h->elf.def_dynamic)) - && !(tls_type == (TLS_TLS | TLS_TPREL) + || !UNDEFWEAK_NO_DYNAMIC_RELOC (info, &h->elf)) + && !(tls_type != 0 && bfd_link_executable (info) && SYMBOL_REFERENCES_LOCAL (info, &h->elf)))) relgot = ppc64_elf_tdata (ent->owner)->relgot; @@ -14550,7 +15710,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, outrel.r_offset = (got->output_section->vma + got->output_offset + off); - outrel.r_addend = addend; + outrel.r_addend = orig_rel.r_addend; if (tls_type & (TLS_LD | TLS_GD)) { outrel.r_addend = 0; @@ -14563,7 +15723,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc); outrel.r_offset += 8; - outrel.r_addend = addend; + outrel.r_addend = orig_rel.r_addend; outrel.r_info = ELF64_R_INFO (indx, R_PPC64_DTPREL64); } @@ -14609,7 +15769,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, emitting a reloc. */ else { - relocation += addend; + relocation += orig_rel.r_addend; if (tls_type != 0) { if (htab->elf.tls_sec == NULL) @@ -14640,7 +15800,13 @@ ppc64_elf_relocate_section (bfd *output_bfd, abort (); relocation = got->output_section->vma + got->output_offset + off; - addend = -(TOCstart + htab->sec_info[input_section->id].toc_off); + addend = 0; + if (!(r_type == R_PPC64_GOT_PCREL34 + || r_type == R_PPC64_GOT_TLSGD34 + || r_type == R_PPC64_GOT_TLSLD34 + || r_type == R_PPC64_GOT_TPREL34 + || r_type == R_PPC64_GOT_DTPREL34)) + addend = -(TOCstart + htab->sec_info[input_section->id].toc_off); } break; @@ -14648,10 +15814,14 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_PLT16_HI: case R_PPC64_PLT16_LO: case R_PPC64_PLT16_LO_DS: + case R_PPC64_PLT_PCREL34: + case R_PPC64_PLT_PCREL34_NOTOC: case R_PPC64_PLT32: case R_PPC64_PLT64: case R_PPC64_PLTSEQ: + case R_PPC64_PLTSEQ_NOTOC: case R_PPC64_PLTCALL: + case R_PPC64_PLTCALL_NOTOC: /* Relocation is to the entry for this symbol in the procedure linkage table. */ unresolved_reloc = TRUE; @@ -14732,6 +15902,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_TOC16_LO_DS: case R_PPC64_TOC16_HA: addend -= TOCstart + htab->sec_info[input_section->id].toc_off; + if (h != NULL) + goto dodyn; break; /* Relocate against the beginning of the section. */ @@ -14755,14 +15927,18 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_REL16_HIGHERA: case R_PPC64_REL16_HIGHEST: case R_PPC64_REL16_HIGHESTA: + case R_PPC64_REL16_HIGHER34: + case R_PPC64_REL16_HIGHERA34: + case R_PPC64_REL16_HIGHEST34: + case R_PPC64_REL16_HIGHESTA34: case R_PPC64_REL16DX_HA: - break; - case R_PPC64_REL14: case R_PPC64_REL14_BRNTAKEN: case R_PPC64_REL14_BRTAKEN: case R_PPC64_REL24: case R_PPC64_REL24_NOTOC: + case R_PPC64_PCREL34: + case R_PPC64_PCREL28: break; case R_PPC64_TPREL16: @@ -14777,6 +15953,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_TPREL16_HIGHERA: case R_PPC64_TPREL16_HIGHEST: case R_PPC64_TPREL16_HIGHESTA: + case R_PPC64_TPREL34: if (h != NULL && h->elf.root.type == bfd_link_hash_undefweak && h->elf.dynindx == -1) @@ -14812,6 +15989,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_DTPREL16_HIGHERA: case R_PPC64_DTPREL16_HIGHEST: case R_PPC64_DTPREL16_HIGHESTA: + case R_PPC64_DTPREL34: if (htab->elf.tls_sec != NULL) addend -= htab->elf.tls_sec->vma + DTP_OFFSET; break; @@ -14857,12 +16035,21 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_ADDR16_HIGHESTA: case R_PPC64_ADDR16_LO: case R_PPC64_ADDR16_LO_DS: + case R_PPC64_ADDR16_HIGHER34: + case R_PPC64_ADDR16_HIGHERA34: + case R_PPC64_ADDR16_HIGHEST34: + case R_PPC64_ADDR16_HIGHESTA34: case R_PPC64_ADDR24: case R_PPC64_ADDR32: case R_PPC64_ADDR64: case R_PPC64_UADDR16: case R_PPC64_UADDR32: case R_PPC64_UADDR64: + case R_PPC64_D34: + case R_PPC64_D34_LO: + case R_PPC64_D34_HI30: + case R_PPC64_D34_HA30: + case R_PPC64_D28: dodyn: if ((input_section->flags & SEC_ALLOC) == 0) break; @@ -14986,21 +16173,38 @@ ppc64_elf_relocate_section (bfd *output_bfd, } else { - asection *osec; - - osec = sec->output_section; - indx = elf_section_data (osec)->dynindx; + asection *osec = sec->output_section; - if (indx == 0) + if ((osec->flags & SEC_THREAD_LOCAL) != 0) + { + /* TLS symbol values are relative to the + TLS segment. Dynamic relocations for + local TLS symbols therefore can't be + reduced to a relocation against their + section symbol because it holds the + address of the section, not a value + relative to the TLS segment. We could + change the .tdata dynamic section symbol + to be zero value but STN_UNDEF works + and is used elsewhere, eg. for TPREL64 + GOT relocs against local TLS symbols. */ + osec = htab->elf.tls_sec; + indx = 0; + } + else { - if ((osec->flags & SEC_READONLY) == 0 - && htab->elf.data_index_section != NULL) - osec = htab->elf.data_index_section; - else - osec = htab->elf.text_index_section; indx = elf_section_data (osec)->dynindx; + if (indx == 0) + { + if ((osec->flags & SEC_READONLY) == 0 + && htab->elf.data_index_section != NULL) + osec = htab->elf.data_index_section; + else + osec = htab->elf.text_index_section; + indx = elf_section_data (osec)->dynindx; + } + BFD_ASSERT (indx != 0); } - BFD_ASSERT (indx != 0); /* We are turning this relocation into one against a section symbol, so subtract out @@ -15035,6 +16239,19 @@ ppc64_elf_relocate_section (bfd *output_bfd, loc += sreloc->reloc_count++ * sizeof (Elf64_External_Rela); bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc); + if (!warned_dynamic + && !ppc64_glibc_dynamic_reloc (ELF64_R_TYPE (outrel.r_info))) + { + info->callbacks->einfo + /* xgettext:c-format */ + (_("%X%P: %pB: %s against %pT " + "is not supported by glibc as a dynamic relocation\n"), + input_bfd, + ppc64_elf_howto_table[ELF64_R_TYPE (outrel.r_info)]->name, + sym_name); + warned_dynamic = TRUE; + } + /* If this reloc is against an external symbol, it will be computed at runtime, so there's no need to do anything now. However, for the sake of prelink ensure @@ -15114,6 +16331,10 @@ ppc64_elf_relocate_section (bfd *output_bfd, insn. */ break; + case R_PPC64_PLTCALL_NOTOC: + if (!unresolved_reloc) + htab->notoc_plt = 1; + /* Fall through. */ case R_PPC64_PLTCALL: if (unresolved_reloc) { @@ -15122,12 +16343,14 @@ ppc64_elf_relocate_section (bfd *output_bfd, insn = bfd_get_32 (input_bfd, p); insn &= 1; bfd_put_32 (input_bfd, B_DOT | insn, p); - bfd_put_32 (input_bfd, NOP, p + 4); + if (r_type == R_PPC64_PLTCALL) + bfd_put_32 (input_bfd, NOP, p + 4); unresolved_reloc = save_unresolved_reloc; r_type = R_PPC64_REL24; } break; + case R_PPC64_PLTSEQ_NOTOC: case R_PPC64_PLTSEQ: if (unresolved_reloc) { @@ -15136,6 +16359,21 @@ ppc64_elf_relocate_section (bfd *output_bfd, } break; + case R_PPC64_PLT_PCREL34_NOTOC: + if (!unresolved_reloc) + htab->notoc_plt = 1; + /* Fall through. */ + case R_PPC64_PLT_PCREL34: + if (unresolved_reloc) + { + bfd_byte *p = contents + rel->r_offset; + bfd_put_32 (input_bfd, PNOP >> 32, p); + bfd_put_32 (input_bfd, PNOP, p + 4); + unresolved_reloc = FALSE; + goto copy_reloc; + } + break; + case R_PPC64_PLT16_HA: if (unresolved_reloc) { @@ -15181,10 +16419,10 @@ ppc64_elf_relocate_section (bfd *output_bfd, { bfd_byte *p = contents + (rel->r_offset & ~3); insn = bfd_get_32 (input_bfd, p); - if ((insn & (0x3f << 26)) == 12u << 26 /* addic */) + if ((insn & (0x3fu << 26)) == 12u << 26 /* addic */) { /* Transform addic to addi when we change reg. */ - insn &= ~((0x3f << 26) | (0x1f << 16)); + insn &= ~((0x3fu << 26) | (0x1f << 16)); insn |= (14u << 26) | (2 << 16); } else @@ -15201,7 +16439,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, { bfd_byte *p = contents + (rel->r_offset & ~3); insn = bfd_get_32 (input_bfd, p); - if ((insn & ((0x3f << 26) | 0x1f << 16)) + if ((insn & ((0x3fu << 26) | 0x1f << 16)) != ((15u << 26) | (13 << 16)) /* addis rt,13,imm */) /* xgettext:c-format */ info->callbacks->minfo @@ -15274,6 +16512,15 @@ ppc64_elf_relocate_section (bfd *output_bfd, addend += 0x8000; break; + case R_PPC64_D34_HA30: + case R_PPC64_ADDR16_HIGHERA34: + case R_PPC64_ADDR16_HIGHESTA34: + case R_PPC64_REL16_HIGHERA34: + case R_PPC64_REL16_HIGHESTA34: + if (sec != NULL) + addend += 1ULL << 33; + break; + case R_PPC64_ADDR16_DS: case R_PPC64_ADDR16_LO_DS: case R_PPC64_GOT16_DS: @@ -15301,8 +16548,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, forms of all the _DS relocs bloats all reloc switches in this file. It doesn't make much sense to use these relocs in data, so testing the insn should be safe. */ - if ((insn & (0x3f << 26)) == (56u << 26) - || ((insn & (0x3f << 26)) == (61u << 26) && (insn & 3) == 1)) + if ((insn & (0x3fu << 26)) == (56u << 26) + || ((insn & (0x3fu << 26)) == (61u << 26) && (insn & 3) == 1)) mask = 15; relocation += addend; addend = insn & (mask ^ 3); @@ -15351,15 +16598,15 @@ ppc64_elf_relocate_section (bfd *output_bfd, enum complain_overflow complain = complain_overflow_signed; insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3)); - if ((insn & (0x3f << 26)) == 10u << 26 /* cmpli */) + if ((insn & (0x3fu << 26)) == 10u << 26 /* cmpli */) complain = complain_overflow_bitfield; else if (howto->rightshift == 0 - ? ((insn & (0x3f << 26)) == 28u << 26 /* andi */ - || (insn & (0x3f << 26)) == 24u << 26 /* ori */ - || (insn & (0x3f << 26)) == 26u << 26 /* xori */) - : ((insn & (0x3f << 26)) == 29u << 26 /* andis */ - || (insn & (0x3f << 26)) == 25u << 26 /* oris */ - || (insn & (0x3f << 26)) == 27u << 26 /* xoris */)) + ? ((insn & (0x3fu << 26)) == 28u << 26 /* andi */ + || (insn & (0x3fu << 26)) == 24u << 26 /* ori */ + || (insn & (0x3fu << 26)) == 26u << 26 /* xori */) + : ((insn & (0x3fu << 26)) == 29u << 26 /* andis */ + || (insn & (0x3fu << 26)) == 25u << 26 /* oris */ + || (insn & (0x3fu << 26)) == 27u << 26 /* xoris */)) complain = complain_overflow_unsigned; if (howto->complain_on_overflow != complain) { @@ -15369,9 +16616,54 @@ ppc64_elf_relocate_section (bfd *output_bfd, } } - if (r_type == R_PPC64_REL16DX_HA) + switch (r_type) { - /* Split field reloc isn't handled by _bfd_final_link_relocate. */ + /* Split field relocs aren't handled by _bfd_final_link_relocate. */ + case R_PPC64_D34: + case R_PPC64_D34_LO: + case R_PPC64_D34_HI30: + case R_PPC64_D34_HA30: + case R_PPC64_PCREL34: + case R_PPC64_GOT_PCREL34: + case R_PPC64_TPREL34: + case R_PPC64_DTPREL34: + case R_PPC64_GOT_TLSGD34: + case R_PPC64_GOT_TLSLD34: + case R_PPC64_GOT_TPREL34: + case R_PPC64_GOT_DTPREL34: + case R_PPC64_PLT_PCREL34: + case R_PPC64_PLT_PCREL34_NOTOC: + case R_PPC64_D28: + case R_PPC64_PCREL28: + if (rel->r_offset + 8 > input_section->size) + r = bfd_reloc_outofrange; + else + { + relocation += addend; + if (howto->pc_relative) + relocation -= (rel->r_offset + + input_section->output_offset + + input_section->output_section->vma); + relocation >>= howto->rightshift; + + pinsn = bfd_get_32 (input_bfd, contents + rel->r_offset); + pinsn <<= 32; + pinsn |= bfd_get_32 (input_bfd, contents + rel->r_offset + 4); + + pinsn &= ~howto->dst_mask; + pinsn |= (((relocation << 16) | (relocation & 0xffff)) + & howto->dst_mask); + bfd_put_32 (input_bfd, pinsn >> 32, contents + rel->r_offset); + bfd_put_32 (input_bfd, pinsn, contents + rel->r_offset + 4); + r = bfd_reloc_ok; + if (howto->complain_on_overflow == complain_overflow_signed + && (relocation + (1ULL << (howto->bitsize - 1)) + >= 1ULL << howto->bitsize)) + r = bfd_reloc_overflow; + } + break; + + case R_PPC64_REL16DX_HA: if (rel->r_offset + 4 > input_section->size) r = bfd_reloc_outofrange; else @@ -15389,10 +16681,13 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (relocation + 0x8000 > 0xffff) r = bfd_reloc_overflow; } + break; + + default: + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, addend); } - else - r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents, - rel->r_offset, relocation, addend); if (r != bfd_reloc_ok) { @@ -15555,18 +16850,18 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd, break; } - if (h->needs_copy) + if (h->needs_copy + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && (h->root.u.def.section == htab->elf.sdynbss + || h->root.u.def.section == htab->elf.sdynrelro)) { /* This symbol needs a copy reloc. Set it up. */ Elf_Internal_Rela rela; asection *srel; bfd_byte *loc; - if (h->dynindx == -1 - || (h->root.type != bfd_link_hash_defined - && h->root.type != bfd_link_hash_defweak) - || htab->elf.srelbss == NULL - || htab->elf.sreldynrelro == NULL) + if (h->dynindx == -1) abort (); rela.r_offset = (h->root.u.def.value @@ -15670,7 +16965,8 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd, break; case DT_PPC64_OPT: - if (htab->do_multi_toc && htab->multi_toc_needed) + if ((htab->do_multi_toc && htab->multi_toc_needed) + || htab->notoc_plt) dyn.d_un.d_val |= PPC64_OPT_MULTI_TOC; if (htab->has_plt_localentry0) dyn.d_un.d_val |= PPC64_OPT_LOCALENTRY;