Fix internal error with -z relro when .tbss is last relro section.
[deliverable/binutils-gdb.git] / gold / x86_64.cc
index bda622763a4ad6653b25a3e10830cb68218f42f8..4543c8a8abc25f1ba7fea916f676badae0abdf98 100644 (file)
@@ -1,6 +1,6 @@
 // x86_64.cc -- x86_64 target support for gold.
 
-// Copyright (C) 2006-2014 Free Software Foundation, Inc.
+// Copyright (C) 2006-2015 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -48,6 +48,37 @@ namespace
 
 using namespace gold;
 
+// A class to handle the .got.plt section.
+
+class Output_data_got_plt_x86_64 : public Output_section_data_build
+{
+ public:
+  Output_data_got_plt_x86_64(Layout* layout)
+    : Output_section_data_build(8),
+      layout_(layout)
+  { }
+
+  Output_data_got_plt_x86_64(Layout* layout, off_t data_size)
+    : Output_section_data_build(data_size, 8),
+      layout_(layout)
+  { }
+
+ protected:
+  // Write out the PLT data.
+  void
+  do_write(Output_file*);
+
+  // Write to a map file.
+  void
+  do_print_to_mapfile(Mapfile* mapfile) const
+  { mapfile->print_output_data(this, "** GOT PLT"); }
+
+ private:
+  // A pointer to the Layout class, so that we can find the .dynamic
+  // section when we write out the GOT PLT section.
+  Layout* layout_;
+};
+
 // A class to handle the PLT data.
 // This is an abstract base class that handles most of the linker details
 // but does not know the actual contents of PLT entries.  The derived
@@ -61,9 +92,9 @@ class Output_data_plt_x86_64 : public Output_section_data
 
   Output_data_plt_x86_64(Layout* layout, uint64_t addralign,
                         Output_data_got<64, false>* got,
-                        Output_data_space* got_plt,
+                        Output_data_got_plt_x86_64* got_plt,
                         Output_data_space* got_irelative)
-    : Output_section_data(addralign), layout_(layout), tlsdesc_rel_(NULL),
+    : Output_section_data(addralign), tlsdesc_rel_(NULL),
       irelative_rel_(NULL), got_(got), got_plt_(got_plt),
       got_irelative_(got_irelative), count_(0), irelative_count_(0),
       tlsdesc_got_offset_(-1U), free_list_()
@@ -71,12 +102,12 @@ class Output_data_plt_x86_64 : public Output_section_data
 
   Output_data_plt_x86_64(Layout* layout, uint64_t plt_entry_size,
                         Output_data_got<64, false>* got,
-                        Output_data_space* got_plt,
+                        Output_data_got_plt_x86_64* got_plt,
                         Output_data_space* got_irelative,
                         unsigned int plt_count)
     : Output_section_data((plt_count + 1) * plt_entry_size,
                          plt_entry_size, false),
-      layout_(layout), tlsdesc_rel_(NULL), irelative_rel_(NULL), got_(got),
+      tlsdesc_rel_(NULL), irelative_rel_(NULL), got_(got),
       got_plt_(got_plt), got_irelative_(got_irelative), count_(plt_count),
       irelative_count_(0), tlsdesc_got_offset_(-1U), free_list_()
   {
@@ -268,9 +299,6 @@ class Output_data_plt_x86_64 : public Output_section_data
   void
   do_write(Output_file*);
 
-  // A pointer to the Layout class, so that we can find the .dynamic
-  // section when we write out the GOT PLT section.
-  Layout* layout_;
   // The reloc section.
   Reloc_section* rel_;
   // The TLSDESC relocs, if necessary.  These must follow the regular
@@ -282,7 +310,7 @@ class Output_data_plt_x86_64 : public Output_section_data
   // The .got section.
   Output_data_got<64, false>* got_;
   // The .got.plt section.
-  Output_data_space* got_plt_;
+  Output_data_got_plt_x86_64* got_plt_;
   // The part of the .got.plt section used for IRELATIVE relocs.
   Output_data_space* got_irelative_;
   // The number of PLT entries.
@@ -303,7 +331,7 @@ class Output_data_plt_x86_64_standard : public Output_data_plt_x86_64<size>
  public:
   Output_data_plt_x86_64_standard(Layout* layout,
                                  Output_data_got<64, false>* got,
-                                 Output_data_space* got_plt,
+                                 Output_data_got_plt_x86_64* got_plt,
                                  Output_data_space* got_irelative)
     : Output_data_plt_x86_64<size>(layout, plt_entry_size,
                                   got, got_plt, got_irelative)
@@ -311,7 +339,7 @@ class Output_data_plt_x86_64_standard : public Output_data_plt_x86_64<size>
 
   Output_data_plt_x86_64_standard(Layout* layout,
                                  Output_data_got<64, false>* got,
-                                 Output_data_space* got_plt,
+                                 Output_data_got_plt_x86_64* got_plt,
                                  Output_data_space* got_irelative,
                                  unsigned int plt_count)
     : Output_data_plt_x86_64<size>(layout, plt_entry_size,
@@ -618,7 +646,7 @@ class Target_x86_64 : public Sized_target<size, false>
   Output_data_plt_x86_64<size>*
   make_data_plt(Layout* layout,
                Output_data_got<64, false>* got,
-               Output_data_space* got_plt,
+               Output_data_got_plt_x86_64* got_plt,
                Output_data_space* got_irelative)
   {
     return this->do_make_data_plt(layout, got, got_plt, got_irelative);
@@ -627,7 +655,7 @@ class Target_x86_64 : public Sized_target<size, false>
   Output_data_plt_x86_64<size>*
   make_data_plt(Layout* layout,
                Output_data_got<64, false>* got,
-               Output_data_space* got_plt,
+               Output_data_got_plt_x86_64* got_plt,
                Output_data_space* got_irelative,
                unsigned int plt_count)
   {
@@ -638,7 +666,7 @@ class Target_x86_64 : public Sized_target<size, false>
   virtual Output_data_plt_x86_64<size>*
   do_make_data_plt(Layout* layout,
                   Output_data_got<64, false>* got,
-                  Output_data_space* got_plt,
+                  Output_data_got_plt_x86_64* got_plt,
                   Output_data_space* got_irelative)
   {
     return new Output_data_plt_x86_64_standard<size>(layout, got, got_plt,
@@ -648,7 +676,7 @@ class Target_x86_64 : public Sized_target<size, false>
   virtual Output_data_plt_x86_64<size>*
   do_make_data_plt(Layout* layout,
                   Output_data_got<64, false>* got,
-                  Output_data_space* got_plt,
+                  Output_data_got_plt_x86_64* got_plt,
                   Output_data_space* got_irelative,
                   unsigned int plt_count)
   {
@@ -847,7 +875,7 @@ class Target_x86_64 : public Sized_target<size, false>
   got_section(Symbol_table*, Layout*);
 
   // Get the GOT PLT section.
-  Output_data_space*
+  Output_data_got_plt_x86_64*
   got_plt_section() const
   {
     gold_assert(this->got_plt_ != NULL);
@@ -958,7 +986,7 @@ class Target_x86_64 : public Sized_target<size, false>
   // The PLT section.
   Output_data_plt_x86_64<size>* plt_;
   // The GOT PLT section.
-  Output_data_space* got_plt_;
+  Output_data_got_plt_x86_64* got_plt_;
   // The GOT section for IRELATIVE relocations.
   Output_data_space* got_irelative_;
   // The GOT section for TLSDESC relocations.
@@ -1074,7 +1102,7 @@ Target_x86_64<size>::got_section(Symbol_table* symtab, Layout* layout)
                                       | elfcpp::SHF_WRITE),
                                      this->got_, got_order, true);
 
-      this->got_plt_ = new Output_data_space(8, "** GOT PLT");
+      this->got_plt_ = new Output_data_got_plt_x86_64(layout);
       layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
                                      (elfcpp::SHF_ALLOC
                                       | elfcpp::SHF_WRITE),
@@ -1163,6 +1191,27 @@ Target_x86_64<size>::rela_irelative_section(Layout* layout)
   return this->rela_irelative_;
 }
 
+// Write the first three reserved words of the .got.plt section.
+// The remainder of the section is written while writing the PLT
+// in Output_data_plt_i386::do_write.
+
+void
+Output_data_got_plt_x86_64::do_write(Output_file* of)
+{
+  // The first entry in the GOT is the address of the .dynamic section
+  // aka the PT_DYNAMIC segment.  The next two entries are reserved.
+  // We saved space for them when we created the section in
+  // Target_x86_64::got_section.
+  const off_t got_file_offset = this->offset();
+  gold_assert(this->data_size() >= 24);
+  unsigned char* const got_view = of->get_output_view(got_file_offset, 24);
+  Output_section* dynamic = this->layout_->dynamic_section();
+  uint64_t dynamic_addr = dynamic == NULL ? 0 : dynamic->address();
+  elfcpp::Swap<64, false>::writeval(got_view, dynamic_addr);
+  memset(got_view + 8, 0, 16);
+  of->write_output_view(got_file_offset, 24, got_view);
+}
+
 // Initialize the PLT section.
 
 template<int size>
@@ -1198,7 +1247,7 @@ Output_data_plt_x86_64<size>::add_entry(Symbol_table* symtab, Layout* layout,
   unsigned int* pcount;
   unsigned int offset;
   unsigned int reserved;
-  Output_data_space* got;
+  Output_section_data_build* got;
   if (gsym->type() == elfcpp::STT_GNU_IFUNC
       && gsym->can_use_relative_reloc(false))
     {
@@ -1467,11 +1516,16 @@ Output_data_plt_x86_64_standard<size>::do_fill_plt_entry(
     unsigned int plt_offset,
     unsigned int plt_index)
 {
+  // Check PC-relative offset overflow in PLT entry.
+  uint64_t plt_got_pcrel_offset = (got_address + got_offset
+                                  - (plt_address + plt_offset + 6));
+  if (Bits<32>::has_overflow(plt_got_pcrel_offset))
+    gold_error(_("PC-relative offset overflow in PLT entry %d"),
+              plt_index + 1);
+
   memcpy(pov, plt_entry, plt_entry_size);
   elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
-                                             (got_address + got_offset
-                                              - (plt_address + plt_offset
-                                                 + 6)));
+                                             plt_got_pcrel_offset);
 
   elfcpp::Swap_unaligned<32, false>::writeval(pov + 7, plt_index);
   elfcpp::Swap<32, false>::writeval(pov + 12,
@@ -1605,18 +1659,9 @@ Output_data_plt_x86_64<size>::do_write(Output_file* of)
   this->fill_first_plt_entry(pov, got_address, plt_address);
   pov += this->get_plt_entry_size();
 
-  unsigned char* got_pov = got_view;
-
-  // The first entry in the GOT is the address of the .dynamic section
-  // aka the PT_DYNAMIC segment.  The next two entries are reserved.
-  // We saved space for them when we created the section in
-  // Target_x86_64::got_section.
-  Output_section* dynamic = this->layout_->dynamic_section();
-  uint32_t dynamic_addr = dynamic == NULL ? 0 : dynamic->address();
-  elfcpp::Swap<64, false>::writeval(got_pov, dynamic_addr);
-  got_pov += 8;
-  memset(got_pov, 0, 16);
-  got_pov += 16;
+  // The first three entries in the GOT are reserved, and are written
+  // by Output_data_got_plt_x86_64::do_write.
+  unsigned char* got_pov = got_view + 24;
 
   unsigned int plt_offset = this->get_plt_entry_size();
   unsigned int got_offset = 24;
@@ -1777,7 +1822,7 @@ Target_x86_64<size>::init_got_plt_for_update(Symbol_table* symtab,
                                  true);
 
   // Add the three reserved entries.
-  this->got_plt_ = new Output_data_space((plt_count + 3) * 8, 8, "** GOT PLT");
+  this->got_plt_ = new Output_data_got_plt_x86_64(layout, (plt_count + 3) * 8);
   layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
                                  (elfcpp::SHF_ALLOC
                                   | elfcpp::SHF_WRITE),
@@ -2734,7 +2779,8 @@ Target_x86_64<size>::Scan::global(Symbol_table* symtab,
        // Make a dynamic relocation if necessary.
        if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type)))
          {
-           if (gsym->may_need_copy_reloc())
+           if (!parameters->options().output_is_position_independent()
+               && gsym->may_need_copy_reloc())
              {
                target->copy_reloc(symtab, layout, object,
                                   data_shndx, output_section, gsym, reloc);
@@ -2795,7 +2841,8 @@ Target_x86_64<size>::Scan::global(Symbol_table* symtab,
        // Make a dynamic relocation if necessary.
        if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type)))
          {
-           if (gsym->may_need_copy_reloc())
+           if (parameters->options().output_is_executable()
+               && gsym->may_need_copy_reloc())
              {
                target->copy_reloc(symtab, layout, object,
                                   data_shndx, output_section, gsym, reloc);
@@ -2881,11 +2928,6 @@ Target_x86_64<size>::Scan::global(Symbol_table* symtab,
                  }
              }
          }
-       // For GOTPLT64, we also need a PLT entry (but only if the
-       // symbol is not fully resolved).
-       if (r_type == elfcpp::R_X86_64_GOTPLT64
-           && !gsym->final_value_is_known())
-         target->make_plt_entry(symtab, layout, gsym);
       }
       break;
 
@@ -2941,7 +2983,12 @@ Target_x86_64<size>::Scan::global(Symbol_table* symtab,
     case elfcpp::R_X86_64_GOTTPOFF:         // Initial-exec
     case elfcpp::R_X86_64_TPOFF32:          // Local-exec
       {
-       const bool is_final = gsym->final_value_is_known();
+       // For the Initial-Exec model, we can treat undef symbols as final
+       // when building an executable.
+       const bool is_final = (gsym->final_value_is_known() ||
+                              (r_type == elfcpp::R_X86_64_GOTTPOFF &&
+                               gsym->is_undefined() &&
+                               parameters->options().output_is_executable()));
        const tls::Tls_optimization optimized_type
            = Target_x86_64<size>::optimize_tls_reloc(is_final, r_type);
        switch (r_type)
@@ -3285,7 +3332,9 @@ Target_x86_64<size>::Relocate::relocate(
   // 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;
+  // Since the actual offset is always negative, we use signed int to
+  // support 64-bit GOT relocations.
+  int got_offset = 0;
   switch (r_type)
     {
     case elfcpp::R_X86_64_GOT32:
@@ -3388,10 +3437,12 @@ Target_x86_64<size>::Relocate::relocate(
        gold_assert(gsym->has_plt_offset()
                    || gsym->final_value_is_known());
        typename elfcpp::Elf_types<size>::Elf_Addr got_address;
-       got_address = target->got_section(NULL, NULL)->address();
+       // This is the address of GLOBAL_OFFSET_TABLE.
+       got_address = target->got_plt_section()->address();
        Relocate_functions<size, false>::rela64(view, object, psymval,
                                                addend - got_address);
       }
+      break;
 
     case elfcpp::R_X86_64_GOT32:
       gold_assert(have_got_offset);
@@ -3408,9 +3459,9 @@ Target_x86_64<size>::Relocate::relocate(
       break;
 
     case elfcpp::R_X86_64_GOT64:
-      // The ABI doc says "Like GOT64, but indicates a PLT entry is needed."
-      // Since we always add a PLT entry, this is equivalent.
     case elfcpp::R_X86_64_GOTPLT64:
+      // R_X86_64_GOTPLT64 is obsolete and treated the the same as
+      // GOT64.
       gold_assert(have_got_offset);
       Relocate_functions<size, false>::rela64(view, got_offset, addend);
       break;
@@ -3733,7 +3784,17 @@ Target_x86_64<size>::Relocate::relocate_tls(
       break;
 
     case elfcpp::R_X86_64_GOTTPOFF:         // Initial-exec
-      if (optimized_type == tls::TLSOPT_TO_LE)
+      if (gsym != NULL
+         && gsym->is_undefined()
+         && parameters->options().output_is_executable())
+       {
+         Target_x86_64<size>::Relocate::tls_ie_to_le(relinfo, relnum,
+                                                     NULL, rela,
+                                                     r_type, value, view,
+                                                     view_size);
+         break;
+       }
+      else if (optimized_type == tls::TLSOPT_TO_LE)
        {
          if (tls_segment == NULL)
            {
@@ -4054,6 +4115,8 @@ Target_x86_64<size>::Relocate::tls_ie_to_le(
       // movq
       if (op1 == 0x4c)
        view[-3] = 0x49;
+      else if (size == 32 && op1 == 0x44)
+       view[-3] = 0x41;
       view[-2] = 0xc7;
       view[-1] = 0xc0 | reg;
     }
@@ -4062,6 +4125,8 @@ Target_x86_64<size>::Relocate::tls_ie_to_le(
       // Special handling for %rsp.
       if (op1 == 0x4c)
        view[-3] = 0x49;
+      else if (size == 32 && op1 == 0x44)
+       view[-3] = 0x41;
       view[-2] = 0x81;
       view[-1] = 0xc0 | reg;
     }
@@ -4070,11 +4135,14 @@ Target_x86_64<size>::Relocate::tls_ie_to_le(
       // addq
       if (op1 == 0x4c)
        view[-3] = 0x4d;
+      else if (size == 32 && op1 == 0x44)
+       view[-3] = 0x45;
       view[-2] = 0x8d;
       view[-1] = 0x80 | reg | (reg << 3);
     }
 
-  value -= tls_segment->memsz();
+  if (tls_segment != NULL)
+    value -= tls_segment->memsz();
   Relocate_functions<size, false>::rela32(view, value, 0);
 }
 
@@ -4411,6 +4479,14 @@ Target_x86_64<size>::do_ehframe_datarel_base() const
 // code.  We have to change the function so that it always ensures
 // that it has enough stack space to run some random function.
 
+static const unsigned char cmp_insn_32[] = { 0x64, 0x3b, 0x24, 0x25 };
+static const unsigned char lea_r10_insn_32[] = { 0x44, 0x8d, 0x94, 0x24 };
+static const unsigned char lea_r11_insn_32[] = { 0x44, 0x8d, 0x9c, 0x24 };
+
+static const unsigned char cmp_insn_64[] = { 0x64, 0x48, 0x3b, 0x24, 0x25 };
+static const unsigned char lea_r10_insn_64[] = { 0x4c, 0x8d, 0x94, 0x24 };
+static const unsigned char lea_r11_insn_64[] = { 0x4c, 0x8d, 0x9c, 0x24 };
+
 template<int size>
 void
 Target_x86_64<size>::do_calls_non_split(Relobj* object, unsigned int shndx,
@@ -4421,25 +4497,40 @@ Target_x86_64<size>::do_calls_non_split(Relobj* object, unsigned int shndx,
                                        std::string* from,
                                        std::string* to) const
 {
+  const char* const cmp_insn = reinterpret_cast<const char*>
+      (size == 32 ? cmp_insn_32 : cmp_insn_64);
+  const char* const lea_r10_insn = reinterpret_cast<const char*>
+      (size == 32 ? lea_r10_insn_32 : lea_r10_insn_64);
+  const char* const lea_r11_insn = reinterpret_cast<const char*>
+      (size == 32 ? lea_r11_insn_32 : lea_r11_insn_64);
+
+  const size_t cmp_insn_len =
+      (size == 32 ? sizeof(cmp_insn_32) : sizeof(cmp_insn_64));
+  const size_t lea_r10_insn_len =
+      (size == 32 ? sizeof(lea_r10_insn_32) : sizeof(lea_r10_insn_64));
+  const size_t lea_r11_insn_len =
+      (size == 32 ? sizeof(lea_r11_insn_32) : sizeof(lea_r11_insn_64));
+  const size_t nop_len = (size == 32 ? 7 : 8);
+
   // The function starts with a comparison of the stack pointer and a
   // field in the TCB.  This is followed by a jump.
 
   // cmp %fs:NN,%rsp
-  if (this->match_view(view, view_size, fnoffset, "\x64\x48\x3b\x24\x25", 5)
-      && fnsize > 9)
+  if (this->match_view(view, view_size, fnoffset, cmp_insn, cmp_insn_len)
+      && fnsize > nop_len + 1)
     {
       // We will call __morestack if the carry flag is set after this
       // comparison.  We turn the comparison into an stc instruction
       // and some nops.
       view[fnoffset] = '\xf9';
-      this->set_view_to_nop(view, view_size, fnoffset + 1, 8);
+      this->set_view_to_nop(view, view_size, fnoffset + 1, nop_len);
     }
   // lea NN(%rsp),%r10
   // lea NN(%rsp),%r11
   else if ((this->match_view(view, view_size, fnoffset,
-                            "\x4c\x8d\x94\x24", 4)
+                            lea_r10_insn, lea_r10_insn_len)
            || this->match_view(view, view_size, fnoffset,
-                               "\x4c\x8d\x9c\x24", 4))
+                               lea_r11_insn, lea_r11_insn_len))
           && fnsize > 8)
     {
       // This is loading an offset from the stack pointer for a
@@ -4499,7 +4590,7 @@ class Output_data_plt_x86_64_nacl : public Output_data_plt_x86_64<size>
  public:
   Output_data_plt_x86_64_nacl(Layout* layout,
                              Output_data_got<64, false>* got,
-                             Output_data_space* got_plt,
+                             Output_data_got_plt_x86_64* got_plt,
                              Output_data_space* got_irelative)
     : Output_data_plt_x86_64<size>(layout, plt_entry_size,
                                   got, got_plt, got_irelative)
@@ -4507,7 +4598,7 @@ class Output_data_plt_x86_64_nacl : public Output_data_plt_x86_64<size>
 
   Output_data_plt_x86_64_nacl(Layout* layout,
                              Output_data_got<64, false>* got,
-                             Output_data_space* got_plt,
+                             Output_data_got_plt_x86_64* got_plt,
                              Output_data_space* got_irelative,
                              unsigned int plt_count)
     : Output_data_plt_x86_64<size>(layout, plt_entry_size,
@@ -4580,7 +4671,7 @@ class Target_x86_64_nacl : public Target_x86_64<size>
   virtual Output_data_plt_x86_64<size>*
   do_make_data_plt(Layout* layout,
                   Output_data_got<64, false>* got,
-                  Output_data_space* got_plt,
+                  Output_data_got_plt_x86_64* got_plt,
                   Output_data_space* got_irelative)
   {
     return new Output_data_plt_x86_64_nacl<size>(layout, got, got_plt,
@@ -4590,7 +4681,7 @@ class Target_x86_64_nacl : public Target_x86_64<size>
   virtual Output_data_plt_x86_64<size>*
   do_make_data_plt(Layout* layout,
                   Output_data_got<64, false>* got,
-                  Output_data_space* got_plt,
+                  Output_data_got_plt_x86_64* got_plt,
                   Output_data_space* got_irelative,
                   unsigned int plt_count)
   {
This page took 0.03057 seconds and 4 git commands to generate.