Support special always-defined symbols for targets.
[platform/upstream/binutils.git] / gold / i386.cc
index e36b22c..9b90c79 100644 (file)
@@ -70,9 +70,10 @@ class Target_i386 : public Sized_target<32, false>
              unsigned int sh_type,
              const unsigned char* prelocs,
              size_t reloc_count,
+             Output_section* output_section,
+             bool needs_special_offset_handling,
              size_t local_symbol_count,
-             const unsigned char* plocal_symbols,
-             Symbol** global_symbols);
+             const unsigned char* plocal_symbols);
 
   // Finalize the sections.
   void
@@ -83,12 +84,19 @@ class Target_i386 : public Sized_target<32, false>
   uint64_t
   do_dynsym_value(const Symbol*) const;
 
+  // Return whether SYM is always defined.
+  bool
+  do_is_always_defined(Symbol* sym) const
+  { return strcmp(sym->name(), "___tls_get_addr") == 0; }
+
   // Relocate a section.
   void
   relocate_section(const Relocate_info<32, false>*,
                   unsigned int sh_type,
                   const unsigned char* prelocs,
                   size_t reloc_count,
+                  Output_section* output_section,
+                  bool needs_special_offset_handling,
                   unsigned char* view,
                   elfcpp::Elf_types<32>::Elf_Addr view_address,
                   off_t view_size);
@@ -97,6 +105,14 @@ class Target_i386 : public Sized_target<32, false>
   std::string
   do_code_fill(off_t length);
 
+  // Return the size of the GOT section.
+  off_t
+  got_size()
+  {
+    gold_assert(this->got_ != NULL);
+    return this->got_->data_size();
+  }
+
  private:
   // The class which scans relocations.
   struct Scan
@@ -143,6 +159,13 @@ class Target_i386 : public Sized_target<32, false>
        }
     }
 
+    // Return whether the static relocation needs to be applied.
+    inline bool
+    should_apply_static_reloc(const Sized_symbol<32>* gsym,
+                              bool is_absolute_ref,
+                              bool is_function_call,
+                              bool is_32bit);
+
     // Do a relocation.  Return false if the caller should not issue
     // any warnings about this relocation.
     inline bool
@@ -162,27 +185,27 @@ class Target_i386 : public Sized_target<32, false>
                 const Symbol_value<32>*,
                 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,
+    // Do a TLS General-Dynamic to Local-Exec transition.
+    inline void
+    tls_gd_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);
 
-    // Do a TLS General-Dynamic to Local-Exec transition.
+    // Do a TLS Local-Dynamic to Local-Exec transition.
     inline void
-    tls_gd_to_le(const Relocate_info<32, false>*, size_t relnum,
+    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);
 
-    // Do a TLS Local-Dynamic to Local-Exec transition.
-    inline void
-    tls_ld_to_le(const Relocate_info<32, false>*, size_t relnum,
+    // 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,
@@ -215,6 +238,14 @@ class Target_i386 : public Sized_target<32, false>
   Output_data_got<32, false>*
   got_section(Symbol_table*, Layout*);
 
+  // Get the GOT PLT section.
+  Output_data_space*
+  got_plt_section() const
+  {
+    gold_assert(this->got_plt_ != NULL);
+    return this->got_plt_;
+  }
+
   // Create a PLT entry for a global symbol.
   void
   make_plt_entry(Symbol_table*, Layout*, Symbol*);
@@ -231,6 +262,17 @@ class Target_i386 : public Sized_target<32, false>
   Reloc_section*
   rel_dyn_section(Layout*);
 
+  // Return true if the symbol may need a COPY relocation.
+  // References from an executable object to non-function symbols
+  // defined in a dynamic object may need a COPY relocation.
+  bool
+  may_need_copy_reloc(Symbol* gsym)
+  {
+    return (!parameters->output_is_shared()
+            && gsym->is_from_dynobj()
+            && gsym->type() != elfcpp::STT_FUNC);
+  }
+
   // Copy a relocation against a global symbol.
   void
   copy_reloc(const General_options*, Symbol_table*, Layout*,
@@ -746,8 +788,6 @@ Target_i386::Scan::local(const General_options&,
       break;
 
     case elfcpp::R_386_32:
-    case elfcpp::R_386_16:
-    case elfcpp::R_386_8:
       // If building a shared library (or a position-independent
       // executable), we need to create a dynamic relocation for
       // this location. The relocation applied at link time will
@@ -762,6 +802,22 @@ Target_i386::Scan::local(const General_options&,
         }
       break;
 
+    case elfcpp::R_386_16:
+    case elfcpp::R_386_8:
+      // If building a shared library (or a position-independent
+      // executable), we need to create a dynamic relocation for
+      // this location. Because the addend needs to remain in the
+      // data section, we need to be careful not to apply this
+      // relocation statically.
+      if (parameters->output_is_position_independent())
+        {
+          Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+          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());
+        }
+      break;
+
     case elfcpp::R_386_PC32:
     case elfcpp::R_386_PC16:
     case elfcpp::R_386_PC8:
@@ -814,57 +870,57 @@ Target_i386::Scan::local(const General_options&,
 
       // These are initial TLS relocs, which are expected when
       // linking.
-    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_GD:            // Global-dynamic
+    case elfcpp::R_386_TLS_GOTDESC:       // Global-dynamic (from ~oliva url)
+    case elfcpp::R_386_TLS_DESC_CALL:
+    case elfcpp::R_386_TLS_LDM:           // Local-dynamic
+    case elfcpp::R_386_TLS_LDO_32:        // Alternate local-dynamic
+    case elfcpp::R_386_TLS_IE:            // Initial-exec
     case elfcpp::R_386_TLS_IE_32:
+    case elfcpp::R_386_TLS_GOTIE:
+    case elfcpp::R_386_TLS_LE:            // Local-exec
     case elfcpp::R_386_TLS_LE_32:
-    case elfcpp::R_386_TLS_GOTDESC:
-    case elfcpp::R_386_TLS_DESC_CALL:
       {
        bool output_is_shared = parameters->output_is_shared();
        const tls::Tls_optimization optimized_type
             = Target_i386::optimize_tls_reloc(!output_is_shared, 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.
-           gold_assert(!output_is_shared);
-           break;
-
-         case elfcpp::R_386_TLS_IE:
-         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.
+         case elfcpp::R_386_TLS_GD:          // Global-dynamic
+         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);
            break;
 
-         case elfcpp::R_386_TLS_LDM:
+         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)
              unsupported_reloc_local(object, r_type);
            break;
 
-         case elfcpp::R_386_TLS_LDO_32:
+         case elfcpp::R_386_TLS_LDO_32:      // Alternate local-dynamic
            break;
 
-         case elfcpp::R_386_TLS_GD:
-         case elfcpp::R_386_TLS_GOTDESC:
-         case elfcpp::R_386_TLS_DESC_CALL:
-           // FIXME: If not relaxing to LE, we need to generate
-           // DTPMOD32 and DTPOFF32 relocs.
+         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)
              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);
+           break;
+
          default:
            gold_unreachable();
          }
@@ -919,47 +975,69 @@ Target_i386::Scan::global(const General_options& options,
       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:
-      if (gsym->is_from_dynobj()
-          || (parameters->output_is_shared()
-              && gsym->is_preemptible()))
-       {
-         // (a) This symbol is defined in a dynamic object.  If it is a
-         // function, we make a PLT entry.  Otherwise we need to
-         // either generate a COPY reloc or copy this reloc.
-         // (b) We are building a shared object and this symbol is
-         // preemptible. If it is a function, we make a PLT entry.
-         // Otherwise, we copy the reloc. We do not make COPY relocs
-         // in shared objects.
-         if (gsym->type() == elfcpp::STT_FUNC)
-           {
-             target->make_plt_entry(symtab, layout, gsym);
-
-             // If this is not a PC relative reference, then we may
-             // be taking the address of the function.  In that case
-             // we need to set the entry in the dynamic symbol table
-             // to the address of the PLT entry.
-             if (r_type != elfcpp::R_386_PC32
-                 && r_type != elfcpp::R_386_PC16
-                 && r_type != elfcpp::R_386_PC8
-                 && gsym->is_from_dynobj())
-               gsym->set_needs_dynsym_value();
-           }
-         else if (parameters->output_is_shared())
-           {
-              Reloc_section* rel_dyn = target->rel_dyn_section(layout);
-              rel_dyn->add_global(gsym, r_type, object, data_shndx, 
-                                  reloc.get_r_offset());
-           }
-         else
-           target->copy_reloc(&options, symtab, layout, object, data_shndx,
-                              gsym, reloc);
-       }
+      {
+        // Make a PLT entry if necessary.
+        if (gsym->needs_plt_entry())
+          {
+            target->make_plt_entry(symtab, layout, gsym);
+            // Since this is not a PC-relative relocation, we may be
+            // taking the address of a function. In that case we need to
+            // set the entry in the dynamic symbol table to the address of
+            // the PLT entry.
+            if (gsym->is_from_dynobj())
+              gsym->set_needs_dynsym_value();
+          }
+        // Make a dynamic relocation if necessary.
+        if (gsym->needs_dynamic_reloc(true, false))
+          {
+            if (target->may_need_copy_reloc(gsym))
+              {
+               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());
+              }
+            else
+              {
+                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                rel_dyn->add_global(gsym, r_type, object, data_shndx, 
+                                    reloc.get_r_offset());
+              }
+          }
+      }
+      break;
 
+    case elfcpp::R_386_PC32:
+    case elfcpp::R_386_PC16:
+    case elfcpp::R_386_PC8:
+      {
+        // Make a PLT entry if necessary.
+        if (gsym->needs_plt_entry())
+          target->make_plt_entry(symtab, layout, gsym);
+        // Make a dynamic relocation if necessary.
+        bool is_function_call = (gsym->type() == elfcpp::STT_FUNC);
+        if (gsym->needs_dynamic_reloc(false, is_function_call))
+          {
+            if (target->may_need_copy_reloc(gsym))
+              {
+               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());
+              }
+          }
+      }
       break;
 
     case elfcpp::R_386_GOT32:
@@ -973,8 +1051,17 @@ Target_i386::Scan::global(const General_options& options,
             if (!gsym->final_value_is_known())
               {
                 Reloc_section* rel_dyn = target->rel_dyn_section(layout);
-                rel_dyn->add_global(gsym, elfcpp::R_386_GLOB_DAT, got,
-                                    gsym->got_offset());
+                if (gsym->is_from_dynobj()
+                   || gsym->is_preemptible())
+                 rel_dyn->add_global(gsym, elfcpp::R_386_GLOB_DAT, got,
+                                     gsym->got_offset());
+                else
+                  {
+                    rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE,
+                                       got, gsym->got_offset());
+                    // Make sure we write the link-time value to the GOT.
+                    gsym->set_needs_value_in_got();
+                  }
               }
           }
       }
@@ -1018,57 +1105,57 @@ Target_i386::Scan::global(const General_options& options,
 
       // These are initial tls relocs, which are expected when
       // linking.
-    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_GD:            // Global-dynamic
+    case elfcpp::R_386_TLS_GOTDESC:       // Global-dynamic (from ~oliva url)
+    case elfcpp::R_386_TLS_DESC_CALL:
+    case elfcpp::R_386_TLS_LDM:           // Local-dynamic
+    case elfcpp::R_386_TLS_LDO_32:        // Alternate local-dynamic
+    case elfcpp::R_386_TLS_IE:            // Initial-exec
     case elfcpp::R_386_TLS_IE_32:
+    case elfcpp::R_386_TLS_GOTIE:
+    case elfcpp::R_386_TLS_LE:            // Local-exec
     case elfcpp::R_386_TLS_LE_32:
-    case elfcpp::R_386_TLS_GOTDESC:
-    case elfcpp::R_386_TLS_DESC_CALL:
       {
        const bool is_final = gsym->final_value_is_known();
        const tls::Tls_optimization optimized_type
             = Target_i386::optimize_tls_reloc(is_final, 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.
-           gold_assert(!parameters->output_is_shared());
-           break;
-
-         case elfcpp::R_386_TLS_IE:
-         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.
+         case elfcpp::R_386_TLS_GD:          // Global-dynamic
+         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);
            break;
 
-         case elfcpp::R_386_TLS_LDM:
+         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)
              unsupported_reloc_global(object, r_type, gsym);
            break;
 
-         case elfcpp::R_386_TLS_LDO_32:
+         case elfcpp::R_386_TLS_LDO_32:      // Alternate local-dynamic
            break;
 
-         case elfcpp::R_386_TLS_GD:
-         case elfcpp::R_386_TLS_GOTDESC:
-         case elfcpp::R_386_TLS_DESC_CALL:
-           // FIXME: If not relaxing to LE, we need to generate
-           // DTPMOD32 and DTPOFF32 relocs.
+         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)
              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());
+           break;
+
          default:
            gold_unreachable();
          }
@@ -1102,9 +1189,10 @@ Target_i386::scan_relocs(const General_options& options,
                         unsigned int sh_type,
                         const unsigned char* prelocs,
                         size_t reloc_count,
+                        Output_section* output_section,
+                        bool needs_special_offset_handling,
                         size_t local_symbol_count,
-                        const unsigned char* plocal_symbols,
-                        Symbol** global_symbols)
+                        const unsigned char* plocal_symbols)
 {
   if (sh_type == elfcpp::SHT_RELA)
     {
@@ -1123,9 +1211,10 @@ Target_i386::scan_relocs(const General_options& options,
     data_shndx,
     prelocs,
     reloc_count,
+    output_section,
+    needs_special_offset_handling,
     local_symbol_count,
-    plocal_symbols,
-    global_symbols);
+    plocal_symbols);
 }
 
 // Finalize the sections.
@@ -1178,6 +1267,32 @@ Target_i386::do_finalize_sections(Layout* layout)
   this->copy_relocs_ = NULL;
 }
 
+// Return whether a direct absolute static relocation needs to be applied.
+// In cases where Scan::local() or Scan::global() has created
+// a dynamic relocation other than R_386_RELATIVE, the addend
+// of the relocation is carried in the data, and we must not
+// apply the static relocation.
+
+inline bool
+Target_i386::Relocate::should_apply_static_reloc(const Sized_symbol<32>* gsym,
+                                                 bool is_absolute_ref,
+                                                 bool is_function_call,
+                                                 bool is_32bit)
+{
+  // For local symbols, we will have created a non-RELATIVE dynamic
+  // relocation only if (a) the output is position independent,
+  // (b) the relocation is absolute (not pc- or segment-relative), and
+  // (c) the relocation is not 32 bits wide.
+  if (gsym == NULL)
+    return !(parameters->output_is_position_independent()
+             && is_absolute_ref
+             && !is_32bit);
+
+  // For global symbols, we use the same helper routines used in the scan pass.
+  return !(gsym->needs_dynamic_reloc(is_absolute_ref, is_function_call)
+           && !gsym->can_use_relative_reloc(is_function_call));
+}
+
 // Perform a relocation.
 
 inline bool
@@ -1222,6 +1337,9 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
   const Sized_relobj<32, false>* object = relinfo->object;
 
   // Get the GOT offset if needed.
+  // The GOT pointer points to the end of the GOT section.
+  // We need to subtract the size of the GOT section to get
+  // the actual offset to use in the relocation.
   bool have_got_offset = false;
   unsigned int got_offset = 0;
   switch (r_type)
@@ -1230,12 +1348,12 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
       if (gsym != NULL)
         {
           gold_assert(gsym->has_got_offset());
-          got_offset = gsym->got_offset();
+          got_offset = gsym->got_offset() - target->got_size();
         }
       else
         {
           unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
-          got_offset = object->local_got_offset(r_sym);
+          got_offset = object->local_got_offset(r_sym) - target->got_size();
         }
       have_got_offset = true;
       break;
@@ -1252,27 +1370,45 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
       break;
 
     case elfcpp::R_386_32:
-      Relocate_functions<32, false>::rel32(view, object, psymval);
+      if (should_apply_static_reloc(gsym, true, false, true))
+        Relocate_functions<32, false>::rel32(view, object, psymval);
       break;
 
     case elfcpp::R_386_PC32:
-      Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
+      {
+        bool is_function_call = (gsym != NULL
+                                 && gsym->type() == elfcpp::STT_FUNC);
+        if (should_apply_static_reloc(gsym, false, is_function_call, true))
+          Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
+      }
       break;
 
     case elfcpp::R_386_16:
-      Relocate_functions<32, false>::rel16(view, object, psymval);
+      if (should_apply_static_reloc(gsym, true, false, false))
+        Relocate_functions<32, false>::rel16(view, object, psymval);
       break;
 
     case elfcpp::R_386_PC16:
-      Relocate_functions<32, false>::pcrel16(view, object, psymval, address);
+      {
+        bool is_function_call = (gsym != NULL
+                                 && gsym->type() == elfcpp::STT_FUNC);
+        if (should_apply_static_reloc(gsym, false, is_function_call, false))
+          Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
+      }
       break;
 
     case elfcpp::R_386_8:
-      Relocate_functions<32, false>::rel8(view, object, psymval);
+      if (should_apply_static_reloc(gsym, true, false, false))
+        Relocate_functions<32, false>::rel8(view, object, psymval);
       break;
 
     case elfcpp::R_386_PC8:
-      Relocate_functions<32, false>::pcrel8(view, object, psymval, address);
+      {
+        bool is_function_call = (gsym != NULL
+                                 && gsym->type() == elfcpp::STT_FUNC);
+        if (should_apply_static_reloc(gsym, false, is_function_call, false))
+          Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
+      }
       break;
 
     case elfcpp::R_386_PLT32:
@@ -1291,7 +1427,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
       {
        elfcpp::Elf_types<32>::Elf_Addr value;
        value = (psymval->value(object, 0)
-                - target->got_section(NULL, NULL)->address());
+                - target->got_plt_section()->address());
        Relocate_functions<32, false>::rel32(view, value);
       }
       break;
@@ -1299,7 +1435,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
     case elfcpp::R_386_GOTPC:
       {
        elfcpp::Elf_types<32>::Elf_Addr value;
-       value = target->got_section(NULL, NULL)->address();
+       value = target->got_plt_section()->address();
        Relocate_functions<32, false>::pcrel32(view, value, address);
       }
       break;
@@ -1322,16 +1458,16 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
 
       // These are initial tls relocs, which are expected when
       // linking.
-    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_GD:             // Global-dynamic
+    case elfcpp::R_386_TLS_GOTDESC:        // Global-dynamic (from ~oliva url)
+    case elfcpp::R_386_TLS_DESC_CALL:
+    case elfcpp::R_386_TLS_LDM:            // Local-dynamic
+    case elfcpp::R_386_TLS_LDO_32:         // Alternate local-dynamic
+    case elfcpp::R_386_TLS_IE:             // Initial-exec
     case elfcpp::R_386_TLS_IE_32:
+    case elfcpp::R_386_TLS_GOTIE:
+    case elfcpp::R_386_TLS_LE:             // Local-exec
     case elfcpp::R_386_TLS_LE_32:
-    case elfcpp::R_386_TLS_GOTDESC:
-    case elfcpp::R_386_TLS_DESC_CALL:
       this->relocate_tls(relinfo, relnum, rel, r_type, gsym, psymval, view,
                         address, view_size);
       break;
@@ -1386,24 +1522,12 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
       = Target_i386::optimize_tls_reloc(is_final, 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:
+    case elfcpp::R_386_TLS_GD:           // Global-dynamic
       if (optimized_type == tls::TLSOPT_TO_LE)
        {
-         Target_i386::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment,
-                                             rel, r_type, value, view,
-                                             view_size);
+         this->tls_gd_to_le(relinfo, relnum, tls_segment,
+                            rel, r_type, value, view,
+                            view_size);
          break;
        }
       gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
@@ -1411,20 +1535,14 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
                             r_type);
       break;
 
-    case elfcpp::R_386_TLS_GD:
-      if (optimized_type == tls::TLSOPT_TO_LE)
-       {
-         this->tls_gd_to_le(relinfo, relnum, tls_segment,
-                            rel, r_type, value, view,
-                            view_size);
-         break;
-       }
+    case elfcpp::R_386_TLS_GOTDESC:      // Global-dynamic (from ~oliva url)
+    case elfcpp::R_386_TLS_DESC_CALL:
       gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
                             _("unsupported reloc %u"),
                             r_type);
       break;
 
-    case elfcpp::R_386_TLS_LDM:
+    case elfcpp::R_386_TLS_LDM:          // Local-dynamic
       if (this->local_dynamic_type_ == LOCAL_DYNAMIC_SUN)
        {
          gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
@@ -1444,7 +1562,7 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
                             r_type);
       break;
 
-    case elfcpp::R_386_TLS_LDO_32:
+    case elfcpp::R_386_TLS_LDO_32:       // Alternate local-dynamic
       // 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.
@@ -1458,109 +1576,31 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
       Relocate_functions<32, false>::rel32(view, value);
       break;
 
-    case elfcpp::R_386_TLS_GOTDESC:
-    case elfcpp::R_386_TLS_DESC_CALL:
+    case elfcpp::R_386_TLS_IE:           // Initial-exec
+    case elfcpp::R_386_TLS_GOTIE:
+    case elfcpp::R_386_TLS_IE_32:
+      if (optimized_type == tls::TLSOPT_TO_LE)
+       {
+         Target_i386::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment,
+                                             rel, r_type, value, view,
+                                             view_size);
+         break;
+       }
       gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
                             _("unsupported reloc %u"),
                             r_type);
       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
-      tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -1);
-      tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 4);
-
-      unsigned char op1 = view[-1];
-      if (op1 == 0xa1)
-       {
-         // movl XX,%eax  ==>  movl $YY,%eax
-         view[-1] = 0xb8;
-       }
-      else
-       {
-         tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2);
 
-         unsigned char op2 = view[-2];
-         if (op2 == 0x8b)
-           {
-             // movl XX,%reg  ==>  movl $YY,%reg
-             tls::check_tls(relinfo, relnum, rel.get_r_offset(),
-                             (op1 & 0xc7) == 0x05);
-             view[-2] = 0xc7;
-             view[-1] = 0xc0 | ((op1 >> 3) & 7);
-           }
-         else if (op2 == 0x03)
-           {
-             // addl XX,%reg  ==>  addl $YY,%reg
-             tls::check_tls(relinfo, relnum, rel.get_r_offset(),
-                             (op1 & 0xc7) == 0x05);
-             view[-2] = 0x81;
-             view[-1] = 0xc0 | ((op1 >> 3) & 7);
-           }
-         else
-           tls::check_tls(relinfo, relnum, rel.get_r_offset(), 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
-      tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2);
-      tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 4);
+    case elfcpp::R_386_TLS_LE:           // Local-exec
+      value = value - (tls_segment->vaddr() + tls_segment->memsz());
+      Relocate_functions<32, false>::rel32(view, value);
+      break;
 
-      unsigned char op1 = view[-1];
-      unsigned char op2 = view[-2];
-      tls::check_tls(relinfo, relnum, rel.get_r_offset(),
-                     (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
-       tls::check_tls(relinfo, relnum, rel.get_r_offset(), 0);
+    case elfcpp::R_386_TLS_LE_32:
+      value = tls_segment->vaddr() + tls_segment->memsz() - value;
+      Relocate_functions<32, false>::rel32(view, value);
+      break;
     }
-
-  value = tls_segment->vaddr() + tls_segment->memsz() - value;
-  if (r_type == elfcpp::R_386_TLS_IE || r_type == elfcpp::R_386_TLS_GOTIE)
-    value = - value;
-
-  Relocate_functions<32, false>::rel32(view, value);
 }
 
 // Do a relocation in which we convert a TLS General-Dynamic to a
@@ -1659,6 +1699,102 @@ Target_i386::Relocate::tls_ld_to_le(const Relocate_info<32, false>* relinfo,
   this->skip_call_tls_get_addr_ = true;
 }
 
+// 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
+      tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -1);
+      tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 4);
+
+      unsigned char op1 = view[-1];
+      if (op1 == 0xa1)
+       {
+         // movl XX,%eax  ==>  movl $YY,%eax
+         view[-1] = 0xb8;
+       }
+      else
+       {
+         tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2);
+
+         unsigned char op2 = view[-2];
+         if (op2 == 0x8b)
+           {
+             // movl XX,%reg  ==>  movl $YY,%reg
+             tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+                             (op1 & 0xc7) == 0x05);
+             view[-2] = 0xc7;
+             view[-1] = 0xc0 | ((op1 >> 3) & 7);
+           }
+         else if (op2 == 0x03)
+           {
+             // addl XX,%reg  ==>  addl $YY,%reg
+             tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+                             (op1 & 0xc7) == 0x05);
+             view[-2] = 0x81;
+             view[-1] = 0xc0 | ((op1 >> 3) & 7);
+           }
+         else
+           tls::check_tls(relinfo, relnum, rel.get_r_offset(), 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
+      tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2);
+      tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 4);
+
+      unsigned char op1 = view[-1];
+      unsigned char op2 = view[-2];
+      tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+                     (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
+       tls::check_tls(relinfo, relnum, rel.get_r_offset(), 0);
+    }
+
+  value = tls_segment->vaddr() + tls_segment->memsz() - value;
+  if (r_type == elfcpp::R_386_TLS_IE || r_type == elfcpp::R_386_TLS_GOTIE)
+    value = - value;
+
+  Relocate_functions<32, false>::rel32(view, value);
+}
+
 // Relocate section data.
 
 void
@@ -1666,6 +1802,8 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo,
                              unsigned int sh_type,
                              const unsigned char* prelocs,
                              size_t reloc_count,
+                             Output_section* output_section,
+                             bool needs_special_offset_handling,
                              unsigned char* view,
                              elfcpp::Elf_types<32>::Elf_Addr address,
                              off_t view_size)
@@ -1678,6 +1816,8 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo,
     this,
     prelocs,
     reloc_count,
+    output_section,
+    needs_special_offset_handling,
     view,
     address,
     view_size);