{
public:
Relocate()
- : skip_call_tls_get_addr_(false)
+ : skip_call_tls_get_addr_(false),
+ local_dynamic_type_(LOCAL_DYNAMIC_NONE)
{ }
~Relocate()
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,
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
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:
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:
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:
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"),
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
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