]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gold/i386.cc
Framework for relocation scanning. Implement simple static TLS
[thirdparty/binutils-gdb.git] / gold / i386.cc
index 32ee8814bbf55f8d16c61bed8d4f9054609010ab..468cb90efe7c095ed291c06340ee3efacfc71529 100644 (file)
@@ -2,8 +2,11 @@
 
 #include "gold.h"
 #include "elfcpp.h"
+#include "reloc.h"
 #include "i386.h"
 #include "object.h"
+#include "layout.h"
+#include "output.h"
 #include "target.h"
 #include "target-reloc.h"
 #include "target-select.h"
@@ -22,31 +25,92 @@ class Target_i386 : public Sized_target<32, false>
     : Sized_target<32, false>(&i386_info)
   { }
 
+  // Scan the relocations to look for symbol adjustments.
   void
-  relocate_section(const Symbol_table* symtab,
-                  Sized_object<32, false>*,
-                  unsigned int,
-                  const unsigned char*,
-                  size_t,
-                  unsigned int,
-                  const elfcpp::Elf_types<32>::Elf_Addr*,
-                  Symbol**,
-                  unsigned char*,
-                  elfcpp::Elf_types<32>::Elf_Addr,
-                  off_t);
+  scan_relocs(const General_options& options,
+             Symbol_table* symtab,
+             Sized_object<32, false>* object,
+             unsigned int sh_type,
+             const unsigned char* prelocs,
+             size_t reloc_count,
+             size_t local_symbol_count,
+             const unsigned char* plocal_symbols,
+             Symbol** global_symbols);
 
-  // The class which implements relocation.
-  struct Relocate
+  // Relocate a section.
+  void
+  relocate_section(const Relocate_info<32, false>*,
+                  unsigned int sh_type,
+                  const unsigned char* prelocs,
+                  size_t reloc_count,
+                  unsigned char* view,
+                  elfcpp::Elf_types<32>::Elf_Addr view_address,
+                  off_t view_size);
+
+ private:
+  // The class which scans relocations.
+  struct Scan
   {
     inline void
-    operator()(Sized_object<32, false>*, const elfcpp::Rel<32, false>&,
-              unsigned int r_type, Sized_symbol<32>*,
-              elfcpp::Elf_types<32>::Elf_Addr,
-              unsigned char*, elfcpp::Elf_types<32>::Elf_Addr);
+    local(const General_options& options, Sized_object<32, false>* object,
+         const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
+         const elfcpp::Sym<32, false>& lsym);
 
+    inline void
+    global(const General_options& options, Sized_object<32, false>* object,
+          const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
+          Symbol* gsym);
   };
 
- private:
+  // The class which implements relocation.
+  class Relocate
+  {
+   public:
+    // Do a relocation.
+    static inline void
+    relocate(const Relocate_info<32, false>*, size_t relnum,
+            const elfcpp::Rel<32, false>&,
+            unsigned int r_type, Sized_symbol<32>*,
+            elfcpp::Elf_types<32>::Elf_Addr,
+            unsigned char*, elfcpp::Elf_types<32>::Elf_Addr,
+            off_t);
+
+   private:
+    // Do a TLS relocation.
+    static inline void
+    relocate_tls(const Relocate_info<32, false>*, size_t relnum,
+                const elfcpp::Rel<32, false>&,
+                unsigned int r_type, Sized_symbol<32>*,
+                elfcpp::Elf_types<32>::Elf_Addr,
+                unsigned char*, elfcpp::Elf_types<32>::Elf_Addr, off_t);
+
+    // Do a TLS Initial-Exec to Local-Exec transition.
+    static inline void
+    tls_ie_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,
+               const elfcpp::Rel<32, false>&, off_t, off_t);
+
+    // Check the validity of a TLS relocation.  This is like assert.
+    static inline void
+    check_tls(const Relocate_info<32, false>*, size_t relnum,
+             const elfcpp::Rel<32, false>&, bool);
+  };
+
+  // Adjust TLS relocation type based on the options and whether this
+  // is a local symbol.
+  static unsigned int
+  optimize_tls_reloc(const General_options*, bool is_local, int r_type);
+
+  // Information about this specific target which we pass to the
+  // general Target structure.
   static const Target::Target_info i386_info;
 };
 
@@ -62,75 +126,608 @@ const Target::Target_info Target_i386::i386_info =
   0x1000               // common_pagesize
 };
 
+// Optimize the TLS relocation type based on what we know about the
+// symbol.  IS_LOCAL is true if this symbol can be resolved entirely
+// locally--i.e., does not have to be in the dynamic symbol table.
+
+unsigned int
+Target_i386::optimize_tls_reloc(const General_options* options, bool is_local,
+                               int r_type)
+{
+  // If we are generating a shared library, then we can't do anything
+  // in the linker.
+  if (options->is_shared())
+    return r_type;
+
+  switch (r_type)
+    {
+    case elfcpp::R_386_TLS_GD:
+    case elfcpp::R_386_TLS_GOTDESC:
+    case elfcpp::R_386_TLS_DESC_CALL:
+      // These are Global-Dynamic which permits fully general TLS
+      // access.  Since we know that we are generating an executable,
+      // we can convert this to Initial-Exec.  If we also know that
+      // this is a local symbol, we can further switch to Local-Exec.
+      if (is_local)
+       return elfcpp::R_386_TLS_LE_32;
+      return elfcpp::R_386_TLS_IE_32;
+
+    case elfcpp::R_386_TLS_LDM:
+      // This is Local-Dynamic, which refers to a local symbol in the
+      // dynamic TLS block.  Since we know that we generating an
+      // executable, we can switch to Local-Exec.
+      return elfcpp::R_386_TLS_LE_32;
+
+    case elfcpp::R_386_TLS_LDO_32:
+      // Another type of Local-Dynamic relocation.
+      return elfcpp::R_386_TLS_LE;
+
+    case elfcpp::R_386_TLS_IE:
+    case elfcpp::R_386_TLS_GOTIE:
+    case elfcpp::R_386_TLS_IE_32:
+      // These are Initial-Exec relocs which get the thread offset
+      // from the GOT.  If we know that we are linking against the
+      // local symbol, we can switch to Local-Exec, which links the
+      // thread offset into the instruction.
+      if (is_local)
+       return elfcpp::R_386_TLS_LE_32;
+      return r_type;
+       
+    case elfcpp::R_386_TLS_LE:
+    case elfcpp::R_386_TLS_LE_32:
+      // When we already have Local-Exec, there is nothing further we
+      // can do.
+      return r_type;
+
+    default:
+      abort();
+    }
+}
+
+// Scan a relocation for a local symbol.
+
+inline void
+Target_i386::Scan::local(const General_options& options,
+                        Sized_object<32, false>* object,
+                        const elfcpp::Rel<32, false>&, unsigned int r_type,
+                        const elfcpp::Sym<32, false>&)
+{
+  switch (r_type)
+    {
+    case elfcpp::R_386_NONE:
+    case elfcpp::R_386_GNU_VTINHERIT:
+    case elfcpp::R_386_GNU_VTENTRY:
+      break;
+
+    case elfcpp::R_386_32:
+    case elfcpp::R_386_16:
+    case elfcpp::R_386_8:
+      // FIXME: If we are generating a shared object we need to copy
+      // this relocation into the object.
+      break;
+
+    case elfcpp::R_386_PC32:
+    case elfcpp::R_386_PC16:
+    case elfcpp::R_386_PC8:
+      break;
+
+    case elfcpp::R_386_COPY:
+    case elfcpp::R_386_GLOB_DAT:
+    case elfcpp::R_386_JUMP_SLOT:
+    case elfcpp::R_386_RELATIVE:
+    case elfcpp::R_386_TLS_TPOFF:
+    case elfcpp::R_386_TLS_DTPMOD32:
+    case elfcpp::R_386_TLS_DTPOFF32:
+    case elfcpp::R_386_TLS_TPOFF32:
+    case elfcpp::R_386_TLS_DESC:
+      fprintf(stderr, _("%s: %s: unexpected reloc %u in object file\n"),
+             program_name, object->name().c_str(), r_type);
+      gold_exit(false);
+      break;
+
+    case elfcpp::R_386_TLS_IE:
+    case elfcpp::R_386_TLS_GOTIE:
+    case elfcpp::R_386_TLS_LE:
+    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_LE_32:
+    case elfcpp::R_386_TLS_GOTDESC:
+    case elfcpp::R_386_TLS_DESC_CALL:
+      r_type = Target_i386::optimize_tls_reloc(&options, true, r_type);
+      switch (r_type)
+       {
+       case elfcpp::R_386_TLS_LE:
+       case elfcpp::R_386_TLS_LE_32:
+         // FIXME: If generating a shared object, we need to copy
+         // this relocation into the object.
+         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:
+         fprintf(stderr,
+                 _("%s: %s: unsupported reloc %u against local symbol\n"),
+                 program_name, object->name().c_str(), r_type);
+         break;
+       }
+      break;
+
+    case elfcpp::R_386_GOT32:
+    case elfcpp::R_386_PLT32:
+    case elfcpp::R_386_GOTOFF:
+    case elfcpp::R_386_GOTPC:
+    case elfcpp::R_386_32PLT:
+    case elfcpp::R_386_TLS_GD_32:
+    case elfcpp::R_386_TLS_GD_PUSH:
+    case elfcpp::R_386_TLS_GD_CALL:
+    case elfcpp::R_386_TLS_GD_POP:
+    case elfcpp::R_386_TLS_LDM_32:
+    case elfcpp::R_386_TLS_LDM_PUSH:
+    case elfcpp::R_386_TLS_LDM_CALL:
+    case elfcpp::R_386_TLS_LDM_POP:
+    case elfcpp::R_386_USED_BY_INTEL_200:
+    default:
+      fprintf(stderr, _("%s: %s: unsupported reloc %u against local symbol\n"),
+             program_name, object->name().c_str(), r_type);
+      break;
+    }
+}
+
+// Scan a relocation for a global symbol.
+
+inline void
+Target_i386::Scan::global(const General_options& options,
+                         Sized_object<32, false>* object,
+                         const elfcpp::Rel<32, false>&, unsigned int r_type,
+                         Symbol* gsym)
+{
+  switch (r_type)
+    {
+    case elfcpp::R_386_NONE:
+    case elfcpp::R_386_GNU_VTINHERIT:
+    case elfcpp::R_386_GNU_VTENTRY: 
+      break;
+
+    case elfcpp::R_386_32:
+    case elfcpp::R_386_PC32:
+    case elfcpp::R_386_16:
+    case elfcpp::R_386_PC16:
+    case elfcpp::R_386_8:
+    case elfcpp::R_386_PC8:
+      // FIXME: If we are generating a shared object we may need to
+      // copy this relocation into the object.  If this symbol is
+      // defined in a shared object, we may need to copy this
+      // relocation in order to avoid a COPY relocation.
+      break;
+
+    case elfcpp::R_386_COPY:
+    case elfcpp::R_386_GLOB_DAT:
+    case elfcpp::R_386_JUMP_SLOT:
+    case elfcpp::R_386_RELATIVE:
+    case elfcpp::R_386_TLS_TPOFF:
+    case elfcpp::R_386_TLS_DTPMOD32:
+    case elfcpp::R_386_TLS_DTPOFF32:
+    case elfcpp::R_386_TLS_TPOFF32:
+    case elfcpp::R_386_TLS_DESC:
+      fprintf(stderr, _("%s: %s: unexpected reloc %u in object file\n"),
+             program_name, object->name().c_str(), r_type);
+      gold_exit(false);
+      break;
+
+    case elfcpp::R_386_TLS_IE:
+    case elfcpp::R_386_TLS_GOTIE:
+    case elfcpp::R_386_TLS_LE:
+    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_LE_32:
+    case elfcpp::R_386_TLS_GOTDESC:
+    case elfcpp::R_386_TLS_DESC_CALL:
+      r_type = Target_i386::optimize_tls_reloc(&options,
+                                              !gsym->in_dynsym(),
+                                              r_type);
+      switch (r_type)
+       {
+       case elfcpp::R_386_TLS_LE:
+       case elfcpp::R_386_TLS_LE_32:
+         // FIXME: If generating a shared object, we need to copy
+         // this relocation into the object.
+         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:
+         fprintf(stderr,
+                 _("%s: %s: unsupported reloc %u against global symbol %s\n"),
+                 program_name, object->name().c_str(), r_type, gsym->name());
+         break;
+       }
+      break;
+
+    case elfcpp::R_386_GOT32:
+    case elfcpp::R_386_PLT32:
+    case elfcpp::R_386_GOTOFF:
+    case elfcpp::R_386_GOTPC:
+    case elfcpp::R_386_32PLT:
+    case elfcpp::R_386_TLS_GD_32:
+    case elfcpp::R_386_TLS_GD_PUSH:
+    case elfcpp::R_386_TLS_GD_CALL:
+    case elfcpp::R_386_TLS_GD_POP:
+    case elfcpp::R_386_TLS_LDM_32:
+    case elfcpp::R_386_TLS_LDM_PUSH:
+    case elfcpp::R_386_TLS_LDM_CALL:
+    case elfcpp::R_386_TLS_LDM_POP:
+    case elfcpp::R_386_USED_BY_INTEL_200:
+    default:
+      fprintf(stderr,
+             _("%s: %s: unsupported reloc %u against global symbol %s\n"),
+             program_name, object->name().c_str(), r_type, gsym->name());
+      break;
+    }
+}
+
+// Scan relocations for a section.
+
+void
+Target_i386::scan_relocs(const General_options& options,
+                        Symbol_table* symtab,
+                        Sized_object<32, false>* object,
+                        unsigned int sh_type,
+                        const unsigned char* prelocs,
+                        size_t reloc_count,
+                        size_t local_symbol_count,
+                        const unsigned char* plocal_symbols,
+                        Symbol** global_symbols)
+{
+  if (sh_type == elfcpp::SHT_RELA)
+    {
+      fprintf(stderr, _("%s: %s: unsupported RELA reloc section\n"),
+             program_name, object->name().c_str());
+      gold_exit(false);
+    }
+
+  gold::scan_relocs<32, false, elfcpp::SHT_REL, Target_i386::Scan>(
+    options,
+    symtab,
+    object,
+    prelocs,
+    reloc_count,
+    local_symbol_count,
+    plocal_symbols,
+    global_symbols);
+}
+
 // Perform a relocation.
 
 inline void
-Target_i386::Relocate::operator()(Sized_object<32, false>* object,
-                                 const elfcpp::Rel<32, false>&,
-                                 unsigned int r_type,
-                                 Sized_symbol<32>*,
-                                 elfcpp::Elf_types<32>::Elf_Addr value,
-                                 unsigned char* view,
-                                 elfcpp::Elf_types<32>::Elf_Addr address)
+Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
+                               size_t relnum,
+                               const elfcpp::Rel<32, false>& rel,
+                               unsigned int r_type,
+                               Sized_symbol<32>* gsym,
+                               elfcpp::Elf_types<32>::Elf_Addr value,
+                               unsigned char* view,
+                               elfcpp::Elf_types<32>::Elf_Addr address,
+                               off_t view_size)
 {
   switch (r_type)
     {
     case elfcpp::R_386_NONE:
+    case elfcpp::R_386_GNU_VTINHERIT:
+    case elfcpp::R_386_GNU_VTENTRY:
       break;
 
     case elfcpp::R_386_32:
-      {
-       elfcpp::Elf_Word* wv = reinterpret_cast<elfcpp::Elf_Word*>(view);
-       unsigned int x = elfcpp::read_elf_word<false>(wv);
-       elfcpp::write_elf_word<false>(wv, x + value);
-      }
+      Relocate_functions<32, false>::rel32(view, value);
       break;
 
     case elfcpp::R_386_PC32:
-      {
-       elfcpp::Elf_Word* wv = reinterpret_cast<elfcpp::Elf_Word*>(view);
-       unsigned int x = elfcpp::read_elf_word<false>(wv);
-       elfcpp::write_elf_word<false>(wv, x + value - address);
-      }
+      Relocate_functions<32, false>::pcrel32(view, value, address);
+      break;
+
+    case elfcpp::R_386_16:
+      Relocate_functions<32, false>::rel16(view, value);
+      break;
+
+    case elfcpp::R_386_PC16:
+      Relocate_functions<32, false>::pcrel16(view, value, address);
       break;
 
+    case elfcpp::R_386_8:
+      Relocate_functions<32, false>::rel8(view, value);
+      break;
+
+    case elfcpp::R_386_PC8:
+      Relocate_functions<32, false>::pcrel8(view, value, address);
+      break;
+
+    case elfcpp::R_386_COPY:
+    case elfcpp::R_386_GLOB_DAT:
+    case elfcpp::R_386_JUMP_SLOT:
+    case elfcpp::R_386_RELATIVE:
+    case elfcpp::R_386_TLS_TPOFF:
+    case elfcpp::R_386_TLS_DTPMOD32:
+    case elfcpp::R_386_TLS_DTPOFF32:
+    case elfcpp::R_386_TLS_TPOFF32:
+    case elfcpp::R_386_TLS_DESC:
+      fprintf(stderr, _("%s: %s: unexpected reloc %u in object file\n"),
+             program_name,
+             relinfo->location(relnum, rel.get_r_offset()).c_str(),
+             r_type);
+      gold_exit(false);
+      break;
+
+    case elfcpp::R_386_TLS_IE:
+    case elfcpp::R_386_TLS_GOTIE:
+    case elfcpp::R_386_TLS_LE:
+    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_LE_32:
+    case elfcpp::R_386_TLS_GOTDESC:
+    case elfcpp::R_386_TLS_DESC_CALL:
+      Target_i386::Relocate::relocate_tls(relinfo, relnum, rel, r_type,
+                                         gsym, value, view, address,
+                                         view_size);
+      break;
+
+    case elfcpp::R_386_GOT32:
+    case elfcpp::R_386_PLT32:
+    case elfcpp::R_386_GOTOFF:
+    case elfcpp::R_386_GOTPC:
+    case elfcpp::R_386_32PLT:
+    case elfcpp::R_386_TLS_GD_32:
+    case elfcpp::R_386_TLS_GD_PUSH:
+    case elfcpp::R_386_TLS_GD_CALL:
+    case elfcpp::R_386_TLS_GD_POP:
+    case elfcpp::R_386_TLS_LDM_32:
+    case elfcpp::R_386_TLS_LDM_PUSH:
+    case elfcpp::R_386_TLS_LDM_CALL:
+    case elfcpp::R_386_TLS_LDM_POP:
+    case elfcpp::R_386_USED_BY_INTEL_200:
     default:
       fprintf(stderr, _("%s: %s: unsupported reloc %u\n"),
-             program_name, object->name().c_str(), r_type);
+             program_name,
+             relinfo->location(relnum, rel.get_r_offset()).c_str(),
+             r_type);
       // gold_exit(false);
+      break;
+    }
+}
+
+// Perform a TLS relocation.
+
+inline void
+Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
+                                   size_t relnum,
+                                   const elfcpp::Rel<32, false>& rel,
+                                   unsigned int r_type,
+                                   Sized_symbol<32>* gsym,
+                                   elfcpp::Elf_types<32>::Elf_Addr value,
+                                   unsigned char* view,
+                                   elfcpp::Elf_types<32>::Elf_Addr,
+                                   off_t view_size)
+{
+  Output_segment* tls_segment = relinfo->layout->tls_segment();
+  if (tls_segment == NULL)
+    {
+      fprintf(stderr, _("%s: %s: TLS reloc but no TLS segment\n"),
+             program_name,
+             relinfo->location(relnum, rel.get_r_offset()).c_str());
+      gold_exit(false);
+    }
+
+  const bool is_local = gsym == NULL || !gsym->in_dynsym();
+  const unsigned int opt_r_type =
+    Target_i386::optimize_tls_reloc(relinfo->options, is_local, r_type);
+  switch (r_type)
+    {
+    case elfcpp::R_386_TLS_LE_32:
+      value = tls_segment->vaddr() + tls_segment->memsz() - value;
+      Relocate_functions<32, false>::rel32(view, value);
+      break;
+
+    case elfcpp::R_386_TLS_LE:
+      value = value - (tls_segment->vaddr() + tls_segment->memsz());
+      Relocate_functions<32, false>::rel32(view, value);
+      break;
+
+    case elfcpp::R_386_TLS_IE:
+    case elfcpp::R_386_TLS_GOTIE:
+    case elfcpp::R_386_TLS_IE_32:
+      if (opt_r_type == elfcpp::R_386_TLS_LE_32)
+       {
+         Target_i386::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment,
+                                             rel, r_type, value, view,
+                                             view_size);
+         break;
+       }
+      fprintf(stderr, _("%s: %s: unsupported reloc type %u\n"),
+             program_name,
+             relinfo->location(relnum, rel.get_r_offset()).c_str(),
+             r_type);
+      // gold_exit(false);
+      break;
+
+    case elfcpp::R_386_TLS_GD:
+    case elfcpp::R_386_TLS_LDM:
+    case elfcpp::R_386_TLS_LDO_32:
+    case elfcpp::R_386_TLS_GOTDESC:
+    case elfcpp::R_386_TLS_DESC_CALL:
+      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;
+    }
+}
+
+// Do a relocation in which we convert a TLS Initial-Exec to a
+// Local-Exec.
+
+inline void
+Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo,
+                                   size_t relnum,
+                                   Output_segment* tls_segment,
+                                   const elfcpp::Rel<32, false>& rel,
+                                   unsigned int r_type,
+                                   elfcpp::Elf_types<32>::Elf_Addr value,
+                                   unsigned char* view,
+                                   off_t view_size)
+{
+  // We have to actually change the instructions, which means that we
+  // need to examine the opcodes to figure out which instruction we
+  // are looking at.
+  if (r_type == elfcpp::R_386_TLS_IE)
+    {
+      // movl %gs:XX,%eax  ==>  movl $YY,%eax
+      // movl %gs:XX,%reg  ==>  movl $YY,%reg
+      // addl %gs:XX,%reg  ==>  addl $YY,%reg
+      Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -1);
+      Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 4);
+
+      unsigned char op1 = view[-1];
+      if (op1 == 0xa1)
+       {
+         // movl XX,%eax  ==>  movl $YY,%eax
+         view[-1] = 0xb8;
+       }
+      else
+       {
+         Target_i386::Relocate::check_range(relinfo, relnum, rel,
+                                            view_size, -2);
+
+         unsigned char op2 = view[-2];
+         if (op2 == 0x8b)
+           {
+             // movl XX,%reg  ==>  movl $YY,%reg
+             Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+                                              (op1 & 0xc7) == 0x05);
+             view[-2] = 0xc7;
+             view[-1] = 0xc0 | ((op1 >> 3) & 7);
+           }
+         else if (op2 == 0x03)
+           {
+             // addl XX,%reg  ==>  addl $YY,%reg
+             Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+                                              (op1 & 0xc7) == 0x05);
+             view[-2] = 0x81;
+             view[-1] = 0xc0 | ((op1 >> 3) & 7);
+           }
+         else
+           Target_i386::Relocate::check_tls(relinfo, relnum, rel, 0);
+       }
+    }
+  else
+    {
+      // subl %gs:XX(%reg1),%reg2  ==>  subl $YY,%reg2
+      // movl %gs:XX(%reg1),%reg2  ==>  movl $YY,%reg2
+      // addl %gs:XX(%reg1),%reg2  ==>  addl $YY,$reg2
+      Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -2);
+      Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 4);
+
+      unsigned char op1 = view[-1];
+      unsigned char op2 = view[-2];
+      Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+                                      (op1 & 0xc0) == 0x80 && (op1 & 7) != 4);
+      if (op2 == 0x8b)
+       {
+         // movl %gs:XX(%reg1),%reg2  ==>  movl $YY,%reg2
+         view[-2] = 0xc7;
+         view[-1] = 0xc0 | ((op1 >> 3) & 7);
+       }
+      else if (op2 == 0x2b)
+       {
+         // subl %gs:XX(%reg1),%reg2  ==>  subl $YY,%reg2
+         view[-2] = 0x81;
+         view[-1] = 0xe8 | ((op1 >> 3) & 7);
+       }
+      else if (op2 == 0x03)
+       {
+         // addl %gs:XX(%reg1),%reg2  ==>  addl $YY,$reg2
+         view[-2] = 0x81;
+         view[-1] = 0xc0 | ((op1 >> 3) & 7);
+       }
+      else
+       Target_i386::Relocate::check_tls(relinfo, relnum, rel, 0);
+    }
+
+  if (r_type == elfcpp::R_386_TLS_IE_32)
+    value = tls_segment->vaddr() + tls_segment->memsz() - value;
+  else // elfcpp::R_386_TLS_IE, elfcpp::R_386_TLS_GOTIE
+    value = value - (tls_segment->vaddr() + tls_segment->memsz());
+
+  Relocate_functions<32, false>::rel32(view, value);
+}
+
+// Check the range for a TLS relocation.
+
+inline void
+Target_i386::Relocate::check_range(const Relocate_info<32, false>* relinfo,
+                                  size_t relnum,
+                                  const elfcpp::Rel<32, false>& rel,
+                                  off_t view_size, off_t off)
+{
+  off_t offset = rel.get_r_offset() + off;
+  if (offset < 0 || offset > view_size)
+    {
+      fprintf(stderr, _("%s: %s: TLS relocation out of range\n"),
+             program_name,
+             relinfo->location(relnum, rel.get_r_offset()).c_str());
+      gold_exit(false);
+    }
+}
+
+// Check the validity of a TLS relocation.  This is like assert.
+
+inline void
+Target_i386::Relocate::check_tls(const Relocate_info<32, false>* relinfo,
+                                size_t relnum,
+                                const elfcpp::Rel<32, false>& rel,
+                                bool valid)
+{
+  if (!valid)
+    {
+      fprintf(stderr,
+             _("%s: %s: TLS relocation against invalid instruction\n"),
+             program_name,
+             relinfo->location(relnum, rel.get_r_offset()).c_str());
+      gold_exit(false);
     }
 }
 
 // Relocate section data.
 
 void
-Target_i386::relocate_section(const Symbol_table* symtab,
-                             Sized_object<32, false>* object,
+Target_i386::relocate_section(const Relocate_info<32, false>* relinfo,
                              unsigned int sh_type,
                              const unsigned char* prelocs,
                              size_t reloc_count,
-                             unsigned int local_count,
-                             const elfcpp::Elf_types<32>::Elf_Addr* values,
-                             Symbol** global_syms,
                              unsigned char* view,
                              elfcpp::Elf_types<32>::Elf_Addr address,
                              off_t view_size)
 {
-  if (sh_type == elfcpp::SHT_RELA)
-    {
-      fprintf(stderr, _("%s: %s: unsupported RELA reloc section\n"),
-             program_name, object->name().c_str());
-      gold_exit(false);
-    }
+  assert(sh_type == elfcpp::SHT_REL);
 
   gold::relocate_section<32, false, elfcpp::SHT_REL, Target_i386::Relocate>(
-    symtab,
-    object,
+    relinfo,
     prelocs,
     reloc_count,
-    local_count,
-    values,
-    global_syms,
     view,
     address,
     view_size);