2010-03-08 Hui Zhu <teawater@gmail.com>
[binutils-gdb.git] / gold / x86_64.cc
index cbc45beda15eae87d85970bb140e2f154e34893d..e9dd5ae5d5a6ba4f84735757cd2a0526c6ce6c41 100644 (file)
@@ -39,6 +39,7 @@
 #include "tls.h"
 #include "freebsd.h"
 #include "gc.h"
+#include "icf.h"
 
 namespace
 {
@@ -63,11 +64,22 @@ class Target_x86_64 : public Target_freebsd<64, false>
 
   Target_x86_64()
     : Target_freebsd<64, false>(&x86_64_info),
-      got_(NULL), plt_(NULL), got_plt_(NULL), rela_dyn_(NULL),
-      copy_relocs_(elfcpp::R_X86_64_COPY), dynbss_(NULL),
-      got_mod_index_offset_(-1U), tls_base_symbol_defined_(false)
+      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), 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;
@@ -102,7 +114,7 @@ class Target_x86_64 : public Target_freebsd<64, false>
 
   // Finalize the sections.
   void
-  do_finalize_sections(Layout*, const Input_objects*);
+  do_finalize_sections(Layout*, const Input_objects*, Symbol_table*);
 
   // Return the value to use for a dynamic which requires special
   // treatment.
@@ -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,12 +465,29 @@ 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.
   Output_data_plt_x86_64* plt_;
   // The GOT PLT section.
   Output_data_space* got_plt_;
+  // The _GLOBAL_OFFSET_TABLE_ symbol.
+  Symbol* global_offset_table_;
   // The dynamic reloc section.
   Reloc_section* rela_dyn_;
   // Relocs saved to avoid a COPY reloc.
@@ -418,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_;
 };
@@ -439,7 +521,9 @@ const Target::Target_info Target_x86_64::x86_64_info =
   elfcpp::SHN_UNDEF,   // small_common_shndx
   elfcpp::SHN_X86_64_LCOMMON,  // large_common_shndx
   0,                   // small_common_section_flags
-  elfcpp::SHF_X86_64_LARGE     // large_common_section_flags
+  elfcpp::SHF_X86_64_LARGE,    // large_common_section_flags
+  NULL,                        // attributes_section
+  NULL                 // attributes_vendor
 };
 
 // This is called when a new output section is created.  This is where
@@ -467,30 +551,31 @@ Target_x86_64::got_section(Symbol_table* symtab, Layout* layout)
       os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
                                           (elfcpp::SHF_ALLOC
                                            | elfcpp::SHF_WRITE),
-                                          this->got_, false);
-      os->set_is_relro();
+                                          this->got_, false, true, true,
+                                          false);
 
-      // The old GNU linker creates a .got.plt section.  We just
-      // create another set of data in the .got section.  Note that we
-      // always create a PLT if we create a GOT, although the PLT
-      // might be empty.
       this->got_plt_ = new Output_data_space(8, "** GOT PLT");
-      os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
+      os = layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
                                           (elfcpp::SHF_ALLOC
                                            | elfcpp::SHF_WRITE),
-                                          this->got_plt_, false);
-      os->set_is_relro();
+                                          this->got_plt_, false, false,
+                                          false, true);
 
       // The first three entries are reserved.
       this->got_plt_->set_current_data_size(3 * 8);
 
+      // Those bytes can go into the relro segment.
+      layout->increase_relro(3 * 8);
+
       // Define _GLOBAL_OFFSET_TABLE_ at the start of the PLT.
-      symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
-                                   this->got_plt_,
-                                   0, 0, elfcpp::STT_OBJECT,
-                                   elfcpp::STB_LOCAL,
-                                   elfcpp::STV_HIDDEN, 0,
-                                   false, false);
+      this->global_offset_table_ =
+       symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
+                                     Symbol_table::PREDEFINED,
+                                     this->got_plt_,
+                                     0, 0, elfcpp::STT_OBJECT,
+                                     elfcpp::STB_LOCAL,
+                                     elfcpp::STV_HIDDEN, 0,
+                                     false, false);
     }
 
   return this->got_;
@@ -506,7 +591,8 @@ Target_x86_64::rela_dyn_section(Layout* layout)
       gold_assert(layout != NULL);
       this->rela_dyn_ = new Reloc_section(parameters->options().combreloc());
       layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA,
-                                     elfcpp::SHF_ALLOC, this->rela_dyn_, true);
+                                     elfcpp::SHF_ALLOC, this->rela_dyn_, true,
+                                     false, false, false);
     }
   return this->rela_dyn_;
 }
@@ -545,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);
@@ -584,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.
@@ -601,12 +694,13 @@ 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,
-                                 elfcpp::SHF_ALLOC, this->rel_, true);
+                                 elfcpp::SHF_ALLOC, this->rel_, true,
+                                 false, false, false);
 }
 
 void
@@ -645,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()
@@ -802,10 +914,18 @@ Target_x86_64::make_plt_section(Symbol_table* symtab, Layout* layout)
       layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
                                      (elfcpp::SHF_ALLOC
                                       | elfcpp::SHF_EXECINSTR),
-                                     this->plt_, false);
+                                     this->plt_, false, false, false, false);
     }
 }
 
+// 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
@@ -834,6 +954,7 @@ Target_x86_64::define_tls_base_symbol(Symbol_table* symtab, Layout* layout)
     {
       bool is_exec = parameters->options().output_is_executable();
       symtab->define_in_output_segment("_TLS_MODULE_BASE_", NULL,
+                                      Symbol_table::PREDEFINED,
                                       tls_segment, 0, 0,
                                       elfcpp::STT_TLS,
                                       elfcpp::STB_LOCAL,
@@ -1191,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);
@@ -1274,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
@@ -1350,7 +1544,7 @@ Target_x86_64::Scan::global(Symbol_table* symtab,
           target->make_plt_entry(symtab, layout, gsym);
         // Make a dynamic relocation if necessary.
         int flags = Symbol::NON_PIC_REF;
-        if (gsym->type() == elfcpp::STT_FUNC)
+        if (gsym->is_func())
           flags |= Symbol::FUNCTION_CALL;
         if (gsym->needs_dynamic_reloc(flags))
           {
@@ -1497,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)
@@ -1642,50 +1836,32 @@ Target_x86_64::scan_relocs(Symbol_table* symtab,
 // Finalize the sections.
 
 void
-Target_x86_64::do_finalize_sections(Layout* layout, const Input_objects*)
+Target_x86_64::do_finalize_sections(
+    Layout* layout,
+    const Input_objects*,
+    Symbol_table* symtab)
 {
+  const Reloc_section* rel_plt = (this->plt_ == NULL
+                                 ? NULL
+                                 : this->plt_->rela_plt());
+  layout->add_target_dynamic_tags(false, this->got_plt_, rel_plt,
+                                 this->rela_dyn_, true, false);
+                                 
   // Fill in some more dynamic tags.
   Output_data_dynamic* const odyn = layout->dynamic_data();
   if (odyn != NULL)
     {
-      if (this->got_plt_ != NULL
-         && this->got_plt_->output_section() != NULL)
-       odyn->add_section_address(elfcpp::DT_PLTGOT, this->got_plt_);
-
       if (this->plt_ != NULL
-         && this->plt_->output_section() != NULL)
-       {
-         const Output_data* od = this->plt_->rel_plt();
-         odyn->add_section_size(elfcpp::DT_PLTRELSZ, od);
-         odyn->add_section_address(elfcpp::DT_JMPREL, od);
-         odyn->add_constant(elfcpp::DT_PLTREL, elfcpp::DT_RELA);
-         if (this->plt_->has_tlsdesc_entry())
-           {
-              unsigned int plt_offset = this->plt_->get_tlsdesc_plt_offset();
-              unsigned int got_offset = this->plt_->get_tlsdesc_got_offset();
-              this->got_->finalize_data_size();
-              odyn->add_section_plus_offset(elfcpp::DT_TLSDESC_PLT,
-                                            this->plt_, plt_offset);
-              odyn->add_section_plus_offset(elfcpp::DT_TLSDESC_GOT,
-                                            this->got_, got_offset);
-           }
-       }
-
-      if (this->rela_dyn_ != NULL
-         && this->rela_dyn_->output_section() != NULL)
+         && this->plt_->output_section() != NULL
+         && this->plt_->has_tlsdesc_entry())
        {
-         const Output_data* od = this->rela_dyn_;
-         odyn->add_section_address(elfcpp::DT_RELA, od);
-         odyn->add_section_size(elfcpp::DT_RELASZ, od);
-         odyn->add_constant(elfcpp::DT_RELAENT,
-                            elfcpp::Elf_sizes<64>::rela_size);
-       }
-
-      if (!parameters->options().shared())
-       {
-         // The value of the DT_DEBUG tag is filled in by the dynamic
-         // linker at run time, and used by the debugger.
-         odyn->add_constant(elfcpp::DT_DEBUG, 0);
+         unsigned int plt_offset = this->plt_->get_tlsdesc_plt_offset();
+         unsigned int got_offset = this->plt_->get_tlsdesc_got_offset();
+         this->got_->finalize_data_size();
+         odyn->add_section_plus_offset(elfcpp::DT_TLSDESC_PLT,
+                                       this->plt_, plt_offset);
+         odyn->add_section_plus_offset(elfcpp::DT_TLSDESC_GOT,
+                                       this->got_, got_offset);
        }
     }
 
@@ -1693,6 +1869,15 @@ Target_x86_64::do_finalize_sections(Layout* layout, const Input_objects*)
   // relocs.
   if (this->copy_relocs_.any_saved_relocs())
     this->copy_relocs_.emit(this->rela_dyn_section(layout));
+
+  // Set the size of the _GLOBAL_OFFSET_TABLE_ symbol to the size of
+  // the .got.plt section.
+  Symbol* sym = this->global_offset_table_;
+  if (sym != NULL)
+    {
+      uint64_t data_size = this->got_plt_->current_data_size();
+      symtab->get_sized_symbol<64>(sym)->set_symsize(data_size);
+    }
 }
 
 // Perform a relocation.
@@ -1972,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);
@@ -2659,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
@@ -2687,7 +2891,11 @@ Target_x86_64::do_calls_non_split(Relobj* object, unsigned int shndx,
       this->set_view_to_nop(view, view_size, fnoffset + 1, 8);
     }
   // lea NN(%rsp),%r10
-  else if (this->match_view(view, view_size, fnoffset, "\x4c\x8d\x94\x24", 4)
+  // lea NN(%rsp),%r11
+  else if ((this->match_view(view, view_size, fnoffset,
+                            "\x4c\x8d\x94\x24", 4)
+           || this->match_view(view, view_size, fnoffset,
+                               "\x4c\x8d\x9c\x24", 4))
           && fnsize > 8)
     {
       // This is loading an offset from the stack pointer for a
@@ -2705,7 +2913,7 @@ Target_x86_64::do_calls_non_split(Relobj* object, unsigned int shndx,
       if (!object->has_no_split_stack())
        object->error(_("failed to match split-stack sequence at "
                        "section %u offset %0zx"),
-                     shndx, fnoffset);
+                     shndx, static_cast<size_t>(fnoffset));
       return;
     }