PR gold/12980
[deliverable/binutils-gdb.git] / gold / i386.cc
index bd5eaaf7145db28dfe52737a294ac5bd9815c3ab..84b9f0724a590969c459193852e3c703dba162bd 100644 (file)
@@ -25,6 +25,7 @@
 #include <cstring>
 
 #include "elfcpp.h"
+#include "dwarf.h"
 #include "parameters.h"
 #include "reloc.h"
 #include "i386.h"
@@ -52,15 +53,16 @@ class Output_data_plt_i386 : public Output_section_data
  public:
   typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, false> Reloc_section;
 
-  Output_data_plt_i386(Symbol_table*, Layout*, Output_data_space*);
+  Output_data_plt_i386(Layout*, Output_data_space*, Output_data_space*);
 
   // Add an entry to the PLT.
   void
-  add_entry(Symbol* gsym);
+  add_entry(Symbol_table*, Layout*, Symbol* gsym);
 
   // Add an entry to the PLT for a local STT_GNU_IFUNC symbol.
   unsigned int
-  add_local_ifunc_entry(Sized_relobj_file<32, false>* relobj,
+  add_local_ifunc_entry(Symbol_table*, Layout*,
+                       Sized_relobj_file<32, false>* relobj,
                        unsigned int local_sym_index);
 
   // Return the .rel.plt section data.
@@ -72,10 +74,19 @@ class Output_data_plt_i386 : public Output_section_data
   Reloc_section*
   rel_tls_desc(Layout*);
 
+  // Return where the IRELATIVE relocations should go.
+  Reloc_section*
+  rel_irelative(Symbol_table*, Layout*);
+
+  // Return whether we created a section for IRELATIVE relocations.
+  bool
+  has_irelative_section() const
+  { return this->irelative_rel_ != NULL; }
+
   // Return the number of PLT entries.
   unsigned int
   entry_count() const
-  { return this->count_; }
+  { return this->count_ + this->irelative_count_; }
 
   // Return the offset of the first non-reserved PLT entry.
   static unsigned int
@@ -87,6 +98,14 @@ class Output_data_plt_i386 : public Output_section_data
   get_plt_entry_size()
   { return plt_entry_size; }
 
+  // Return the PLT address to use for a global symbol.
+  uint64_t
+  address_for_global(const Symbol*);
+
+  // Return the PLT address to use for a local symbol.
+  uint64_t
+  address_for_local(const Relobj*, unsigned int symndx);
+
  protected:
   void
   do_adjust_output_section(Output_section* os);
@@ -101,21 +120,30 @@ class Output_data_plt_i386 : public Output_section_data
   static const int plt_entry_size = 16;
 
   // The first entry in the PLT for an executable.
-  static unsigned char exec_first_plt_entry[plt_entry_size];
+  static const unsigned char exec_first_plt_entry[plt_entry_size];
 
   // The first entry in the PLT for a shared object.
-  static unsigned char dyn_first_plt_entry[plt_entry_size];
+  static const unsigned char dyn_first_plt_entry[plt_entry_size];
 
   // Other entries in the PLT for an executable.
-  static unsigned char exec_plt_entry[plt_entry_size];
+  static const unsigned char exec_plt_entry[plt_entry_size];
 
   // Other entries in the PLT for a shared object.
-  static unsigned char dyn_plt_entry[plt_entry_size];
+  static const unsigned char dyn_plt_entry[plt_entry_size];
+
+  // The .eh_frame unwind information for the PLT.
+  static const int plt_eh_frame_cie_size = 16;
+  static const int plt_eh_frame_fde_size = 32;
+  static const unsigned char plt_eh_frame_cie[plt_eh_frame_cie_size];
+  static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size];
 
   // Set the final size.
   void
   set_final_data_size()
-  { this->set_data_size((this->count_ + 1) * plt_entry_size); }
+  {
+    this->set_data_size((this->count_ + this->irelative_count_ + 1)
+                       * plt_entry_size);
+  }
 
   // Write out the PLT data.
   void
@@ -143,10 +171,18 @@ class Output_data_plt_i386 : public Output_section_data
   // The TLS_DESC relocations, if necessary.  These must follow the
   // regular PLT relocs.
   Reloc_section* tls_desc_rel_;
+  // The IRELATIVE relocations, if necessary.  These must follow the
+  // regular relocatoins and the TLS_DESC relocations.
+  Reloc_section* irelative_rel_;
   // The .got.plt section.
   Output_data_space* got_plt_;
+  // The part of the .got.plt section used for IRELATIVE relocs.
+  Output_data_space* got_irelative_;
   // The number of PLT entries.
   unsigned int count_;
+  // Number of PLT entries with R_386_IRELATIVE relocs.  These follow
+  // the regular PLT entries.
+  unsigned int irelative_count_;
   // Global STT_GNU_IFUNC symbols.
   std::vector<Global_ifunc> global_ifuncs_;
   // Local STT_GNU_IFUNC symbols.
@@ -158,16 +194,16 @@ class Output_data_plt_i386 : public Output_section_data
 //   http://people.redhat.com/drepper/tls.pdf
 //   http://www.lsd.ic.unicamp.br/~oliva/writeups/TLS/RFC-TLSDESC-x86.txt
 
-class Target_i386 : public Target_freebsd<32, false>
+class Target_i386 : public Sized_target<32, false>
 {
  public:
   typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, false> Reloc_section;
 
   Target_i386()
-    : Target_freebsd<32, false>(&i386_info),
-      got_(NULL), plt_(NULL), got_plt_(NULL), got_tlsdesc_(NULL),
-      global_offset_table_(NULL), rel_dyn_(NULL),
-      copy_relocs_(elfcpp::R_386_COPY), dynbss_(NULL),
+    : Sized_target<32, false>(&i386_info),
+      got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
+      got_tlsdesc_(NULL), global_offset_table_(NULL), rel_dyn_(NULL),
+      rel_irelative_(NULL), copy_relocs_(elfcpp::R_386_COPY), dynbss_(NULL),
       got_mod_index_offset_(-1U), tls_base_symbol_defined_(false)
   { }
 
@@ -274,20 +310,24 @@ class Target_i386 : public Target_freebsd<32, false>
     return Target::do_is_local_label_name(name);
   }
 
-  // Return the PLT section.
-  Output_data*
-  do_plt_section_for_global(const Symbol*) const
-  { return this->plt_section(); }
+  // Return the PLT address to use for a global symbol.
+  uint64_t
+  do_plt_address_for_global(const Symbol* gsym) const
+  { return this->plt_section()->address_for_global(gsym); }
 
-  Output_data*
-  do_plt_section_for_local(const Relobj*, unsigned int) const
-  { return this->plt_section(); }
+  uint64_t
+  do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const
+  { return this->plt_section()->address_for_local(relobj, symndx); }
 
   // We can tell whether we take the address of a function.
   inline bool
   do_can_check_for_function_pointers() const
   { return true; }
 
+  // Return the base for a DW_EH_PE_datarel encoding.
+  uint64_t
+  do_ehframe_datarel_base() const;
+
   // Return whether SYM is call to a non-split function.
   bool
   do_is_call_to_non_split(const Symbol* sym, unsigned int) const;
@@ -578,6 +618,10 @@ class Target_i386 : public Target_freebsd<32, false>
   Reloc_section*
   rel_tls_desc_section(Layout*) const;
 
+  // Get the section to use for IRELATIVE relocations.
+  Reloc_section*
+  rel_irelative_section(Layout*);
+
   // Add a potential copy relocation.
   void
   copy_reloc(Symbol_table* symtab, Layout* layout,
@@ -614,12 +658,16 @@ class Target_i386 : public Target_freebsd<32, false>
   Output_data_plt_i386* plt_;
   // The GOT PLT section.
   Output_data_space* got_plt_;
+  // The GOT section for IRELATIVE relocations.
+  Output_data_space* got_irelative_;
   // The GOT section for TLSDESC relocations.
   Output_data_got<32, false>* got_tlsdesc_;
   // The _GLOBAL_OFFSET_TABLE_ symbol.
   Symbol* global_offset_table_;
   // The dynamic reloc section.
   Reloc_section* rel_dyn_;
+  // The section to use for IRELATIVE relocs.
+  Reloc_section* rel_irelative_;
   // Relocs saved to avoid a COPY reloc.
   Copy_relocs<elfcpp::SHT_REL, 32, false> copy_relocs_;
   // Space for variables copied with a COPY reloc.
@@ -692,6 +740,15 @@ Target_i386::got_section(Symbol_table* symtab, Layout* layout)
                                      elfcpp::STV_HIDDEN, 0,
                                      false, false);
 
+      // If there are any IRELATIVE relocations, they get GOT entries
+      // in .got.plt after the jump slot relocations.
+      this->got_irelative_ = new Output_data_space(4, "** GOT IRELATIVE PLT");
+      layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
+                                     (elfcpp::SHF_ALLOC
+                                      | elfcpp::SHF_WRITE),
+                                     this->got_irelative_,
+                                     ORDER_NON_RELRO_FIRST, false);
+
       // If there are any TLSDESC relocations, they get GOT entries in
       // .got.plt after the jump slot entries.
       this->got_tlsdesc_ = new Output_data_got<32, false>();
@@ -721,38 +778,49 @@ Target_i386::rel_dyn_section(Layout* layout)
   return this->rel_dyn_;
 }
 
+// Get the section to use for IRELATIVE relocs, creating it if
+// necessary.  These go in .rel.dyn, but only after all other dynamic
+// relocations.  They need to follow the other dynamic relocations so
+// that they can refer to global variables initialized by those
+// relocs.
+
+Target_i386::Reloc_section*
+Target_i386::rel_irelative_section(Layout* layout)
+{
+  if (this->rel_irelative_ == NULL)
+    {
+      // Make sure we have already create the dynamic reloc section.
+      this->rel_dyn_section(layout);
+      this->rel_irelative_ = new Reloc_section(false);
+      layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL,
+                                     elfcpp::SHF_ALLOC, this->rel_irelative_,
+                                     ORDER_DYNAMIC_RELOCS, false);
+      gold_assert(this->rel_dyn_->output_section()
+                 == this->rel_irelative_->output_section());
+    }
+  return this->rel_irelative_;
+}
+
 // Create the PLT section.  The ordinary .got section is an argument,
 // since we need to refer to the start.  We also create our own .got
 // section just for PLT entries.
 
-Output_data_plt_i386::Output_data_plt_i386(Symbol_table* symtab,
-                                          Layout* layout,
-                                          Output_data_space* got_plt)
-  : Output_section_data(4), tls_desc_rel_(NULL), got_plt_(got_plt), count_(0),
-    global_ifuncs_(), local_ifuncs_()
+Output_data_plt_i386::Output_data_plt_i386(Layout* layout,
+                                          Output_data_space* got_plt,
+                                          Output_data_space* got_irelative)
+  : Output_section_data(16), tls_desc_rel_(NULL), irelative_rel_(NULL),
+    got_plt_(got_plt), got_irelative_(got_irelative), count_(0),
+    irelative_count_(0), global_ifuncs_(), local_ifuncs_()
 {
   this->rel_ = new Reloc_section(false);
   layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
                                  elfcpp::SHF_ALLOC, this->rel_,
                                  ORDER_DYNAMIC_PLT_RELOCS, false);
 
-  if (parameters->doing_static_link())
-    {
-      // A statically linked executable will only have a .rel.plt
-      // section to hold R_386_IRELATIVE relocs for STT_GNU_IFUNC
-      // symbols.  The library will use these symbols to locate the
-      // IRELATIVE relocs at program startup time.
-      symtab->define_in_output_data("__rel_iplt_start", NULL,
-                                   Symbol_table::PREDEFINED,
-                                   this->rel_, 0, 0, elfcpp::STT_NOTYPE,
-                                   elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN,
-                                   0, false, true);
-      symtab->define_in_output_data("__rel_iplt_end", NULL,
-                                   Symbol_table::PREDEFINED,
-                                   this->rel_, 0, 0, elfcpp::STT_NOTYPE,
-                                   elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN,
-                                   0, true, true);
-    }
+  // Add unwind information if requested.
+  if (parameters->options().ld_generated_unwind_info())
+    layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size,
+                                plt_eh_frame_fde, plt_eh_frame_fde_size);
 }
 
 void
@@ -766,29 +834,23 @@ Output_data_plt_i386::do_adjust_output_section(Output_section* os)
 // Add an entry to the PLT.
 
 void
-Output_data_plt_i386::add_entry(Symbol* gsym)
+Output_data_plt_i386::add_entry(Symbol_table* symtab, Layout* layout,
+                               Symbol* gsym)
 {
   gold_assert(!gsym->has_plt_offset());
 
-  // Note that when setting the PLT offset we skip the initial
-  // reserved PLT entry.
-  gsym->set_plt_offset((this->count_ + 1) * plt_entry_size);
-
-  ++this->count_;
-
-  section_offset_type got_offset = this->got_plt_->current_data_size();
-
-  // Every PLT entry needs a GOT entry which points back to the PLT
-  // entry (this will be changed by the dynamic linker, normally
-  // lazily when the function is called).
-  this->got_plt_->set_current_data_size(got_offset + 4);
-
   // Every PLT entry needs a reloc.
   if (gsym->type() == elfcpp::STT_GNU_IFUNC
       && gsym->can_use_relative_reloc(false))
     {
-      this->rel_->add_symbolless_global_addend(gsym, elfcpp::R_386_IRELATIVE,
-                                              this->got_plt_, got_offset);
+      gsym->set_plt_offset(this->irelative_count_ * plt_entry_size);
+      ++this->irelative_count_;
+      section_offset_type got_offset =
+       this->got_irelative_->current_data_size();
+      this->got_irelative_->set_current_data_size(got_offset + 4);
+      Reloc_section* rel = this->rel_irelative(symtab, layout);
+      rel->add_symbolless_global_addend(gsym, elfcpp::R_386_IRELATIVE,
+                                       this->got_irelative_, got_offset);
       struct Global_ifunc gi;
       gi.sym = gsym;
       gi.got_offset = got_offset;
@@ -796,6 +858,19 @@ Output_data_plt_i386::add_entry(Symbol* gsym)
     }
   else
     {
+      // When setting the PLT offset we skip the initial reserved PLT
+      // entry.
+      gsym->set_plt_offset((this->count_ + 1) * plt_entry_size);
+
+      ++this->count_;
+
+      section_offset_type got_offset = this->got_plt_->current_data_size();
+
+      // Every PLT entry needs a GOT entry which points back to the
+      // PLT entry (this will be changed by the dynamic linker,
+      // normally lazily when the function is called).
+      this->got_plt_->set_current_data_size(got_offset + 4);
+
       gsym->set_needs_dynsym_entry();
       this->rel_->add_global(gsym, elfcpp::R_386_JUMP_SLOT, this->got_plt_,
                             got_offset);
@@ -811,22 +886,25 @@ Output_data_plt_i386::add_entry(Symbol* gsym)
 
 unsigned int
 Output_data_plt_i386::add_local_ifunc_entry(
+    Symbol_table* symtab,
+    Layout* layout,
     Sized_relobj_file<32, false>* relobj,
     unsigned int local_sym_index)
 {
-  unsigned int plt_offset = (this->count_ + 1) * plt_entry_size;
-  ++this->count_;
+  unsigned int plt_offset = this->irelative_count_ * plt_entry_size;
+  ++this->irelative_count_;
 
-  section_offset_type got_offset = this->got_plt_->current_data_size();
+  section_offset_type got_offset = this->got_irelative_->current_data_size();
 
   // Every PLT entry needs a GOT entry which points back to the PLT
   // entry.
-  this->got_plt_->set_current_data_size(got_offset + 4);
+  this->got_irelative_->set_current_data_size(got_offset + 4);
 
   // Every PLT entry needs a reloc.
-  this->rel_->add_symbolless_local_addend(relobj, local_sym_index,
-                                         elfcpp::R_386_IRELATIVE,
-                                         this->got_plt_, got_offset);
+  Reloc_section* rel = this->rel_irelative(symtab, layout);
+  rel->add_symbolless_local_addend(relobj, local_sym_index,
+                                  elfcpp::R_386_IRELATIVE,
+                                  this->got_irelative_, got_offset);
 
   struct Local_ifunc li;
   li.object = relobj;
@@ -849,15 +927,75 @@ Output_data_plt_i386::rel_tls_desc(Layout* layout)
       layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
                                      elfcpp::SHF_ALLOC, this->tls_desc_rel_,
                                      ORDER_DYNAMIC_PLT_RELOCS, false);
-      gold_assert(this->tls_desc_rel_->output_section() ==
-                 this->rel_->output_section());
+      gold_assert(this->tls_desc_rel_->output_section()
+                 == this->rel_->output_section());
     }
   return this->tls_desc_rel_;
 }
 
+// Return where the IRELATIVE relocations should go in the PLT.  These
+// follow the JUMP_SLOT and TLS_DESC relocations.
+
+Output_data_plt_i386::Reloc_section*
+Output_data_plt_i386::rel_irelative(Symbol_table* symtab, Layout* layout)
+{
+  if (this->irelative_rel_ == NULL)
+    {
+      // Make sure we have a place for the TLS_DESC relocations, in
+      // case we see any later on.
+      this->rel_tls_desc(layout);
+      this->irelative_rel_ = new Reloc_section(false);
+      layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
+                                     elfcpp::SHF_ALLOC, this->irelative_rel_,
+                                     ORDER_DYNAMIC_PLT_RELOCS, false);
+      gold_assert(this->irelative_rel_->output_section()
+                 == this->rel_->output_section());
+
+      if (parameters->doing_static_link())
+       {
+         // A statically linked executable will only have a .rel.plt
+         // section to hold R_386_IRELATIVE relocs for STT_GNU_IFUNC
+         // symbols.  The library will use these symbols to locate
+         // the IRELATIVE relocs at program startup time.
+         symtab->define_in_output_data("__rel_iplt_start", NULL,
+                                       Symbol_table::PREDEFINED,
+                                       this->irelative_rel_, 0, 0,
+                                       elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+                                       elfcpp::STV_HIDDEN, 0, false, true);
+         symtab->define_in_output_data("__rel_iplt_end", NULL,
+                                       Symbol_table::PREDEFINED,
+                                       this->irelative_rel_, 0, 0,
+                                       elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+                                       elfcpp::STV_HIDDEN, 0, true, true);
+       }
+    }
+  return this->irelative_rel_;
+}
+
+// Return the PLT address to use for a global symbol.
+
+uint64_t
+Output_data_plt_i386::address_for_global(const Symbol* gsym)
+{
+  uint64_t offset = 0;
+  if (gsym->type() == elfcpp::STT_GNU_IFUNC
+      && gsym->can_use_relative_reloc(false))
+    offset = (this->count_ + 1) * plt_entry_size;
+  return this->address() + offset;
+}
+
+// Return the PLT address to use for a local symbol.  These are always
+// IRELATIVE relocs.
+
+uint64_t
+Output_data_plt_i386::address_for_local(const Relobj*, unsigned int)
+{
+  return this->address() + (this->count_ + 1) * plt_entry_size;
+}
+
 // The first entry in the PLT for an executable.
 
-unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] =
+const unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] =
 {
   0xff, 0x35,  // pushl contents of memory address
   0, 0, 0, 0,  // replaced with address of .got + 4
@@ -868,7 +1006,7 @@ unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] =
 
 // The first entry in the PLT for a shared object.
 
-unsigned char Output_data_plt_i386::dyn_first_plt_entry[plt_entry_size] =
+const unsigned char Output_data_plt_i386::dyn_first_plt_entry[plt_entry_size] =
 {
   0xff, 0xb3, 4, 0, 0, 0,      // pushl 4(%ebx)
   0xff, 0xa3, 8, 0, 0, 0,      // jmp *8(%ebx)
@@ -877,7 +1015,7 @@ unsigned char Output_data_plt_i386::dyn_first_plt_entry[plt_entry_size] =
 
 // Subsequent entries in the PLT for an executable.
 
-unsigned char Output_data_plt_i386::exec_plt_entry[plt_entry_size] =
+const unsigned char Output_data_plt_i386::exec_plt_entry[plt_entry_size] =
 {
   0xff, 0x25,  // jmp indirect
   0, 0, 0, 0,  // replaced with address of symbol in .got
@@ -889,7 +1027,7 @@ unsigned char Output_data_plt_i386::exec_plt_entry[plt_entry_size] =
 
 // Subsequent entries in the PLT for a shared object.
 
-unsigned char Output_data_plt_i386::dyn_plt_entry[plt_entry_size] =
+const unsigned char Output_data_plt_i386::dyn_plt_entry[plt_entry_size] =
 {
   0xff, 0xa3,  // jmp *offset(%ebx)
   0, 0, 0, 0,  // replaced with offset of symbol in .got
@@ -899,6 +1037,54 @@ unsigned char Output_data_plt_i386::dyn_plt_entry[plt_entry_size] =
   0, 0, 0, 0   // replaced with offset to start of .plt
 };
 
+// The .eh_frame unwind information for the PLT.
+
+const unsigned char
+Output_data_plt_i386::plt_eh_frame_cie[plt_eh_frame_cie_size] =
+{
+  1,                           // CIE version.
+  'z',                         // Augmentation: augmentation size included.
+  'R',                         // Augmentation: FDE encoding included.
+  '\0',                                // End of augmentation string.
+  1,                           // Code alignment factor.
+  0x7c,                                // Data alignment factor.
+  8,                           // Return address column.
+  1,                           // Augmentation size.
+  (elfcpp::DW_EH_PE_pcrel      // FDE encoding.
+   | elfcpp::DW_EH_PE_sdata4),
+  elfcpp::DW_CFA_def_cfa, 4, 4,        // DW_CFA_def_cfa: r4 (esp) ofs 4.
+  elfcpp::DW_CFA_offset + 8, 1,        // DW_CFA_offset: r8 (eip) at cfa-4.
+  elfcpp::DW_CFA_nop,          // Align to 16 bytes.
+  elfcpp::DW_CFA_nop
+};
+
+const unsigned char
+Output_data_plt_i386::plt_eh_frame_fde[plt_eh_frame_fde_size] =
+{
+  0, 0, 0, 0,                          // Replaced with offset to .plt.
+  0, 0, 0, 0,                          // Replaced with size of .plt.
+  0,                                   // Augmentation size.
+  elfcpp::DW_CFA_def_cfa_offset, 8,    // DW_CFA_def_cfa_offset: 8.
+  elfcpp::DW_CFA_advance_loc + 6,      // Advance 6 to __PLT__ + 6.
+  elfcpp::DW_CFA_def_cfa_offset, 12,   // DW_CFA_def_cfa_offset: 12.
+  elfcpp::DW_CFA_advance_loc + 10,     // Advance 10 to __PLT__ + 16.
+  elfcpp::DW_CFA_def_cfa_expression,   // DW_CFA_def_cfa_expression.
+  11,                                  // Block length.
+  elfcpp::DW_OP_breg4, 4,              // Push %esp + 4.
+  elfcpp::DW_OP_breg8, 0,              // Push %eip.
+  elfcpp::DW_OP_lit15,                 // Push 0xf.
+  elfcpp::DW_OP_and,                   // & (%eip & 0xf).
+  elfcpp::DW_OP_lit11,                 // Push 0xb.
+  elfcpp::DW_OP_ge,                    // >= ((%eip & 0xf) >= 0xb)
+  elfcpp::DW_OP_lit2,                  // Push 2.
+  elfcpp::DW_OP_shl,                   // << (((%eip & 0xf) >= 0xb) << 2)
+  elfcpp::DW_OP_plus,                  // + ((((%eip&0xf)>=0xb)<<2)+%esp+4
+  elfcpp::DW_CFA_nop,                  // Align to 32 bytes.
+  elfcpp::DW_CFA_nop,
+  elfcpp::DW_CFA_nop,
+  elfcpp::DW_CFA_nop
+};
+
 // Write out the PLT.  This uses the hand-coded instructions above,
 // and adjusts them as needed.  This is all specified by the i386 ELF
 // Processor Supplement.
@@ -912,8 +1098,12 @@ Output_data_plt_i386::do_write(Output_file* of)
   unsigned char* const oview = of->get_output_view(offset, oview_size);
 
   const off_t got_file_offset = this->got_plt_->offset();
+  gold_assert(parameters->incremental_update()
+             || (got_file_offset + this->got_plt_->data_size()
+                 == this->got_irelative_->offset()));
   const section_size_type got_size =
-    convert_to_section_size_type(this->got_plt_->data_size());
+    convert_to_section_size_type(this->got_plt_->data_size()
+                                + this->got_irelative_->data_size());
   unsigned char* const got_view = of->get_output_view(got_file_offset,
                                                      got_size);
 
@@ -942,7 +1132,7 @@ Output_data_plt_i386::do_write(Output_file* of)
   unsigned int plt_offset = plt_entry_size;
   unsigned int plt_rel_offset = 0;
   unsigned int got_offset = 12;
-  const unsigned int count = this->count_;
+  const unsigned int count = this->count_ + this->irelative_count_;
   for (unsigned int i = 0;
        i < count;
        ++i,
@@ -979,6 +1169,7 @@ Output_data_plt_i386::do_write(Output_file* of)
   // the GOT to point to the actual symbol value, rather than point to
   // the PLT entry.  That will let the dynamic linker call the right
   // function when resolving IRELATIVE relocations.
+  unsigned char* got_irelative_view = got_view + this->got_plt_->data_size();
   for (std::vector<Global_ifunc>::const_iterator p =
         this->global_ifuncs_.begin();
        p != this->global_ifuncs_.end();
@@ -986,7 +1177,7 @@ Output_data_plt_i386::do_write(Output_file* of)
     {
       const Sized_symbol<32>* ssym =
        static_cast<const Sized_symbol<32>*>(p->sym);
-      elfcpp::Swap<32, false>::writeval(got_view + p->got_offset,
+      elfcpp::Swap<32, false>::writeval(got_irelative_view + p->got_offset,
                                        ssym->value());
     }
 
@@ -997,7 +1188,7 @@ Output_data_plt_i386::do_write(Output_file* of)
     {
       const Symbol_value<32>* psymval =
        p->object->local_symbol(p->local_sym_index);
-      elfcpp::Swap<32, false>::writeval(got_view + p->got_offset,
+      elfcpp::Swap<32, false>::writeval(got_irelative_view + p->got_offset,
                                        psymval->value(p->object, 0));
     }
 
@@ -1018,7 +1209,8 @@ Target_i386::make_plt_section(Symbol_table* symtab, Layout* layout)
       // Create the GOT sections first.
       this->got_section(symtab, layout);
 
-      this->plt_ = new Output_data_plt_i386(symtab, layout, this->got_plt_);
+      this->plt_ = new Output_data_plt_i386(layout, this->got_plt_,
+                                           this->got_irelative_);
       layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
                                      (elfcpp::SHF_ALLOC
                                       | elfcpp::SHF_EXECINSTR),
@@ -1039,7 +1231,7 @@ Target_i386::make_plt_entry(Symbol_table* symtab, Layout* layout, Symbol* gsym)
     return;
   if (this->plt_ == NULL)
     this->make_plt_section(symtab, layout);
-  this->plt_->add_entry(gsym);
+  this->plt_->add_entry(symtab, layout, gsym);
 }
 
 // Make a PLT entry for a local STT_GNU_IFUNC symbol.
@@ -1053,7 +1245,8 @@ Target_i386::make_local_ifunc_plt_entry(Symbol_table* symtab, Layout* layout,
     return;
   if (this->plt_ == NULL)
     this->make_plt_section(symtab, layout);
-  unsigned int plt_offset = this->plt_->add_local_ifunc_entry(relobj,
+  unsigned int plt_offset = this->plt_->add_local_ifunc_entry(symtab, layout,
+                                                             relobj,
                                                              local_sym_index);
   relobj->set_local_plt_offset(local_sym_index, plt_offset);
 }
@@ -1715,7 +1908,7 @@ Target_i386::Scan::global(Symbol_table* symtab,
                // STT_GNU_IFUNC symbol.  This makes a function
                // address in a PIE executable match the address in a
                // shared library that it links against.
-               Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+               Reloc_section* rel_dyn = target->rel_irelative_section(layout);
                rel_dyn->add_symbolless_global_addend(gsym,
                                                      elfcpp::R_386_IRELATIVE,
                                                      output_section,
@@ -1792,9 +1985,24 @@ Target_i386::Scan::global(Symbol_table* symtab,
             // If this symbol is not fully resolved, we need to add a
             // GOT entry with a dynamic relocation.
             Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+
+           // Use a GLOB_DAT rather than a RELATIVE reloc if:
+           //
+           // 1) The symbol may be defined in some other module.
+           //
+           // 2) We are building a shared library and this is a
+           // protected symbol; using GLOB_DAT means that the dynamic
+           // linker can use the address of the PLT in the main
+           // executable when appropriate so that function address
+           // comparisons work.
+           //
+           // 3) This is a STT_GNU_IFUNC symbol in position dependent
+           // code, again so that function address comparisons work.
             if (gsym->is_from_dynobj()
                 || gsym->is_undefined()
                 || gsym->is_preemptible()
+               || (gsym->visibility() == elfcpp::STV_PROTECTED
+                   && parameters->options().shared())
                || (gsym->type() == elfcpp::STT_GNU_IFUNC
                    && parameters->options().output_is_position_independent()))
               got->add_global_with_rel(gsym, GOT_TYPE_STANDARD,
@@ -2117,6 +2325,47 @@ Target_i386::do_finalize_sections(
       uint32_t data_size = this->got_plt_->current_data_size();
       symtab->get_sized_symbol<32>(sym)->set_symsize(data_size);
     }
+
+  if (parameters->doing_static_link()
+      && (this->plt_ == NULL || !this->plt_->has_irelative_section()))
+    {
+      // If linking statically, make sure that the __rel_iplt symbols
+      // were defined if necessary, even if we didn't create a PLT.
+      static const Define_symbol_in_segment syms[] =
+       {
+         {
+           "__rel_iplt_start",         // name
+           elfcpp::PT_LOAD,            // segment_type
+           elfcpp::PF_W,               // segment_flags_set
+           elfcpp::PF(0),              // segment_flags_clear
+           0,                          // value
+           0,                          // size
+           elfcpp::STT_NOTYPE,         // type
+           elfcpp::STB_GLOBAL,         // binding
+           elfcpp::STV_HIDDEN,         // visibility
+           0,                          // nonvis
+           Symbol::SEGMENT_START,      // offset_from_base
+           true                        // only_if_ref
+         },
+         {
+           "__rel_iplt_end",           // name
+           elfcpp::PT_LOAD,            // segment_type
+           elfcpp::PF_W,               // segment_flags_set
+           elfcpp::PF(0),              // segment_flags_clear
+           0,                          // value
+           0,                          // size
+           elfcpp::STT_NOTYPE,         // type
+           elfcpp::STB_GLOBAL,         // binding
+           elfcpp::STV_HIDDEN,         // visibility
+           0,                          // nonvis
+           Symbol::SEGMENT_START,      // offset_from_base
+           true                        // only_if_ref
+         }
+       };
+
+      symtab->define_symbols(layout, 2, syms,
+                            layout->script_options()->saw_sections_clause());
+    }
 }
 
 // Return whether a direct absolute static relocation needs to be applied.
@@ -2208,7 +2457,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
   else if (gsym != NULL
           && gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
     {
-      symval.set_output_value(target->plt_section()->address()
+      symval.set_output_value(target->plt_address_for_global(gsym)
                              + gsym->plt_offset());
       psymval = &symval;
     }
@@ -2217,7 +2466,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
       unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
       if (object->local_has_plt_offset(r_sym))
        {
-         symval.set_output_value(target->plt_section()->address()
+         symval.set_output_value(target->plt_address_for_local(object, r_sym)
                                  + object->local_plt_offset(r_sym));
          psymval = &symval;
        }
@@ -2404,7 +2653,12 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
     case elfcpp::R_386_TLS_GD:           // Global-dynamic
       if (optimized_type == tls::TLSOPT_TO_LE)
        {
-         gold_assert(tls_segment != NULL);
+         if (tls_segment == NULL)
+           {
+             gold_assert(parameters->errors()->error_count() > 0
+                         || issue_undefined_symbol_error(gsym));
+             return;
+           }
          this->tls_gd_to_le(relinfo, relnum, tls_segment,
                             rel, r_type, value, view,
                             view_size);
@@ -2430,7 +2684,12 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
             }
           if (optimized_type == tls::TLSOPT_TO_IE)
            {
-              gold_assert(tls_segment != NULL);
+             if (tls_segment == NULL)
+               {
+                 gold_assert(parameters->errors()->error_count() > 0
+                             || issue_undefined_symbol_error(gsym));
+                 return;
+               }
              this->tls_gd_to_ie(relinfo, relnum, tls_segment, rel, r_type,
                                  got_offset, view, view_size);
               break;
@@ -2453,7 +2712,12 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
       this->local_dynamic_type_ = LOCAL_DYNAMIC_GNU;
       if (optimized_type == tls::TLSOPT_TO_LE)
         {
-         gold_assert(tls_segment != NULL);
+         if (tls_segment == NULL)
+           {
+             gold_assert(parameters->errors()->error_count() > 0
+                         || issue_undefined_symbol_error(gsym));
+             return;
+           }
          this->tls_desc_gd_to_le(relinfo, relnum, tls_segment,
                                  rel, r_type, value, view,
                                  view_size);
@@ -2488,7 +2752,12 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
             }
           if (optimized_type == tls::TLSOPT_TO_IE)
            {
-              gold_assert(tls_segment != NULL);
+             if (tls_segment == NULL)
+               {
+                 gold_assert(parameters->errors()->error_count() > 0
+                             || issue_undefined_symbol_error(gsym));
+                 return;
+               }
              this->tls_desc_gd_to_ie(relinfo, relnum, tls_segment, rel, r_type,
                                       got_offset, view, view_size);
               break;
@@ -2520,7 +2789,12 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
       this->local_dynamic_type_ = LOCAL_DYNAMIC_GNU;
       if (optimized_type == tls::TLSOPT_TO_LE)
        {
-          gold_assert(tls_segment != NULL);
+         if (tls_segment == NULL)
+           {
+             gold_assert(parameters->errors()->error_count() > 0
+                         || issue_undefined_symbol_error(gsym));
+             return;
+           }
          this->tls_ld_to_le(relinfo, relnum, tls_segment, rel, r_type,
                             value, view, view_size);
          break;
@@ -2551,7 +2825,12 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
          elfcpp::Shdr<32, false> shdr(relinfo->data_shdr);
          if ((shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) != 0)
            {
-             gold_assert(tls_segment != NULL);
+             if (tls_segment == NULL)
+               {
+                 gold_assert(parameters->errors()->error_count() > 0
+                             || issue_undefined_symbol_error(gsym));
+                 return;
+               }
              value -= tls_segment->memsz();
            }
        }
@@ -2563,7 +2842,12 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
     case elfcpp::R_386_TLS_IE_32:
       if (optimized_type == tls::TLSOPT_TO_LE)
        {
-          gold_assert(tls_segment != NULL);
+         if (tls_segment == NULL)
+           {
+             gold_assert(parameters->errors()->error_count() > 0
+                         || issue_undefined_symbol_error(gsym));
+             return;
+           }
          Target_i386::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment,
                                              rel, r_type, value, view,
                                              view_size);
@@ -2607,7 +2891,12 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
       // have been created for this location, so do not apply it now.
       if (!parameters->options().shared())
         {
-          gold_assert(tls_segment != NULL);
+         if (tls_segment == NULL)
+           {
+             gold_assert(parameters->errors()->error_count() > 0
+                         || issue_undefined_symbol_error(gsym));
+             return;
+           }
           value -= tls_segment->memsz();
           Relocate_functions<32, false>::rel32(view, value);
         }
@@ -2618,7 +2907,12 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
       // have been created for this location, so do not apply it now.
       if (!parameters->options().shared())
         {
-          gold_assert(tls_segment != NULL);
+         if (tls_segment == NULL)
+           {
+             gold_assert(parameters->errors()->error_count() > 0
+                         || issue_undefined_symbol_error(gsym));
+             return;
+           }
           value = tls_segment->memsz() - value;
           Relocate_functions<32, false>::rel32(view, value);
         }
@@ -3141,7 +3435,7 @@ uint64_t
 Target_i386::do_dynsym_value(const Symbol* gsym) const
 {
   gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
-  return this->plt_section()->address() + gsym->plt_offset();
+  return this->plt_address_for_global(gsym) + gsym->plt_offset();
 }
 
 // Return a string used to fill a code section with nops to take up
@@ -3207,6 +3501,21 @@ Target_i386::do_code_fill(section_size_type length) const
   return std::string(nops[length], length);
 }
 
+// Return the value to use for the base of a DW_EH_PE_datarel offset
+// in an FDE.  Solaris and SVR4 use DW_EH_PE_datarel because their
+// assembler can not write out the difference between two labels in
+// different sections, so instead of using a pc-relative value they
+// use an offset from the GOT.
+
+uint64_t
+Target_i386::do_ehframe_datarel_base() const
+{
+  gold_assert(this->global_offset_table_ != NULL);
+  Symbol* sym = this->global_offset_table_;
+  Sized_symbol<32>* ssym = static_cast<Sized_symbol<32>*>(sym);
+  return ssym->value();
+}
+
 // Return whether SYM should be treated as a call to a non-split
 // function.  We don't want that to be true of a call to a
 // get_pc_thunk function.
@@ -3284,7 +3593,8 @@ class Target_selector_i386 : public Target_selector_freebsd
 public:
   Target_selector_i386()
     : Target_selector_freebsd(elfcpp::EM_386, 32, false,
-                             "elf32-i386", "elf32-i386-freebsd")
+                             "elf32-i386", "elf32-i386-freebsd",
+                             "elf_i386")
   { }
 
   Target*
This page took 0.034145 seconds and 4 git commands to generate.