From 2e684e75ae3939f5f14fb89ca8a300b610d093dd Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Thu, 2 Nov 2017 12:09:34 +1030 Subject: [PATCH] hppa-linux TLS relocs This patch fixes various problems with TLS relocations. 1) Report an error if a symbol has both TLS and normal GOT entries. 2) The GOT entry size calculation was obscure and made use of the fact that a symbol shouldn't have both normal and TLS GOT entries. 3) The second word of a GD GOT entry sometimes omitted a dynamic reloc, which was fine except that doing so makes it impossible for ld.so to differentiate GD and LD entries. Also, a NONE reloc was emitted. 4) Unnecessary relocs were emitted for GOT entries. 5) GOT relocs didn't take note of UNDEFWEAK_NO_DYNAMIC_RELOC. * elf32-hppa.c (enum _tls_type): Move. (struct elf32_hppa_link_hash_entry): Make tls_type a bitfield. (elf32_hppa_check_relocs): Set DF_STATIC_TLS only for shared libraries. Tidy tls_type handling. Set symbol tls_type for GOT_TLS_LDM too. (got_entries_needed, got_relocs_needed): New functions. (allocate_dynrelocs): Use them. (elf32_hppa_size_dynamic_sections): Likewise. (elf32_hppa_relocate_section): Delete bogus FIXME. Formatting. Correct code emitting relocs on GD/IE got entries. Report an error when a symbol has both normal and TLS GOT relocs. --- bfd/ChangeLog | 13 +++ bfd/elf32-hppa.c | 268 ++++++++++++++++++++++++++++++++----------------------- 2 files changed, 170 insertions(+), 111 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 9bdc0f0..6401301 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,18 @@ 2017-11-04 Alan Modra + * elf32-hppa.c (enum _tls_type): Move. + (struct elf32_hppa_link_hash_entry): Make tls_type a bitfield. + (elf32_hppa_check_relocs): Set DF_STATIC_TLS only for shared libraries. + Tidy tls_type handling. Set symbol tls_type for GOT_TLS_LDM too. + (got_entries_needed, got_relocs_needed): New functions. + (allocate_dynrelocs): Use them. + (elf32_hppa_size_dynamic_sections): Likewise. + (elf32_hppa_relocate_section): Delete bogus FIXME. Formatting. + Correct code emitting relocs on GD/IE got entries. Report an + error when a symbol has both normal and TLS GOT relocs. + +2017-11-04 Alan Modra + PR 22394 * elf32-hppa.c (elf32_hppa_check_relocs): Don't create dyn_relocs for plabels when non-pic. diff --git a/bfd/elf32-hppa.c b/bfd/elf32-hppa.c index acc96af..f9629e4 100644 --- a/bfd/elf32-hppa.c +++ b/bfd/elf32-hppa.c @@ -206,6 +206,15 @@ struct elf32_hppa_stub_hash_entry asection *id_sec; }; +enum _tls_type + { + GOT_UNKNOWN = 0, + GOT_NORMAL = 1, + GOT_TLS_GD = 2, + GOT_TLS_LDM = 4, + GOT_TLS_IE = 8 + }; + struct elf32_hppa_link_hash_entry { struct elf_link_hash_entry eh; @@ -233,10 +242,7 @@ struct elf32_hppa_link_hash_entry #endif } *dyn_relocs; - enum - { - GOT_UNKNOWN = 0, GOT_NORMAL = 1, GOT_TLS_GD = 2, GOT_TLS_LDM = 4, GOT_TLS_IE = 8 - } tls_type; + ENUM_BITFIELD (_tls_type) tls_type : 8; /* Set if this symbol is used by a plabel reloc. */ unsigned int plabel:1; @@ -1132,7 +1138,6 @@ elf32_hppa_check_relocs (bfd *abfd, const Elf_Internal_Rela *rela_end; struct elf32_hppa_link_hash_table *htab; asection *sreloc; - int tls_type = GOT_UNKNOWN, old_tls_type = GOT_UNKNOWN; if (bfd_link_relocatable (info)) return TRUE; @@ -1312,7 +1317,7 @@ elf32_hppa_check_relocs (bfd *abfd, case R_PARISC_TLS_IE21L: case R_PARISC_TLS_IE14R: - if (bfd_link_pic (info)) + if (bfd_link_dll (info)) info->flags |= DF_STATIC_TLS; need_entry = NEED_GOT; break; @@ -1324,22 +1329,23 @@ elf32_hppa_check_relocs (bfd *abfd, /* Now carry out our orders. */ if (need_entry & NEED_GOT) { + int tls_type = GOT_NORMAL; + switch (r_type) { default: - tls_type = GOT_NORMAL; break; case R_PARISC_TLS_GD21L: case R_PARISC_TLS_GD14R: - tls_type |= GOT_TLS_GD; + tls_type = GOT_TLS_GD; break; case R_PARISC_TLS_LDM21L: case R_PARISC_TLS_LDM14R: - tls_type |= GOT_TLS_LDM; + tls_type = GOT_TLS_LDM; break; case R_PARISC_TLS_IE21L: case R_PARISC_TLS_IE14R: - tls_type |= GOT_TLS_IE; + tls_type = GOT_TLS_IE; break; } @@ -1351,39 +1357,28 @@ elf32_hppa_check_relocs (bfd *abfd, return FALSE; } - if (r_type == R_PARISC_TLS_LDM21L - || r_type == R_PARISC_TLS_LDM14R) - htab->tls_ldm_got.refcount += 1; - else + if (hh != NULL) { - if (hh != NULL) - { - hh->eh.got.refcount += 1; - old_tls_type = hh->tls_type; - } + if (tls_type == GOT_TLS_LDM) + htab->tls_ldm_got.refcount += 1; else - { - bfd_signed_vma *local_got_refcounts; - - /* This is a global offset table entry for a local symbol. */ - local_got_refcounts = hppa32_elf_local_refcounts (abfd); - if (local_got_refcounts == NULL) - return FALSE; - local_got_refcounts[r_symndx] += 1; - - old_tls_type = hppa_elf_local_got_tls_type (abfd) [r_symndx]; - } - - tls_type |= old_tls_type; + hh->eh.got.refcount += 1; + hh->tls_type |= tls_type; + } + else + { + bfd_signed_vma *local_got_refcounts; - if (old_tls_type != tls_type) - { - if (hh != NULL) - hh->tls_type = tls_type; - else - hppa_elf_local_got_tls_type (abfd) [r_symndx] = tls_type; - } + /* This is a global offset table entry for a local symbol. */ + local_got_refcounts = hppa32_elf_local_refcounts (abfd); + if (local_got_refcounts == NULL) + return FALSE; + if (tls_type == GOT_TLS_LDM) + htab->tls_ldm_got.refcount += 1; + else + local_got_refcounts[r_symndx] += 1; + hppa_elf_local_got_tls_type (abfd) [r_symndx] |= tls_type; } } @@ -1916,6 +1911,39 @@ allocate_plt_static (struct elf_link_hash_entry *eh, void *inf) return TRUE; } +/* Calculate size of GOT entries for symbol given its TLS_TYPE. */ + +static inline unsigned int +got_entries_needed (int tls_type) +{ + unsigned int need = 0; + + if ((tls_type & GOT_NORMAL) != 0) + need += GOT_ENTRY_SIZE; + if ((tls_type & GOT_TLS_GD) != 0) + need += GOT_ENTRY_SIZE * 2; + if ((tls_type & GOT_TLS_IE) != 0) + need += GOT_ENTRY_SIZE; + return need; +} + +/* Calculate size of relocs needed for symbol given its TLS_TYPE and + NEEDed GOT entries. KNOWN says a TPREL offset can be calculated + at link time. */ + +static inline unsigned int +got_relocs_needed (int tls_type, unsigned int need, bfd_boolean known) +{ + /* All the entries we allocated need relocs. + Except IE in executable with a local symbol. We could also omit + the DTPOFF reloc on the second word of a GD entry under the same + condition as that for IE, but ld.so might want to differentiate + LD and GD entries at some stage. */ + if ((tls_type & GOT_TLS_IE) != 0 && known) + need -= GOT_ENTRY_SIZE; + return need * sizeof (Elf32_External_Rela) / GOT_ENTRY_SIZE; +} + /* Allocate space in .plt, .got and associated reloc sections for global syms. */ @@ -1955,28 +1983,25 @@ allocate_dynrelocs (struct elf_link_hash_entry *eh, void *inf) if (eh->got.refcount > 0) { + unsigned int need; + if (!ensure_undef_dynamic (info, eh)) return FALSE; sec = htab->etab.sgot; eh->got.offset = sec->size; - sec->size += GOT_ENTRY_SIZE; - /* R_PARISC_TLS_GD* needs two GOT entries */ - if ((hh->tls_type & (GOT_TLS_GD | GOT_TLS_IE)) == (GOT_TLS_GD | GOT_TLS_IE)) - sec->size += GOT_ENTRY_SIZE * 2; - else if ((hh->tls_type & GOT_TLS_GD) == GOT_TLS_GD) - sec->size += GOT_ENTRY_SIZE; + need = got_entries_needed (hh->tls_type); + sec->size += need; if (htab->etab.dynamic_sections_created && (bfd_link_pic (info) || (eh->dynindx != -1 && !SYMBOL_REFERENCES_LOCAL (info, eh))) && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, eh)) { - htab->etab.srelgot->size += sizeof (Elf32_External_Rela); - if ((hh->tls_type & (GOT_TLS_GD | GOT_TLS_IE)) == (GOT_TLS_GD | GOT_TLS_IE)) - htab->etab.srelgot->size += 2 * sizeof (Elf32_External_Rela); - else if ((hh->tls_type & GOT_TLS_GD) == GOT_TLS_GD) - htab->etab.srelgot->size += sizeof (Elf32_External_Rela); + bfd_boolean tprel_known = (bfd_link_executable (info) + && SYMBOL_REFERENCES_LOCAL (info, eh)); + htab->etab.srelgot->size + += got_relocs_needed (hh->tls_type, need, tprel_known); } } else @@ -2195,20 +2220,17 @@ elf32_hppa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, { if (*local_got > 0) { + unsigned int need; + *local_got = sec->size; - sec->size += GOT_ENTRY_SIZE; - if ((*local_tls_type & (GOT_TLS_GD | GOT_TLS_IE)) == (GOT_TLS_GD | GOT_TLS_IE)) - sec->size += 2 * GOT_ENTRY_SIZE; - else if ((*local_tls_type & GOT_TLS_GD) == GOT_TLS_GD) - sec->size += GOT_ENTRY_SIZE; + need = got_entries_needed (*local_tls_type); + sec->size += need; if (bfd_link_pic (info)) - { - srel->size += sizeof (Elf32_External_Rela); - if ((*local_tls_type & (GOT_TLS_GD | GOT_TLS_IE)) == (GOT_TLS_GD | GOT_TLS_IE)) - srel->size += 2 * sizeof (Elf32_External_Rela); - else if ((*local_tls_type & GOT_TLS_GD) == GOT_TLS_GD) - srel->size += sizeof (Elf32_External_Rela); - } + { + bfd_boolean tprel_known = bfd_link_executable (info); + htab->etab.srelgot->size + += got_relocs_needed (*local_tls_type, need, tprel_known); + } } else *local_got = (bfd_vma) -1; @@ -4025,17 +4047,17 @@ elf32_hppa_relocate_section (bfd *output_bfd, indx = 0; if (hh != NULL) { - bfd_boolean dyn; - dyn = htab->etab.dynamic_sections_created; - - if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, - bfd_link_pic (info), - &hh->eh) - && (!bfd_link_pic (info) - || !SYMBOL_REFERENCES_LOCAL (info, &hh->eh))) - { - indx = hh->eh.dynindx; - } + if (!htab->etab.dynamic_sections_created + || hh->eh.dynindx == -1 + || SYMBOL_REFERENCES_LOCAL (info, &hh->eh) + || UNDEFWEAK_NO_DYNAMIC_RELOC (info, &hh->eh)) + /* This is actually a static link, or it is a + -Bsymbolic link and the symbol is defined + locally, or the symbol was forced to be local + because of a version file. */ + ; + else + indx = hh->eh.dynindx; off = hh->eh.got.offset; tls_type = hh->tls_type; } @@ -4061,44 +4083,41 @@ elf32_hppa_relocate_section (bfd *output_bfd, now, and emit any relocations. If both an IE GOT and a GD GOT are necessary, we emit the GD first. */ - if ((bfd_link_pic (info) || indx != 0) - && (hh == NULL - || ELF_ST_VISIBILITY (hh->eh.other) == STV_DEFAULT - || hh->eh.root.type != bfd_link_hash_undefweak)) + if (indx != 0 + || (bfd_link_pic (info) + && (hh == NULL + || !UNDEFWEAK_NO_DYNAMIC_RELOC (info, &hh->eh)))) { need_relocs = TRUE; loc = htab->etab.srelgot->contents; - /* FIXME (CAO): Should this be reloc_count++ ? */ - loc += htab->etab.srelgot->reloc_count * sizeof (Elf32_External_Rela); + loc += (htab->etab.srelgot->reloc_count + * sizeof (Elf32_External_Rela)); } if (tls_type & GOT_TLS_GD) { if (need_relocs) { - outrel.r_offset = (cur_off - + htab->etab.sgot->output_section->vma - + htab->etab.sgot->output_offset); - outrel.r_info = ELF32_R_INFO (indx,R_PARISC_TLS_DTPMOD32); + outrel.r_offset + = (cur_off + + htab->etab.sgot->output_section->vma + + htab->etab.sgot->output_offset); + outrel.r_info + = ELF32_R_INFO (indx, R_PARISC_TLS_DTPMOD32); outrel.r_addend = 0; - bfd_put_32 (output_bfd, 0, htab->etab.sgot->contents + cur_off); bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); htab->etab.srelgot->reloc_count++; loc += sizeof (Elf32_External_Rela); - - if (indx == 0) - bfd_put_32 (output_bfd, relocation - dtpoff_base (info), - htab->etab.sgot->contents + cur_off + 4); - else - { - bfd_put_32 (output_bfd, 0, - htab->etab.sgot->contents + cur_off + 4); - outrel.r_info = ELF32_R_INFO (indx, R_PARISC_TLS_DTPOFF32); - outrel.r_offset += 4; - bfd_elf32_swap_reloca_out (output_bfd, &outrel,loc); - htab->etab.srelgot->reloc_count++; - loc += sizeof (Elf32_External_Rela); - } + outrel.r_info + = ELF32_R_INFO (indx, R_PARISC_TLS_DTPOFF32); + outrel.r_offset += 4; + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + htab->etab.srelgot->reloc_count++; + loc += sizeof (Elf32_External_Rela); + bfd_put_32 (output_bfd, 0, + htab->etab.sgot->contents + cur_off); + bfd_put_32 (output_bfd, 0, + htab->etab.sgot->contents + cur_off + 4); } else { @@ -4109,28 +4128,28 @@ elf32_hppa_relocate_section (bfd *output_bfd, to module 1, the executable. */ bfd_put_32 (output_bfd, 1, htab->etab.sgot->contents + cur_off); - bfd_put_32 (output_bfd, relocation - dtpoff_base (info), + bfd_put_32 (output_bfd, relocation - dtpoff_base (info), htab->etab.sgot->contents + cur_off + 4); } - - cur_off += 8; } if (tls_type & GOT_TLS_IE) { - if (need_relocs) + if (need_relocs + && !(bfd_link_executable (info) + && SYMBOL_REFERENCES_LOCAL (info, &hh->eh))) { - outrel.r_offset = (cur_off - + htab->etab.sgot->output_section->vma - + htab->etab.sgot->output_offset); - outrel.r_info = ELF32_R_INFO (indx, R_PARISC_TLS_TPREL32); - + outrel.r_offset + = (cur_off + + htab->etab.sgot->output_section->vma + + htab->etab.sgot->output_offset); + outrel.r_info = ELF32_R_INFO (indx, + R_PARISC_TLS_TPREL32); if (indx == 0) outrel.r_addend = relocation - dtpoff_base (info); else outrel.r_addend = 0; - bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); htab->etab.srelgot->reloc_count++; loc += sizeof (Elf32_External_Rela); @@ -4138,7 +4157,6 @@ elf32_hppa_relocate_section (bfd *output_bfd, else bfd_put_32 (output_bfd, tpoff (info, relocation), htab->etab.sgot->contents + cur_off); - cur_off += 4; } @@ -4148,6 +4166,35 @@ elf32_hppa_relocate_section (bfd *output_bfd, local_got_offsets[r_symndx] |= 1; } + if ((tls_type & GOT_NORMAL) != 0 + && (tls_type & (GOT_TLS_GD | GOT_TLS_LDM | GOT_TLS_IE)) != 0) + { + if (hh != NULL) + _bfd_error_handler (_("%s has both normal and TLS relocs"), + hh_name (hh)); + else + { + Elf_Internal_Sym *isym + = bfd_sym_from_r_symndx (&htab->sym_cache, + input_bfd, r_symndx); + if (isym == NULL) + return FALSE; + sym_name + = bfd_elf_string_from_elf_section (input_bfd, + symtab_hdr->sh_link, + isym->st_name); + if (sym_name == NULL) + return FALSE; + if (*sym_name == '\0') + sym_name = bfd_section_name (input_bfd, sym_sec); + _bfd_error_handler + (_("%B:%s has both normal and TLS relocs"), + input_bfd, sym_name); + } + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + if ((tls_type & GOT_TLS_GD) && r_type != R_PARISC_TLS_GD21L && r_type != R_PARISC_TLS_GD14R) @@ -4290,8 +4337,7 @@ elf32_hppa_finish_dynamic_symbol (bfd *output_bfd, } if (eh->got.offset != (bfd_vma) -1 - && (hppa_elf_hash_entry (eh)->tls_type & GOT_TLS_GD) == 0 - && (hppa_elf_hash_entry (eh)->tls_type & GOT_TLS_IE) == 0 + && (hppa_elf_hash_entry (eh)->tls_type & GOT_NORMAL) != 0 && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, eh)) { bfd_boolean is_dyn = (eh->dynindx != -1 -- 2.7.4