Use partial register read/writes in transfer_regset
[deliverable/binutils-gdb.git] / gold / x86_64.cc
index 4116429e27b99fc1952ba4fd1f0375c4682449f9..603ac300f72f1b1ab37b3c2332528b5ce06d302e 100644 (file)
@@ -1,6 +1,6 @@
 // x86_64.cc -- x86_64 target support for gold.
 
-// Copyright (C) 2006-2017 Free Software Foundation, Inc.
+// Copyright (C) 2006-2018 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -478,29 +478,29 @@ class Output_data_plt_x86_64_bnd : public Output_data_plt_x86_64<64>
 
   virtual void
   do_fill_first_plt_entry(unsigned char* pov,
-                         typename elfcpp::Elf_types<64>::Elf_Addr got_addr,
-                         typename elfcpp::Elf_types<64>::Elf_Addr plt_addr);
+                         elfcpp::Elf_types<64>::Elf_Addr got_addr,
+                         elfcpp::Elf_types<64>::Elf_Addr plt_addr);
 
   virtual unsigned int
   do_fill_plt_entry(unsigned char* pov,
-                   typename elfcpp::Elf_types<64>::Elf_Addr got_address,
-                   typename elfcpp::Elf_types<64>::Elf_Addr plt_address,
+                   elfcpp::Elf_types<64>::Elf_Addr got_address,
+                   elfcpp::Elf_types<64>::Elf_Addr plt_address,
                    unsigned int got_offset,
                    unsigned int plt_offset,
                    unsigned int plt_index);
 
   virtual void
   do_fill_tlsdesc_entry(unsigned char* pov,
-                       typename elfcpp::Elf_types<64>::Elf_Addr got_address,
-                       typename elfcpp::Elf_types<64>::Elf_Addr plt_address,
-                       typename elfcpp::Elf_types<64>::Elf_Addr got_base,
+                       elfcpp::Elf_types<64>::Elf_Addr got_address,
+                       elfcpp::Elf_types<64>::Elf_Addr plt_address,
+                       elfcpp::Elf_types<64>::Elf_Addr got_base,
                        unsigned int tlsdesc_got_offset,
                        unsigned int plt_offset);
 
   void
   fill_aplt_entry(unsigned char* pov,
-                 typename elfcpp::Elf_types<64>::Elf_Addr got_address,
-                 typename elfcpp::Elf_types<64>::Elf_Addr plt_address,
+                 elfcpp::Elf_types<64>::Elf_Addr got_address,
+                 elfcpp::Elf_types<64>::Elf_Addr plt_address,
                  unsigned int got_offset,
                  unsigned int plt_offset,
                  unsigned int plt_index);
@@ -729,10 +729,13 @@ class Target_x86_64 : public Sized_target<size, false>
   // 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.
+  // pie binaries, safe ICF cannot be done by looking at only relocation
+  // types, and for certain cases (e.g. R_X86_64_PC32), the instruction
+  // opcode is checked as well to distinguish a function call from taking
+  // a function's pointer.
   bool
   do_can_check_for_function_pointers() const
-  { return !parameters->options().pie(); }
+  { return true; }
 
   // Return the base for a DW_EH_PE_datarel encoding.
   uint64_t
@@ -924,7 +927,10 @@ class Target_x86_64 : public Sized_target<size, false>
     check_non_pic(Relobj*, unsigned int r_type, Symbol*);
 
     inline bool
-    possible_function_pointer_reloc(unsigned int r_type);
+    possible_function_pointer_reloc(Sized_relobj_file<size, false>* src_obj,
+                                    unsigned int src_indx,
+                                    unsigned int r_offset,
+                                    unsigned int r_type);
 
     bool
     reloc_needs_plt_for_ifunc(Sized_relobj_file<size, false>*,
@@ -1046,8 +1052,11 @@ class Target_x86_64 : public Sized_target<size, false>
       return false;
     // We cannot convert references to IFUNC symbols, or to symbols that
     // are not local to the current module.
+    // We can't do predefined symbols because they may become undefined
+    // (e.g., __ehdr_start when the headers aren't mapped to a segment).
     if (gsym->type() == elfcpp::STT_GNU_IFUNC
-        || gsym->is_undefined ()
+        || gsym->is_undefined()
+        || gsym->is_predefined()
         || gsym->is_from_dynobj()
         || gsym->is_preemptible())
       return false;
@@ -1264,6 +1273,7 @@ const Target::Target_info Target_x86_64<64>::x86_64_info =
   NULL,                        // attributes_vendor
   "_start",            // entry_symbol_name
   32,                  // hash_entry_size
+  elfcpp::SHT_X86_64_UNWIND,   // unwind_section_type
 };
 
 template<>
@@ -1292,6 +1302,7 @@ const Target::Target_info Target_x86_64<32>::x86_64_info =
   NULL,                        // attributes_vendor
   "_start",            // entry_symbol_name
   32,                  // hash_entry_size
+  elfcpp::SHT_X86_64_UNWIND,   // unwind_section_type
 };
 
 // This is called when a new output section is created.  This is where
@@ -1870,8 +1881,8 @@ Output_data_plt_x86_64_bnd::first_plt_entry[plt_entry_size] =
 void
 Output_data_plt_x86_64_bnd::do_fill_first_plt_entry(
     unsigned char* pov,
-    typename elfcpp::Elf_types<64>::Elf_Addr got_address,
-    typename elfcpp::Elf_types<64>::Elf_Addr plt_address)
+    elfcpp::Elf_types<64>::Elf_Addr got_address,
+    elfcpp::Elf_types<64>::Elf_Addr plt_address)
 {
   memcpy(pov, first_plt_entry, plt_entry_size);
   // We do a jmp relative to the PC at the end of this instruction.
@@ -1910,8 +1921,8 @@ Output_data_plt_x86_64_bnd::aplt_entry[aplt_entry_size] =
 unsigned int
 Output_data_plt_x86_64_bnd::do_fill_plt_entry(
     unsigned char* pov,
-    typename elfcpp::Elf_types<64>::Elf_Addr,
-    typename elfcpp::Elf_types<64>::Elf_Addr,
+    elfcpp::Elf_types<64>::Elf_Addr,
+    elfcpp::Elf_types<64>::Elf_Addr,
     unsigned int,
     unsigned int plt_offset,
     unsigned int plt_index)
@@ -1925,8 +1936,8 @@ Output_data_plt_x86_64_bnd::do_fill_plt_entry(
 void
 Output_data_plt_x86_64_bnd::fill_aplt_entry(
     unsigned char* pov,
-    typename elfcpp::Elf_types<64>::Elf_Addr got_address,
-    typename elfcpp::Elf_types<64>::Elf_Addr plt_address,
+    elfcpp::Elf_types<64>::Elf_Addr got_address,
+    elfcpp::Elf_types<64>::Elf_Addr plt_address,
     unsigned int got_offset,
     unsigned int plt_offset,
     unsigned int plt_index)
@@ -1959,9 +1970,9 @@ Output_data_plt_x86_64_bnd::tlsdesc_plt_entry[plt_entry_size] =
 void
 Output_data_plt_x86_64_bnd::do_fill_tlsdesc_entry(
     unsigned char* pov,
-    typename elfcpp::Elf_types<64>::Elf_Addr got_address,
-    typename elfcpp::Elf_types<64>::Elf_Addr plt_address,
-    typename elfcpp::Elf_types<64>::Elf_Addr got_base,
+    elfcpp::Elf_types<64>::Elf_Addr got_address,
+    elfcpp::Elf_types<64>::Elf_Addr plt_address,
+    elfcpp::Elf_types<64>::Elf_Addr got_base,
     unsigned int tlsdesc_got_offset,
     unsigned int plt_offset)
 {
@@ -2161,13 +2172,13 @@ Output_data_plt_x86_64_bnd::do_write(Output_file* of)
   unsigned char* pov = oview;
 
   // The base address of the .plt section.
-  typename elfcpp::Elf_types<64>::Elf_Addr plt_address = this->address();
+  elfcpp::Elf_types<64>::Elf_Addr plt_address = this->address();
   // The base address of the .got section.
-  typename elfcpp::Elf_types<64>::Elf_Addr got_base = got->address();
+  elfcpp::Elf_types<64>::Elf_Addr got_base = got->address();
   // The base address of the PLT portion of the .got section,
   // which is where the GOT pointer will point, and where the
   // three reserved GOT entries are located.
-  typename elfcpp::Elf_types<64>::Elf_Addr got_address = got_plt->address();
+  elfcpp::Elf_types<64>::Elf_Addr got_address = got_plt->address();
 
   this->fill_first_plt_entry(pov, got_address, plt_address);
   pov += plt_entry_size;
@@ -3057,9 +3068,10 @@ Target_x86_64<size>::Scan::local(Symbol_table* symtab,
        // mov foo@GOTPCREL(%rip), %reg
        // to lea foo(%rip), %reg.
        // in Relocate::relocate.
-       if ((r_type == elfcpp::R_X86_64_GOTPCREL
-            || r_type == elfcpp::R_X86_64_GOTPCRELX
-            || r_type == elfcpp::R_X86_64_REX_GOTPCRELX)
+       if (!parameters->incremental()
+           && (r_type == elfcpp::R_X86_64_GOTPCREL
+               || r_type == elfcpp::R_X86_64_GOTPCRELX
+               || r_type == elfcpp::R_X86_64_REX_GOTPCRELX)
            && reloc.get_r_offset() >= 2
            && !is_ifunc)
          {
@@ -3070,7 +3082,6 @@ Target_x86_64<size>::Scan::local(Symbol_table* symtab,
              break;
          }
 
-
        // The symbol requires a GOT entry.
        unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
 
@@ -3274,7 +3285,11 @@ Target_x86_64<size>::Scan::unsupported_reloc_global(
 // Returns true if this relocation type could be that of a function pointer.
 template<int size>
 inline bool
-Target_x86_64<size>::Scan::possible_function_pointer_reloc(unsigned int r_type)
+Target_x86_64<size>::Scan::possible_function_pointer_reloc(
+    Sized_relobj_file<size, false>* src_obj,
+    unsigned int src_indx,
+    unsigned int r_offset,
+    unsigned int r_type)
 {
   switch (r_type)
     {
@@ -3293,6 +3308,41 @@ Target_x86_64<size>::Scan::possible_function_pointer_reloc(unsigned int r_type)
       {
        return true;
       }
+    case elfcpp::R_X86_64_PC32:
+      {
+        // This relocation may be used both for function calls and
+        // for taking address of a function. We distinguish between
+        // them by checking the opcodes.
+        uint64_t sh_flags = src_obj->section_flags(src_indx);
+        bool is_executable = (sh_flags & elfcpp::SHF_EXECINSTR) != 0;
+        if (is_executable)
+          {
+            section_size_type stype;
+            const unsigned char* view = src_obj->section_contents(src_indx,
+                                                                  &stype,
+                                                                  true);
+
+            // call
+            if (r_offset >= 1
+                && view[r_offset - 1] == 0xe8)
+              return false;
+
+            // jmp
+            if (r_offset >= 1
+                && view[r_offset - 1] == 0xe9)
+              return false;
+
+            // jo/jno/jb/jnb/je/jne/jna/ja/js/jns/jp/jnp/jl/jge/jle/jg
+            if (r_offset >= 2
+                && view[r_offset - 2] == 0x0f
+                && view[r_offset - 1] >= 0x80
+                && view[r_offset - 1] <= 0x8f)
+              return false;
+          }
+
+        // Be conservative and treat all others as function pointers.
+        return true;
+      }
     }
   return false;
 }
@@ -3307,18 +3357,21 @@ Target_x86_64<size>::Scan::local_reloc_may_be_function_pointer(
   Symbol_table* ,
   Layout* ,
   Target_x86_64<size>* ,
-  Sized_relobj_file<size, false>* ,
-  unsigned int ,
+  Sized_relobj_file<size, false>* src_obj,
+  unsigned int src_indx,
   Output_section* ,
-  const elfcpp::Rela<size, false>& ,
+  const elfcpp::Rela<size, false>& reloc,
   unsigned int r_type,
   const elfcpp::Sym<size, 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));
+  if (parameters->options().shared())
+    return true;
+
+  return possible_function_pointer_reloc(src_obj, src_indx,
+                                         reloc.get_r_offset(), r_type);
 }
 
 // For safe ICF, scan a relocation for a global symbol to check if it
@@ -3331,20 +3384,23 @@ Target_x86_64<size>::Scan::global_reloc_may_be_function_pointer(
   Symbol_table*,
   Layout* ,
   Target_x86_64<size>* ,
-  Sized_relobj_file<size, false>* ,
-  unsigned int ,
+  Sized_relobj_file<size, false>* src_obj,
+  unsigned int src_indx,
   Output_section* ,
-  const elfcpp::Rela<size, false>& ,
+  const elfcpp::Rela<size, false>& reloc,
   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));
+  if (parameters->options().shared()
+      && (gsym->visibility() == elfcpp::STV_INTERNAL
+         || gsym->visibility() == elfcpp::STV_PROTECTED
+         || gsym->visibility() == elfcpp::STV_HIDDEN))
+    return true;
+
+  return possible_function_pointer_reloc(src_obj, src_indx,
+                                         reloc.get_r_offset(), r_type);
 }
 
 // Scan a relocation for a global symbol.
@@ -3492,15 +3548,21 @@ Target_x86_64<size>::Scan::global(Symbol_table* symtab,
        // (callq|jmpq) *foo@GOTPCRELX(%rip) to
        // (callq|jmpq) foo
        // in Relocate::relocate, then there is nothing to do here.
+       // We cannot make these optimizations in incremental linking mode,
+       // because we look at the opcode to decide whether or not to make
+       // change, and during an incremental update, the change may have
+       // already been applied.
 
         Lazy_view<size> view(object, data_shndx);
         size_t r_offset = reloc.get_r_offset();
-        if (r_offset >= 2
+        if (!parameters->incremental()
+           && r_offset >= 2
             && Target_x86_64<size>::can_convert_mov_to_lea(gsym, r_type,
                                                            r_offset, &view))
           break;
 
-       if (r_offset >= 2
+       if (!parameters->incremental()
+           && r_offset >= 2
            && Target_x86_64<size>::can_convert_callq_to_direct(gsym, r_type,
                                                                r_offset,
                                                                &view))
@@ -4157,7 +4219,7 @@ Target_x86_64<size>::Relocate::relocate(
 
     case elfcpp::R_X86_64_GOT64:
     case elfcpp::R_X86_64_GOTPLT64:
-      // R_X86_64_GOTPLT64 is obsolete and treated the the same as
+      // R_X86_64_GOTPLT64 is obsolete and treated the same as
       // GOT64.
       gold_assert(have_got_offset);
       Reloc_funcs::rela64(view, got_offset, addend);
@@ -4189,14 +4251,15 @@ Target_x86_64<size>::Relocate::relocate(
       // mov foo@GOTPCREL(%rip), %reg
       // to lea foo(%rip), %reg.
       // if possible.
-       if ((gsym == NULL
-             && rela.get_r_offset() >= 2
-             && view[-2] == 0x8b
-             && !psymval->is_ifunc_symbol())
-            || (gsym != NULL
-                && rela.get_r_offset() >= 2
-                && Target_x86_64<size>::can_convert_mov_to_lea(gsym, r_type,
-                                                               0, &view)))
+      if (!parameters->incremental()
+         && ((gsym == NULL
+              && rela.get_r_offset() >= 2
+              && view[-2] == 0x8b
+              && !psymval->is_ifunc_symbol())
+             || (gsym != NULL
+                 && rela.get_r_offset() >= 2
+                 && Target_x86_64<size>::can_convert_mov_to_lea(gsym, r_type,
+                                                                0, &view))))
        {
          view[-2] = 0x8d;
          Reloc_funcs::pcrela32(view, object, psymval, addend, address);
@@ -4207,7 +4270,8 @@ Target_x86_64<size>::Relocate::relocate(
       // and jmpq *foo@GOTPCRELX(%rip) to
       // jmpq foo
       // nop
-      else if (gsym != NULL
+      else if (!parameters->incremental()
+              && gsym != NULL
               && rela.get_r_offset() >= 2
               && Target_x86_64<size>::can_convert_callq_to_direct(gsym,
                                                                   r_type,
@@ -4244,12 +4308,14 @@ Target_x86_64<size>::Relocate::relocate(
          if (gsym != NULL)
            {
              gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD));
-             got_offset = gsym->got_offset(GOT_TYPE_STANDARD) - target->got_size();
+             got_offset = (gsym->got_offset(GOT_TYPE_STANDARD)
+                           - target->got_size());
            }
          else
            {
              unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
-             gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD));
+             gold_assert(object->local_has_got_offset(r_sym,
+                                                      GOT_TYPE_STANDARD));
              got_offset = (object->local_got_offset(r_sym, GOT_TYPE_STANDARD)
                            - target->got_size());
            }
@@ -5514,6 +5580,7 @@ const Target::Target_info Target_x86_64_nacl<64>::x86_64_nacl_info =
   NULL,                        // attributes_vendor
   "_start",            // entry_symbol_name
   32,                  // hash_entry_size
+  elfcpp::SHT_X86_64_UNWIND,   // unwind_section_type
 };
 
 template<>
@@ -5542,6 +5609,7 @@ const Target::Target_info Target_x86_64_nacl<32>::x86_64_nacl_info =
   NULL,                        // attributes_vendor
   "_start",            // entry_symbol_name
   32,                  // hash_entry_size
+  elfcpp::SHT_X86_64_UNWIND,   // unwind_section_type
 };
 
 #define        NACLMASK        0xe0            // 32-byte alignment mask.
This page took 0.028175 seconds and 4 git commands to generate.