From Cary Coutant: Improve i386 shared library TLS support.
This commit is contained in:
parent
c224138d88
commit
07f397aba3
9 changed files with 536 additions and 49 deletions
363
gold/i386.cc
363
gold/i386.cc
|
@ -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
|
||||
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)
|
||||
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:
|
||||
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);
|
||||
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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue