From Cary Coutant: Improve i386 shared library TLS support.

This commit is contained in:
Ian Lance Taylor 2007-11-14 22:31:02 +00:00
parent c224138d88
commit 07f397aba3
9 changed files with 536 additions and 49 deletions

View File

@ -122,6 +122,7 @@ class Target_i386 : public Sized_target<32, false>
Layout* layout, Target_i386* target,
Sized_relobj<32, false>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
const elfcpp::Sym<32, false>& lsym);
@ -130,6 +131,7 @@ class Target_i386 : public Sized_target<32, false>
Layout* layout, Target_i386* target,
Sized_relobj<32, false>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
Symbol* gsym);
@ -179,12 +181,21 @@ class Target_i386 : public Sized_target<32, false>
private:
// Do a TLS relocation.
inline void
relocate_tls(const Relocate_info<32, false>*, size_t relnum,
const elfcpp::Rel<32, false>&,
relocate_tls(const Relocate_info<32, false>*, Target_i386* target,
size_t relnum, const elfcpp::Rel<32, false>&,
unsigned int r_type, const Sized_symbol<32>*,
const Symbol_value<32>*,
unsigned char*, elfcpp::Elf_types<32>::Elf_Addr, off_t);
// Do a TLS General-Dynamic to Initial-Exec transition.
inline void
tls_gd_to_ie(const Relocate_info<32, false>*, size_t relnum,
Output_segment* tls_segment,
const elfcpp::Rel<32, false>&, unsigned int r_type,
elfcpp::Elf_types<32>::Elf_Addr value,
unsigned char* view,
off_t view_size);
// Do a TLS General-Dynamic to Local-Exec transition.
inline void
tls_gd_to_le(const Relocate_info<32, false>*, size_t relnum,
@ -776,6 +787,7 @@ Target_i386::Scan::local(const General_options&,
Target_i386* target,
Sized_relobj<32, false>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rel<32, false>& reloc,
unsigned int r_type,
const elfcpp::Sym<32, false>&)
@ -799,6 +811,8 @@ Target_i386::Scan::local(const General_options&,
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx,
reloc.get_r_offset());
if (!output_section->is_section_flag_set(elfcpp::SHF_WRITE))
layout->set_have_textrel();
}
break;
@ -815,6 +829,8 @@ Target_i386::Scan::local(const General_options&,
unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
rel_dyn->add_local(object, r_sym, r_type, data_shndx,
reloc.get_r_offset());
if (!output_section->is_section_flag_set(elfcpp::SHF_WRITE))
layout->set_have_textrel();
}
break;
@ -848,6 +864,8 @@ Target_i386::Scan::local(const General_options&,
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE,
data_shndx, reloc.get_r_offset());
if (!output_section->is_section_flag_set(elfcpp::SHF_WRITE))
layout->set_have_textrel();
}
}
}
@ -887,18 +905,53 @@ Target_i386::Scan::local(const General_options&,
switch (r_type)
{
case elfcpp::R_386_TLS_GD: // Global-dynamic
if (optimized_type == tls::TLSOPT_NONE)
{
// Create a pair of GOT entries for the module index and
// dtv-relative offset.
Output_data_got<32, false>* got
= target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
if (got->add_local_tls(object, r_sym, true))
{
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
unsigned int got_off
= object->local_tls_got_offset(r_sym, true);
rel_dyn->add_local(object, r_sym,
elfcpp::R_386_TLS_DTPMOD32,
got, got_off);
rel_dyn->add_local(object, r_sym,
elfcpp::R_386_TLS_DTPOFF32,
got, got_off + 4);
}
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_local(object, r_type);
break;
case elfcpp::R_386_TLS_GOTDESC: // Global-dynamic (from ~oliva)
case elfcpp::R_386_TLS_DESC_CALL:
// FIXME: If not relaxing to LE, we need to generate
// DTPMOD32 and DTPOFF32 relocs.
if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_local(object, r_type);
break;
case elfcpp::R_386_TLS_LDM: // Local-dynamic
// FIXME: If not relaxing to LE, we need to generate a
// DTPMOD32 reloc.
if (optimized_type != tls::TLSOPT_TO_LE)
if (optimized_type == tls::TLSOPT_NONE)
{
// Create a GOT entry for the module index.
Output_data_got<32, false>* got
= target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
if (got->add_local_tls(object, r_sym, false))
{
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
unsigned int got_off
= object->local_tls_got_offset(r_sym, false);
rel_dyn->add_local(object, r_sym,
elfcpp::R_386_TLS_DTPMOD32, got,
got_off);
}
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_local(object, r_type);
break;
@ -908,17 +961,32 @@ Target_i386::Scan::local(const General_options&,
case elfcpp::R_386_TLS_IE: // Initial-exec
case elfcpp::R_386_TLS_IE_32:
case elfcpp::R_386_TLS_GOTIE:
// FIXME: If not relaxing to LE, we need to generate a
// TPOFF or TPOFF32 reloc.
if (optimized_type != tls::TLSOPT_TO_LE)
if (optimized_type == tls::TLSOPT_NONE)
{
// Create a GOT entry for the tp-relative offset.
Output_data_got<32, false>* got
= target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
if (got->add_local(object, r_sym))
{
unsigned int dyn_r_type
= (r_type == elfcpp::R_386_TLS_IE_32
? elfcpp::R_386_TLS_TPOFF32
: elfcpp::R_386_TLS_TPOFF);
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
unsigned int got_off = object->local_got_offset(r_sym);
rel_dyn->add_local(object, r_sym, dyn_r_type, got,
got_off);
}
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_local(object, r_type);
break;
case elfcpp::R_386_TLS_LE: // Local-exec
case elfcpp::R_386_TLS_LE_32:
// FIXME: If generating a shared object, we need to copy
// this relocation into the object.
gold_assert(!output_is_shared);
if (output_is_shared)
unsupported_reloc_local(object, r_type);
break;
default:
@ -963,6 +1031,7 @@ Target_i386::Scan::global(const General_options& options,
Target_i386* target,
Sized_relobj<32, false>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rel<32, false>& reloc,
unsigned int r_type,
Symbol* gsym)
@ -994,21 +1063,25 @@ Target_i386::Scan::global(const General_options& options,
{
if (target->may_need_copy_reloc(gsym))
{
target->copy_reloc(&options, symtab, layout, object, data_shndx,
gsym, reloc);
target->copy_reloc(&options, symtab, layout, object,
data_shndx, gsym, reloc);
}
else if (r_type == elfcpp::R_386_32
&& gsym->can_use_relative_reloc(false))
{
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx,
reloc.get_r_offset());
rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE,
data_shndx, reloc.get_r_offset());
if (!output_section->is_section_flag_set(elfcpp::SHF_WRITE))
layout->set_have_textrel();
}
else
{
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
rel_dyn->add_global(gsym, r_type, object, data_shndx,
reloc.get_r_offset());
if (!output_section->is_section_flag_set(elfcpp::SHF_WRITE))
layout->set_have_textrel();
}
}
}
@ -1027,14 +1100,16 @@ Target_i386::Scan::global(const General_options& options,
{
if (target->may_need_copy_reloc(gsym))
{
target->copy_reloc(&options, symtab, layout, object, data_shndx,
gsym, reloc);
target->copy_reloc(&options, symtab, layout, object,
data_shndx, gsym, reloc);
}
else
{
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
rel_dyn->add_global(gsym, r_type, object, data_shndx,
reloc.get_r_offset());
if (!output_section->is_section_flag_set(elfcpp::SHF_WRITE))
layout->set_have_textrel();
}
}
}
@ -1122,18 +1197,61 @@ Target_i386::Scan::global(const General_options& options,
switch (r_type)
{
case elfcpp::R_386_TLS_GD: // Global-dynamic
if (optimized_type == tls::TLSOPT_NONE)
{
// Create a pair of GOT entries for the module index and
// dtv-relative offset.
Output_data_got<32, false>* got
= target->got_section(symtab, layout);
if (got->add_global_tls(gsym, true))
{
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
unsigned int got_off = gsym->tls_got_offset(true);
rel_dyn->add_global(gsym, elfcpp::R_386_TLS_DTPMOD32,
got, got_off);
rel_dyn->add_global(gsym, elfcpp::R_386_TLS_DTPOFF32,
got, got_off + 4);
}
}
else if (optimized_type == tls::TLSOPT_TO_IE)
{
// Create a GOT entry for the tp-relative offset.
Output_data_got<32, false>* got
= target->got_section(symtab, layout);
if (got->add_global(gsym))
{
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
unsigned int got_off = gsym->got_offset();
rel_dyn->add_global(gsym, elfcpp::R_386_TLS_TPOFF32,
got, got_off);
}
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_global(object, r_type, gsym);
break;
case elfcpp::R_386_TLS_GOTDESC: // Global-dynamic (~oliva url)
case elfcpp::R_386_TLS_DESC_CALL:
// FIXME: If not relaxing to LE, we need to generate
// DTPMOD32 and DTPOFF32 relocs.
if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_global(object, r_type, gsym);
break;
case elfcpp::R_386_TLS_LDM: // Local-dynamic
// FIXME: If not relaxing to LE, we need to generate a
// DTPMOD32 reloc.
if (optimized_type != tls::TLSOPT_TO_LE)
if (optimized_type == tls::TLSOPT_NONE)
{
// Create a GOT entry for the module index.
Output_data_got<32, false>* got
= target->got_section(symtab, layout);
if (got->add_global_tls(gsym, false))
{
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
unsigned int got_off = gsym->tls_got_offset(false);
rel_dyn->add_global(gsym, elfcpp::R_386_TLS_DTPMOD32,
got, got_off);
}
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_global(object, r_type, gsym);
break;
@ -1143,17 +1261,30 @@ Target_i386::Scan::global(const General_options& options,
case elfcpp::R_386_TLS_IE: // Initial-exec
case elfcpp::R_386_TLS_IE_32:
case elfcpp::R_386_TLS_GOTIE:
// FIXME: If not relaxing to LE, we need to generate a
// TPOFF or TPOFF32 reloc.
if (optimized_type != tls::TLSOPT_TO_LE)
if (optimized_type == tls::TLSOPT_NONE)
{
// Create a GOT entry for the tp-relative offset.
Output_data_got<32, false>* got
= target->got_section(symtab, layout);
if (got->add_global(gsym))
{
unsigned int dyn_r_type
= (r_type == elfcpp::R_386_TLS_IE_32
? elfcpp::R_386_TLS_TPOFF32
: elfcpp::R_386_TLS_TPOFF);
Reloc_section* rel_dyn = target->rel_dyn_section(layout);
unsigned int got_off = gsym->got_offset();
rel_dyn->add_global(gsym, dyn_r_type, got, got_off);
}
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_global(object, r_type, gsym);
break;
case elfcpp::R_386_TLS_LE: // Local-exec
case elfcpp::R_386_TLS_LE_32:
// FIXME: If generating a shared object, we need to copy
// this relocation into the object.
gold_assert(!parameters->output_is_shared());
if (parameters->output_is_shared())
unsupported_reloc_global(object, r_type, gsym);
break;
default:
@ -1353,6 +1484,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
else
{
unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
gold_assert(object->local_has_got_offset(r_sym));
got_offset = object->local_got_offset(r_sym) - target->got_size();
}
have_got_offset = true;
@ -1468,8 +1600,8 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
case elfcpp::R_386_TLS_GOTIE:
case elfcpp::R_386_TLS_LE: // Local-exec
case elfcpp::R_386_TLS_LE_32:
this->relocate_tls(relinfo, relnum, rel, r_type, gsym, psymval, view,
address, view_size);
this->relocate_tls(relinfo, target, relnum, rel, r_type, gsym, psymval,
view, address, view_size);
break;
case elfcpp::R_386_32PLT:
@ -1496,6 +1628,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
inline void
Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
Target_i386* target,
size_t relnum,
const elfcpp::Rel<32, false>& rel,
unsigned int r_type,
@ -1506,14 +1639,10 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
off_t view_size)
{
Output_segment* tls_segment = relinfo->layout->tls_segment();
if (tls_segment == NULL)
{
gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
_("TLS reloc but no TLS segment"));
return;
}
elfcpp::Elf_types<32>::Elf_Addr value = psymval->value(relinfo->object, 0);
const Sized_relobj<32, false>* object = relinfo->object;
elfcpp::Elf_types<32>::Elf_Addr value = psymval->value(object, 0);
const bool is_final = (gsym == NULL
? !parameters->output_is_position_independent()
@ -1525,11 +1654,43 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
case elfcpp::R_386_TLS_GD: // Global-dynamic
if (optimized_type == tls::TLSOPT_TO_LE)
{
gold_assert(tls_segment != NULL);
this->tls_gd_to_le(relinfo, relnum, tls_segment,
rel, r_type, value, view,
view_size);
break;
}
else
{
unsigned int got_offset;
if (gsym != NULL)
{
gold_assert(gsym->has_tls_got_offset(true));
got_offset = gsym->tls_got_offset(true) - target->got_size();
}
else
{
unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
gold_assert(object->local_has_tls_got_offset(r_sym, true));
got_offset = (object->local_tls_got_offset(r_sym, true)
- target->got_size());
}
if (optimized_type == tls::TLSOPT_TO_IE)
{
gold_assert(tls_segment != NULL);
this->tls_gd_to_ie(relinfo, relnum, tls_segment,
rel, r_type, got_offset, view,
view_size);
break;
}
else if (optimized_type == tls::TLSOPT_NONE)
{
// Relocate the field with the offset of the pair of GOT
// entries.
Relocate_functions<32, false>::rel32(view, got_offset);
break;
}
}
gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
_("unsupported reloc %u"),
r_type);
@ -1553,10 +1714,31 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
this->local_dynamic_type_ = LOCAL_DYNAMIC_GNU;
if (optimized_type == tls::TLSOPT_TO_LE)
{
gold_assert(tls_segment != NULL);
this->tls_ld_to_le(relinfo, relnum, tls_segment, rel, r_type,
value, view, view_size);
break;
}
else if (optimized_type == tls::TLSOPT_NONE)
{
// Relocate the field with the offset of the GOT entry for
// the module index.
unsigned int got_offset;
if (gsym != NULL)
{
gold_assert(gsym->has_tls_got_offset(false));
got_offset = gsym->tls_got_offset(false) - target->got_size();
}
else
{
unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
gold_assert(object->local_has_tls_got_offset(r_sym, false));
got_offset = (object->local_tls_got_offset(r_sym, false)
- target->got_size());
}
Relocate_functions<32, false>::rel32(view, got_offset);
break;
}
gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
_("unsupported reloc %u"),
r_type);
@ -1566,6 +1748,7 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
// This reloc can appear in debugging sections, in which case we
// won't see the TLS_LDM reloc. The local_dynamic_type field
// tells us this.
gold_assert(tls_segment != NULL);
if (optimized_type != tls::TLSOPT_TO_LE
|| this->local_dynamic_type_ == LOCAL_DYNAMIC_NONE)
value = value - tls_segment->vaddr();
@ -1581,22 +1764,50 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
case elfcpp::R_386_TLS_IE_32:
if (optimized_type == tls::TLSOPT_TO_LE)
{
gold_assert(tls_segment != NULL);
Target_i386::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment,
rel, r_type, value, view,
view_size);
break;
}
else if (optimized_type == tls::TLSOPT_NONE)
{
// Relocate the field with the offset of the GOT entry for
// the tp-relative offset of the symbol.
unsigned int got_offset;
if (gsym != NULL)
{
gold_assert(gsym->has_got_offset());
got_offset = gsym->got_offset();
}
else
{
unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
gold_assert(object->local_has_got_offset(r_sym));
got_offset = object->local_got_offset(r_sym);
}
// For the R_386_TLS_IE relocation, we need to apply the
// absolute address of the GOT entry.
if (r_type == elfcpp::R_386_TLS_IE)
got_offset += target->got_plt_section()->address();
// All GOT offsets are relative to the end of the GOT.
got_offset -= target->got_size();
Relocate_functions<32, false>::rel32(view, got_offset);
break;
}
gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
_("unsupported reloc %u"),
r_type);
break;
case elfcpp::R_386_TLS_LE: // Local-exec
gold_assert(tls_segment != NULL);
value = value - (tls_segment->vaddr() + tls_segment->memsz());
Relocate_functions<32, false>::rel32(view, value);
break;
case elfcpp::R_386_TLS_LE_32:
gold_assert(tls_segment != NULL);
value = tls_segment->vaddr() + tls_segment->memsz() - value;
Relocate_functions<32, false>::rel32(view, value);
break;
@ -1667,6 +1878,74 @@ Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo,
this->skip_call_tls_get_addr_ = true;
}
// Do a relocation in which we convert a TLS General-Dynamic to a
// Initial-Exec.
inline void
Target_i386::Relocate::tls_gd_to_ie(const Relocate_info<32, false>* relinfo,
size_t relnum,
Output_segment* tls_segment,
const elfcpp::Rel<32, false>& rel,
unsigned int,
elfcpp::Elf_types<32>::Elf_Addr value,
unsigned char* view,
off_t view_size)
{
// leal foo(,%ebx,1),%eax; call ___tls_get_addr
// ==> movl %gs:0,%eax; addl foo@gotntpoff(%ebx),%eax
tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2);
tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 9);
unsigned char op1 = view[-1];
unsigned char op2 = view[-2];
tls::check_tls(relinfo, relnum, rel.get_r_offset(),
op2 == 0x8d || op2 == 0x04);
tls::check_tls(relinfo, relnum, rel.get_r_offset(), view[4] == 0xe8);
int roff = 5;
// FIXME: For now, support only one form.
tls::check_tls(relinfo, relnum, rel.get_r_offset(),
op1 == 0x8d && op2 == 0x04);
if (op2 == 0x04)
{
tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -3);
tls::check_tls(relinfo, relnum, rel.get_r_offset(), view[-3] == 0x8d);
tls::check_tls(relinfo, relnum, rel.get_r_offset(),
((op1 & 0xc7) == 0x05 && op1 != (4 << 3)));
memcpy(view - 3, "\x65\xa1\0\0\0\0\x03\x83\0\0\0", 12);
}
else
{
tls::check_tls(relinfo, relnum, rel.get_r_offset(),
(op1 & 0xf8) == 0x80 && (op1 & 7) != 4);
if (static_cast<off_t>(rel.get_r_offset() + 9) < view_size
&& view[9] == 0x90)
{
// FIXME: This is not the right instruction sequence.
// There is a trailing nop. Use the size byte subl.
memcpy(view - 2, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
roff = 6;
}
else
{
// FIXME: This is not the right instruction sequence.
// Use the five byte subl.
memcpy(view - 2, "\x65\xa1\0\0\0\0\x2d\0\0\0", 11);
}
}
value = tls_segment->vaddr() + tls_segment->memsz() - value;
Relocate_functions<32, false>::rel32(view + roff, value);
// The next reloc should be a PLT32 reloc against __tls_get_addr.
// We can skip it.
this->skip_call_tls_get_addr_ = true;
}
// Do a relocation in which we convert a TLS Local-Dynamic to a
// Local-Exec.

View File

@ -70,7 +70,8 @@ Layout::Layout(const General_options& options)
eh_frame_section_(NULL), output_file_size_(-1),
input_requires_executable_stack_(false),
input_with_gnu_stack_note_(false),
input_without_gnu_stack_note_(false)
input_without_gnu_stack_note_(false),
have_textrel_(false)
{
// Make space for more than enough segments for a typical file.
// This is just for efficiency--it's OK if we wind up needing more.
@ -1582,6 +1583,13 @@ Layout::finish_dynamic_section(const Input_objects* input_objects,
odyn->add_string(elfcpp::DT_RPATH, rpath_val);
}
// Add a DT_FLAGS entry. We add it even if no flags are set so that
// post-link tools can easily modify these flags if desired.
unsigned int flags = 0;
if (this->have_textrel_)
flags |= elfcpp::DF_TEXTREL;
odyn->add_constant(elfcpp::DT_FLAGS, flags);
}
// The mapping of .gnu.linkonce section names to real section names.

View File

@ -170,6 +170,11 @@ class Layout
off_t
finalize(const Input_objects*, Symbol_table*);
// Record that we have seen a relocation in the text section.
void
set_have_textrel()
{ this->have_textrel_ = true; }
// Return the size of the output file.
off_t
output_file_size() const
@ -434,6 +439,8 @@ class Layout
// Whether we have seen at least one object file without an
// executable stack marker.
bool input_without_gnu_stack_note_;
// Whether we have seen a relocation in the text section.
bool have_textrel_;
};
// This task handles writing out data in output sections which is not

View File

@ -746,6 +746,7 @@ class Sized_relobj : public Relobj
Address addend) const;
// Return whether the local symbol SYMNDX has a GOT offset.
// For TLS symbols, the GOT entry will hold its tp-relative offset.
bool
local_has_got_offset(unsigned int symndx) const
{
@ -772,6 +773,63 @@ class Sized_relobj : public Relobj
gold_assert(ins.second);
}
// Return whether the local TLS symbol SYMNDX has a GOT offset.
// The GOT entry at this offset will contain a module index. If
// NEED_PAIR is true, a second entry immediately following the first
// will contain the dtv-relative offset.
bool
local_has_tls_got_offset(unsigned int symndx, bool need_pair) const
{
typename Local_tls_got_offsets::const_iterator p =
this->local_tls_got_offsets_.find(symndx);
if (p == this->local_tls_got_offsets_.end()
|| (need_pair && !p->second.have_pair_))
return false;
return true;
}
// Return the offset of the GOT entry for the local TLS symbol SYMNDX.
// If NEED_PAIR is true, we need the offset of a pair of GOT entries;
// otherwise we need the offset of the GOT entry for the module index.
unsigned int
local_tls_got_offset(unsigned int symndx, bool need_pair) const
{
typename Local_tls_got_offsets::const_iterator p =
this->local_tls_got_offsets_.find(symndx);
gold_assert(p != this->local_tls_got_offsets_.end());
gold_assert(!need_pair || p->second.have_pair_);
return p->second.got_offset_;
}
// Set the offset of the GOT entry for the local TLS symbol SYMNDX
// to GOT_OFFSET. If HAVE_PAIR is true, we have a pair of GOT entries;
// otherwise, we have just a single entry for the module index.
void
set_local_tls_got_offset(unsigned int symndx, unsigned int got_offset,
bool have_pair)
{
typename Local_tls_got_offsets::iterator p =
this->local_tls_got_offsets_.find(symndx);
if (p != this->local_tls_got_offsets_.end())
{
// An entry already existed for this symbol. This can happen
// if we see a relocation asking for the module index before
// a relocation asking for the pair. In that case, the original
// GOT entry will remain, but won't get used by any further
// relocations.
p->second.got_offset_ = got_offset;
gold_assert(have_pair);
p->second.have_pair_ = true;
}
else
{
std::pair<typename Local_tls_got_offsets::iterator, bool> ins =
this->local_tls_got_offsets_.insert(
std::make_pair(symndx, Tls_got_entry(got_offset, have_pair)));
gold_assert(ins.second);
}
}
// Return the name of the symbol that spans the given offset in the
// specified section in this object. This is used only for error
// messages and is not particularly efficient.
@ -901,9 +959,25 @@ class Sized_relobj : public Relobj
write_local_symbols(Output_file*,
const Stringpool_template<char>*);
// The GOT offsets of local symbols.
// The GOT offsets of local symbols. This map also stores GOT offsets
// for tp-relative offsets for TLS symbols.
typedef Unordered_map<unsigned int, unsigned int> Local_got_offsets;
// The TLS GOT offsets of local symbols. The map stores the offsets
// for either a single GOT entry that holds the module index of a TLS
// symbol, or a pair of GOT entries containing the module index and
// dtv-relative offset.
struct Tls_got_entry
{
Tls_got_entry(int got_offset, bool have_pair)
: got_offset_(got_offset),
have_pair_(have_pair)
{ }
int got_offset_;
bool have_pair_;
};
typedef Unordered_map<unsigned int, Tls_got_entry> Local_tls_got_offsets;
// General access to the ELF file.
elfcpp::Elf_file<size, big_endian, Object> elf_file_;
// Index of SHT_SYMTAB section.
@ -918,8 +992,12 @@ class Sized_relobj : public Relobj
off_t local_symbol_offset_;
// Values of local symbols.
Local_values local_values_;
// GOT offsets for local symbols, indexed by symbol number.
// GOT offsets for local non-TLS symbols, and tp-relative offsets
// for TLS symbols, indexed by symbol number.
Local_got_offsets local_got_offsets_;
// GOT offsets for local TLS symbols, indexed by symbol number
// and GOT entry type.
Local_tls_got_offsets local_tls_got_offsets_;
// Whether this object has a GNU style .eh_frame section.
bool has_eh_frame_;
};

View File

@ -743,12 +743,59 @@ Output_data_got<size, big_endian>::add_local(
{
if (object->local_has_got_offset(symndx))
return false;
this->entries_.push_back(Got_entry(object, symndx));
this->set_got_size();
object->set_local_got_offset(symndx, this->last_got_offset());
return true;
}
// Add an entry (or a pair of entries) for a global TLS symbol to the GOT.
// In a pair of entries, the first value in the pair will be used for the
// module index, and the second value will be used for the dtv-relative
// offset. This returns true if this is a new GOT entry, false if the symbol
// already has a GOT entry.
template<int size, bool big_endian>
bool
Output_data_got<size, big_endian>::add_global_tls(Symbol* gsym,
bool need_pair)
{
if (gsym->has_tls_got_offset(need_pair))
return false;
this->entries_.push_back(Got_entry(gsym));
gsym->set_tls_got_offset(this->last_got_offset(), need_pair);
if (need_pair)
this->entries_.push_back(Got_entry(gsym));
this->set_got_size();
return true;
}
// Add an entry (or a pair of entries) for a local TLS symbol to the GOT.
// In a pair of entries, the first value in the pair will be used for the
// module index, and the second value will be used for the dtv-relative
// offset. This returns true if this is a new GOT entry, false if the symbol
// already has a GOT entry.
template<int size, bool big_endian>
bool
Output_data_got<size, big_endian>::add_local_tls(
Sized_relobj<size, big_endian>* object,
unsigned int symndx,
bool need_pair)
{
if (object->local_has_tls_got_offset(symndx, need_pair))
return false;
this->entries_.push_back(Got_entry(object, symndx));
object->set_local_tls_got_offset(symndx, this->last_got_offset(), need_pair);
if (need_pair)
this->entries_.push_back(Got_entry(object, symndx));
this->set_got_size();
return true;
}
// Write out the GOT.
template<int size, bool big_endian>
@ -1432,8 +1479,12 @@ Output_segment::add_output_section(Output_section* os,
// SHF_TLS sections. An SHF_TLS/SHT_NOBITS section is a special
// case: we group the SHF_TLS/SHT_NOBITS sections right after the
// SHF_TLS/SHT_PROGBITS sections. This lets us set up PT_TLS
// correctly.
if ((os->flags() & elfcpp::SHF_TLS) != 0 && !this->output_data_.empty())
// correctly. SHF_TLS sections get added to both a PT_LOAD segment
// and the PT_TLS segment -- we do this grouping only for the
// PT_LOAD segment.
if (this->type_ != elfcpp::PT_TLS
&& (os->flags() & elfcpp::SHF_TLS) != 0
&& !this->output_data_.empty())
{
pdl = &this->output_data_;
bool nobits = os->type() == elfcpp::SHT_NOBITS;

View File

@ -920,6 +920,19 @@ class Output_data_got : public Output_section_data
bool
add_local(Sized_relobj<size, big_endian>* object, unsigned int sym_index);
// Add an entry (or pair of entries) for a global TLS symbol to the GOT.
// Return true if this is a new GOT entry, false if the symbol was
// already in the GOT.
bool
add_global_tls(Symbol* gsym, bool need_pair);
// Add an entry (or pair of entries) for a local TLS symbol to the GOT.
// This returns true if this is a new GOT entry, false if the symbol
// already has a GOT entry.
bool
add_local_tls(Sized_relobj<size, big_endian>* object,
unsigned int sym_index, bool need_pair);
// Add a constant to the GOT. This returns the offset of the new
// entry from the start of the GOT.
unsigned int

View File

@ -299,6 +299,7 @@ class Symbol
{ return this->dynsym_index_ != 0; }
// Return whether this symbol has an entry in the GOT section.
// For a TLS symbol, this GOT entry will hold its tp-relative offset.
bool
has_got_offset() const
{ return this->has_got_offset_; }
@ -319,6 +320,35 @@ class Symbol
this->got_offset_ = got_offset;
}
// Return whether this TLS symbol has an entry in the GOT section for
// its module index or, if NEED_PAIR is true, has a pair of entries
// for its module index and dtv-relative offset.
bool
has_tls_got_offset(bool need_pair) const
{
return (this->has_tls_mod_got_offset_
&& (!need_pair || this->has_tls_pair_got_offset_));
}
// Return the offset into the GOT section for this symbol's TLS module
// index or, if NEED_PAIR is true, for the pair of entries for the
// module index and dtv-relative offset.
unsigned int
tls_got_offset(bool need_pair) const
{
gold_assert(this->has_tls_got_offset(need_pair));
return this->tls_mod_got_offset_;
}
// Set the GOT offset of this symbol.
void
set_tls_got_offset(unsigned int got_offset, bool have_pair)
{
this->has_tls_mod_got_offset_ = true;
this->has_tls_pair_got_offset_ = have_pair;
this->tls_mod_got_offset_ = got_offset;
}
// Return whether this symbol has an entry in the PLT section.
bool
has_plt_offset() const
@ -620,8 +650,18 @@ class Symbol
// If this symbol has an entry in the GOT section (has_got_offset_
// is true), this is the offset from the start of the GOT section.
// For a TLS symbol, if has_tls_tpoff_got_offset_ is true, this
// serves as the GOT offset for the GOT entry that holds its
// TP-relative offset.
unsigned int got_offset_;
// If this is a TLS symbol and has an entry in the GOT section
// for a module index or a pair of entries (module index,
// dtv-relative offset), these are the offsets from the start
// of the GOT section.
unsigned int tls_mod_got_offset_;
unsigned int tls_pair_got_offset_;
// If this symbol has an entry in the PLT section (has_plt_offset_
// is true), then this is the offset from the start of the PLT
// section.
@ -660,7 +700,14 @@ class Symbol
// True if we've seen this symbol in a dynamic object.
bool in_dyn_ : 1;
// True if the symbol has an entry in the GOT section.
// For a TLS symbol, this GOT entry will hold its tp-relative offset.
bool has_got_offset_ : 1;
// True if the symbol has an entry in the GOT section for its
// module index.
bool has_tls_mod_got_offset_ : 1;
// True if the symbol has a pair of entries in the GOT section for its
// module index and dtv-relative offset.
bool has_tls_pair_got_offset_ : 1;
// True if the symbol has an entry in the PLT section.
bool has_plt_offset_ : 1;
// True if this is a dynamic symbol which needs a special value in

View File

@ -100,7 +100,7 @@ scan_relocs(
}
scan.local(options, symtab, layout, target, object, data_shndx,
reloc, r_type, lsym);
output_section, reloc, r_type, lsym);
}
else
{
@ -110,7 +110,7 @@ scan_relocs(
gsym = symtab->resolve_forwards(gsym);
scan.global(options, symtab, layout, target, object, data_shndx,
reloc, r_type, gsym);
output_section, reloc, r_type, gsym);
}
}
}

View File

@ -135,6 +135,7 @@ class Target_x86_64 : public Sized_target<64, false>
Layout* layout, Target_x86_64* target,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<64, false>& reloc, unsigned int r_type,
const elfcpp::Sym<64, false>& lsym);
@ -143,6 +144,7 @@ class Target_x86_64 : public Sized_target<64, false>
Layout* layout, Target_x86_64* target,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<64, false>& reloc, unsigned int r_type,
Symbol* gsym);
@ -738,6 +740,7 @@ Target_x86_64::Scan::local(const General_options&,
Target_x86_64* target,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
Output_section*,
const elfcpp::Rela<64, false>& reloc,
unsigned int r_type,
const elfcpp::Sym<64, false>&)
@ -927,6 +930,7 @@ Target_x86_64::Scan::global(const General_options& options,
Target_x86_64* target,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
Output_section*,
const elfcpp::Rela<64, false>& reloc,
unsigned int r_type,
Symbol* gsym)