Support special always-defined symbols for targets.
[platform/upstream/binutils.git] / gold / i386.cc
index 8be178c..9b90c79 100644 (file)
@@ -1,5 +1,25 @@
 // i386.cc -- i386 target support for gold.
 
+// Copyright 2006, 2007 Free Software Foundation, Inc.
+// Written by Ian Lance Taylor <iant@google.com>.
+
+// This file is part of gold.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+// MA 02110-1301, USA.
+
 #include "gold.h"
 
 #include <cstring>
@@ -15,6 +35,7 @@
 #include "target.h"
 #include "target-reloc.h"
 #include "target-select.h"
+#include "tls.h"
 
 namespace
 {
@@ -24,6 +45,9 @@ using namespace gold;
 class Output_data_plt_i386;
 
 // The i386 target class.
+// TLS info comes from
+//   http://people.redhat.com/drepper/tls.pdf
+//   http://www.lsd.ic.unicamp.br/~oliva/writeups/TLS/RFC-TLSDESC-x86.txt
 
 class Target_i386 : public Sized_target<32, false>
 {
@@ -46,20 +70,33 @@ 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
   do_finalize_sections(Layout*);
 
+  // Return the value to use for a dynamic which requires special
+  // treatment.
+  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);
@@ -68,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
@@ -87,6 +132,13 @@ class Target_i386 : public Sized_target<32, false>
           unsigned int data_shndx,
           const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
           Symbol* gsym);
+
+    static void
+    unsupported_reloc_local(Sized_relobj<32, false>*, unsigned int r_type);
+
+    static void
+    unsupported_reloc_global(Sized_relobj<32, false>*, unsigned int r_type,
+                            Symbol*);
   };
 
   // The class which implements relocation.
@@ -94,7 +146,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()
@@ -102,12 +155,17 @@ class Target_i386 : public Sized_target<32, false>
       if (this->skip_call_tls_get_addr_)
        {
          // FIXME: This needs to specify the location somehow.
-         fprintf(stderr, _("%s: missing expected TLS relocation\n"),
-                 program_name);
-         gold_exit(false);
+         gold_error(_("missing expected TLS relocation"));
        }
     }
 
+    // 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
@@ -127,54 +185,73 @@ 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 Global-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);
 
-    // Check the range for a TLS relocation.
+    // Do a TLS Initial-Exec to Local-Exec transition.
     static inline void
-    check_range(const Relocate_info<32, false>*, size_t relnum,
-               const elfcpp::Rel<32, false>&, off_t, off_t);
+    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 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);
+    // 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
   // is a local symbol.
-  static unsigned int
+  static tls::Tls_optimization
   optimize_tls_reloc(bool is_final, int r_type);
 
   // Get the GOT section, creating it if necessary.
   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*);
 
   // Get the PLT section.
-  Output_data_plt_i386*
+  const Output_data_plt_i386*
   plt_section() const
   {
     gold_assert(this->plt_ != NULL);
@@ -185,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*,
@@ -217,8 +305,9 @@ const Target::Target_info Target_i386::i386_info =
   false,               // has_make_symbol
   false,               // has_resolve
   true,                        // has_code_fill
+  true,                        // is_default_stack_executable
   "/usr/lib/libc.so.1",        // dynamic_linker
-  0x08048000,          // text_segment_address
+  0x08048000,          // default_text_segment_address
   0x1000,              // abi_pagesize
   0x1000               // common_pagesize
 };
@@ -338,15 +427,13 @@ class Output_data_plt_i386 : public Output_section_data
 
 Output_data_plt_i386::Output_data_plt_i386(Layout* layout,
                                           Output_data_space* got_plt)
-  : Output_section_data(4), got_plt_(got_plt)
+  : Output_section_data(4), got_plt_(got_plt), count_(0)
 {
   this->rel_ = new Reloc_section();
   layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
                                  elfcpp::SHF_ALLOC, this->rel_);
 }
 
-// For some reason
-
 void
 Output_data_plt_i386::do_adjust_output_section(Output_section* os)
 {
@@ -605,14 +692,9 @@ Target_i386::copy_reloc(const General_options* options,
       off_t offset = dynbss_size;
       dynbss->set_space_size(dynbss_size + symsize);
 
-      // Define the symbol in the .dynbss section.
-      symtab->define_in_output_data(this, ssym->name(), ssym->version(),
-                                   dynbss, offset, symsize, ssym->type(),
-                                   ssym->binding(), ssym->visibility(),
-                                   ssym->nonvis(), false, false);
+      symtab->define_with_copy_reloc(this, ssym, dynbss, offset);
 
       // Add the COPY reloc.
-      ssym->set_needs_dynsym_entry();
       Reloc_section* rel_dyn = this->rel_dyn_section(layout);
       rel_dyn->add_global(ssym, elfcpp::R_386_COPY, dynbss, offset);
     }
@@ -622,36 +704,36 @@ Target_i386::copy_reloc(const General_options* options,
 // symbol.  IS_FINAL is true if the final address of this symbol is
 // known at link time.
 
-unsigned int
+tls::Tls_optimization
 Target_i386::optimize_tls_reloc(bool is_final, int r_type)
 {
   // If we are generating a shared library, then we can't do anything
   // in the linker.
   if (parameters->output_is_shared())
-    return r_type;
+    return tls::TLSOPT_NONE;
 
   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
+      // These are General-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_final)
-       return elfcpp::R_386_TLS_LE_32;
-      return elfcpp::R_386_TLS_IE_32;
+       return tls::TLSOPT_TO_LE;
+      return tls::TLSOPT_TO_IE;
 
     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;
+      return tls::TLSOPT_TO_LE;
 
     case elfcpp::R_386_TLS_LDO_32:
       // Another type of Local-Dynamic relocation.
-      return elfcpp::R_386_TLS_LE;
+      return tls::TLSOPT_TO_LE;
 
     case elfcpp::R_386_TLS_IE:
     case elfcpp::R_386_TLS_GOTIE:
@@ -661,20 +743,30 @@ Target_i386::optimize_tls_reloc(bool is_final, int r_type)
       // local symbol, we can switch to Local-Exec, which links the
       // thread offset into the instruction.
       if (is_final)
-       return elfcpp::R_386_TLS_LE_32;
-      return r_type;
+       return tls::TLSOPT_TO_LE;
+      return tls::TLSOPT_NONE;
 
     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;
+      return tls::TLSOPT_NONE;
 
     default:
       gold_unreachable();
     }
 }
 
+// Report an unsupported relocation against a local symbol.
+
+void
+Target_i386::Scan::unsupported_reloc_local(Sized_relobj<32, false>* object,
+                                          unsigned int r_type)
+{
+  gold_error(_("%s: unsupported reloc %u against local symbol"),
+            object->name().c_str(), r_type);
+}
+
 // Scan a relocation for a local symbol.
 
 inline void
@@ -683,8 +775,8 @@ Target_i386::Scan::local(const General_options&,
                         Layout* layout,
                         Target_i386* target,
                         Sized_relobj<32, false>* object,
-                        unsigned int,
-                        const elfcpp::Rel<32, false>&,
+                        unsigned int data_shndx,
+                        const elfcpp::Rel<32, false>& reloc,
                         unsigned int r_type,
                         const elfcpp::Sym<32, false>&)
 {
@@ -696,11 +788,34 @@ Target_i386::Scan::local(const General_options&,
       break;
 
     case elfcpp::R_386_32:
+      // 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
+      // apply the link-time value, so we flag the location with
+      // an R_386_RELATIVE relocation so the dynamic loader can
+      // relocate it easily.
+      if (parameters->output_is_position_independent())
+        {
+          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());
+        }
+      break;
+
     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.
-      gold_assert(!parameters->output_is_shared());
+      // 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:
@@ -708,12 +823,38 @@ Target_i386::Scan::local(const General_options&,
     case elfcpp::R_386_PC8:
       break;
 
+    case elfcpp::R_386_PLT32:
+      // Since we know this is a local symbol, we can handle this as a
+      // PC32 reloc.
+      break;
+
     case elfcpp::R_386_GOTOFF:
     case elfcpp::R_386_GOTPC:
       // We need a GOT section.
       target->got_section(symtab, layout);
       break;
 
+    case elfcpp::R_386_GOT32:
+      {
+        // The symbol requires a GOT entry.
+        Output_data_got<32, false>* got = target->got_section(symtab, layout);
+        unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+        if (got->add_local(object, r_sym))
+          {
+            // If we are generating a shared object, we need to add a
+            // dynamic RELATIVE relocation for this symbol.
+            if (parameters->output_is_position_independent())
+              {
+                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());
+              }
+          }
+      }
+      break;
+
+      // These are relocations which should only be seen by the
+      // dynamic linker, and should never be seen here.
     case elfcpp::R_386_COPY:
     case elfcpp::R_386_GLOB_DAT:
     case elfcpp::R_386_JUMP_SLOT:
@@ -723,52 +864,69 @@ Target_i386::Scan::local(const General_options&,
     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);
+      gold_error(_("%s: unexpected reloc %u in object file"),
+                object->name().c_str(), r_type);
       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:
+      // These are initial TLS relocs, which are expected when
+      // linking.
+    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_executable = parameters->output_is_executable();
-       r_type = Target_i386::optimize_tls_reloc(output_is_executable,
-                                                r_type);
+       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_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:         // 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:      // Alternate local-dynamic
+           break;
+
+         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_executable);
+           gold_assert(!output_is_shared);
            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;
+         default:
+           gold_unreachable();
          }
       }
       break;
 
-    case elfcpp::R_386_GOT32:
-    case elfcpp::R_386_PLT32:
     case elfcpp::R_386_32PLT:
     case elfcpp::R_386_TLS_GD_32:
     case elfcpp::R_386_TLS_GD_PUSH:
@@ -780,12 +938,22 @@ Target_i386::Scan::local(const General_options&,
     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);
+      unsupported_reloc_local(object, r_type);
       break;
     }
 }
 
+// Report an unsupported relocation against a global symbol.
+
+void
+Target_i386::Scan::unsupported_reloc_global(Sized_relobj<32, false>* object,
+                                           unsigned int r_type,
+                                           Symbol* gsym)
+{
+  gold_error(_("%s: unsupported reloc %u against global symbol %s"),
+            object->name().c_str(), r_type, gsym->name());
+}
+
 // Scan a relocation for a global symbol.
 
 inline void
@@ -807,29 +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:
-      // 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.
-      gold_assert(!parameters->output_is_shared());
-
-      if (gsym->is_from_dynobj())
-       {
-         // 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.
-         if (gsym->type() == elfcpp::STT_FUNC)
-           target->make_plt_entry(symtab, layout, gsym);
-         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:
@@ -843,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();
+                  }
               }
           }
       }
@@ -855,6 +1072,13 @@ Target_i386::Scan::global(const General_options& options,
       // Otherwise we need a PLT entry.
       if (gsym->final_value_is_known())
        break;
+      // If building a shared library, we can also skip the PLT entry
+      // if the symbol is defined in the output file and is protected
+      // or hidden.
+      if (gsym->is_defined()
+          && !gsym->is_from_dynobj()
+          && !gsym->is_preemptible())
+       break;
       target->make_plt_entry(symtab, layout, gsym);
       break;
 
@@ -864,6 +1088,8 @@ Target_i386::Scan::global(const General_options& options,
       target->got_section(symtab, layout);
       break;
 
+      // These are relocations which should only be seen by the
+      // dynamic linker, and should never be seen here.
     case elfcpp::R_386_COPY:
     case elfcpp::R_386_GLOB_DAT:
     case elfcpp::R_386_JUMP_SLOT:
@@ -873,47 +1099,65 @@ Target_i386::Scan::global(const General_options& options,
     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);
+      gold_error(_("%s: unexpected reloc %u in object file"),
+                object->name().c_str(), r_type);
       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:
+      // These are initial tls relocs, which are expected when
+      // linking.
+    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();
-       r_type = Target_i386::optimize_tls_reloc(is_final, r_type);
+       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_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:         // 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:      // Alternate local-dynamic
+           break;
+
+         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;
 
-         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;
+         default:
+           gold_unreachable();
          }
       }
       break;
@@ -929,9 +1173,7 @@ Target_i386::Scan::global(const General_options& options,
     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());
+      unsupported_reloc_global(object, r_type, gsym);
       break;
     }
 }
@@ -947,15 +1189,16 @@ 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)
     {
-      fprintf(stderr, _("%s: %s: unsupported RELA reloc section\n"),
-             program_name, object->name().c_str());
-      gold_exit(false);
+      gold_error(_("%s: unsupported RELA reloc section"),
+                object->name().c_str());
+      return;
     }
 
   gold::scan_relocs<32, false, Target_i386, elfcpp::SHT_REL,
@@ -968,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.
@@ -1023,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
@@ -1042,21 +1312,22 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
       if (r_type != elfcpp::R_386_PLT32
          || gsym == NULL
          || strcmp(gsym->name(), "___tls_get_addr") != 0)
+       gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
+                              _("missing expected TLS relocation"));
+      else
        {
-         fprintf(stderr, _("%s: %s: missing expected TLS relocation\n"),
-                 program_name,
-                 relinfo->location(relnum, rel.get_r_offset()).c_str());
-         gold_exit(false);
+         this->skip_call_tls_get_addr_ = false;
+         return false;
        }
-
-      this->skip_call_tls_get_addr_ = false;
-
-      return false;
     }
 
   // Pick the value to use for symbols defined in shared objects.
   Symbol_value<32> symval;
-  if (gsym != NULL && gsym->is_from_dynobj() && gsym->has_plt_offset())
+  if (gsym != NULL
+      && (gsym->is_from_dynobj()
+          || (parameters->output_is_shared()
+              && gsym->is_preemptible()))
+      && gsym->has_plt_offset())
     {
       symval.set_output_value(target->plt_section()->address()
                              + gsym->plt_offset());
@@ -1065,6 +1336,32 @@ 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)
+    {
+    case elfcpp::R_386_GOT32:
+      if (gsym != NULL)
+        {
+          gold_assert(gsym->has_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) - target->got_size();
+        }
+      have_got_offset = true;
+      break;
+
+    default:
+      break;
+    }
+
   switch (r_type)
     {
     case elfcpp::R_386_NONE:
@@ -1073,47 +1370,64 @@ 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:
-      gold_assert(gsym->has_plt_offset()
+      gold_assert(gsym == NULL
+                 || gsym->has_plt_offset()
                  || gsym->final_value_is_known());
       Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
       break;
 
     case elfcpp::R_386_GOT32:
-      // Local GOT offsets not yet supported.
-      gold_assert(gsym);
-      gold_assert(gsym->has_got_offset());
-      Relocate_functions<32, false>::rel32(view, gsym->got_offset());
+      gold_assert(have_got_offset);
+      Relocate_functions<32, false>::rel32(view, got_offset);
       break;
 
     case elfcpp::R_386_GOTOFF:
       {
        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;
@@ -1121,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;
@@ -1130,28 +1444,30 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
     case elfcpp::R_386_GLOB_DAT:
     case elfcpp::R_386_JUMP_SLOT:
     case elfcpp::R_386_RELATIVE:
+      // These are outstanding tls relocs, which are unexpected when
+      // linking.
     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);
+      gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
+                            _("unexpected reloc %u in object file"),
+                            r_type);
       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:
+      // These are initial tls relocs, which are expected when
+      // linking.
+    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;
@@ -1167,11 +1483,9 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
     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,
-             relinfo->location(relnum, rel.get_r_offset()).c_str(),
-             r_type);
-      // gold_exit(false);
+      gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
+                            _("unsupported reloc %u"),
+                            r_type);
       break;
     }
 
@@ -1194,76 +1508,197 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
   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);
+      gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
+                            _("TLS reloc but no TLS segment"));
+      return;
     }
 
   elfcpp::Elf_types<32>::Elf_Addr value = psymval->value(relinfo->object, 0);
 
   const bool is_final = (gsym == NULL
-                        ? !parameters->output_is_shared()
+                        ? !parameters->output_is_position_independent()
                         : gsym->final_value_is_known());
-  const unsigned int opt_r_type =
-    Target_i386::optimize_tls_reloc(is_final, r_type);
+  const tls::Tls_optimization optimized_type
+      = 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);
+    case elfcpp::R_386_TLS_GD:           // Global-dynamic
+      if (optimized_type == tls::TLSOPT_TO_LE)
+       {
+         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(),
+                            _("unsupported reloc %u"),
+                            r_type);
       break;
 
-    case elfcpp::R_386_TLS_LE:
-      value = value - (tls_segment->vaddr() + tls_segment->memsz());
+    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:          // Local-dynamic
+      if (this->local_dynamic_type_ == LOCAL_DYNAMIC_SUN)
+       {
+         gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
+                                _("both SUN and GNU model "
+                                  "TLS relocations"));
+         break;
+       }
+      this->local_dynamic_type_ = LOCAL_DYNAMIC_GNU;
+      if (optimized_type == tls::TLSOPT_TO_LE)
+       {
+         this->tls_ld_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;
+
+    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.
+      if (optimized_type != tls::TLSOPT_TO_LE
+         || 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_IE:
+    case elfcpp::R_386_TLS_IE:           // Initial-exec
     case elfcpp::R_386_TLS_GOTIE:
     case elfcpp::R_386_TLS_IE_32:
-      if (opt_r_type == elfcpp::R_386_TLS_LE_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;
        }
-      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);
+      gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
+                            _("unsupported reloc %u"),
+                            r_type);
       break;
 
-    case elfcpp::R_386_TLS_GD:
-      if (opt_r_type == elfcpp::R_386_TLS_LE_32)
-       {
-         this->tls_gd_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);
+    case elfcpp::R_386_TLS_LE:           // Local-exec
+      value = value - (tls_segment->vaddr() + tls_segment->memsz());
+      Relocate_functions<32, false>::rel32(view, value);
       break;
 
-    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);
+    case elfcpp::R_386_TLS_LE_32:
+      value = tls_segment->vaddr() + tls_segment->memsz() - value;
+      Relocate_functions<32, false>::rel32(view, value);
       break;
     }
 }
 
+// Do a relocation in which we convert a TLS General-Dynamic to a
+// Local-Exec.
+
+inline void
+Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo,
+                                   size_t relnum,
+                                   Output_segment* tls_segment,
+                                   const elfcpp::Rel<32, false>& rel,
+                                   unsigned int,
+                                   elfcpp::Elf_types<32>::Elf_Addr value,
+                                   unsigned char* view,
+                                   off_t view_size)
+{
+  // leal foo(,%reg,1),%eax; call ___tls_get_addr
+  //  ==> movl %gs:0,%eax; subl $foo@tpoff,%eax
+  // leal foo(%reg),%eax; call ___tls_get_addr
+  //  ==> movl %gs:0,%eax; subl $foo@tpoff,%eax
+
+  tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2);
+  tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 9);
+
+  unsigned char op1 = view[-1];
+  unsigned char op2 = view[-2];
+
+  tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+                 op2 == 0x8d || op2 == 0x04);
+  tls::check_tls(relinfo, relnum, rel.get_r_offset(), view[4] == 0xe8);
+
+  int roff = 5;
+
+  if (op2 == 0x04)
+    {
+      tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -3);
+      tls::check_tls(relinfo, relnum, rel.get_r_offset(), view[-3] == 0x8d);
+      tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+                     ((op1 & 0xc7) == 0x05 && op1 != (4 << 3)));
+      memcpy(view - 3, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
+    }
+  else
+    {
+      tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+                     (op1 & 0xf8) == 0x80 && (op1 & 7) != 4);
+      if (static_cast<off_t>(rel.get_r_offset() + 9) < view_size
+          && view[9] == 0x90)
+       {
+         // There is a trailing nop.  Use the size byte subl.
+         memcpy(view - 2, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
+         roff = 6;
+       }
+      else
+       {
+         // Use the five byte subl.
+         memcpy(view - 2, "\x65\xa1\0\0\0\0\x2d\0\0\0", 11);
+       }
+    }
+
+  value = tls_segment->vaddr() + tls_segment->memsz() - value;
+  Relocate_functions<32, false>::rel32(view + roff, value);
+
+  // The next reloc should be a PLT32 reloc against __tls_get_addr.
+  // We can skip it.
+  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
+
+  tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2);
+  tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 9);
+
+  // FIXME: Does this test really always pass?
+  tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+                 view[-2] == 0x8d && view[-1] == 0x83);
+
+  tls::check_tls(relinfo, relnum, rel.get_r_offset(), 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;
+}
+
 // Do a relocation in which we convert a TLS Initial-Exec to a
 // Local-Exec.
 
@@ -1285,8 +1720,8 @@ Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo,
       // 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);
+      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)
@@ -1296,28 +1731,27 @@ Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo,
        }
       else
        {
-         Target_i386::Relocate::check_range(relinfo, relnum, rel,
-                                            view_size, -2);
+         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
-             Target_i386::Relocate::check_tls(relinfo, relnum, rel,
-                                              (op1 & 0xc7) == 0x05);
+             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
-             Target_i386::Relocate::check_tls(relinfo, relnum, rel,
-                                              (op1 & 0xc7) == 0x05);
+             tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+                             (op1 & 0xc7) == 0x05);
              view[-2] = 0x81;
              view[-1] = 0xc0 | ((op1 >> 3) & 7);
            }
          else
-           Target_i386::Relocate::check_tls(relinfo, relnum, rel, 0);
+           tls::check_tls(relinfo, relnum, rel.get_r_offset(), 0);
        }
     }
   else
@@ -1325,13 +1759,13 @@ Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo,
       // 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);
+      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];
-      Target_i386::Relocate::check_tls(relinfo, relnum, rel,
-                                      (op1 & 0xc0) == 0x80 && (op1 & 7) != 4);
+      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
@@ -1351,7 +1785,7 @@ Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo,
          view[-1] = 0xc0 | ((op1 >> 3) & 7);
        }
       else
-       Target_i386::Relocate::check_tls(relinfo, relnum, rel, 0);
+       tls::check_tls(relinfo, relnum, rel.get_r_offset(), 0);
     }
 
   value = tls_segment->vaddr() + tls_segment->memsz() - value;
@@ -1361,108 +1795,6 @@ Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo,
   Relocate_functions<32, false>::rel32(view, value);
 }
 
-// Do a relocation in which we convert a TLS Global-Dynamic to a
-// Local-Exec.
-
-inline void
-Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo,
-                                   size_t relnum,
-                                   Output_segment* tls_segment,
-                                   const elfcpp::Rel<32, false>& rel,
-                                   unsigned int,
-                                   elfcpp::Elf_types<32>::Elf_Addr value,
-                                   unsigned char* view,
-                                   off_t view_size)
-{
-  // leal foo(,%reg,1),%eax; call ___tls_get_addr
-  //  ==> movl %gs,0,%eax; subl $foo@tpoff,%eax
-  // leal foo(%reg),%eax; call ___tls_get_addr
-  //  ==> movl %gs:0,%eax; subl $foo@tpoff,%eax
-
-  Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -2);
-  Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 9);
-
-  unsigned char op1 = view[-1];
-  unsigned char op2 = view[-2];
-
-  Target_i386::Relocate::check_tls(relinfo, relnum, rel,
-                                  op2 == 0x8d || op2 == 0x04);
-  Target_i386::Relocate::check_tls(relinfo, relnum, rel,
-                                  view[4] == 0xe8);
-
-  int roff = 5;
-
-  if (op2 == 0x04)
-    {
-      Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -3);
-      Target_i386::Relocate::check_tls(relinfo, relnum, rel,
-                                      view[-3] == 0x8d);
-      Target_i386::Relocate::check_tls(relinfo, relnum, rel,
-                                      ((op1 & 0xc7) == 0x05
-                                       && op1 != (4 << 3)));
-      memcpy(view - 3, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
-    }
-  else
-    {
-      Target_i386::Relocate::check_tls(relinfo, relnum, rel,
-                                      (op1 & 0xf8) == 0x80 && (op1 & 7) != 4);
-      if (rel.get_r_offset() + 9 < view_size && view[9] == 0x90)
-       {
-         // There is a trailing nop.  Use the size byte subl.
-         memcpy(view - 2, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
-         roff = 6;
-       }
-      else
-       {
-         // Use the five byte subl.
-         memcpy(view - 2, "\x65\xa1\0\0\0\0\x2d\0\0\0", 11);
-       }
-    }
-
-  value = tls_segment->vaddr() + tls_segment->memsz() - value;
-  Relocate_functions<32, false>::rel32(view + roff, value);
-
-  // 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
-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
@@ -1470,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)
@@ -1482,11 +1816,25 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo,
     this,
     prelocs,
     reloc_count,
+    output_section,
+    needs_special_offset_handling,
     view,
     address,
     view_size);
 }
 
+// Return the value to use for a dynamic which requires special
+// treatment.  This is how we support equality comparisons of function
+// pointers across shared library boundaries, as described in the
+// processor specific ABI supplement.
+
+uint64_t
+Target_i386::do_dynsym_value(const Symbol* gsym) const
+{
+  gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
+  return this->plt_section()->address() + gsym->plt_offset();
+}
+
 // Return a string used to fill a code section with nops to take up
 // the specified length.