X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=bfd%2Felf64-ppc.c;h=5f99d4344b7623c8d29c733a5f5d26a935093f5d;hb=refs%2Fheads%2Fconcurrent-displaced-stepping-2020-04-01;hp=75189b6967f9fffe0cd0a60d01a3beb45b2e749e;hpb=4a421c53cf609d68fe956c2e7270d34c0ab8500f;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index 75189b6967..5f99d4344b 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-2019 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 @@ -122,6 +125,7 @@ static bfd_vma opd_entry_value #define elf_backend_finish_dynamic_sections ppc64_elf_finish_dynamic_sections #define elf_backend_link_output_symbol_hook ppc64_elf_output_symbol_hook #define elf_backend_special_sections ppc64_elf_special_sections +#define elf_backend_section_flags ppc64_elf_section_flags #define elf_backend_merge_symbol_attribute ppc64_elf_merge_symbol_attribute #define elf_backend_merge_symbol ppc64_elf_merge_symbol #define elf_backend_get_reloc_section bfd_get_section_by_name @@ -187,18 +191,24 @@ static bfd_vma opd_entry_value #define LD_R2_0R12 0xe84c0000 /* ld %r2,0(%r12) */ #define ADD_R2_R2_R12 0x7c426214 /* add %r2,%r2,%r12 */ +#define LI_R11_0 0x39600000 /* li %r11,0 */ #define LIS_R2 0x3c400000 /* lis %r2,xxx@ha */ +#define LIS_R11 0x3d600000 /* lis %r11,xxx@ha */ #define LIS_R12 0x3d800000 /* lis %r12,xxx@ha */ #define ADDIS_R2_R12 0x3c4c0000 /* addis %r2,%r12,xxx@ha */ #define ADDIS_R12_R2 0x3d820000 /* addis %r12,%r2,xxx@ha */ #define ADDIS_R12_R11 0x3d8b0000 /* addis %r12,%r11,xxx@ha */ #define ADDIS_R12_R12 0x3d8c0000 /* addis %r12,%r12,xxx@ha */ #define ORIS_R12_R12_0 0x658c0000 /* oris %r12,%r12,xxx@hi */ +#define ORI_R11_R11_0 0x616b0000 /* ori %r11,%r11,xxx@l */ #define ORI_R12_R12_0 0x618c0000 /* ori %r12,%r12,xxx@l */ #define LD_R12_0R12 0xe98c0000 /* ld %r12,xxx@l(%r12) */ +#define SLDI_R11_R11_34 0x796b1746 /* sldi %r11,%r11,34 */ #define SLDI_R12_R12_32 0x799c07c6 /* sldi %r12,%r12,32 */ #define LDX_R12_R11_R12 0x7d8b602a /* ldx %r12,%r11,%r12 */ #define ADD_R12_R11_R12 0x7d8b6214 /* add %r12,%r11,%r12 */ +#define PADDI_R12_PC 0x0610000039800000ULL +#define PLD_R12_PC 0x04100000e5800000ULL #define PNOP 0x0700000000000000ULL /* __glink_PLTresolve stub instructions. We enter with the index in R0. */ @@ -914,6 +924,24 @@ static reloc_howto_type ppc64_elf_howto_raw[] = 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), @@ -1113,6 +1141,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; @@ -1247,6 +1276,18 @@ ppc64_elf_reloc_type_lookup (bfd *abfd, 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; @@ -1368,7 +1409,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); @@ -1443,7 +1484,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; @@ -1593,7 +1634,7 @@ 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; } @@ -1743,8 +1784,9 @@ struct ppc64_elf_obj_tdata instruction not one we handle. */ unsigned int unexpected_toc_insn : 1; - /* Set if got relocs that can be optimised are present in this file. */ - unsigned int has_gotrel : 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) \ @@ -1945,8 +1987,9 @@ struct _ppc64_elf_section_data /* Flag set when PLTCALL relocs are detected. */ unsigned int has_pltcall:1; - /* Flag set when section has GOT relocations that can be optimised. */ - unsigned int has_gotrel: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) \ @@ -1958,7 +2001,7 @@ ppc64_elf_new_section_hook (bfd *abfd, asection *sec) if (!sec->used_by_bfd) { struct _ppc64_elf_section_data *sdata; - bfd_size_type amt = sizeof (*sdata); + size_t amt = sizeof (*sdata); sdata = bfd_zalloc (abfd, amt); if (sdata == NULL) @@ -1969,6 +2012,18 @@ ppc64_elf_new_section_hook (bfd *abfd, asection *sec) return _bfd_elf_new_section_hook (abfd, sec); } +static bfd_boolean +ppc64_elf_section_flags (const Elf_Internal_Shdr *hdr) +{ + const char *name = hdr->bfd_section->name; + + if (strncmp (name, ".sbss", 5) == 0 + || strncmp (name, ".sdata", 6) == 0) + hdr->bfd_section->flags |= SEC_SMALL_DATA; + + return TRUE; +} + static struct _opd_sec_data * get_opd_info (asection * sec) { @@ -2062,15 +2117,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) { @@ -2356,8 +2422,7 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd, free_contents_and_exit_err: count = -1; free_contents_and_exit: - if (contents) - free (contents); + free (contents); goto done; } @@ -2706,6 +2771,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: @@ -2721,6 +2792,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); @@ -2728,20 +2800,20 @@ must_be_dyn_reloc (struct bfd_link_info *info, } /* If ELIMINATE_COPY_RELOCS is non-zero, the linker will try to avoid - copying dynamic variables from a shared lib into an app's dynbss + copying dynamic variables from a shared lib into an app's .dynbss section, and instead use a dynamic relocation to point into the - shared lib. With code that gcc generates, it's vital that this be - enabled; In the PowerPC64 ABI, the address of a function is actually - the address of a function descriptor, which resides in the .opd - section. gcc uses the descriptor directly rather than going via the - GOT as some other ABI's do, which means that initialized function - pointers must reference the descriptor. Thus, a function pointer - initialized to the address of a function in a shared library will - either require a copy reloc, or a dynamic reloc. Using a copy reloc - redefines the function descriptor symbol to point to the copy. This - presents a problem as a plt entry for that function is also - initialized from the function descriptor symbol and the copy reloc - may not be initialized first. */ + shared lib. With code that gcc generates it is vital that this be + enabled; In the PowerPC64 ELFv1 ABI the address of a function is + actually the address of a function descriptor which resides in the + .opd section. gcc uses the descriptor directly rather than going + via the GOT as some other ABIs do, which means that initialized + function pointers reference the descriptor. Thus, a function + pointer initialized to the address of a function in a shared + library will either require a .dynbss copy and a copy reloc, or a + dynamic reloc. Using a .dynbss copy redefines the function + descriptor symbol to point to the copy. This presents a problem as + a PLT entry for that function is also initialized from the function + descriptor symbol and the copy may not be initialized first. */ #define ELIMINATE_COPY_RELOCS 1 /* Section name for stubs is the associated section name plus this @@ -2757,8 +2829,8 @@ must_be_dyn_reloc (struct bfd_link_info *info, ppc_stub_plt_branch: Similar to the above, but a 24 bit branch in the stub section won't reach its destination. - . addis %r11,%r2,xxx@toc@ha - . ld %r12,xxx@toc@l(%r11) + . addis %r12,%r2,xxx@toc@ha + . ld %r12,xxx@toc@l(%r12) . mtctr %r12 . bctr @@ -2784,8 +2856,8 @@ must_be_dyn_reloc (struct bfd_link_info *info, A ppc_stub_plt_branch with an r2 offset looks like: . std %r2,40(%r1) - . addis %r11,%r2,xxx@toc@ha - . ld %r12,xxx@toc@l(%r11) + . addis %r12,%r2,xxx@toc@ha + . ld %r12,xxx@toc@l(%r12) . addis %r2,%r2,off@ha . addi %r2,%r2,off@l . mtctr %r12 @@ -2841,8 +2913,34 @@ must_be_dyn_reloc (struct bfd_link_info *info, . mtctr %r12 . bctr + There are also ELFv1 power10 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 power10 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 @@ -2977,9 +3075,6 @@ struct ppc_link_hash_entry struct ppc_link_hash_entry *next_dot_sym; } u; - /* Track dynamic relocs copied for this symbol. */ - struct elf_dyn_relocs *dyn_relocs; - /* Link between function code and descriptor symbols. */ struct ppc_link_hash_entry *oh; @@ -3007,9 +3102,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. */ @@ -3017,8 +3110,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 @@ -3028,6 +3121,12 @@ struct ppc_link_hash_entry #define NON_GOT 256 /* local symbol plt, not stored. */ }; +static inline struct ppc_link_hash_entry * +ppc_elf_hash_entry (struct elf_link_hash_entry *ent) +{ + return (struct ppc_link_hash_entry *) ent; +} + /* ppc64 ELF linker hash table. */ struct ppc_link_hash_table @@ -3090,6 +3189,9 @@ struct ppc_link_hash_table /* Shortcut to .__tls_get_addr and __tls_get_addr. */ struct ppc_link_hash_entry *tls_get_addr; struct ppc_link_hash_entry *tls_get_addr_fd; + struct ppc_link_hash_entry *tga_desc; + struct ppc_link_hash_entry *tga_desc_fd; + struct map_stub *tga_group; /* The size of reliplt used by got entry relocs. */ bfd_size_type got_reli_size; @@ -3132,6 +3234,9 @@ struct ppc_link_hash_table /* Whether calls are made via the PLT from NOTOC functions. */ unsigned int notoc_plt:1; + /* Whether to use power10 instructions in linkage stubs. */ + unsigned int power10_stubs:1; + /* Incremented every time we size stubs. */ unsigned int stub_iteration; @@ -3145,8 +3250,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 @@ -3338,7 +3444,7 @@ static struct bfd_link_hash_table * ppc64_elf_link_hash_table_create (bfd *abfd) { struct ppc_link_hash_table *htab; - bfd_size_type amt = sizeof (struct ppc_link_hash_table); + size_t amt = sizeof (struct ppc_link_hash_table); htab = bfd_zmalloc (amt); if (htab == NULL) @@ -3414,7 +3520,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; } @@ -3425,7 +3531,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 @@ -3433,7 +3539,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) @@ -3444,14 +3550,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 @@ -3459,7 +3565,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. */ @@ -3468,7 +3574,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 @@ -3476,7 +3582,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)) @@ -3487,13 +3593,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; @@ -3689,13 +3795,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; @@ -3723,7 +3829,7 @@ elf_follow_link (struct elf_link_hash_entry *h) static inline struct ppc_link_hash_entry * ppc_follow_link (struct ppc_link_hash_entry *h) { - return (struct ppc_link_hash_entry *) follow_link (&h->elf.root); + return ppc_elf_hash_entry (elf_follow_link (&h->elf)); } /* Merge PLT info on FROM with that on TO. */ @@ -3770,8 +3876,8 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info, { struct ppc_link_hash_entry *edir, *eind; - edir = (struct ppc_link_hash_entry *) dir; - eind = (struct ppc_link_hash_entry *) ind; + edir = ppc_elf_hash_entry (dir); + eind = ppc_elf_hash_entry (ind); edir->is_func |= eind->is_func; edir->is_func_descriptor |= eind->is_func_descriptor; @@ -3797,20 +3903,20 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info, return; /* Copy over any dynamic relocs we may have on the indirect sym. */ - if (eind->dyn_relocs != NULL) + if (ind->dyn_relocs != NULL) { - if (edir->dyn_relocs != NULL) + if (dir->dyn_relocs != NULL) { struct elf_dyn_relocs **pp; struct elf_dyn_relocs *p; /* Add reloc counts against the indirect sym to the direct sym list. Merge any entries against the same section. */ - for (pp = &eind->dyn_relocs; (p = *pp) != NULL; ) + for (pp = &ind->dyn_relocs; (p = *pp) != NULL; ) { struct elf_dyn_relocs *q; - for (q = edir->dyn_relocs; q != NULL; q = q->next) + for (q = dir->dyn_relocs; q != NULL; q = q->next) if (q->sec == p->sec) { q->pc_count += p->pc_count; @@ -3821,11 +3927,11 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info, if (q == NULL) pp = &p->next; } - *pp = edir->dyn_relocs; + *pp = dir->dyn_relocs; } - edir->dyn_relocs = eind->dyn_relocs; - eind->dyn_relocs = NULL; + dir->dyn_relocs = ind->dyn_relocs; + ind->dyn_relocs = NULL; } /* Copy over got entries that we may have already seen to the @@ -3887,8 +3993,8 @@ lookup_fdh (struct ppc_link_hash_entry *fh, struct ppc_link_hash_table *htab) { const char *fd_name = fh->elf.root.root.string + 1; - fdh = (struct ppc_link_hash_entry *) - elf_link_hash_lookup (&htab->elf, fd_name, FALSE, FALSE, FALSE); + fdh = ppc_elf_hash_entry (elf_link_hash_lookup (&htab->elf, fd_name, + FALSE, FALSE, FALSE)); if (fdh == NULL) return fdh; @@ -4016,9 +4122,9 @@ ppc64_elf_merge_symbol (struct elf_link_hash_entry *h, bfd *oldbfd ATTRIBUTE_UNUSED, const asection *oldsec ATTRIBUTE_UNUSED) { - ((struct ppc_link_hash_entry *) h)->fake = 0; + ppc_elf_hash_entry (h)->fake = 0; if ((STO_PPC64_LOCAL_MASK & isym->st_other) != 0) - ((struct ppc_link_hash_entry *) h)->non_zero_localentry = 1; + ppc_elf_hash_entry (h)->non_zero_localentry = 1; return TRUE; } @@ -4040,7 +4146,7 @@ ppc64_elf_archive_symbol_lookup (bfd *abfd, if (h != NULL /* Don't return this sym if it is a fake function descriptor created by add_symbol_adjust. */ - && !((struct ppc_link_hash_entry *) h)->fake) + && !ppc_elf_hash_entry (h)->fake) return h; if (name[0] == '.') @@ -4054,6 +4160,11 @@ ppc64_elf_archive_symbol_lookup (bfd *abfd, memcpy (dot_name + 1, name, len + 1); h = _bfd_elf_archive_symbol_lookup (abfd, info, dot_name); bfd_release (abfd, dot_name); + if (h != NULL) + return h; + + if (strcmp (name, "__tls_get_addr_opt") == 0) + h = _bfd_elf_archive_symbol_lookup (abfd, info, "__tls_get_addr_desc"); return h; } @@ -4329,7 +4440,7 @@ update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr, break; if (ent == NULL) { - bfd_size_type amt = sizeof (*ent); + size_t amt = sizeof (*ent); ent = bfd_alloc (abfd, amt); if (ent == NULL) return FALSE; @@ -4361,7 +4472,7 @@ update_plt_info (bfd *abfd, struct plt_entry **plist, bfd_vma addend) break; if (ent == NULL) { - bfd_size_type amt = sizeof (*ent); + size_t amt = sizeof (*ent); ent = bfd_alloc (abfd, amt); if (ent == NULL) return FALSE; @@ -4457,7 +4568,6 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, int tls_type; struct _ppc64_elf_section_data *ppc64_sec; struct plt_entry **ifunc, **plt_list; - bfd_vma sym_addend; r_symndx = ELF64_R_SYM (rel->r_info); if (r_symndx < symtab_hdr->sh_info) @@ -4471,26 +4581,59 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, sec->has_toc_reloc = 1; } - tls_type = 0; - ifunc = NULL; r_type = ELF64_R_TYPE (rel->r_info); switch (r_type) { - default: - /* Somewhat foolishly, because the ABIs don't specifically - allow it, ppc64 gas and ld support GOT and PLT relocs - with non-zero addends where the addend results in - sym+addend being stored in the GOT or PLT entry. This - can't be supported for pcrel relocs because the addend is - used to specify the pcrel offset. */ - sym_addend = rel->r_addend; - break; + 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: - sym_addend = 0; + case R_PPC64_PCREL28: + htab->power10_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) { if (h->type == STT_GNU_IFUNC) @@ -4509,13 +4652,14 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) { ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx, - sym_addend, + rel->r_addend, NON_GOT | PLT_IFUNC); if (ifunc == NULL) return FALSE; } } + tls_type = 0; switch (r_type) { case R_PPC64_TLSGD: @@ -4523,10 +4667,10 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, /* These special tls relocs tie a call to __tls_get_addr with its parameter symbol. */ if (h != NULL) - ((struct ppc_link_hash_entry *) h)->tls_mask |= TLS_TLS | TLS_MARK; + ppc_elf_hash_entry (h)->tls_mask |= TLS_TLS | TLS_MARK; else if (!update_local_sym_info (abfd, symtab_hdr, r_symndx, - sym_addend, + rel->r_addend, NON_GOT | TLS_TLS | TLS_MARK)) return FALSE; sec->has_tls_reloc = 1; @@ -4536,6 +4680,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; @@ -4543,6 +4688,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; @@ -4550,6 +4696,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; @@ -4559,22 +4706,19 @@ 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; goto dogot; - case R_PPC64_GOT16_DS: + case R_PPC64_GOT16: + 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: - ppc64_elf_tdata (abfd)->has_gotrel = 1; - ppc64_elf_section_data (sec)->has_gotrel = 1; - /* Fall through. */ - - case R_PPC64_GOT16: - case R_PPC64_GOT16_HI: - case R_PPC64_GOT16_LO: dogot: /* This symbol requires a global offset table entry. */ sec->has_toc_reloc = 1; @@ -4598,20 +4742,20 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, struct ppc_link_hash_entry *eh; struct got_entry *ent; - eh = (struct ppc_link_hash_entry *) h; + eh = ppc_elf_hash_entry (h); for (ent = eh->elf.got.glist; ent != NULL; ent = ent->next) - if (ent->addend == sym_addend + if (ent->addend == rel->r_addend && ent->owner == abfd && ent->tls_type == tls_type) break; if (ent == NULL) { - bfd_size_type amt = sizeof (*ent); + size_t amt = sizeof (*ent); ent = bfd_alloc (abfd, amt); if (ent == NULL) return FALSE; ent->next = eh->elf.got.glist; - ent->addend = sym_addend; + ent->addend = rel->r_addend; ent->owner = abfd; ent->tls_type = tls_type; ent->is_indirect = FALSE; @@ -4624,16 +4768,8 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, else /* This is a global offset table entry for a local symbol. */ if (!update_local_sym_info (abfd, symtab_hdr, r_symndx, - sym_addend, tls_type)) + rel->r_addend, tls_type)) return FALSE; - - /* We may also need a plt entry if the symbol turns out to be - an ifunc. */ - if (h != NULL && !bfd_link_pic (info) && abiversion (abfd) != 1) - { - if (!update_plt_info (abfd, &h->plt.plist, sym_addend)) - return FALSE; - } break; case R_PPC64_PLT16_HA: @@ -4651,15 +4787,15 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, h->needs_plt = 1; if (h->root.root.string[0] == '.' && h->root.root.string[1] != '\0') - ((struct ppc_link_hash_entry *) h)->is_func = 1; - ((struct ppc_link_hash_entry *) h)->tls_mask |= PLT_KEEP; + ppc_elf_hash_entry (h)->is_func = 1; + ppc_elf_hash_entry (h)->tls_mask |= PLT_KEEP; plt_list = &h->plt.plist; } if (plt_list == NULL) plt_list = update_local_sym_info (abfd, symtab_hdr, r_symndx, - sym_addend, + rel->r_addend, NON_GOT | PLT_KEEP); - if (!update_plt_info (abfd, plt_list, sym_addend)) + if (!update_plt_info (abfd, plt_list, rel->r_addend)) return FALSE; break; @@ -4730,6 +4866,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. */ @@ -4796,7 +4942,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, h->needs_plt = 1; if (h->root.root.string[0] == '.' && h->root.root.string[1] != '\0') - ((struct ppc_link_hash_entry *) h)->is_func = 1; + ppc_elf_hash_entry (h)->is_func = 1; if (h == tga || h == dottga) { @@ -4809,7 +4955,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; } @@ -4817,7 +4963,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, /* We may need a .plt entry if the function this reloc refers to is in a shared lib. */ if (plt_list - && !update_plt_info (abfd, plt_list, sym_addend)) + && !update_plt_info (abfd, plt_list, rel->r_addend)) return FALSE; break; @@ -4854,14 +5000,10 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, dotlstoc: sec->has_tls_reloc = 1; if (h != NULL) - { - struct ppc_link_hash_entry *eh; - eh = (struct ppc_link_hash_entry *) h; - eh->tls_mask |= tls_type; - } + ppc_elf_hash_entry (h)->tls_mask |= tls_type & 0xff; else if (!update_local_sym_info (abfd, symtab_hdr, r_symndx, - sym_addend, tls_type)) + rel->r_addend, tls_type)) return FALSE; ppc64_sec = ppc64_elf_section_data (sec); @@ -4883,7 +5025,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, } BFD_ASSERT (rel->r_offset % 8 == 0); ppc64_sec->u.toc.symndx[rel->r_offset / 8] = r_symndx; - ppc64_sec->u.toc.add[rel->r_offset / 8] = sym_addend; + ppc64_sec->u.toc.add[rel->r_offset / 8] = rel->r_addend; /* Mark the second slot of a GD or LD entry. -1 to indicate GD and -2 to indicate LD. */ @@ -4905,6 +5047,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; @@ -4915,7 +5058,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, && ELF64_R_TYPE ((rel + 1)->r_info) == R_PPC64_TOC) { if (h != NULL) - ((struct ppc_link_hash_entry *) h)->is_func = 1; + ppc_elf_hash_entry (h)->is_func = 1; } /* Fall through. */ @@ -4959,7 +5102,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; @@ -4989,17 +5132,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)) { @@ -5022,7 +5162,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, struct elf_dyn_relocs *p; struct elf_dyn_relocs **head; - head = &((struct ppc_link_hash_entry *) h)->dyn_relocs; + head = &h->dyn_relocs; p = *head; if (p == NULL || p->sec != sec) { @@ -5403,7 +5543,7 @@ is_elfv2_localentry0 (struct elf_link_hash_entry *h) && h->type == STT_FUNC && h->root.type == bfd_link_hash_defined && (STO_PPC64_LOCAL_MASK & h->other) == 0 - && !((struct ppc_link_hash_entry *) h)->non_zero_localentry + && !ppc_elf_hash_entry (h)->non_zero_localentry && is_ppc64_elf (h->root.u.def.section->owner) && abiversion (h->root.u.def.section->owner) >= 2); } @@ -5452,6 +5592,27 @@ defined_func_desc (struct ppc_link_hash_entry *fh) return NULL; } +/* Given H is a symbol that satisfies is_static_defined, return the + value in the output file. */ + +static bfd_vma +defined_sym_val (struct elf_link_hash_entry *h) +{ + return (h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset + + h->root.u.def.value); +} + +/* Return true if H matches __tls_get_addr or one of its variants. */ + +static bfd_boolean +is_tls_get_addr (struct elf_link_hash_entry *h, + struct ppc_link_hash_table *htab) +{ + return (h == &htab->tls_get_addr_fd->elf || h == &htab->tga_desc_fd->elf + || h == &htab->tls_get_addr->elf || h == &htab->tga_desc->elf); +} + static bfd_boolean func_desc_adjust (struct elf_link_hash_entry *, void *); /* Garbage collect sections, after first dealing with dot-symbols. */ @@ -5485,8 +5646,8 @@ ppc64_elf_gc_keep (struct bfd_link_info *info) struct ppc_link_hash_entry *eh, *fh; asection *sec; - eh = (struct ppc_link_hash_entry *) - elf_link_hash_lookup (&htab->elf, sym->name, FALSE, FALSE, TRUE); + eh = ppc_elf_hash_entry (elf_link_hash_lookup (&htab->elf, sym->name, + FALSE, FALSE, TRUE)); if (eh == NULL) continue; if (eh->elf.root.type != bfd_link_hash_defined @@ -5518,7 +5679,7 @@ static bfd_boolean ppc64_elf_gc_mark_dynamic_ref (struct elf_link_hash_entry *h, void *inf) { struct bfd_link_info *info = (struct bfd_link_info *) inf; - struct ppc_link_hash_entry *eh = (struct ppc_link_hash_entry *) h; + struct ppc_link_hash_entry *eh = ppc_elf_hash_entry (h); struct ppc_link_hash_entry *fdh; struct bfd_elf_dynamic_list *d = info->dynamic_list; @@ -5602,7 +5763,7 @@ ppc64_elf_gc_mark_hook (asection *sec, { case bfd_link_hash_defined: case bfd_link_hash_defweak: - eh = (struct ppc_link_hash_entry *) h; + eh = ppc_elf_hash_entry (h); fdh = defined_func_desc (eh); if (fdh != NULL) { @@ -5698,8 +5859,8 @@ sfpr_define (struct bfd_link_info *info, sym[len + 0] = i / 10 + '0'; sym[len + 1] = i % 10 + '0'; - h = (struct ppc_link_hash_entry *) - elf_link_hash_lookup (&htab->elf, sym, writing, TRUE, TRUE); + h = ppc_elf_hash_entry (elf_link_hash_lookup (&htab->elf, sym, + writing, TRUE, TRUE)); if (stub_sec != NULL) { if (h != NULL @@ -5712,9 +5873,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; @@ -5928,6 +6087,84 @@ restvr_tail (bfd *abfd, bfd_byte *p, int r) return p + 4; } +#define STDU_R1_0R1 0xf8210001 +#define ADDI_R1_R1 0x38210000 + +/* Emit prologue of wrapper preserving regs around a call to + __tls_get_addr_opt. */ + +static bfd_byte * +tls_get_addr_prologue (bfd *obfd, bfd_byte *p, struct ppc_link_hash_table *htab) +{ + unsigned int i; + + bfd_put_32 (obfd, MFLR_R0, p); + p += 4; + bfd_put_32 (obfd, STD_R0_0R1 + 16, p); + p += 4; + + if (htab->opd_abi) + { + for (i = 4; i < 12; i++) + { + bfd_put_32 (obfd, + STD_R0_0R1 | i << 21 | (-(13 - i) * 8 & 0xffff), p); + p += 4; + } + bfd_put_32 (obfd, STDU_R1_0R1 | (-128 & 0xffff), p); + p += 4; + } + else + { + for (i = 4; i < 12; i++) + { + bfd_put_32 (obfd, + STD_R0_0R1 | i << 21 | (-(12 - i) * 8 & 0xffff), p); + p += 4; + } + bfd_put_32 (obfd, STDU_R1_0R1 | (-96 & 0xffff), p); + p += 4; + } + return p; +} + +/* Emit epilogue of wrapper preserving regs around a call to + __tls_get_addr_opt. */ + +static bfd_byte * +tls_get_addr_epilogue (bfd *obfd, bfd_byte *p, struct ppc_link_hash_table *htab) +{ + unsigned int i; + + if (htab->opd_abi) + { + for (i = 4; i < 12; i++) + { + bfd_put_32 (obfd, LD_R0_0R1 | i << 21 | (128 - (13 - i) * 8), p); + p += 4; + } + bfd_put_32 (obfd, ADDI_R1_R1 | 128, p); + p += 4; + } + else + { + for (i = 4; i < 12; i++) + { + bfd_put_32 (obfd, LD_R0_0R1 | i << 21 | (96 - (12 - i) * 8), p); + p += 4; + } + bfd_put_32 (obfd, ADDI_R1_R1 | 96, p); + p += 4; + } + bfd_put_32 (obfd, LD_R0_0R1 | 16, p); + p += 4; + bfd_put_32 (obfd, MTLR_R0, p); + p += 4; + bfd_put_32 (obfd, BLR, p); + p += 4; + return p; +} + /* Called via elf_link_hash_traverse to transfer dynamic linking information on function code symbol entries to their corresponding function descriptor symbol entries. */ @@ -5941,7 +6178,7 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf) struct ppc_link_hash_entry *fdh; bfd_boolean force_local; - fh = (struct ppc_link_hash_entry *) h; + fh = ppc_elf_hash_entry (h); if (fh->elf.root.type == bfd_link_hash_indirect) return TRUE; @@ -6119,25 +6356,6 @@ ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED, return TRUE; } -/* Find dynamic relocs for H that apply to read-only sections. */ - -static asection * -readonly_dynrelocs (struct elf_link_hash_entry *h) -{ - struct ppc_link_hash_entry *eh; - struct elf_dyn_relocs *p; - - eh = (struct ppc_link_hash_entry *) h; - for (p = eh->dyn_relocs; p != NULL; p = p->next) - { - asection *s = p->sec->output_section; - - if (s != NULL && (s->flags & SEC_READONLY) != 0) - return p->sec; - } - return NULL; -} - /* Return true if we have dynamic relocs against H or any of its weak aliases, that apply to read-only sections. Cannot be used after size_dynamic_sections. */ @@ -6145,14 +6363,12 @@ readonly_dynrelocs (struct elf_link_hash_entry *h) static bfd_boolean alias_readonly_dynrelocs (struct elf_link_hash_entry *h) { - struct ppc_link_hash_entry *eh; - - eh = (struct ppc_link_hash_entry *) h; + struct ppc_link_hash_entry *eh = ppc_elf_hash_entry (h); do { - if (readonly_dynrelocs (&eh->elf)) + if (_bfd_elf_readonly_dynrelocs (&eh->elf)) return TRUE; - eh = (struct ppc_link_hash_entry *) eh->elf.u.alias; + eh = ppc_elf_hash_entry (eh->elf.u.alias); } while (eh != NULL && &eh->elf != h); @@ -6166,7 +6382,7 @@ pc_dynrelocs (struct ppc_link_hash_entry *eh) { struct elf_dyn_relocs *p; - for (p = eh->dyn_relocs; p != NULL; p = p->next) + for (p = eh->elf.dyn_relocs; p != NULL; p = p->next) if (p->pc_count != 0) return TRUE; return FALSE; @@ -6214,7 +6430,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, || h->type == STT_GNU_IFUNC || h->needs_plt) { - bfd_boolean local = (((struct ppc_link_hash_entry *) h)->save_res + bfd_boolean local = (ppc_elf_hash_entry (h)->save_res || SYMBOL_CALLS_LOCAL (info, h) || UNDEFWEAK_NO_DYNAMIC_RELOC (info, h)); /* Discard dyn_relocs when non-pic if we've decided that a @@ -6229,7 +6445,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, if (!bfd_link_pic (info) && h->type != STT_GNU_IFUNC && local) - ((struct ppc_link_hash_entry *) h)->dyn_relocs = NULL; + h->dyn_relocs = NULL; /* Clear procedure linkage table information for any symbol that won't need a .plt entry. */ @@ -6241,7 +6457,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, || (h->type != STT_GNU_IFUNC && local && (htab->can_convert_all_inline_plt - || (((struct ppc_link_hash_entry *) h)->tls_mask + || (ppc_elf_hash_entry (h)->tls_mask & (TLS_TLS | PLT_KEEP)) != PLT_KEEP))) { h->plt.plist = NULL; @@ -6259,7 +6475,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, extra work in ld.so when resolving these symbols. */ if (global_entry_stub (h)) { - if (!readonly_dynrelocs (h)) + if (!_bfd_elf_readonly_dynrelocs (h)) { h->pointer_equality_needed = 0; /* If we haven't seen a branch reloc and the symbol @@ -6270,14 +6486,14 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, else if (!bfd_link_pic (info)) /* We are going to be defining the function symbol on the plt stub, so no dyn_relocs needed when non-pic. */ - ((struct ppc_link_hash_entry *) h)->dyn_relocs = NULL; + h->dyn_relocs = NULL; } /* ELFv2 function symbols can't have copy relocs. */ return TRUE; } else if (!h->needs_plt - && !readonly_dynrelocs (h)) + && !_bfd_elf_readonly_dynrelocs (h)) { /* If we haven't seen a branch reloc and the symbol isn't an ifunc then we don't need a plt entry. */ @@ -6300,7 +6516,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, h->root.u.def.value = def->root.u.def.value; if (def->root.u.def.section == htab->elf.sdynbss || def->root.u.def.section == htab->elf.sdynrelro) - ((struct ppc_link_hash_entry *) h)->dyn_relocs = NULL; + h->dyn_relocs = NULL; return TRUE; } @@ -6308,7 +6524,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 @@ -6324,7 +6540,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 @@ -6333,13 +6551,23 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, || h->protected_def) return TRUE; - if (h->plt.plist != NULL) + if (h->type == STT_FUNC + || h->type == STT_GNU_IFUNC) { - /* We should never get here, but unfortunately there are versions - of gcc out there that improperly (for this ABI) put initialized - function pointers, vtable refs and suchlike in read-only - sections. Allow them to proceed, but warn that this might - break at runtime. */ + /* .dynbss copies of function symbols only work if we have + ELFv1 dot-symbols. ELFv1 compilers since 2004 default to not + use dot-symbols and set the function symbol size to the text + size of the function rather than the size of the descriptor. + That's wrong for copying a descriptor. */ + if (ppc_elf_hash_entry (h)->oh == NULL + || !(h->size == 24 || h->size == 16)) + return TRUE; + + /* We should never get here, but unfortunately there are old + versions of gcc (circa gcc-3.2) that improperly for the + ELFv1 ABI put initialized function pointers, vtable refs and + suchlike in read-only sections. Allow them to proceed, but + warn that this might break at runtime. */ info->callbacks->einfo (_("%P: copy reloc against `%pT' requires lazy plt linking; " "avoid setting LD_BIND_NOW=1 or upgrade gcc\n"), @@ -6378,7 +6606,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, } /* We no longer want dyn_relocs. */ - ((struct ppc_link_hash_entry *) h)->dyn_relocs = NULL; + h->dyn_relocs = NULL; return _bfd_elf_adjust_dynamic_copy (info, h, s); } @@ -6395,7 +6623,7 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info, if (ppc_hash_table (info) == NULL) return; - eh = (struct ppc_link_hash_entry *) h; + eh = ppc_elf_hash_entry (h); if (eh->is_func_descriptor) { struct ppc_link_hash_entry *fh = eh->oh; @@ -6418,8 +6646,8 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info, p = eh->elf.root.root.string - 1; save = *p; *(char *) p = '.'; - fh = (struct ppc_link_hash_entry *) - elf_link_hash_lookup (htab, p, FALSE, FALSE, FALSE); + fh = ppc_elf_hash_entry (elf_link_hash_lookup (htab, p, FALSE, + FALSE, FALSE)); *(char *) p = save; /* Unfortunately, if it so happens that the string we were @@ -6432,8 +6660,8 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info, while (q >= eh->elf.root.root.string && *q == *p) --q, --p; if (q < eh->elf.root.root.string && *p == '.') - fh = (struct ppc_link_hash_entry *) - elf_link_hash_lookup (htab, p, FALSE, FALSE, FALSE); + fh = ppc_elf_hash_entry (elf_link_hash_lookup (htab, p, FALSE, + FALSE, FALSE)); } if (fh != NULL) { @@ -6481,12 +6709,7 @@ get_sym_h (struct elf_link_hash_entry **hp, } if (tls_maskp != NULL) - { - struct ppc_link_hash_entry *eh; - - eh = (struct ppc_link_hash_entry *) h; - *tls_maskp = &eh->tls_mask; - } + *tls_maskp = &ppc_elf_hash_entry (h)->tls_mask; } else { @@ -6657,7 +6880,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED) && h->root.type != bfd_link_hash_defweak) return TRUE; - eh = (struct ppc_link_hash_entry *) h; + eh = ppc_elf_hash_entry (h); if (eh->adjust_done) return TRUE; @@ -6712,6 +6935,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: @@ -6725,6 +6958,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: @@ -6774,17 +7008,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; @@ -6793,7 +7028,7 @@ dec_dynrel_count (bfd_vma r_info, { struct elf_dyn_relocs *p; struct elf_dyn_relocs **pp; - pp = &((struct ppc_link_hash_entry *) h)->dyn_relocs; + pp = &h->dyn_relocs; /* elf_gc_sweep may have already removed all dyn relocs associated with local syms for a given section. Also, symbol flags are @@ -7046,11 +7281,9 @@ ppc64_elf_edit_opd (struct bfd_link_info *info) bfd_byte *loc; if (!bfd_malloc_and_get_section (ibfd, sec, &loc)) { - if (loc != NULL) - free (loc); + free (loc); error_ret: - if (local_syms != NULL - && symtab_hdr->contents != (unsigned char *) local_syms) + if (symtab_hdr->contents != (unsigned char *) local_syms) free (local_syms); if (elf_section_data (sec)->relocs != relstart) free (relstart); @@ -7110,7 +7343,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info) if (h != NULL && h->root.root.string[0] == '.') { - fdh = ((struct ppc_link_hash_entry *) h)->oh; + fdh = ppc_elf_hash_entry (h)->oh; if (fdh != NULL) { fdh = ppc_follow_link (fdh); @@ -7356,7 +7589,7 @@ ppc64_elf_inline_plt (struct bfd_link_info *info) return FALSE; relend = relstart + sec->reloc_count; - for (rel = relstart; rel < relend; ) + for (rel = relstart; rel < relend; rel++) { enum elf_ppc64_reloc_type r_type; unsigned long r_symndx; @@ -7376,8 +7609,7 @@ ppc64_elf_inline_plt (struct bfd_link_info *info) { if (elf_section_data (sec)->relocs != relstart) free (relstart); - if (local_syms != NULL - && symtab_hdr->contents != (bfd_byte *) local_syms) + if (symtab_hdr->contents != (bfd_byte *) local_syms) free (local_syms); return FALSE; } @@ -7399,7 +7631,7 @@ ppc64_elf_inline_plt (struct bfd_link_info *info) && !(r_type == R_PPC64_PLTCALL_NOTOC && (((h ? h->other : sym->st_other) & STO_PPC64_LOCAL_MASK) - != 1 << STO_PPC64_LOCAL_BIT))) + > 1 << STO_PPC64_LOCAL_BIT))) *tls_maskp &= ~PLT_KEEP; } } @@ -7426,6 +7658,7 @@ asection * ppc64_elf_tls_setup (struct bfd_link_info *info) { struct ppc_link_hash_table *htab; + struct elf_link_hash_entry *tga, *tga_fd, *desc, *desc_fd; htab = ppc_hash_table (info); if (htab == NULL) @@ -7462,18 +7695,29 @@ ppc64_elf_tls_setup (struct bfd_link_info *info) (_("warning: --plt-localentry is especially dangerous without " "ld.so support to detect ABI violations")); - htab->tls_get_addr = ((struct ppc_link_hash_entry *) - elf_link_hash_lookup (&htab->elf, ".__tls_get_addr", - FALSE, FALSE, TRUE)); + tga = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr", + FALSE, FALSE, TRUE); + htab->tls_get_addr = ppc_elf_hash_entry (tga); + /* Move dynamic linking info to the function descriptor sym. */ - if (htab->tls_get_addr != NULL) - func_desc_adjust (&htab->tls_get_addr->elf, info); - htab->tls_get_addr_fd = ((struct ppc_link_hash_entry *) - elf_link_hash_lookup (&htab->elf, "__tls_get_addr", - FALSE, FALSE, TRUE)); + if (tga != NULL) + func_desc_adjust (tga, info); + tga_fd = elf_link_hash_lookup (&htab->elf, "__tls_get_addr", + FALSE, FALSE, TRUE); + htab->tls_get_addr_fd = ppc_elf_hash_entry (tga_fd); + + desc = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr_desc", + FALSE, FALSE, TRUE); + htab->tga_desc = ppc_elf_hash_entry (desc); + if (desc != NULL) + func_desc_adjust (desc, info); + desc_fd = elf_link_hash_lookup (&htab->elf, "__tls_get_addr_desc", + FALSE, FALSE, TRUE); + htab->tga_desc_fd = ppc_elf_hash_entry (desc_fd); + if (htab->params->tls_get_addr_opt) { - struct elf_link_hash_entry *opt, *opt_fd, *tga, *tga_fd; + struct elf_link_hash_entry *opt, *opt_fd; opt = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr_opt", FALSE, FALSE, TRUE); @@ -7489,24 +7733,49 @@ ppc64_elf_tls_setup (struct bfd_link_info *info) signalled by the presence of __tls_get_addr_opt, and we'll be calling __tls_get_addr via a plt call stub, then make __tls_get_addr point to __tls_get_addr_opt. */ - tga_fd = &htab->tls_get_addr_fd->elf; - if (htab->elf.dynamic_sections_created - && tga_fd != NULL - && (tga_fd->type == STT_FUNC - || tga_fd->needs_plt) - && !(SYMBOL_CALLS_LOCAL (info, tga_fd) - || UNDEFWEAK_NO_DYNAMIC_RELOC (info, tga_fd))) + if (!(htab->elf.dynamic_sections_created + && tga_fd != NULL + && (tga_fd->type == STT_FUNC + || tga_fd->needs_plt) + && !(SYMBOL_CALLS_LOCAL (info, tga_fd) + || UNDEFWEAK_NO_DYNAMIC_RELOC (info, tga_fd)))) + tga_fd = NULL; + if (!(htab->elf.dynamic_sections_created + && desc_fd != NULL + && (desc_fd->type == STT_FUNC + || desc_fd->needs_plt) + && !(SYMBOL_CALLS_LOCAL (info, desc_fd) + || UNDEFWEAK_NO_DYNAMIC_RELOC (info, desc_fd)))) + desc_fd = NULL; + + if (tga_fd != NULL || desc_fd != NULL) { - struct plt_entry *ent; + struct plt_entry *ent = NULL; - for (ent = tga_fd->plt.plist; ent != NULL; ent = ent->next) - if (ent->plt.refcount > 0) - break; + if (tga_fd != NULL) + for (ent = tga_fd->plt.plist; ent != NULL; ent = ent->next) + if (ent->plt.refcount > 0) + break; + if (ent == NULL && desc_fd != NULL) + for (ent = desc_fd->plt.plist; ent != NULL; ent = ent->next) + if (ent->plt.refcount > 0) + break; if (ent != NULL) { - tga_fd->root.type = bfd_link_hash_indirect; - tga_fd->root.u.i.link = &opt_fd->root; - ppc64_elf_copy_indirect_symbol (info, opt_fd, tga_fd); + if (tga_fd != NULL) + { + tga_fd->root.type = bfd_link_hash_indirect; + tga_fd->root.u.i.link = &opt_fd->root; + tga_fd->root.u.i.warning = NULL; + ppc64_elf_copy_indirect_symbol (info, opt_fd, tga_fd); + } + if (desc_fd != NULL) + { + desc_fd->root.type = bfd_link_hash_indirect; + desc_fd->root.u.i.link = &opt_fd->root; + desc_fd->root.u.i.warning = NULL; + ppc64_elf_copy_indirect_symbol (info, opt_fd, desc_fd); + } opt_fd->mark = 1; if (opt_fd->dynindx != -1) { @@ -7517,25 +7786,50 @@ ppc64_elf_tls_setup (struct bfd_link_info *info) if (!bfd_elf_link_record_dynamic_symbol (info, opt_fd)) return NULL; } - htab->tls_get_addr_fd - = (struct ppc_link_hash_entry *) opt_fd; - tga = &htab->tls_get_addr->elf; - if (opt != NULL && tga != NULL) + if (tga_fd != NULL) { - tga->root.type = bfd_link_hash_indirect; - tga->root.u.i.link = &opt->root; - ppc64_elf_copy_indirect_symbol (info, opt, tga); - opt->mark = 1; - _bfd_elf_link_hash_hide_symbol (info, opt, - tga->forced_local); - htab->tls_get_addr = (struct ppc_link_hash_entry *) opt; + htab->tls_get_addr_fd = ppc_elf_hash_entry (opt_fd); + tga = &htab->tls_get_addr->elf; + if (opt != NULL && tga != NULL) + { + tga->root.type = bfd_link_hash_indirect; + tga->root.u.i.link = &opt->root; + tga->root.u.i.warning = NULL; + ppc64_elf_copy_indirect_symbol (info, opt, tga); + opt->mark = 1; + _bfd_elf_link_hash_hide_symbol (info, opt, + tga->forced_local); + htab->tls_get_addr = ppc_elf_hash_entry (opt); + } + htab->tls_get_addr_fd->oh = htab->tls_get_addr; + htab->tls_get_addr_fd->is_func_descriptor = 1; + if (htab->tls_get_addr != NULL) + { + htab->tls_get_addr->oh = htab->tls_get_addr_fd; + htab->tls_get_addr->is_func = 1; + } } - htab->tls_get_addr_fd->oh = htab->tls_get_addr; - htab->tls_get_addr_fd->is_func_descriptor = 1; - if (htab->tls_get_addr != NULL) + if (desc_fd != NULL) { - htab->tls_get_addr->oh = htab->tls_get_addr_fd; - htab->tls_get_addr->is_func = 1; + htab->tga_desc_fd = ppc_elf_hash_entry (opt_fd); + if (opt != NULL && desc != NULL) + { + desc->root.type = bfd_link_hash_indirect; + desc->root.u.i.link = &opt->root; + desc->root.u.i.warning = NULL; + ppc64_elf_copy_indirect_symbol (info, opt, desc); + opt->mark = 1; + _bfd_elf_link_hash_hide_symbol (info, opt, + desc->forced_local); + htab->tga_desc = ppc_elf_hash_entry (opt); + } + htab->tga_desc_fd->oh = htab->tga_desc; + htab->tga_desc_fd->is_func_descriptor = 1; + if (htab->tga_desc != NULL) + { + htab->tga_desc->oh = htab->tga_desc_fd; + htab->tga_desc->is_func = 1; + } } } } @@ -7543,17 +7837,25 @@ ppc64_elf_tls_setup (struct bfd_link_info *info) else if (htab->params->tls_get_addr_opt < 0) htab->params->tls_get_addr_opt = 0; } + + if (htab->tga_desc_fd != NULL + && htab->params->tls_get_addr_opt + && htab->params->no_tls_get_addr_regsave == -1) + htab->params->no_tls_get_addr_regsave = 0; + return _bfd_elf_tls_setup (info->output_bfd, info); } /* Return TRUE iff REL is a branch reloc with a global symbol matching - HASH1 or HASH2. */ + any of HASH1, HASH2, HASH3, or HASH4. */ static bfd_boolean branch_reloc_hash_match (const bfd *ibfd, const Elf_Internal_Rela *rel, const struct ppc_link_hash_entry *hash1, - const struct ppc_link_hash_entry *hash2) + const struct ppc_link_hash_entry *hash2, + const struct ppc_link_hash_entry *hash3, + const struct ppc_link_hash_entry *hash4) { Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (ibfd); enum elf_ppc64_reloc_type r_type = ELF64_R_TYPE (rel->r_info); @@ -7566,7 +7868,8 @@ branch_reloc_hash_match (const bfd *ibfd, h = sym_hashes[r_symndx - symtab_hdr->sh_info]; h = elf_follow_link (h); - if (h == &hash1->elf || h == &hash2->elf) + if (h == &hash1->elf || h == &hash2->elf + || h == &hash3->elf || h == &hash4->elf) return TRUE; } return FALSE; @@ -7633,7 +7936,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; @@ -7647,11 +7950,9 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) err_free_rel: if (elf_section_data (sec)->relocs != relstart) free (relstart); - if (toc_ref != NULL) - free (toc_ref); - if (locsyms != NULL - && (elf_symtab_hdr (ibfd).contents - != (unsigned char *) locsyms)) + free (toc_ref); + if (elf_symtab_hdr (ibfd).contents + != (unsigned char *) locsyms) free (locsyms); return ret; } @@ -7675,11 +7976,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; @@ -7688,9 +7987,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; } } @@ -7702,10 +8006,9 @@ 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) + && is_tls_get_addr (h, htab) && !found_tls_get_addr_arg && is_branch_reloc (r_type)) { @@ -7721,6 +8024,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. */ @@ -7741,6 +8045,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. */ @@ -7752,11 +8057,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: @@ -7771,8 +8077,11 @@ 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))) { @@ -7879,7 +8188,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 @@ -7900,13 +8209,15 @@ 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 && branch_reloc_hash_match (ibfd, rel + 1, + htab->tls_get_addr_fd, + htab->tga_desc_fd, htab->tls_get_addr, - htab->tls_get_addr_fd)) + htab->tga_desc)) { if (expecting_tls_get_addr == 2) { @@ -7952,24 +8263,38 @@ 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; - if (htab->tls_get_addr != NULL) + if (htab->tls_get_addr_fd != NULL) + for (ent = htab->tls_get_addr_fd->elf.plt.plist; + ent != NULL; + ent = ent->next) + if (ent->addend == 0) + break; + + if (ent == NULL && htab->tga_desc_fd != NULL) + for (ent = htab->tga_desc_fd->elf.plt.plist; + ent != NULL; + ent = ent->next) + if (ent->addend == 0) + break; + + if (ent == NULL && htab->tls_get_addr != NULL) for (ent = htab->tls_get_addr->elf.plt.plist; ent != NULL; ent = ent->next) if (ent->addend == 0) break; - if (ent == NULL && htab->tls_get_addr_fd != NULL) - for (ent = htab->tls_get_addr_fd->elf.plt.plist; + if (ent == NULL && htab->tga_desc != NULL) + for (ent = htab->tga_desc->elf.plt.plist; ent != NULL; ent = ent->next) if (ent->addend == 0) @@ -8024,7 +8349,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) } } - *tls_mask |= tls_set; + *tls_mask |= tls_set & 0xff; *tls_mask &= ~tls_clear; } @@ -8042,8 +8367,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) } } - if (toc_ref != NULL) - free (toc_ref); + free (toc_ref); htab->do_tls_opt = 1; return TRUE; } @@ -8074,7 +8398,7 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf) && h->root.type != bfd_link_hash_defweak) return TRUE; - eh = (struct ppc_link_hash_entry *) h; + eh = ppc_elf_hash_entry (h); if (eh->adjust_done) return TRUE; @@ -8110,61 +8434,81 @@ 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,0(ra) + load/store rt,off(ra) or - paddi ra,symbol@pcrel - load/store rt,0(ra) + pla ra,symbol@pcrel + load/store rt,off(ra) may be translated to - pload/pstore rt,symbol@pcrel + pload/pstore rt,symbol+off@pcrel nop. This function returns true if the optimization is possible, placing - the prefix insn in *PINSN1 and a NOP in *PINSN2. + 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 paddi: *PINSN1 is that paddi insn, + 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) +xlate_pcrel_opt (uint64_t *pinsn1, uint64_t *pinsn2, bfd_signed_vma *poff) { - uint32_t insn2 = *pinsn2 >> 32; - uint64_t i1new; + 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) != ((*pinsn1 >> 21) & 31)) + if (((insn2 >> 16) & 31) != ((insn1 >> 21) & 31)) return FALSE; switch ((insn2 >> 26) & 63) @@ -8184,27 +8528,28 @@ xlate_pcrel_opt (uint64_t *pinsn1, uint64_t *pinsn2) case 52: /* stfs */ case 54: /* stfd */ /* These are the PMLS cases, where we just need to tack a prefix - on the insn. Check that the D field is zero. */ - if ((insn2 & 0xffff) != 0) - return FALSE; - i1new = ((1ULL << 58) | (2ULL << 56) | (1ULL << 52) + 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 & 0xfffd) != 0) + if ((insn2 & 1) != 0) return FALSE; - i1new = ((1ULL << 58) | (1ULL << 52) + insn1 = ((1ULL << 58) | (1ULL << 52) | (insn2 & 2 ? 41ULL << 26 : 57ULL << 26) | (insn2 & (31ULL << 21))); + off = insn2 & 0xfffc; break; case 57: /* lxsd, lxssp */ - if ((insn2 & 0xfffc) != 0 || (insn2 & 3) < 2) + if ((insn2 & 3) < 2) return FALSE; - i1new = ((1ULL << 58) | (1ULL << 52) + insn1 = ((1ULL << 58) | (1ULL << 52) | ((40ULL | (insn2 & 3)) << 26) | (insn2 & (31ULL << 21))); + off = insn2 & 0xfffc; break; case 61: /* stxsd, stxssp, lxv, stxv */ @@ -8212,40 +8557,48 @@ xlate_pcrel_opt (uint64_t *pinsn1, uint64_t *pinsn2) return FALSE; else if ((insn2 & 3) >= 2) { - if ((insn2 & 0xfffc) != 0) - return FALSE; - i1new = ((1ULL << 58) | (1ULL << 52) + insn1 = ((1ULL << 58) | (1ULL << 52) | ((44ULL | (insn2 & 3)) << 26) | (insn2 & (31ULL << 21))); + off = insn2 & 0xfffc; } else { - if ((insn2 & 0xfff0) != 0) - return FALSE; - i1new = ((1ULL << 58) | (1ULL << 52) + insn1 = ((1ULL << 58) | (1ULL << 52) | ((50ULL | (insn2 & 4) | ((insn2 & 8) >> 3)) << 26) | (insn2 & (31ULL << 21))); + off = insn2 & 0xfff0; } break; case 56: /* lq */ - if ((insn2 & 0xffff) != 0) - return FALSE; - i1new = ((1ULL << 58) | (1ULL << 52) + insn1 = ((1ULL << 58) | (1ULL << 52) | (insn2 & ((63ULL << 26) | (31ULL << 21)))); + off = insn2 & 0xffff; + break; + + case 6: /* lxvp, stxvp */ + if ((insn2 & 0xe) != 0) + return FALSE; + insn1 = ((1ULL << 58) | (1ULL << 52) + | ((insn2 & 1) == 0 ? 58ULL << 26 : 62ULL << 26) + | (insn2 & (31ULL << 21))); + off = insn2 & 0xfff0; break; case 62: /* std, stq */ - if ((insn2 & 0xfffd) != 0) + if ((insn2 & 1) != 0) return FALSE; - i1new = ((1ULL << 58) | (1ULL << 52) + insn1 = ((1ULL << 58) | (1ULL << 52) | ((insn2 & 2) == 0 ? 61ULL << 26 : 60ULL << 26) | (insn2 & (31ULL << 21))); + off = insn2 & 0xfffc; break; } - *pinsn1 = i1new; + *pinsn1 = insn1; *pinsn2 = (uint64_t) NOP << 32; + *poff = (off ^ 0x8000) - 0x8000; return TRUE; } @@ -8454,18 +8807,14 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) if (used == NULL) { error_ret: - if (local_syms != NULL - && symtab_hdr->contents != (unsigned char *) local_syms) + if (symtab_hdr->contents != (unsigned char *) local_syms) free (local_syms); if (sec != NULL - && relstart != NULL && elf_section_data (sec)->relocs != relstart) free (relstart); - if (toc_relocs != NULL - && elf_section_data (toc)->relocs != toc_relocs) + if (elf_section_data (toc)->relocs != toc_relocs) free (toc_relocs); - if (skip != NULL) - free (skip); + free (skip); return FALSE; } @@ -8508,65 +8857,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: @@ -8889,8 +9181,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) rel_hdr->sh_size = toc->reloc_count * sz; } } - else if (toc_relocs != NULL - && elf_section_data (toc)->relocs != toc_relocs) + else if (elf_section_data (toc)->relocs != toc_relocs) free (toc_relocs); if (local_syms != NULL @@ -8918,11 +9209,13 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) if (!is_ppc64_elf (ibfd)) continue; - if (!ppc64_elf_tdata (ibfd)->has_gotrel) + if (!ppc64_elf_tdata (ibfd)->has_optrel) continue; sec = ppc64_elf_tdata (ibfd)->got; - got = sec->output_section->vma + sec->output_offset + 0x8000; + got = 0; + if (sec != NULL) + got = sec->output_section->vma + sec->output_offset + 0x8000; local_syms = NULL; symtab_hdr = &elf_symtab_hdr (ibfd); @@ -8930,7 +9223,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) for (sec = ibfd->sections; sec != NULL; sec = sec->next) { if (sec->reloc_count == 0 - || !ppc64_elf_section_data (sec)->has_gotrel + || !ppc64_elf_section_data (sec)->has_optrel || discarded_section (sec)) continue; @@ -8939,11 +9232,9 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) if (relstart == NULL) { got_error_ret: - if (local_syms != NULL - && symtab_hdr->contents != (unsigned char *) local_syms) + if (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; @@ -8957,24 +9248,82 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) asection *sym_sec; struct elf_link_hash_entry *h; struct got_entry *ent; - bfd_vma sym_addend, val, pc; + 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: - continue; + insn_check = no_check; + break; - case R_PPC64_GOT16_DS: + 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: - sym_addend = rel->r_addend; + 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: - sym_addend = 0; break; } @@ -8983,6 +9332,14 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) r_symndx, ibfd)) goto got_error_ret; + if (sym_sec == NULL + || sym_sec->output_section == NULL + || discarded_section (sym_sec)) + continue; + + if ((h ? h->type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC) + continue; + if (!SYMBOL_REFERENCES_LOCAL (info, h)) continue; @@ -8990,53 +9347,49 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) val = h->root.u.def.value; else val = sym->st_value; - val += sym_addend; + 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_DS: - if (val - got + 0x8000 >= 0x10000) - 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 & (0x3f << 26 | 0x3)) != 58u << 26 /* ld */) - continue; - break; - case R_PPC64_GOT16_HA: - if (val - got + 0x80008000ULL >= 0x100000000ULL) + 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 & ((0x3f << 26) | 0x1f << 16)) + 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 + 0x80008000ULL >= 0x100000000ULL) + 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 & (0x3f << 26 | 0x3)) != 58u << 26 /* ld */) + 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 + (1ULL << 33) >= 1ULL << 34) + 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)) @@ -9045,10 +9398,11 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) if ((insn & (-1u << 18)) != ((1u << 26) | (1u << 20))) continue; insn = bfd_get_32 (ibfd, buf + 4); - if ((insn & (0x3f << 26)) != 57u << 26) + if ((insn & (0x3fu << 26)) != 57u << 26) continue; break; } +#undef LIMIT_ADJUST if (h != NULL) ent = h->got.glist; @@ -9058,7 +9412,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) ent = local_got_ents[r_symndx]; } for (; ent != NULL; ent = ent->next) - if (ent->addend == sym_addend + if (ent->addend == rel->r_addend && ent->owner == ibfd && ent->tls_type == 0) break; @@ -9101,7 +9455,7 @@ allocate_got (struct elf_link_hash_entry *h, struct got_entry *gent) { struct ppc_link_hash_table *htab = ppc_hash_table (info); - struct ppc_link_hash_entry *eh = (struct ppc_link_hash_entry *) h; + struct ppc_link_hash_entry *eh = ppc_elf_hash_entry (h); int entsize = (gent->tls_type & eh->tls_mask & (TLS_GD | TLS_LD) ? 16 : 8); int rentsize = (gent->tls_type & eh->tls_mask & TLS_GD @@ -9117,7 +9471,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 @@ -9189,10 +9543,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) if (htab == NULL) return FALSE; - eh = (struct ppc_link_hash_entry *) h; + eh = ppc_elf_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) @@ -9223,7 +9577,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; @@ -9240,7 +9594,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; @@ -9254,19 +9609,19 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) IFUNCs which are handled even in static executables. */ if (!htab->elf.dynamic_sections_created && h->type != STT_GNU_IFUNC) - eh->dyn_relocs = NULL; + h->dyn_relocs = NULL; /* Discard relocs on undefined symbols that must be local. */ else if (h->root.type == bfd_link_hash_undefined && ELF_ST_VISIBILITY (h->other) != STV_DEFAULT) - eh->dyn_relocs = NULL; + h->dyn_relocs = NULL; /* Also discard relocs on undefined weak syms with non-default visibility, or when dynamic_undefined_weak says so. */ else if (UNDEFWEAK_NO_DYNAMIC_RELOC (info, h)) - eh->dyn_relocs = NULL; + h->dyn_relocs = NULL; - if (eh->dyn_relocs != NULL) + if (h->dyn_relocs != NULL) { struct elf_dyn_relocs *p, **pp; @@ -9275,7 +9630,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 @@ -9287,7 +9641,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) avoid writing weird assembly. */ if (SYMBOL_CALLS_LOCAL (info, h)) { - for (pp = &eh->dyn_relocs; (p = *pp) != NULL; ) + for (pp = &h->dyn_relocs; (p = *pp) != NULL; ) { p->count -= p->pc_count; p->pc_count = 0; @@ -9298,35 +9652,38 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) } } - if (eh->dyn_relocs != NULL) + if (h->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; + h->dyn_relocs = NULL; } else - eh->dyn_relocs = NULL; + h->dyn_relocs = NULL; } /* Finally, allocate space. */ - for (p = eh->dyn_relocs; p != NULL; p = p->next) + for (p = h->dyn_relocs; p != NULL; p = p->next) { asection *sreloc = elf_section_data (p->sec)->sreloc; if (eh->elf.type == STT_GNU_IFUNC) @@ -9347,7 +9704,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) && h->def_regular && !htab->elf.dynamic_sections_created && !htab->can_convert_all_inline_plt - && (((struct ppc_link_hash_entry *) h)->tls_mask + && (ppc_elf_hash_entry (h)->tls_mask & (TLS_TLS | PLT_KEEP)) == PLT_KEEP)) { struct plt_entry *pent; @@ -9427,6 +9784,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, @@ -9502,33 +9862,6 @@ size_global_entry_stubs (struct elf_link_hash_entry *h, void *inf) return TRUE; } -/* Set DF_TEXTREL if we find any dynamic relocs that apply to - read-only sections. */ - -static bfd_boolean -maybe_set_textrel (struct elf_link_hash_entry *h, void *inf) -{ - asection *sec; - - if (h->root.type == bfd_link_hash_indirect) - return TRUE; - - sec = readonly_dynrelocs (h); - if (sec != NULL) - { - struct bfd_link_info *info = (struct bfd_link_info *) inf; - - info->flags |= DF_TEXTREL; - info->callbacks->minfo (_("%pB: dynamic relocation against `%pT'" - " in read-only section `%pA'\n"), - sec->owner, h->root.root.string, sec); - - /* Not an error, just cut short the traversal. */ - return FALSE; - } - return TRUE; -} - /* Set the sizes of the dynamic sections. */ static bfd_boolean @@ -9646,7 +9979,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; @@ -9723,7 +10056,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); @@ -9884,8 +10217,10 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd, } tls_opt = (htab->params->tls_get_addr_opt - && htab->tls_get_addr_fd != NULL - && htab->tls_get_addr_fd->elf.plt.plist != NULL); + && ((htab->tls_get_addr_fd != NULL + && htab->tls_get_addr_fd->elf.plt.plist != NULL) + || (htab->tga_desc_fd != NULL + && htab->tga_desc_fd->elf.plt.plist != NULL))); if (tls_opt || !htab->opd_abi) { if (!add_dynamic_entry (DT_PPC64_OPT, tls_opt ? PPC64_OPT_TLS : 0)) @@ -9902,7 +10237,8 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd, /* If any dynamic relocs apply to a read-only section, then we need a DT_TEXTREL entry. */ if ((info->flags & DF_TEXTREL) == 0) - elf_link_hash_traverse (&htab->elf, maybe_set_textrel, info); + elf_link_hash_traverse (&htab->elf, + _bfd_elf_maybe_set_textrel, info); if ((info->flags & DF_TEXTREL) != 0) { @@ -10208,6 +10544,146 @@ emit_relocs_for_offset (struct bfd_link_info *info, Elf_Internal_Rela *r, return r; } +static bfd_byte * +build_power10_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_power10_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_power10_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_power10_offset (struct bfd_link_info *info, + Elf_Internal_Rela *r, bfd_vma roff, + bfd_vma targ, bfd_vma off, int odd) +{ + if (off - odd + (1ULL << 33) < 1ULL << 34) + roff += odd; + else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32) + { + int d_offset = bfd_big_endian (info->output_bfd) ? 2 : 0; + r->r_offset = roff + d_offset; + r->r_addend = targ + 8 - odd - d_offset; + r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHERA34); + ++r; + roff += 8 - odd; + } + else + { + int d_offset = bfd_big_endian (info->output_bfd) ? 2 : 0; + r->r_offset = roff + d_offset; + r->r_addend = targ + 8 + odd - d_offset; + r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHESTA34); + ++r; + roff += 4; + r->r_offset = roff + d_offset; + r->r_addend = targ + 4 + odd - d_offset; + r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHERA34); + ++r; + roff += 4 + odd; + } + r->r_offset = roff; + r->r_addend = targ; + r->r_info = ELF64_R_INFO (0, R_PPC64_PCREL34); + return r; +} + /* Emit .eh_frame opcode to advance pc by DELTA. */ static bfd_byte * @@ -10285,7 +10761,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->power10_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_power10_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; @@ -10311,13 +10797,21 @@ plt_stub_size (struct ppc_link_hash_table *htab, size += 4; } if (stub_entry->h != NULL - && (stub_entry->h == htab->tls_get_addr_fd - || stub_entry->h == htab->tls_get_addr) + && is_tls_get_addr (&stub_entry->h->elf, htab) && htab->params->tls_get_addr_opt) { - size += 7 * 4; - if (stub_entry->stub_type == ppc_stub_plt_call_r2save) - size += 6 * 4; + if (htab->params->no_tls_get_addr_regsave) + { + size += 7 * 4; + if (stub_entry->stub_type == ppc_stub_plt_call_r2save) + size += 6 * 4; + } + else + { + size += 30 * 4; + if (stub_entry->stub_type == ppc_stub_plt_call_r2save) + size += 4; + } } return size; } @@ -10373,8 +10867,7 @@ build_plt_stub (struct ppc_link_hash_table *htab, if (!ALWAYS_USE_FAKE_DEP && plt_load_toc && plt_thread_safe - && !((stub_entry->h == htab->tls_get_addr_fd - || stub_entry->h == htab->tls_get_addr) + && !(is_tls_get_addr (&stub_entry->h->elf, htab) && htab->params->tls_get_addr_opt)) { bfd_vma pltoff = stub_entry->plt_ent->plt.offset & ~1; @@ -10533,17 +11026,14 @@ build_plt_stub (struct ppc_link_hash_table *htab, /* Build a special .plt call stub for __tls_get_addr. */ -#define LD_R11_0R3 0xe9630000 +#define LD_R0_0R3 0xe8030000 #define LD_R12_0R3 0xe9830000 #define MR_R0_R3 0x7c601b78 -#define CMPDI_R11_0 0x2c2b0000 +#define CMPDI_R0_0 0x2c200000 #define ADD_R3_R12_R13 0x7c6c6a14 #define BEQLR 0x4d820020 #define MR_R3_R0 0x7c030378 -#define STD_R11_0R1 0xf9610000 #define BCTRL 0x4e800421 -#define LD_R11_0R1 0xe9610000 -#define MTLR_R11 0x7d6803a6 static inline bfd_byte * build_tls_get_addr_stub (struct ppc_link_hash_table *htab, @@ -10552,48 +11042,121 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab, { bfd *obfd = htab->params->stub_bfd; bfd_byte *loc = p; + unsigned int i; - bfd_put_32 (obfd, LD_R11_0R3 + 0, p), p += 4; + bfd_put_32 (obfd, LD_R0_0R3 + 0, p), p += 4; bfd_put_32 (obfd, LD_R12_0R3 + 8, p), p += 4; + bfd_put_32 (obfd, CMPDI_R0_0, p), p += 4; bfd_put_32 (obfd, MR_R0_R3, p), p += 4; - bfd_put_32 (obfd, CMPDI_R11_0, p), p += 4; bfd_put_32 (obfd, ADD_R3_R12_R13, p), p += 4; bfd_put_32 (obfd, BEQLR, p), p += 4; bfd_put_32 (obfd, MR_R3_R0, p), p += 4; - if (r != NULL) - r[0].r_offset += 7 * 4; - if (stub_entry->stub_type != ppc_stub_plt_call_r2save) - return build_plt_stub (htab, stub_entry, p, offset, r); + if (htab->params->no_tls_get_addr_regsave) + { + if (r != NULL) + r[0].r_offset += 7 * 4; + if (stub_entry->stub_type != ppc_stub_plt_call_r2save) + return build_plt_stub (htab, stub_entry, p, offset, r); + + bfd_put_32 (obfd, MFLR_R0, p); + p += 4; + bfd_put_32 (obfd, STD_R0_0R1 + STK_LINKER (htab), p); + p += 4; + + if (r != NULL) + r[0].r_offset += 2 * 4; + p = build_plt_stub (htab, stub_entry, p, offset, r); + bfd_put_32 (obfd, BCTRL, p - 4); + + bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p); + p += 4; + bfd_put_32 (obfd, LD_R0_0R1 + STK_LINKER (htab), p); + p += 4; + bfd_put_32 (obfd, MTLR_R0, p); + p += 4; + bfd_put_32 (obfd, BLR, p); + p += 4; + } + else + { + p = tls_get_addr_prologue (obfd, p, htab); - bfd_put_32 (obfd, MFLR_R11, p), p += 4; - bfd_put_32 (obfd, STD_R11_0R1 + STK_LINKER (htab), p), p += 4; + if (r != NULL) + r[0].r_offset += 18 * 4; - if (r != NULL) - r[0].r_offset += 2 * 4; - p = build_plt_stub (htab, stub_entry, p, offset, r); - bfd_put_32 (obfd, BCTRL, p - 4); + p = build_plt_stub (htab, stub_entry, p, offset, r); + bfd_put_32 (obfd, BCTRL, p - 4); - bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p), p += 4; - bfd_put_32 (obfd, LD_R11_0R1 + STK_LINKER (htab), p), p += 4; - bfd_put_32 (obfd, MTLR_R11, p), p += 4; - bfd_put_32 (obfd, BLR, p), p += 4; + if (stub_entry->stub_type == ppc_stub_plt_call_r2save) + { + bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p); + p += 4; + } + + p = tls_get_addr_epilogue (obfd, p, htab); + } if (htab->glink_eh_frame != NULL && htab->glink_eh_frame->size != 0) { bfd_byte *base, *eh; - unsigned int lr_used, delta; base = htab->glink_eh_frame->contents + stub_entry->group->eh_base + 17; eh = base + stub_entry->group->eh_size; - lr_used = stub_entry->stub_offset + (p - 20 - loc); - delta = lr_used - stub_entry->group->lr_restore; - stub_entry->group->lr_restore = lr_used + 16; - eh = eh_advance (htab->elf.dynobj, eh, delta); - *eh++ = DW_CFA_offset_extended_sf; - *eh++ = 65; - *eh++ = -(STK_LINKER (htab) / 8) & 0x7f; - *eh++ = DW_CFA_advance_loc + 4; + if (htab->params->no_tls_get_addr_regsave) + { + unsigned int lr_used, delta; + lr_used = stub_entry->stub_offset + (p - 20 - loc); + delta = lr_used - stub_entry->group->lr_restore; + stub_entry->group->lr_restore = lr_used + 16; + eh = eh_advance (htab->elf.dynobj, eh, delta); + *eh++ = DW_CFA_offset_extended_sf; + *eh++ = 65; + *eh++ = -(STK_LINKER (htab) / 8) & 0x7f; + *eh++ = DW_CFA_advance_loc + 4; + } + else + { + unsigned int cfa_updt, delta; + /* After the bctrl, lr has been modified so we need to emit + .eh_frame info saying the return address is on the stack. In + fact we must put the EH info at or before the call rather + than after it, because the EH info for a call needs to be + specified by that point. + See libgcc/unwind-dw2.c execute_cfa_program. + Any stack pointer update must be described immediately after + the instruction making the change, and since the stdu occurs + after saving regs we put all the reg saves and the cfa + change there. */ + cfa_updt = stub_entry->stub_offset + 18 * 4; + delta = cfa_updt - stub_entry->group->lr_restore; + stub_entry->group->lr_restore + = stub_entry->stub_offset + (p - loc) - 4; + eh = eh_advance (htab->elf.dynobj, eh, delta); + *eh++ = DW_CFA_def_cfa_offset; + if (htab->opd_abi) + { + *eh++ = 128; + *eh++ = 1; + } + else + *eh++ = 96; + *eh++ = DW_CFA_offset_extended_sf; + *eh++ = 65; + *eh++ = (-16 / 8) & 0x7f; + for (i = 4; i < 12; i++) + { + *eh++ = DW_CFA_offset + i; + *eh++ = (htab->opd_abi ? 13 : 12) - i; + } + *eh++ = (DW_CFA_advance_loc + + (stub_entry->group->lr_restore - 8 - cfa_updt) / 4); + *eh++ = DW_CFA_def_cfa_offset; + *eh++ = 0; + for (i = 4; i < 12; i++) + *eh++ = DW_CFA_restore + i; + *eh++ = DW_CFA_advance_loc + 2; + } *eh++ = DW_CFA_restore_extended; *eh++ = 65; stub_entry->group->eh_size = eh - base; @@ -10670,9 +11233,7 @@ use_global_in_relocs (struct ppc_link_hash_table *htab, h = ppc_follow_link (h->oh); BFD_ASSERT (h->elf.root.type == bfd_link_hash_defined || h->elf.root.type == bfd_link_hash_defweak); - symval = (h->elf.root.u.def.value - + h->elf.root.u.def.section->output_offset - + h->elf.root.u.def.section->output_section->vma); + symval = defined_sym_val (&h->elf); while (num_rel-- != 0) { r->r_info = ELF64_R_INFO (symndx, ELF64_R_TYPE (r->r_info)); @@ -10738,11 +11299,31 @@ 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; info = in_arg; + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (stub_entry->target_section != NULL + && stub_entry->target_section->output_section == NULL + && info->non_contiguous_regions) + info->callbacks->einfo (_("%F%P: Could not assign '%pA' to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + stub_entry->target_section); + + /* Same for the group. */ + if (stub_entry->group->stub_sec != NULL + && stub_entry->group->stub_sec->output_section == NULL + && info->non_contiguous_regions) + info->callbacks->einfo (_("%F%P: Could not assign group %pA target %pA to an " + "output section. Retry without " + "--enable-non-contiguous-regions.\n"), + stub_entry->group->stub_sec, + stub_entry->target_section); + htab = ppc_hash_table (info); if (htab == NULL) return FALSE; @@ -11016,18 +11597,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->power10_stubs) + { + bfd_boolean load = stub_entry->stub_type >= ppc_stub_plt_call_notoc; + p = build_power10_offset (htab->params->stub_bfd, p, off, odd, load); + } + else + { + /* The notoc stubs calculate their target (either a PLT entry or + the global entry point of a function) relative to the PC + returned by the "bcl" two instructions past the start of the + sequence emitted by build_offset. The offset is therefore 8 + less than calculated from the start of the sequence. */ + off -= 8; + p = build_offset (htab->params->stub_bfd, p, off, + stub_entry->stub_type >= ppc_stub_plt_call_notoc); + } + if (stub_entry->stub_type <= ppc_stub_long_branch_both) { bfd_vma from; @@ -11049,13 +11640,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->power10_stubs) + num_rel += num_relocs_for_power10_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->power10_stubs) + r = emit_relocs_for_power10_offset (info, r, roff, targ, off, odd); + else + r = emit_relocs_for_offset (info, r, roff, targ, off); if (stub_entry->stub_type == ppc_stub_long_branch_notoc || stub_entry->stub_type == ppc_stub_long_branch_both) { @@ -11070,8 +11669,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->power10_stubs + && htab->glink_eh_frame != NULL + && htab->glink_eh_frame->size != 0) { bfd_byte *base, *eh; unsigned int lr_used, delta; @@ -11164,8 +11764,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) r[0].r_addend = targ; } if (stub_entry->h != NULL - && (stub_entry->h == htab->tls_get_addr_fd - || stub_entry->h == htab->tls_get_addr) + && is_tls_get_addr (&stub_entry->h->elf, htab) && htab->params->tls_get_addr_opt) p = build_tls_get_addr_stub (htab, stub_entry, loc, off, r); else @@ -11240,7 +11839,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; @@ -11250,6 +11849,25 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (htab == NULL) return FALSE; + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (stub_entry->target_section != NULL + && stub_entry->target_section->output_section == NULL + && info->non_contiguous_regions) + info->callbacks->einfo (_("%F%P: Could not assign %pA to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + stub_entry->target_section); + + /* Same for the group. */ + if (stub_entry->group->stub_sec != NULL + && stub_entry->group->stub_sec->output_section == NULL + && info->non_contiguous_regions) + info->callbacks->einfo (_("%F%P: Could not assign group %pA target %pA to an " + "output section. Retry without " + "--enable-non-contiguous-regions.\n"), + stub_entry->group->stub_sec, + stub_entry->target_section); + /* Make a note of the offset within the stubs for this entry. */ stub_entry->stub_offset = stub_entry->group->stub_sec->size; @@ -11393,16 +12011,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->power10_stubs) + num_rel = num_relocs_for_power10_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->power10_stubs) + extra = size_power10_offset (off, odd); + else + extra = size_offset (off - 8); /* Include branch insn plus those in the offset sequence. */ size += 4 + extra; /* The branch insn is at the end, or "extra" bytes along. So @@ -11410,17 +12036,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->power10_stubs) + { + /* After the bcl, lr has been modified so we need to emit + .eh_frame info saying the return address is in r12. */ + lr_used = stub_entry->stub_offset + 8; + if (stub_entry->stub_type == ppc_stub_long_branch_both) + lr_used += 4; + /* The eh_frame info will consist of a DW_CFA_advance_loc or + variant, DW_CFA_register, 65, 12, DW_CFA_advance_loc+2, + DW_CFA_restore_extended 65. */ + delta = lr_used - stub_entry->group->lr_restore; + stub_entry->group->eh_size += eh_advance_size (delta) + 6; + stub_entry->group->lr_restore = lr_used + 8; + } /* If the branch can't reach, use a plt_branch. */ if (off + (1 << 25) >= (bfd_vma) (1 << 26)) @@ -11455,6 +12084,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) plt = htab->pltlocal; } targ += plt->output_offset + plt->output_section->vma; + odd = off & 4; off = targ - off; if (htab->params->plt_stub_align != 0) @@ -11468,24 +12098,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->power10_stubs) + num_rel = num_relocs_for_power10_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->power10_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: @@ -11531,24 +12168,27 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) size = plt_stub_size (htab, stub_entry, off); if (stub_entry->h != NULL - && (stub_entry->h == htab->tls_get_addr_fd - || stub_entry->h == htab->tls_get_addr) + && is_tls_get_addr (&stub_entry->h->elf, htab) && htab->params->tls_get_addr_opt && stub_entry->stub_type == ppc_stub_plt_call_r2save) { - /* After the bctrl, lr has been modified so we need to - emit .eh_frame info saying the return address is - on the stack. In fact we put the EH info specifying - that the return address is on the stack *at* the - call rather than after it, because the EH info for a - call needs to be specified by that point. - See libgcc/unwind-dw2.c execute_cfa_program. */ - lr_used = stub_entry->stub_offset + size - 20; - /* The eh_frame info will consist of a DW_CFA_advance_loc - or variant, DW_CFA_offset_externed_sf, 65, -stackoff, - DW_CFA_advance_loc+4, DW_CFA_restore_extended, 65. */ - delta = lr_used - stub_entry->group->lr_restore; - stub_entry->group->eh_size += eh_advance_size (delta) + 6; + if (htab->params->no_tls_get_addr_regsave) + { + lr_used = stub_entry->stub_offset + size - 20; + /* The eh_frame info will consist of a DW_CFA_advance_loc + or variant, DW_CFA_offset_externed_sf, 65, -stackoff, + DW_CFA_advance_loc+4, DW_CFA_restore_extended, 65. */ + delta = lr_used - stub_entry->group->lr_restore; + stub_entry->group->eh_size += eh_advance_size (delta) + 6; + } + else + { + /* Adjustments to r1 need to be described. */ + unsigned int cfa_updt = stub_entry->stub_offset + 18 * 4; + delta = cfa_updt - stub_entry->group->lr_restore; + stub_entry->group->eh_size += eh_advance_size (delta); + stub_entry->group->eh_size += htab->opd_abi ? 36 : 35; + } stub_entry->group->lr_restore = size - 4; } break; @@ -11570,7 +12210,7 @@ int ppc64_elf_setup_section_lists (struct bfd_link_info *info) { unsigned int id; - bfd_size_type amt; + size_t amt; struct ppc_link_hash_table *htab = ppc_hash_table (info); if (htab == NULL) @@ -11829,7 +12469,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; @@ -11855,7 +12495,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); @@ -11980,7 +12620,7 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec) /* Calls to dynamic lib functions go through a plt call stub that uses r2. */ - eh = (struct ppc_link_hash_entry *) h; + eh = ppc_elf_hash_entry (h); if (eh != NULL && (eh->elf.plt.plist != NULL || (eh->oh != NULL @@ -12091,9 +12731,8 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec) } } - if (local_syms != NULL - && (elf_symtab_hdr (isec->owner).contents - != (unsigned char *) local_syms)) + if (elf_symtab_hdr (isec->owner).contents + != (unsigned char *) local_syms) free (local_syms); if (elf_section_data (isec)->relocs != relstart) free (relstart); @@ -12448,6 +13087,40 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) if (!group_sections (info, stub_group_size, stubs_always_before_branch)) return FALSE; + htab->tga_group = NULL; + if (!htab->params->no_tls_get_addr_regsave + && htab->tga_desc_fd != NULL + && (htab->tga_desc_fd->elf.root.type == bfd_link_hash_undefined + || htab->tga_desc_fd->elf.root.type == bfd_link_hash_undefweak) + && htab->tls_get_addr_fd != NULL + && is_static_defined (&htab->tls_get_addr_fd->elf)) + { + asection *sym_sec, *code_sec, *stub_sec; + bfd_vma sym_value; + struct _opd_sec_data *opd; + + sym_sec = htab->tls_get_addr_fd->elf.root.u.def.section; + sym_value = defined_sym_val (&htab->tls_get_addr_fd->elf); + code_sec = sym_sec; + opd = get_opd_info (sym_sec); + if (opd != NULL) + opd_entry_value (sym_sec, sym_value, &code_sec, NULL, FALSE); + htab->tga_group = htab->sec_info[code_sec->id].u.group; + stub_sec = (*htab->params->add_stub_section) (".tga_desc.stub", + htab->tga_group->link_sec); + if (stub_sec == NULL) + return FALSE; + htab->tga_group->stub_sec = stub_sec; + + htab->tga_desc_fd->elf.root.type = bfd_link_hash_defined; + htab->tga_desc_fd->elf.root.u.def.section = stub_sec; + htab->tga_desc_fd->elf.root.u.def.value = 0; + htab->tga_desc_fd->elf.type = STT_FUNC; + htab->tga_desc_fd->elf.def_regular = 1; + htab->tga_desc_fd->elf.non_elf = 0; + _bfd_elf_link_hash_hide_symbol (info, &htab->tga_desc_fd->elf, TRUE); + } + #define STUB_SHRINK_ITER 20 /* Loop until no stubs added. After iteration 20 of this loop we may exit on a stub section shrinking. This is to break out of a @@ -12553,7 +13226,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms, r_indx, input_bfd)) goto error_ret_free_internal; - hash = (struct ppc_link_hash_entry *) h; + hash = ppc_elf_hash_entry (h); ok_dest = FALSE; fdh = NULL; @@ -12659,7 +13332,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) @@ -12689,8 +13362,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) if (stub_type != ppc_stub_plt_call && stub_type != ppc_stub_plt_call_notoc && hash != NULL - && (hash == htab->tls_get_addr - || hash == htab->tls_get_addr_fd) + && is_tls_get_addr (&hash->elf, htab) && section->has_tls_reloc && irela != internal_relocs) { @@ -12700,7 +13372,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) if (!get_tls_mask (&tls_mask, NULL, NULL, &local_syms, irela - 1, input_bfd)) goto error_ret_free_internal; - if ((*tls_mask & TLS_TLS) != 0) + if ((*tls_mask & TLS_TLS) != 0 + && (*tls_mask & (TLS_GD | TLS_LD)) == 0) continue; } @@ -12812,9 +13485,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) if (elf_section_data (section)->relocs == NULL) free (internal_relocs); error_ret_free_local: - if (local_syms != NULL - && (symtab_hdr->contents - != (unsigned char *) local_syms)) + if (symtab_hdr->contents + != (unsigned char *) local_syms) free (local_syms); return FALSE; } @@ -12877,6 +13549,14 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) stub_sec->flags &= ~SEC_RELOC; } } + if (htab->tga_group != NULL) + { + /* See emit_tga_desc and emit_tga_desc_eh_frame. */ + htab->tga_group->eh_size + = 1 + 2 + (htab->opd_abi != 0) + 3 + 8 * 2 + 3 + 8 + 3; + htab->tga_group->lr_restore = 23 * 4; + htab->tga_group->stub_sec->size = 24 * 4; + } if (htab->stub_iteration <= STUB_SHRINK_ITER || htab->brlt->rawsize < htab->brlt->size) @@ -12940,7 +13620,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) || (htab->stub_iteration > STUB_SHRINK_ITER && htab->brlt->rawsize > htab->brlt->size)) && (htab->glink_eh_frame == NULL - || htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size)) + || htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size) + && (htab->tga_group == NULL + || htab->stub_iteration > 1)) break; /* Ask the linker to do its stuff. */ @@ -13036,6 +13718,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) } maybe_strip_output (info, htab->brlt); + if (htab->relbrlt != NULL) + maybe_strip_output (info, htab->relbrlt); if (htab->glink_eh_frame != NULL) maybe_strip_output (info, htab->glink_eh_frame); @@ -13071,9 +13755,7 @@ ppc64_elf_set_toc (struct bfd_link_info *info, bfd *obfd) && (!is_elf_hash_table (htab) || h->def_regular)) { - TOCstart = (h->root.u.def.value - TOC_BASE_OFF - + h->root.u.def.section->output_offset - + h->root.u.def.section->output_section->vma); + TOCstart = defined_sym_val (h) - TOC_BASE_OFF; _bfd_set_gp_value (obfd, TOCstart); return TOCstart; } @@ -13212,10 +13894,7 @@ build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf) else relplt = NULL; } - rela.r_addend = (h->root.u.def.value - + h->root.u.def.section->output_offset - + h->root.u.def.section->output_section->vma - + ent->addend); + rela.r_addend = defined_sym_val (h) + ent->addend; if (relplt == NULL) { @@ -13378,8 +14057,7 @@ write_plt_relocs_for_local_syms (struct bfd_link_info *info) if (!get_sym_h (NULL, &sym, &sym_sec, NULL, &local_syms, lplt - local_plt, ibfd)) { - if (local_syms != NULL - && symtab_hdr->contents != (unsigned char *) local_syms) + if (symtab_hdr->contents != (unsigned char *) local_syms) free (local_syms); return FALSE; } @@ -13451,6 +14129,74 @@ write_plt_relocs_for_local_syms (struct bfd_link_info *info) return TRUE; } +/* Emit the static wrapper function preserving registers around a + __tls_get_addr_opt call. */ + +static bfd_boolean +emit_tga_desc (struct ppc_link_hash_table *htab) +{ + asection *stub_sec = htab->tga_group->stub_sec; + unsigned int cfa_updt = 11 * 4; + bfd_byte *p; + bfd_vma to, from, delta; + + BFD_ASSERT (htab->tga_desc_fd->elf.root.type == bfd_link_hash_defined + && htab->tga_desc_fd->elf.root.u.def.section == stub_sec + && htab->tga_desc_fd->elf.root.u.def.value == 0); + to = defined_sym_val (&htab->tls_get_addr_fd->elf); + from = defined_sym_val (&htab->tga_desc_fd->elf) + cfa_updt; + delta = to - from; + if (delta + (1 << 25) >= 1 << 26) + { + _bfd_error_handler (_("__tls_get_addr call offset overflow")); + htab->stub_error = TRUE; + return FALSE; + } + + p = stub_sec->contents; + p = tls_get_addr_prologue (htab->elf.dynobj, p, htab); + bfd_put_32 (stub_sec->owner, B_DOT | 1 | (delta & 0x3fffffc), p); + p += 4; + p = tls_get_addr_epilogue (htab->elf.dynobj, p, htab); + return stub_sec->size == (bfd_size_type) (p - stub_sec->contents); +} + +/* Emit eh_frame describing the static wrapper function. */ + +static bfd_byte * +emit_tga_desc_eh_frame (struct ppc_link_hash_table *htab, bfd_byte *p) +{ + unsigned int cfa_updt = 11 * 4; + unsigned int i; + + *p++ = DW_CFA_advance_loc + cfa_updt / 4; + *p++ = DW_CFA_def_cfa_offset; + if (htab->opd_abi) + { + *p++ = 128; + *p++ = 1; + } + else + *p++ = 96; + *p++ = DW_CFA_offset_extended_sf; + *p++ = 65; + *p++ = (-16 / 8) & 0x7f; + for (i = 4; i < 12; i++) + { + *p++ = DW_CFA_offset + i; + *p++ = (htab->opd_abi ? 13 : 12) - i; + } + *p++ = DW_CFA_advance_loc + 10; + *p++ = DW_CFA_def_cfa_offset; + *p++ = 0; + for (i = 4; i < 12; i++) + *p++ = DW_CFA_restore + i; + *p++ = DW_CFA_advance_loc + 2; + *p++ = DW_CFA_restore_extended; + *p++ = 65; + return p; +} + /* Build all the stubs associated with the current output file. The stubs are kept in a hash table attached to the main linker hash table. This function is called via gldelf64ppc_finish. */ @@ -13610,6 +14356,24 @@ ppc64_elf_build_stubs (struct bfd_link_info *info, } } + if (htab->tga_group != NULL) + { + htab->tga_group->lr_restore = 23 * 4; + htab->tga_group->stub_sec->size = 24 * 4; + if (!emit_tga_desc (htab)) + return FALSE; + if (htab->glink_eh_frame != NULL + && htab->glink_eh_frame->size != 0) + { + size_t align = 4; + + p = htab->glink_eh_frame->contents; + p += (sizeof (glink_eh_frame_cie) + align - 1) & -align; + p += 17; + htab->tga_group->eh_size = emit_tga_desc_eh_frame (htab, p) - p; + } + } + /* Build .glink global entry stubs, and PLT relocs for globals. */ elf_link_hash_traverse (&htab->elf, build_global_entry_stubs_and_plt, info); @@ -13735,42 +14499,46 @@ ppc64_elf_build_stubs (struct bfd_link_info *info, if (stats != NULL) { - size_t len; - *stats = bfd_malloc (500); - if (*stats == NULL) - return FALSE; - - len = sprintf (*stats, - ngettext ("linker stubs in %u group\n", - "linker stubs in %u groups\n", - stub_sec_count), - stub_sec_count); - sprintf (*stats + len, _(" branch %lu\n" - " branch toc adj %lu\n" - " branch notoc %lu\n" - " branch both %lu\n" - " long branch %lu\n" - " long toc adj %lu\n" - " long notoc %lu\n" - " long both %lu\n" - " plt call %lu\n" - " plt call save %lu\n" - " plt call notoc %lu\n" - " plt call both %lu\n" - " global entry %lu"), - htab->stub_count[ppc_stub_long_branch - 1], - htab->stub_count[ppc_stub_long_branch_r2off - 1], - htab->stub_count[ppc_stub_long_branch_notoc - 1], - htab->stub_count[ppc_stub_long_branch_both - 1], - htab->stub_count[ppc_stub_plt_branch - 1], - htab->stub_count[ppc_stub_plt_branch_r2off - 1], - htab->stub_count[ppc_stub_plt_branch_notoc - 1], - htab->stub_count[ppc_stub_plt_branch_both - 1], - htab->stub_count[ppc_stub_plt_call - 1], - htab->stub_count[ppc_stub_plt_call_r2save - 1], - htab->stub_count[ppc_stub_plt_call_notoc - 1], - htab->stub_count[ppc_stub_plt_call_both - 1], - htab->stub_count[ppc_stub_global_entry - 1]); + char *groupmsg; + if (asprintf (&groupmsg, + ngettext ("linker stubs in %u group\n", + "linker stubs in %u groups\n", + stub_sec_count), + stub_sec_count) < 0) + *stats = NULL; + else + { + if (asprintf (stats, _("%s" + " branch %lu\n" + " branch toc adj %lu\n" + " branch notoc %lu\n" + " branch both %lu\n" + " long branch %lu\n" + " long toc adj %lu\n" + " long notoc %lu\n" + " long both %lu\n" + " plt call %lu\n" + " plt call save %lu\n" + " plt call notoc %lu\n" + " plt call both %lu\n" + " global entry %lu"), + groupmsg, + htab->stub_count[ppc_stub_long_branch - 1], + htab->stub_count[ppc_stub_long_branch_r2off - 1], + htab->stub_count[ppc_stub_long_branch_notoc - 1], + htab->stub_count[ppc_stub_long_branch_both - 1], + htab->stub_count[ppc_stub_plt_branch - 1], + htab->stub_count[ppc_stub_plt_branch_r2off - 1], + htab->stub_count[ppc_stub_plt_branch_notoc - 1], + htab->stub_count[ppc_stub_plt_branch_both - 1], + htab->stub_count[ppc_stub_plt_call - 1], + htab->stub_count[ppc_stub_plt_call_r2save - 1], + htab->stub_count[ppc_stub_plt_call_notoc - 1], + htab->stub_count[ppc_stub_plt_call_both - 1], + htab->stub_count[ppc_stub_global_entry - 1]) < 0) + *stats = NULL; + free (groupmsg); + } } return TRUE; } @@ -13793,6 +14561,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. @@ -13846,6 +14674,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. */ @@ -13995,7 +14824,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, } } } - h = (struct ppc_link_hash_entry *) h_elf; + h = ppc_elf_hash_entry (h_elf); if (sec != NULL && discarded_section (sec)) { @@ -14067,10 +14896,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 @@ -14110,7 +14936,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); @@ -14148,7 +14974,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; @@ -14202,35 +15028,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; @@ -14255,7 +15124,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; @@ -14273,11 +15142,13 @@ 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_fd, + htab->tga_desc_fd, htab->tls_get_addr, - htab->tls_get_addr_fd)) + htab->tga_desc)) offset = rel[1].r_offset; /* We read the low GOT_TLS (or TOC16) insn because we need to keep the destination reg. It may be @@ -14289,15 +15160,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 @@ -14309,20 +15181,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) { @@ -14361,6 +15221,51 @@ 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) @@ -14382,7 +15287,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; @@ -14396,16 +15301,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; @@ -14431,30 +15347,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; @@ -14466,7 +15379,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 { @@ -14691,8 +15604,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, || nop == CROR_313131) { if (h != NULL - && (h == htab->tls_get_addr_fd - || h == htab->tls_get_addr) + && is_tls_get_addr (&h->elf, htab) && htab->params->tls_get_addr_opt) { /* Special stub used, leave nop alone. */ @@ -14818,7 +15730,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; @@ -14857,8 +15769,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, || stub_entry->stub_type == ppc_stub_plt_call_r2save || stub_entry->stub_type == ppc_stub_plt_call_both) && !(h != NULL - && (h == htab->tls_get_addr_fd - || h == htab->tls_get_addr) + && is_tls_get_addr (&h->elf, htab) && htab->params->tls_get_addr_opt) && rel + 1 < relend && rel[1].r_offset == rel->r_offset + 4 @@ -14917,12 +15828,14 @@ ppc64_elf_relocate_section (bfd *output_bfd, break; case R_PPC64_GOT16_DS: + if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC) + break; 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 & (0x3f << 26 | 0x3)) == 58u << 26 /* ld */) + if ((insn & (0x3fu << 26 | 0x3)) == 58u << 26 /* ld */) { insn += (14u << 26) - (58u << 26); bfd_put_32 (input_bfd, insn, contents + (rel->r_offset & ~3)); @@ -14934,19 +15847,21 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_GOT16_LO_DS: case R_PPC64_GOT16_HA: + if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC) + break; 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 & (0x3f << 26 | 0x3)) == 58u << 26 /* ld */) + 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 & (0x3f << 26)) == 15u << 26 /* addis */) + else if ((insn & (0x3fu << 26)) == 15u << 26 /* addis */) { r_type = R_PPC64_TOC16_HA; rel->r_info = ELF64_R_INFO (r_symndx, r_type); @@ -14955,6 +15870,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, break; case R_PPC64_GOT_PCREL34: + if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC) + break; from = (rel->r_offset + input_section->output_section->vma + input_section->output_offset); @@ -15003,18 +15920,29 @@ ppc64_elf_relocate_section (bfd *output_bfd, 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) - break; - if (xlate_pcrel_opt (&pinsn, &pinsn2)) { + 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); } } } @@ -15023,7 +15951,6 @@ ppc64_elf_relocate_section (bfd *output_bfd, break; } - /* Set `addend'. */ tls_type = 0; save_unresolved_reloc = unresolved_reloc; switch (r_type) @@ -15056,6 +15983,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; @@ -15063,6 +15991,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; @@ -15070,6 +15999,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; @@ -15077,6 +16007,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; @@ -15096,14 +16027,9 @@ ppc64_elf_relocate_section (bfd *output_bfd, bfd_vma off; unsigned long indx = 0; struct got_entry *ent; - bfd_vma sym_addend = orig_rel.r_addend; - - if (r_type == R_PPC64_GOT_PCREL34) - sym_addend = 0; 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 { @@ -15133,7 +16059,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, } for (; ent != NULL; ent = ent->next) - if (ent->addend == sym_addend + if (ent->addend == orig_rel.r_addend && ent->owner == input_bfd && ent->tls_type == tls_type) break; @@ -15178,10 +16104,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; @@ -15190,7 +16114,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, outrel.r_offset = (got->output_section->vma + got->output_offset + off); - outrel.r_addend = sym_addend; + outrel.r_addend = orig_rel.r_addend; if (tls_type & (TLS_LD | TLS_GD)) { outrel.r_addend = 0; @@ -15203,7 +16127,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc); outrel.r_offset += 8; - outrel.r_addend = sym_addend; + outrel.r_addend = orig_rel.r_addend; outrel.r_info = ELF64_R_INFO (indx, R_PPC64_DTPREL64); } @@ -15249,7 +16173,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, emitting a reloc. */ else { - relocation += sym_addend; + relocation += orig_rel.r_addend; if (tls_type != 0) { if (htab->elf.tls_sec == NULL) @@ -15280,7 +16204,12 @@ ppc64_elf_relocate_section (bfd *output_bfd, abort (); relocation = got->output_section->vma + got->output_offset + off; - if (r_type != R_PPC64_GOT_PCREL34) + 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; @@ -15313,15 +16242,10 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (plt_list) { struct plt_entry *ent; - bfd_vma sym_addend = orig_rel.r_addend; - - if (r_type == R_PPC64_PLT_PCREL34 - || r_type == R_PPC64_PLT_PCREL34_NOTOC) - sym_addend = 0; for (ent = *plt_list; ent != NULL; ent = ent->next) if (ent->plt.offset != (bfd_vma) -1 - && ent->addend == sym_addend) + && ent->addend == orig_rel.r_addend) { asection *plt; bfd_vma got; @@ -15350,9 +16274,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, + htab->sec_info[input_section->id].toc_off); relocation -= got; } - if (r_type != R_PPC64_PLT_PCREL34 - && r_type != R_PPC64_PLT_PCREL34_NOTOC) - addend = 0; + addend = 0; unresolved_reloc = FALSE; break; } @@ -15384,6 +16306,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. */ @@ -15433,6 +16357,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) @@ -15468,6 +16393,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; @@ -15537,11 +16463,11 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (bfd_link_pic (info) ? ((h == NULL - || h->dyn_relocs != NULL) + || h->elf.dyn_relocs != NULL) && ((h != NULL && pc_dynrelocs (h)) || must_be_dyn_reloc (info, r_type))) : (h != NULL - ? h->dyn_relocs != NULL + ? h->elf.dyn_relocs != NULL : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)) { bfd_boolean skip, relocate; @@ -15651,21 +16577,38 @@ ppc64_elf_relocate_section (bfd *output_bfd, } else { - asection *osec; + asection *osec = sec->output_section; - osec = sec->output_section; - indx = elf_section_data (osec)->dynindx; - - 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 @@ -15700,6 +16643,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 @@ -15867,10 +16823,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 @@ -15887,7 +16843,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 @@ -15996,8 +16952,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); @@ -16046,15 +17002,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) { @@ -16073,6 +17029,12 @@ ppc64_elf_relocate_section (bfd *output_bfd, 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: @@ -16173,8 +17135,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, reloc_name, sym_name, (int) r); ret = FALSE; } - if (more_info != NULL) - free (more_info); + free (more_info); } copy_reloc: if (wrel != rel) @@ -16292,23 +17253,21 @@ 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 - + h->root.u.def.section->output_section->vma - + h->root.u.def.section->output_offset); + rela.r_offset = defined_sym_val (h); rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_COPY); rela.r_addend = 0; if (h->root.u.def.section == htab->elf.sdynrelro)