From: Ian Lance Taylor Date: Wed, 14 Nov 2007 22:31:02 +0000 (+0000) Subject: From Cary Coutant: Improve i386 shared library TLS support. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=07f397aba39f619db4fdce42c9e91cb07dd4dc68;p=binutils-gdb.git From Cary Coutant: Improve i386 shared library TLS support. --- diff --git a/gold/i386.cc b/gold/i386.cc index 7bd5a3a30bd..b4c7e421602 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(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 fea056d1a31..e4eda44a2d8 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 5b9f28defe6..cc07fa3d429 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 61b40358dde..6f06fa81fe0 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 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*); - // 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 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 Local_tls_got_offsets; + // General access to the ELF file. elfcpp::Elf_file 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 b1917257f2b..ef5c5097c6f 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -743,12 +743,59 @@ Output_data_got::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 +bool +Output_data_got::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 +bool +Output_data_got::add_local_tls( + Sized_relobj* 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 @@ -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 053579f9364..e90077ac2a1 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* 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* 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 3d3adbcc5a6..8206cbaa33a 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 0498e9a3385..7c1d327eace 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 349d8c1407f..5078237385e 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)