From: Ian Lance Taylor Date: Sun, 7 Oct 2007 17:05:07 +0000 (+0000) Subject: Add support for local dynamic relocations. X-Git-Url: http://git.efficios.com/?a=commitdiff_plain;h=46cf9fa27e13491561a643178d5ffd2b086183fb;p=deliverable%2Fbinutils-gdb.git Add support for local dynamic relocations. --- diff --git a/gold/i386.cc b/gold/i386.cc index d8b47a8af1..033a9b9a54 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -119,7 +119,8 @@ class Target_i386 : public Sized_target<32, false> { public: Relocate() - : skip_call_tls_get_addr_(false) + : skip_call_tls_get_addr_(false), + local_dynamic_type_(LOCAL_DYNAMIC_NONE) { } ~Relocate() @@ -170,6 +171,15 @@ class Target_i386 : public Sized_target<32, false> unsigned char* view, off_t view_size); + // Do a TLS Local-Dynamic to Local-Exec transition. + inline void + tls_ld_to_le(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); + // Check the range for a TLS relocation. static inline void check_range(const Relocate_info<32, false>*, size_t relnum, @@ -180,9 +190,21 @@ class Target_i386 : public Sized_target<32, false> check_tls(const Relocate_info<32, false>*, size_t relnum, const elfcpp::Rel<32, false>&, bool); + // We need to keep track of which type of local dynamic relocation + // we have seen, so that we can optimize R_386_TLS_LDO_32 correctly. + enum Local_dynamic_type + { + LOCAL_DYNAMIC_NONE, + LOCAL_DYNAMIC_SUN, + LOCAL_DYNAMIC_GNU + }; + // This is set if we should skip the next reloc, which should be a // PLT32 reloc against ___tls_get_addr. bool skip_call_tls_get_addr_; + // The type of local dynamic relocation we have seen in the section + // being relocated, if any. + Local_dynamic_type local_dynamic_type_; }; // Adjust TLS relocation type based on the options and whether this @@ -673,7 +695,9 @@ Target_i386::optimize_tls_reloc(bool is_final, int r_type) return elfcpp::R_386_TLS_LE_32; case elfcpp::R_386_TLS_LDO_32: - // Another type of Local-Dynamic relocation. + // Another type of Local-Dynamic relocation. We return a + // different value as we need to negate the thread segment + // offset. FIXME: Returning reloc types makes no sense. return elfcpp::R_386_TLS_LE; case elfcpp::R_386_TLS_IE: @@ -778,11 +802,13 @@ Target_i386::Scan::local(const General_options&, gold_assert(!output_is_shared); break; + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + break; + case elfcpp::R_386_TLS_IE: case elfcpp::R_386_TLS_GOTIE: case elfcpp::R_386_TLS_GD: - case elfcpp::R_386_TLS_LDM: - case elfcpp::R_386_TLS_LDO_32: case elfcpp::R_386_TLS_IE_32: case elfcpp::R_386_TLS_GOTDESC: case elfcpp::R_386_TLS_DESC_CALL: @@ -942,11 +968,13 @@ Target_i386::Scan::global(const General_options& options, gold_assert(!parameters->output_is_shared()); break; + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + break; + case elfcpp::R_386_TLS_IE: case elfcpp::R_386_TLS_GOTIE: case elfcpp::R_386_TLS_GD: - case elfcpp::R_386_TLS_LDM: - case elfcpp::R_386_TLS_LDO_32: case elfcpp::R_386_TLS_IE_32: case elfcpp::R_386_TLS_GOTDESC: case elfcpp::R_386_TLS_DESC_CALL: @@ -1298,7 +1326,42 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, break; case elfcpp::R_386_TLS_LDM: + if (this->local_dynamic_type_ == LOCAL_DYNAMIC_SUN) + { + fprintf(stderr, + _("%s: %s: both SUN and GNU model TLS relocations\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str()); + gold_exit(false); + } + this->local_dynamic_type_ = LOCAL_DYNAMIC_GNU; + if (opt_r_type == elfcpp::R_386_TLS_LE_32) + { + this->tls_ld_to_le(relinfo, relnum, tls_segment, rel, r_type, + value, view, view_size); + break; + } + fprintf(stderr, _("%s: %s: unsupported reloc %u\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str(), + r_type); + // gold_exit(false); + break; + case elfcpp::R_386_TLS_LDO_32: + // 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. + if (opt_r_type == elfcpp::R_386_TLS_LDO_32 + || this->local_dynamic_type_ == LOCAL_DYNAMIC_NONE) + value = value - tls_segment->vaddr(); + else if (this->local_dynamic_type_ == LOCAL_DYNAMIC_GNU) + value = value - (tls_segment->vaddr() + tls_segment->memsz()); + else + value = tls_segment->vaddr() + tls_segment->memsz() - value; + Relocate_functions<32, false>::rel32(view, value); + break; + case elfcpp::R_386_TLS_GOTDESC: case elfcpp::R_386_TLS_DESC_CALL: fprintf(stderr, _("%s: %s: unsupported reloc %u\n"), @@ -1421,7 +1484,7 @@ Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo, off_t view_size) { // leal foo(,%reg,1),%eax; call ___tls_get_addr - // ==> movl %gs,0,%eax; subl $foo@tpoff,%eax + // ==> movl %gs:0,%eax; subl $foo@tpoff,%eax // leal foo(%reg),%eax; call ___tls_get_addr // ==> movl %gs:0,%eax; subl $foo@tpoff,%eax @@ -1474,6 +1537,39 @@ 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 Local-Dynamic to a +// Local-Exec. + +inline void +Target_i386::Relocate::tls_ld_to_le(const Relocate_info<32, false>* relinfo, + size_t relnum, + Output_segment*, + const elfcpp::Rel<32, false>& rel, + unsigned int, + elfcpp::Elf_types<32>::Elf_Addr, + unsigned char* view, + off_t view_size) +{ + // leal foo(%reg), %eax; call ___tls_get_addr + // ==> movl %gs:0,%eax; nop; leal 0(%esi,1),%esi + + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -2); + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 9); + + // FIXME: Does this test really always pass? + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + view[-2] == 0x8d && view[-1] == 0x83); + + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + view[4] == 0xe8); + + memcpy(view - 2, "\x65\xa1\0\0\0\0\x90\x8d\x74\x26\0", 11); + + // The next reloc should be a PLT32 reloc against __tls_get_addr. + // We can skip it. + this->skip_call_tls_get_addr_ = true; +} + // Check the range for a TLS relocation. inline void