2010-03-08 Hui Zhu <teawater@gmail.com>
[binutils-gdb.git] / gold / x86_64.cc
index f0aade4f49af61de08ca0a8c000a26445b3fa093..e9dd5ae5d5a6ba4f84735757cd2a0526c6ce6c41 100644 (file)
@@ -39,6 +39,7 @@
 #include "tls.h"
 #include "freebsd.h"
 #include "gc.h"
+#include "icf.h"
 
 namespace
 {
@@ -65,9 +66,20 @@ class Target_x86_64 : public Target_freebsd<64, false>
     : Target_freebsd<64, false>(&x86_64_info),
       got_(NULL), plt_(NULL), got_plt_(NULL), global_offset_table_(NULL),
       rela_dyn_(NULL), copy_relocs_(elfcpp::R_X86_64_COPY), dynbss_(NULL),
-      got_mod_index_offset_(-1U), tls_base_symbol_defined_(false)
+      got_mod_index_offset_(-1U), tlsdesc_reloc_info_(),
+      tls_base_symbol_defined_(false)
   { }
 
+  // This function should be defined in targets that can use relocation
+  // types to determine (implemented in local_reloc_may_be_function_pointer
+  // and global_reloc_may_be_function_pointer)
+  // if a function's pointer is taken.  ICF uses this in safe mode to only
+  // fold those functions whose pointer is defintely not taken.  For x86_64
+  // pie binaries, safe ICF cannot be done by looking at relocation types.
+  inline bool
+  can_check_for_function_pointers() const
+  { return !parameters->options().pie(); }
+
   // Hook for a new output section.
   void
   do_new_output_section(Output_section*) const;
@@ -161,6 +173,20 @@ class Target_x86_64 : public Target_freebsd<64, false>
   do_is_defined_by_abi(const Symbol* sym) const
   { return strcmp(sym->name(), "__tls_get_addr") == 0; }
 
+  // Return the symbol index to use for a target specific relocation.
+  // The only target specific relocation is R_X86_64_TLSDESC for a
+  // local symbol, which is an absolute reloc.
+  unsigned int
+  do_reloc_symbol_index(void*, unsigned int r_type) const
+  {
+    gold_assert(r_type == elfcpp::R_X86_64_TLSDESC);
+    return 0;
+  }
+
+  // Return the addend to use for a target specific relocation.
+  uint64_t
+  do_reloc_addend(void* arg, unsigned int r_type, uint64_t addend) const;
+
   // Adjust -fstack-split code which calls non-stack-split code.
   void
   do_calls_non_split(Relobj* object, unsigned int shndx,
@@ -176,6 +202,14 @@ class Target_x86_64 : public Target_freebsd<64, false>
     return this->got_->data_size();
   }
 
+  // Add a new reloc argument, returning the index in the vector.
+  size_t
+  add_tlsdesc_info(Sized_relobj<64, false>* object, unsigned int r_sym)
+  {
+    this->tlsdesc_reloc_info_.push_back(Tlsdesc_info(object, r_sym));
+    return this->tlsdesc_reloc_info_.size() - 1;
+  }
+
  private:
   // The class which scans relocations.
   class Scan
@@ -201,6 +235,26 @@ class Target_x86_64 : public Target_freebsd<64, false>
           const elfcpp::Rela<64, false>& reloc, unsigned int r_type,
           Symbol* gsym);
 
+    inline bool
+    local_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout,
+                                       Target_x86_64* target,
+                                       Sized_relobj<64, false>* object,
+                                       unsigned int data_shndx,
+                                       Output_section* output_section,
+                                       const elfcpp::Rela<64, false>& reloc,
+                                       unsigned int r_type,
+                                       const elfcpp::Sym<64, false>& lsym);
+
+    inline bool
+    global_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout,
+                                        Target_x86_64* target,
+                                        Sized_relobj<64, false>* object,
+                                        unsigned int data_shndx,
+                                        Output_section* output_section,
+                                        const elfcpp::Rela<64, false>& reloc,
+                                        unsigned int r_type,
+                                        Symbol* gsym);
+
   private:
     static void
     unsupported_reloc_local(Sized_relobj<64, false>*, unsigned int r_type);
@@ -212,6 +266,9 @@ class Target_x86_64 : public Target_freebsd<64, false>
     void
     check_non_pic(Relobj*, unsigned int r_type);
 
+    inline bool
+    possible_function_pointer_reloc(unsigned int r_type);
+
     // Whether we have issued an error about a non-PIC compilation.
     bool issued_non_pic_error_;
   };
@@ -379,6 +436,10 @@ class Target_x86_64 : public Target_freebsd<64, false>
   Reloc_section*
   rela_dyn_section(Layout*);
 
+  // Get the section to use for TLSDESC relocations.
+  Reloc_section*
+  rela_tlsdesc_section(Layout*) const;
+
   // Add a potential copy relocation.
   void
   copy_reloc(Symbol_table* symtab, Layout* layout,
@@ -404,6 +465,21 @@ class Target_x86_64 : public Target_freebsd<64, false>
     GOT_TYPE_TLS_DESC = 3       // GOT entry for TLS_DESC pair
   };
 
+  // This type is used as the argument to the target specific
+  // relocation routines.  The only target specific reloc is
+  // R_X86_64_TLSDESC against a local symbol.
+  struct Tlsdesc_info
+  {
+    Tlsdesc_info(Sized_relobj<64, false>* a_object, unsigned int a_r_sym)
+      : object(a_object), r_sym(a_r_sym)
+    { }
+
+    // The object in which the local symbol is defined.
+    Sized_relobj<64, false>* object;
+    // The local symbol index in the object.
+    unsigned int r_sym;
+  };
+
   // The GOT section.
   Output_data_got<64, false>* got_;
   // The PLT section.
@@ -420,6 +496,10 @@ class Target_x86_64 : public Target_freebsd<64, false>
   Output_data_space* dynbss_;
   // Offset of the GOT entry for the TLS module index.
   unsigned int got_mod_index_offset_;
+  // We handle R_X86_64_TLSDESC against a local symbol as a target
+  // specific relocation.  Here we store the object and local symbol
+  // index for the relocation.
+  std::vector<Tlsdesc_info> tlsdesc_reloc_info_;
   // True if the _TLS_MODULE_BASE_ symbol has been defined.
   bool tls_base_symbol_defined_;
 };
@@ -551,11 +631,15 @@ class Output_data_plt_x86_64 : public Output_section_data
   get_tlsdesc_plt_offset() const
   { return (this->count_ + 1) * plt_entry_size; }
 
-  // Return the .rel.plt section data.
+  // Return the .rela.plt section data.
   const Reloc_section*
-  rel_plt() const
+  rela_plt() const
   { return this->rel_; }
 
+  // Return where the TLSDESC relocations should go.
+  Reloc_section*
+  rela_tlsdesc(Layout*);
+
  protected:
   void
   do_adjust_output_section(Output_section* os);
@@ -590,6 +674,9 @@ class Output_data_plt_x86_64 : public Output_section_data
 
   // The reloc section.
   Reloc_section* rel_;
+  // The TLSDESC relocs, if necessary.  These must follow the regular
+  // PLT relocs.
+  Reloc_section* tlsdesc_rel_;
   // The .got section.
   Output_data_got<64, false>* got_;
   // The .got.plt section.
@@ -607,8 +694,8 @@ class Output_data_plt_x86_64 : public Output_section_data
 Output_data_plt_x86_64::Output_data_plt_x86_64(Layout* layout,
                                                Output_data_got<64, false>* got,
                                                Output_data_space* got_plt)
-  : Output_section_data(8), got_(got), got_plt_(got_plt), count_(0),
-    tlsdesc_got_offset_(-1U)
+  : Output_section_data(8), tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt),
+    count_(0), tlsdesc_got_offset_(-1U)
 {
   this->rel_ = new Reloc_section(false);
   layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
@@ -652,6 +739,24 @@ Output_data_plt_x86_64::add_entry(Symbol* gsym)
   // appear in the relocations.
 }
 
+// Return where the TLSDESC relocations should go, creating it if
+// necessary.  These follow the JUMP_SLOT relocations.
+
+Output_data_plt_x86_64::Reloc_section*
+Output_data_plt_x86_64::rela_tlsdesc(Layout* layout)
+{
+  if (this->tlsdesc_rel_ == NULL)
+    {
+      this->tlsdesc_rel_ = new Reloc_section(false);
+      layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
+                                     elfcpp::SHF_ALLOC, this->tlsdesc_rel_,
+                                     true, false, false, false);
+      gold_assert(this->tlsdesc_rel_->output_section() ==
+                 this->rel_->output_section());
+    }
+  return this->tlsdesc_rel_;
+}
+
 // Set the final size.
 void
 Output_data_plt_x86_64::set_final_data_size()
@@ -813,6 +918,14 @@ Target_x86_64::make_plt_section(Symbol_table* symtab, Layout* layout)
     }
 }
 
+// Return the section for TLSDESC relocations.
+
+Target_x86_64::Reloc_section*
+Target_x86_64::rela_tlsdesc_section(Layout* layout) const
+{
+  return this->plt_section()->rela_tlsdesc(layout);
+}
+
 // Create a PLT entry for a global symbol.
 
 void
@@ -1199,18 +1312,21 @@ Target_x86_64::Scan::local(Symbol_table* symtab,
                 Output_data_got<64, false>* got
                     = target->got_section(symtab, layout);
                 unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
-               unsigned int shndx = lsym.get_st_shndx();
-               bool is_ordinary;
-               shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
-               if (!is_ordinary)
-                 object->error(_("local symbol %u has bad shndx %u"),
-                             r_sym, shndx);
-                else
-                 got->add_local_pair_with_rela(object, r_sym,
-                                               shndx,
-                                               GOT_TYPE_TLS_DESC,
-                                               target->rela_dyn_section(layout),
-                                               elfcpp::R_X86_64_TLSDESC, 0);
+               if (!object->local_has_got_offset(r_sym, GOT_TYPE_TLS_DESC))
+                 {
+                   unsigned int got_offset = got->add_constant(0);
+                   got->add_constant(0);
+                   object->set_local_got_offset(r_sym, GOT_TYPE_TLS_DESC,
+                                                got_offset);
+                   Reloc_section* rt = target->rela_tlsdesc_section(layout);
+                   // We store the arguments we need in a vector, and
+                   // use the index into the vector as the parameter
+                   // to pass to the target specific routines.
+                   uintptr_t intarg = target->add_tlsdesc_info(object, r_sym);
+                   void* arg = reinterpret_cast<void*>(intarg);
+                   rt->add_target_specific(elfcpp::R_X86_64_TLSDESC, arg,
+                                           got, got_offset, 0);
+                 }
              }
            else if (optimized_type != tls::TLSOPT_TO_LE)
              unsupported_reloc_local(object, r_type);
@@ -1282,6 +1398,76 @@ Target_x86_64::Scan::unsupported_reloc_global(Sized_relobj<64, false>* object,
             object->name().c_str(), r_type, gsym->demangled_name().c_str());
 }
 
+// Returns true if this relocation type could be that of a function pointer
+// only if the target is not position-independent code.
+inline bool
+Target_x86_64::Scan::possible_function_pointer_reloc(unsigned int r_type)
+{
+  if (parameters->options().shared())
+    return false;
+
+  switch (r_type)
+    {
+    case elfcpp::R_X86_64_64:
+    case elfcpp::R_X86_64_32:
+    case elfcpp::R_X86_64_32S:
+    case elfcpp::R_X86_64_16:
+    case elfcpp::R_X86_64_8:
+      {
+        return true;
+      }
+    }
+  return false;
+}
+
+// For safe ICF, scan a relocation for a local symbol to check if it
+// corresponds to a function pointer being taken.  In that case mark
+// the function whose pointer was taken as not foldable.
+
+inline bool
+Target_x86_64::Scan::local_reloc_may_be_function_pointer(
+  Symbol_table* ,
+  Layout* ,
+  Target_x86_64* ,
+  Sized_relobj<64, false>* ,
+  unsigned int ,
+  Output_section* ,
+  const elfcpp::Rela<64, false>& ,
+  unsigned int r_type,
+  const elfcpp::Sym<64, false>&)
+{
+  // When building a shared library, do not fold any local symbols as it is
+  // not possible to distinguish pointer taken versus a call by looking at
+  // the relocation types.
+  return (parameters->options().shared()
+          || possible_function_pointer_reloc(r_type));
+}
+
+// For safe ICF, scan a relocation for a global symbol to check if it
+// corresponds to a function pointer being taken.  In that case mark
+// the function whose pointer was taken as not foldable.
+
+inline bool
+Target_x86_64::Scan::global_reloc_may_be_function_pointer(
+  Symbol_table*,
+  Layout* ,
+  Target_x86_64* ,
+  Sized_relobj<64, false>* ,
+  unsigned int ,
+  Output_section* ,
+  const elfcpp::Rela<64, false>& ,
+  unsigned int r_type,
+  Symbol* gsym)
+{
+  // When building a shared library, do not fold symbols whose visibility
+  // is hidden, internal or protected.
+  return ((parameters->options().shared()
+           && (gsym->visibility() == elfcpp::STV_INTERNAL
+              || gsym->visibility() == elfcpp::STV_PROTECTED
+              || gsym->visibility() == elfcpp::STV_HIDDEN))
+          || possible_function_pointer_reloc(r_type));
+}
+
 // Scan a relocation for a global symbol.
 
 inline void
@@ -1505,8 +1691,8 @@ Target_x86_64::Scan::global(Symbol_table* symtab,
                // Create a double GOT entry with an R_X86_64_TLSDESC reloc.
                 Output_data_got<64, false>* got
                     = target->got_section(symtab, layout);
-                got->add_global_pair_with_rela(gsym, GOT_TYPE_TLS_DESC,
-                                               target->rela_dyn_section(layout),
+               Reloc_section *rt = target->rela_tlsdesc_section(layout);
+                got->add_global_pair_with_rela(gsym, GOT_TYPE_TLS_DESC, rt,
                                                elfcpp::R_X86_64_TLSDESC, 0);
              }
            else if (optimized_type == tls::TLSOPT_TO_IE)
@@ -1657,9 +1843,9 @@ Target_x86_64::do_finalize_sections(
 {
   const Reloc_section* rel_plt = (this->plt_ == NULL
                                  ? NULL
-                                 : this->plt_->rel_plt());
+                                 : this->plt_->rela_plt());
   layout->add_target_dynamic_tags(false, this->got_plt_, rel_plt,
-                                 this->rela_dyn_, true);
+                                 this->rela_dyn_, true, false);
                                  
   // Fill in some more dynamic tags.
   Output_data_dynamic* const odyn = layout->dynamic_data();
@@ -1971,7 +2157,7 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo,
   elfcpp::Elf_types<64>::Elf_Addr value = psymval->value(relinfo->object, 0);
 
   const bool is_final = (gsym == NULL
-                        ? !parameters->options().output_is_position_independent()
+                        ? !parameters->options().shared()
                         : gsym->final_value_is_known());
   const tls::Tls_optimization optimized_type
       = Target_x86_64::optimize_tls_reloc(is_final, r_type);
@@ -2658,6 +2844,25 @@ Target_x86_64::do_code_fill(section_size_type length) const
   return std::string(nops[length], length);
 }
 
+// Return the addend to use for a target specific relocation.  The
+// only target specific relocation is R_X86_64_TLSDESC for a local
+// symbol.  We want to set the addend is the offset of the local
+// symbol in the TLS segment.
+
+uint64_t
+Target_x86_64::do_reloc_addend(void* arg, unsigned int r_type,
+                              uint64_t) const
+{
+  gold_assert(r_type == elfcpp::R_X86_64_TLSDESC);
+  uintptr_t intarg = reinterpret_cast<uintptr_t>(arg);
+  gold_assert(intarg < this->tlsdesc_reloc_info_.size());
+  const Tlsdesc_info& ti(this->tlsdesc_reloc_info_[intarg]);
+  const Symbol_value<64>* psymval = ti.object->local_symbol(ti.r_sym);
+  gold_assert(psymval->is_tls_symbol());
+  // The value of a TLS symbol is the offset in the TLS segment.
+  return psymval->value(ti.object, 0);
+}
+
 // FNOFFSET in section SHNDX in OBJECT is the start of a function
 // compiled with -fstack-split.  The function calls non-stack-split
 // code.  We have to change the function so that it always ensures