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.
This commit is contained in:
Alan Modra
2017-11-02 12:09:34 +10:30
parent 127e8e9f62
commit 2e684e75ae
2 changed files with 170 additions and 111 deletions

View File

@ -1,3 +1,16 @@
2017-11-04 Alan Modra <amodra@gmail.com>
* 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 <amodra@gmail.com> 2017-11-04 Alan Modra <amodra@gmail.com>
PR 22394 PR 22394

View File

@ -206,6 +206,15 @@ struct elf32_hppa_stub_hash_entry
asection *id_sec; 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 elf32_hppa_link_hash_entry
{ {
struct elf_link_hash_entry eh; struct elf_link_hash_entry eh;
@ -233,10 +242,7 @@ struct elf32_hppa_link_hash_entry
#endif #endif
} *dyn_relocs; } *dyn_relocs;
enum ENUM_BITFIELD (_tls_type) tls_type : 8;
{
GOT_UNKNOWN = 0, GOT_NORMAL = 1, GOT_TLS_GD = 2, GOT_TLS_LDM = 4, GOT_TLS_IE = 8
} tls_type;
/* Set if this symbol is used by a plabel reloc. */ /* Set if this symbol is used by a plabel reloc. */
unsigned int plabel:1; unsigned int plabel:1;
@ -1132,7 +1138,6 @@ elf32_hppa_check_relocs (bfd *abfd,
const Elf_Internal_Rela *rela_end; const Elf_Internal_Rela *rela_end;
struct elf32_hppa_link_hash_table *htab; struct elf32_hppa_link_hash_table *htab;
asection *sreloc; asection *sreloc;
int tls_type = GOT_UNKNOWN, old_tls_type = GOT_UNKNOWN;
if (bfd_link_relocatable (info)) if (bfd_link_relocatable (info))
return TRUE; return TRUE;
@ -1312,7 +1317,7 @@ elf32_hppa_check_relocs (bfd *abfd,
case R_PARISC_TLS_IE21L: case R_PARISC_TLS_IE21L:
case R_PARISC_TLS_IE14R: case R_PARISC_TLS_IE14R:
if (bfd_link_pic (info)) if (bfd_link_dll (info))
info->flags |= DF_STATIC_TLS; info->flags |= DF_STATIC_TLS;
need_entry = NEED_GOT; need_entry = NEED_GOT;
break; break;
@ -1324,22 +1329,23 @@ elf32_hppa_check_relocs (bfd *abfd,
/* Now carry out our orders. */ /* Now carry out our orders. */
if (need_entry & NEED_GOT) if (need_entry & NEED_GOT)
{ {
int tls_type = GOT_NORMAL;
switch (r_type) switch (r_type)
{ {
default: default:
tls_type = GOT_NORMAL;
break; break;
case R_PARISC_TLS_GD21L: case R_PARISC_TLS_GD21L:
case R_PARISC_TLS_GD14R: case R_PARISC_TLS_GD14R:
tls_type |= GOT_TLS_GD; tls_type = GOT_TLS_GD;
break; break;
case R_PARISC_TLS_LDM21L: case R_PARISC_TLS_LDM21L:
case R_PARISC_TLS_LDM14R: case R_PARISC_TLS_LDM14R:
tls_type |= GOT_TLS_LDM; tls_type = GOT_TLS_LDM;
break; break;
case R_PARISC_TLS_IE21L: case R_PARISC_TLS_IE21L:
case R_PARISC_TLS_IE14R: case R_PARISC_TLS_IE14R:
tls_type |= GOT_TLS_IE; tls_type = GOT_TLS_IE;
break; break;
} }
@ -1351,39 +1357,28 @@ elf32_hppa_check_relocs (bfd *abfd,
return FALSE; return FALSE;
} }
if (r_type == R_PARISC_TLS_LDM21L if (hh != NULL)
|| r_type == R_PARISC_TLS_LDM14R) {
htab->tls_ldm_got.refcount += 1; if (tls_type == GOT_TLS_LDM)
htab->tls_ldm_got.refcount += 1;
else
hh->eh.got.refcount += 1;
hh->tls_type |= tls_type;
}
else else
{ {
if (hh != NULL) bfd_signed_vma *local_got_refcounts;
{
hh->eh.got.refcount += 1; /* This is a global offset table entry for a local symbol. */
old_tls_type = hh->tls_type; 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 else
{ local_got_refcounts[r_symndx] += 1;
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;
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;
}
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; 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 /* Allocate space in .plt, .got and associated reloc sections for
global syms. */ global syms. */
@ -1955,28 +1983,25 @@ allocate_dynrelocs (struct elf_link_hash_entry *eh, void *inf)
if (eh->got.refcount > 0) if (eh->got.refcount > 0)
{ {
unsigned int need;
if (!ensure_undef_dynamic (info, eh)) if (!ensure_undef_dynamic (info, eh))
return FALSE; return FALSE;
sec = htab->etab.sgot; sec = htab->etab.sgot;
eh->got.offset = sec->size; eh->got.offset = sec->size;
sec->size += GOT_ENTRY_SIZE; need = got_entries_needed (hh->tls_type);
/* R_PARISC_TLS_GD* needs two GOT entries */ sec->size += need;
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;
if (htab->etab.dynamic_sections_created if (htab->etab.dynamic_sections_created
&& (bfd_link_pic (info) && (bfd_link_pic (info)
|| (eh->dynindx != -1 || (eh->dynindx != -1
&& !SYMBOL_REFERENCES_LOCAL (info, eh))) && !SYMBOL_REFERENCES_LOCAL (info, eh)))
&& !UNDEFWEAK_NO_DYNAMIC_RELOC (info, eh)) && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, eh))
{ {
htab->etab.srelgot->size += sizeof (Elf32_External_Rela); bfd_boolean tprel_known = (bfd_link_executable (info)
if ((hh->tls_type & (GOT_TLS_GD | GOT_TLS_IE)) == (GOT_TLS_GD | GOT_TLS_IE)) && SYMBOL_REFERENCES_LOCAL (info, eh));
htab->etab.srelgot->size += 2 * sizeof (Elf32_External_Rela); htab->etab.srelgot->size
else if ((hh->tls_type & GOT_TLS_GD) == GOT_TLS_GD) += got_relocs_needed (hh->tls_type, need, tprel_known);
htab->etab.srelgot->size += sizeof (Elf32_External_Rela);
} }
} }
else else
@ -2195,20 +2220,17 @@ elf32_hppa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
{ {
if (*local_got > 0) if (*local_got > 0)
{ {
unsigned int need;
*local_got = sec->size; *local_got = sec->size;
sec->size += GOT_ENTRY_SIZE; need = got_entries_needed (*local_tls_type);
if ((*local_tls_type & (GOT_TLS_GD | GOT_TLS_IE)) == (GOT_TLS_GD | GOT_TLS_IE)) sec->size += need;
sec->size += 2 * GOT_ENTRY_SIZE;
else if ((*local_tls_type & GOT_TLS_GD) == GOT_TLS_GD)
sec->size += GOT_ENTRY_SIZE;
if (bfd_link_pic (info)) if (bfd_link_pic (info))
{ {
srel->size += sizeof (Elf32_External_Rela); bfd_boolean tprel_known = bfd_link_executable (info);
if ((*local_tls_type & (GOT_TLS_GD | GOT_TLS_IE)) == (GOT_TLS_GD | GOT_TLS_IE)) htab->etab.srelgot->size
srel->size += 2 * sizeof (Elf32_External_Rela); += got_relocs_needed (*local_tls_type, need, tprel_known);
else if ((*local_tls_type & GOT_TLS_GD) == GOT_TLS_GD) }
srel->size += sizeof (Elf32_External_Rela);
}
} }
else else
*local_got = (bfd_vma) -1; *local_got = (bfd_vma) -1;
@ -4025,17 +4047,17 @@ elf32_hppa_relocate_section (bfd *output_bfd,
indx = 0; indx = 0;
if (hh != NULL) if (hh != NULL)
{ {
bfd_boolean dyn; if (!htab->etab.dynamic_sections_created
dyn = htab->etab.dynamic_sections_created; || hh->eh.dynindx == -1
|| SYMBOL_REFERENCES_LOCAL (info, &hh->eh)
if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, || UNDEFWEAK_NO_DYNAMIC_RELOC (info, &hh->eh))
bfd_link_pic (info), /* This is actually a static link, or it is a
&hh->eh) -Bsymbolic link and the symbol is defined
&& (!bfd_link_pic (info) locally, or the symbol was forced to be local
|| !SYMBOL_REFERENCES_LOCAL (info, &hh->eh))) because of a version file. */
{ ;
indx = hh->eh.dynindx; else
} indx = hh->eh.dynindx;
off = hh->eh.got.offset; off = hh->eh.got.offset;
tls_type = hh->tls_type; 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 now, and emit any relocations. If both an IE GOT and a
GD GOT are necessary, we emit the GD first. */ GD GOT are necessary, we emit the GD first. */
if ((bfd_link_pic (info) || indx != 0) if (indx != 0
&& (hh == NULL || (bfd_link_pic (info)
|| ELF_ST_VISIBILITY (hh->eh.other) == STV_DEFAULT && (hh == NULL
|| hh->eh.root.type != bfd_link_hash_undefweak)) || !UNDEFWEAK_NO_DYNAMIC_RELOC (info, &hh->eh))))
{ {
need_relocs = TRUE; need_relocs = TRUE;
loc = htab->etab.srelgot->contents; loc = htab->etab.srelgot->contents;
/* FIXME (CAO): Should this be reloc_count++ ? */ loc += (htab->etab.srelgot->reloc_count
loc += htab->etab.srelgot->reloc_count * sizeof (Elf32_External_Rela); * sizeof (Elf32_External_Rela));
} }
if (tls_type & GOT_TLS_GD) if (tls_type & GOT_TLS_GD)
{ {
if (need_relocs) if (need_relocs)
{ {
outrel.r_offset = (cur_off outrel.r_offset
+ htab->etab.sgot->output_section->vma = (cur_off
+ htab->etab.sgot->output_offset); + htab->etab.sgot->output_section->vma
outrel.r_info = ELF32_R_INFO (indx,R_PARISC_TLS_DTPMOD32); + htab->etab.sgot->output_offset);
outrel.r_info
= ELF32_R_INFO (indx, R_PARISC_TLS_DTPMOD32);
outrel.r_addend = 0; 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); bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
htab->etab.srelgot->reloc_count++; htab->etab.srelgot->reloc_count++;
loc += sizeof (Elf32_External_Rela); loc += sizeof (Elf32_External_Rela);
outrel.r_info
if (indx == 0) = ELF32_R_INFO (indx, R_PARISC_TLS_DTPOFF32);
bfd_put_32 (output_bfd, relocation - dtpoff_base (info), outrel.r_offset += 4;
htab->etab.sgot->contents + cur_off + 4); bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
else htab->etab.srelgot->reloc_count++;
{ loc += sizeof (Elf32_External_Rela);
bfd_put_32 (output_bfd, 0, bfd_put_32 (output_bfd, 0,
htab->etab.sgot->contents + cur_off + 4); htab->etab.sgot->contents + cur_off);
outrel.r_info = ELF32_R_INFO (indx, R_PARISC_TLS_DTPOFF32); bfd_put_32 (output_bfd, 0,
outrel.r_offset += 4; htab->etab.sgot->contents + cur_off + 4);
bfd_elf32_swap_reloca_out (output_bfd, &outrel,loc);
htab->etab.srelgot->reloc_count++;
loc += sizeof (Elf32_External_Rela);
}
} }
else else
{ {
@ -4109,28 +4128,28 @@ elf32_hppa_relocate_section (bfd *output_bfd,
to module 1, the executable. */ to module 1, the executable. */
bfd_put_32 (output_bfd, 1, bfd_put_32 (output_bfd, 1,
htab->etab.sgot->contents + cur_off); 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); htab->etab.sgot->contents + cur_off + 4);
} }
cur_off += 8; cur_off += 8;
} }
if (tls_type & GOT_TLS_IE) 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 outrel.r_offset
+ htab->etab.sgot->output_section->vma = (cur_off
+ htab->etab.sgot->output_offset); + htab->etab.sgot->output_section->vma
outrel.r_info = ELF32_R_INFO (indx, R_PARISC_TLS_TPREL32); + htab->etab.sgot->output_offset);
outrel.r_info = ELF32_R_INFO (indx,
R_PARISC_TLS_TPREL32);
if (indx == 0) if (indx == 0)
outrel.r_addend = relocation - dtpoff_base (info); outrel.r_addend = relocation - dtpoff_base (info);
else else
outrel.r_addend = 0; outrel.r_addend = 0;
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
htab->etab.srelgot->reloc_count++; htab->etab.srelgot->reloc_count++;
loc += sizeof (Elf32_External_Rela); loc += sizeof (Elf32_External_Rela);
@ -4138,7 +4157,6 @@ elf32_hppa_relocate_section (bfd *output_bfd,
else else
bfd_put_32 (output_bfd, tpoff (info, relocation), bfd_put_32 (output_bfd, tpoff (info, relocation),
htab->etab.sgot->contents + cur_off); htab->etab.sgot->contents + cur_off);
cur_off += 4; cur_off += 4;
} }
@ -4148,6 +4166,35 @@ elf32_hppa_relocate_section (bfd *output_bfd,
local_got_offsets[r_symndx] |= 1; 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) if ((tls_type & GOT_TLS_GD)
&& r_type != R_PARISC_TLS_GD21L && r_type != R_PARISC_TLS_GD21L
&& r_type != R_PARISC_TLS_GD14R) && 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 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_NORMAL) != 0
&& (hppa_elf_hash_entry (eh)->tls_type & GOT_TLS_IE) == 0
&& !UNDEFWEAK_NO_DYNAMIC_RELOC (info, eh)) && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, eh))
{ {
bfd_boolean is_dyn = (eh->dynindx != -1 bfd_boolean is_dyn = (eh->dynindx != -1