Support special always-defined symbols for targets.
[platform/upstream/binutils.git] / gold / i386.cc
index 052b7d3..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);
@@ -151,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
@@ -247,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*,
@@ -762,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
@@ -772,15 +796,28 @@ Target_i386::Scan::local(const General_options&,
       // relocate it easily.
       if (parameters->output_is_position_independent())
         {
-         // FIXME: R_386_RELATIVE only works for a 32-bit relocation.
-         gold_assert(r_type != elfcpp::R_386_16 && r_type != elfcpp::R_386_8);
-
           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:
+      // 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:
@@ -938,76 +975,67 @@ 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:
       {
-        bool is_pcrel = (r_type == elfcpp::R_386_PC32
-                        || r_type == elfcpp::R_386_PC16
-                        || r_type == 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.
-           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. We will also need to
-               // create a dynamic relocation.
-               if (!is_pcrel)
-                 {
-                   if (gsym->is_from_dynobj())
-                     gsym->set_needs_dynsym_value();
-                    if (parameters->output_is_position_independent())
-                      {
-                       // FIXME: If this is an 8-bit or 16-bit
-                       // relocation, R_386_RELATIVE won't work.
-                       gold_assert(r_type != elfcpp::R_386_16
-                                   && r_type != elfcpp::R_386_8);
-
-                        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 if (parameters->output_is_shared())
-             {
-               // We do not make COPY relocs in shared objects.
+        // 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());
-             }
-           else
-             target->copy_reloc(&options, symtab, layout, object, data_shndx,
-                                gsym, reloc);
-         }
-        else if (!is_pcrel && parameters->output_is_position_independent())
+              }
+          }
+      }
+      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))
           {
-           // FIXME: If this is an 8-bit or 16-bit relocation,
-           // R_386_RELATIVE won't work.
-           gold_assert(r_type != elfcpp::R_386_16
-                       && r_type != elfcpp::R_386_8);
-
-            // This is not a PC-relative reference, so we need to generate
-            // a dynamic relocation.
-            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());
+            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;
@@ -1023,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();
+                  }
               }
           }
       }
@@ -1152,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)
     {
@@ -1173,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.
@@ -1228,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
@@ -1305,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:
@@ -1719,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)
@@ -1731,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);