aboutsummaryrefslogtreecommitdiff
path: root/gold
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2007-10-07 17:05:07 +0000
committerIan Lance Taylor <iant@google.com>2007-10-07 17:05:07 +0000
commit46cf9fa27e13491561a643178d5ffd2b086183fb (patch)
tree27ecf7201c1a6e061b9d7723aadbe9484d961e7b /gold
parent599ca73e2cdd240dd4e62b223e38254c7bea44dd (diff)
downloadfsf-binutils-gdb-46cf9fa27e13491561a643178d5ffd2b086183fb.zip
fsf-binutils-gdb-46cf9fa27e13491561a643178d5ffd2b086183fb.tar.gz
fsf-binutils-gdb-46cf9fa27e13491561a643178d5ffd2b086183fb.tar.bz2
Add support for local dynamic relocations.
Diffstat (limited to 'gold')
-rw-r--r--gold/i386.cc110
1 files changed, 103 insertions, 7 deletions
diff --git a/gold/i386.cc b/gold/i386.cc
index d8b47a8..033a9b9 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