aboutsummaryrefslogtreecommitdiff
path: root/gold
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2007-11-14 22:31:02 +0000
committerIan Lance Taylor <iant@google.com>2007-11-14 22:31:02 +0000
commit07f397aba39f619db4fdce42c9e91cb07dd4dc68 (patch)
tree5cc86b1998b729454448c3f3429154dbdebde33d /gold
parentc224138d88196fe7cabce4fe554c10a1520f5a4f (diff)
downloadgdb-07f397aba39f619db4fdce42c9e91cb07dd4dc68.zip
gdb-07f397aba39f619db4fdce42c9e91cb07dd4dc68.tar.gz
gdb-07f397aba39f619db4fdce42c9e91cb07dd4dc68.tar.bz2
From Cary Coutant: Improve i386 shared library TLS support.
Diffstat (limited to 'gold')
-rw-r--r--gold/i386.cc361
-rw-r--r--gold/layout.cc10
-rw-r--r--gold/layout.h7
-rw-r--r--gold/object.h82
-rw-r--r--gold/output.cc55
-rw-r--r--gold/output.h13
-rw-r--r--gold/symtab.h47
-rw-r--r--gold/target-reloc.h4
-rw-r--r--gold/x86_64.cc4
9 files changed, 535 insertions, 48 deletions
diff --git a/gold/i386.cc b/gold/i386.cc
index 7bd5a3a..b4c7e42 100644
--- a/gold/i386.cc
+++ b/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
+ 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);
+ 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.
diff --git a/gold/layout.cc b/gold/layout.cc
index fea056d..e4eda44 100644
--- a/gold/layout.cc
+++ b/gold/layout.cc
@@ -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.
diff --git a/gold/layout.h b/gold/layout.h
index 5b9f28d..cc07fa3 100644
--- a/gold/layout.h
+++ b/gold/layout.h
@@ -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
diff --git a/gold/object.h b/gold/object.h
index 61b4035..6f06fa8 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -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_;
};
diff --git a/gold/output.cc b/gold/output.cc
index b191725..ef5c509 100644
--- a/gold/output.cc
+++ b/gold/output.cc
@@ -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;
diff --git a/gold/output.h b/gold/output.h
index 053579f..e90077a 100644
--- a/gold/output.h
+++ b/gold/output.h
@@ -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
diff --git a/gold/symtab.h b/gold/symtab.h
index 3d3adbc..8206cba 100644
--- a/gold/symtab.h
+++ b/gold/symtab.h
@@ -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
diff --git a/gold/target-reloc.h b/gold/target-reloc.h
index 0498e9a..7c1d327 100644
--- a/gold/target-reloc.h
+++ b/gold/target-reloc.h
@@ -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);
}
}
}
diff --git a/gold/x86_64.cc b/gold/x86_64.cc
index 349d8c1..5078237 100644
--- a/gold/x86_64.cc
+++ b/gold/x86_64.cc
@@ -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)