PR gold/12571
authorIan Lance Taylor <ian@airs.com>
Fri, 1 Jul 2011 22:05:01 +0000 (22:05 +0000)
committerIan Lance Taylor <ian@airs.com>
Fri, 1 Jul 2011 22:05:01 +0000 (22:05 +0000)
* options.h (class General_options): Add
--ld-generated-unwind-info.
* ehframe.cc (Fde::write): Add address parameter.  Change all
callers.  If associated with PLT, fill in address and size.
(Cie::set_output_offset): Only add merge mapping if there is an
object.
(Cie::write): Add address parameter.  Change all callers.
(Eh_frame::add_ehframe_for_plt): New function.
* ehframe.h (class Fde): Update declarations.  Move shndx_ and
input_offset_ fields into union u_, with new plt field.
(Fde::Fde): Adjust for new union field.
(Fde::Fde) [Output_data version]: New constructor.
(Fde::add_mapping): Only add merge mapping if there is an object.
(class Cie): Update declarations.
(class Eh_frame): Declare add_ehframe_for_plt.
* layout.cc (Layout::layout_eh_frame): Break out code into
make_eh_frame_section, and call it.
(Layout::make_eh_frame_section): New function.
(Layout::add_eh_frame_for_plt): New function.
* layout.h (class Layout): Update declarations.
* merge.cc (Merge_map::add_mapping): Add assertion.
* i386.cc: Include "dwarf.h".
(class Output_data_plt_i386): Make first_plt_entry,
dyn_first_plt_entry, exec_plt_entry, and dyn_plt_entry const.  Add
plt_eh_frame_cie_size, plt_eh_frame_fde_size, plt_eh_frame_cie,
and plt_eh_frame_fde.
(Output_data_plt_i386::Output_data_plt_i386): Align to 16-byte
boundary.  Call add_eh_frame_for_plt if appropriate.
* x86_64.cc: Include "dwarf.h".
(class Output_data_plt_x86_64): Align to 16-byte boundary.  Make
first_plt_entry, plt_entry and tlsdesc_plt_entry const.  Add
plt_eh_frame_cie_size, plt_eh_frame_fde_size, plt_eh_frame_cie,
and plt_eh_frame_fde.
(Output_data_plt_x86_64::init): Call add_eh_frame_for_plt if
appropriate.

gold/ChangeLog
gold/ehframe.cc
gold/ehframe.h
gold/i386.cc
gold/layout.cc
gold/layout.h
gold/merge.cc
gold/options.h
gold/x86_64.cc

index 62ee3ca..8b8112d 100644 (file)
@@ -1,3 +1,42 @@
+2011-07-01  Ian Lance Taylor  <iant@google.com>
+
+       PR gold/12571
+       * options.h (class General_options): Add
+       --ld-generated-unwind-info.
+       * ehframe.cc (Fde::write): Add address parameter.  Change all
+       callers.  If associated with PLT, fill in address and size.
+       (Cie::set_output_offset): Only add merge mapping if there is an
+       object.
+       (Cie::write): Add address parameter.  Change all callers.
+       (Eh_frame::add_ehframe_for_plt): New function.
+       * ehframe.h (class Fde): Update declarations.  Move shndx_ and
+       input_offset_ fields into union u_, with new plt field.
+       (Fde::Fde): Adjust for new union field.
+       (Fde::Fde) [Output_data version]: New constructor.
+       (Fde::add_mapping): Only add merge mapping if there is an object.
+       (class Cie): Update declarations.
+       (class Eh_frame): Declare add_ehframe_for_plt.
+       * layout.cc (Layout::layout_eh_frame): Break out code into
+       make_eh_frame_section, and call it.
+       (Layout::make_eh_frame_section): New function.
+       (Layout::add_eh_frame_for_plt): New function.
+       * layout.h (class Layout): Update declarations.
+       * merge.cc (Merge_map::add_mapping): Add assertion.
+       * i386.cc: Include "dwarf.h".
+       (class Output_data_plt_i386): Make first_plt_entry,
+       dyn_first_plt_entry, exec_plt_entry, and dyn_plt_entry const.  Add
+       plt_eh_frame_cie_size, plt_eh_frame_fde_size, plt_eh_frame_cie,
+       and plt_eh_frame_fde.
+       (Output_data_plt_i386::Output_data_plt_i386): Align to 16-byte
+       boundary.  Call add_eh_frame_for_plt if appropriate.
+       * x86_64.cc: Include "dwarf.h".
+       (class Output_data_plt_x86_64): Align to 16-byte boundary.  Make
+       first_plt_entry, plt_entry and tlsdesc_plt_entry const.  Add
+       plt_eh_frame_cie_size, plt_eh_frame_fde_size, plt_eh_frame_cie,
+       and plt_eh_frame_fde.
+       (Output_data_plt_x86_64::init): Call add_eh_frame_for_plt if
+       appropriate.
+
 2011-06-29  Ian Lance Taylor  <iant@google.com>
 
        PR gold/12629
index fbeb8a3..80b0035 100644 (file)
@@ -1,6 +1,6 @@
 // ehframe.cc -- handle exception frame sections for gold
 
-// Copyright 2006, 2007, 2008, 2010 Free Software Foundation, Inc.
+// Copyright 2006, 2007, 2008, 2010, 2011 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -321,14 +321,16 @@ Eh_frame_hdr::get_fde_addresses(Output_file* of,
 
 // Write the FDE to OVIEW starting at OFFSET.  CIE_OFFSET is the
 // offset of the CIE in OVIEW.  FDE_ENCODING is the encoding, from the
-// CIE.  ADDRALIGN is the required alignment.  Record the FDE pc for
-// EH_FRAME_HDR.  Return the new offset.
+// CIE.  ADDRALIGN is the required alignment.  ADDRESS is the virtual
+// address of OVIEW.  Record the FDE pc for EH_FRAME_HDR.  Return the
+// new offset.
 
 template<int size, bool big_endian>
 section_offset_type
 Fde::write(unsigned char* oview, section_offset_type offset,
-          unsigned int addralign, section_offset_type cie_offset,
-           unsigned char fde_encoding, Eh_frame_hdr* eh_frame_hdr)
+          uint64_t address, unsigned int addralign,
+          section_offset_type cie_offset, unsigned char fde_encoding,
+          Eh_frame_hdr* eh_frame_hdr)
 {
   gold_assert((offset & (addralign - 1)) == 0);
 
@@ -355,6 +357,24 @@ Fde::write(unsigned char* oview, section_offset_type offset,
   // will later be applied to the FDE data.
   memcpy(oview + offset + 8, this->contents_.data(), length);
 
+  // If this FDE is associated with a PLT, fill in the PLT's address
+  // and size.
+  if (this->object_ == NULL)
+    {
+      gold_assert(memcmp(oview + offset + 8, "\0\0\0\0\0\0\0\0", 8) == 0);
+      Output_data* plt = this->u_.from_linker.plt;
+      uint64_t poffset = plt->address() - (address + offset + 8);
+      int32_t spoffset = static_cast<int32_t>(poffset);
+      off_t psize = plt->data_size();
+      uint32_t upsize = static_cast<uint32_t>(psize);
+      if (static_cast<uint64_t>(static_cast<int64_t>(spoffset)) != poffset
+         || static_cast<off_t>(upsize) != psize)
+       gold_warning(_("overflow in PLT unwind data; "
+                      "unwinding through PLT may fail"));
+      elfcpp::Swap<32, big_endian>::writeval(oview + offset + 8, spoffset);
+      elfcpp::Swap<32, big_endian>::writeval(oview + offset + 12, upsize);
+    }
+
   if (aligned_full_length > length + 8)
     memset(oview + offset + length + 8, 0, aligned_full_length - (length + 8));
 
@@ -389,8 +409,12 @@ Cie::set_output_offset(section_offset_type output_offset,
   // Add 4 for length and 4 for zero CIE identifier tag.
   length += 8;
 
-  merge_map->add_mapping(this->object_, this->shndx_, this->input_offset_,
-                        length, output_offset);
+  if (this->object_ != NULL)
+    {
+      // Add a mapping so that relocations are applied correctly.
+      merge_map->add_mapping(this->object_, this->shndx_, this->input_offset_,
+                            length, output_offset);
+    }
 
   length = align_address(length, addralign);
 
@@ -415,7 +439,8 @@ Cie::set_output_offset(section_offset_type output_offset,
 template<int size, bool big_endian>
 section_offset_type
 Cie::write(unsigned char* oview, section_offset_type offset,
-          unsigned int addralign, Eh_frame_hdr* eh_frame_hdr)
+          uint64_t address, unsigned int addralign,
+          Eh_frame_hdr* eh_frame_hdr)
 {
   gold_assert((offset & (addralign - 1)) == 0);
 
@@ -448,7 +473,7 @@ Cie::write(unsigned char* oview, section_offset_type offset,
   for (std::vector<Fde*>::const_iterator p = this->fdes_.begin();
        p != this->fdes_.end();
        ++p)
-    offset = (*p)->write<size, big_endian>(oview, offset, addralign,
+    offset = (*p)->write<size, big_endian>(oview, offset, address, addralign,
                                            cie_offset, fde_encoding,
                                            eh_frame_hdr);
 
@@ -994,6 +1019,29 @@ Eh_frame::read_fde(Sized_relobj_file<size, big_endian>* object,
   return true;
 }
 
+// Add unwind information for a PLT.
+
+void
+Eh_frame::add_ehframe_for_plt(Output_data* plt, const unsigned char* cie_data,
+                             size_t cie_length, const unsigned char* fde_data,
+                             size_t fde_length)
+{
+  Cie cie(NULL, 0, 0, elfcpp::DW_EH_PE_pcrel | elfcpp::DW_EH_PE_sdata4, "",
+         cie_data, cie_length);
+  Cie_offsets::iterator find_cie = this->cie_offsets_.find(&cie);
+  Cie* pcie;
+  if (find_cie != this->cie_offsets_.end())
+    pcie = *find_cie;
+  else
+    {
+      pcie = new Cie(cie);
+      this->cie_offsets_.insert(pcie);
+    }
+
+  Fde* fde = new Fde(plt, fde_data, fde_length);
+  pcie->add_fde(fde);
+}
+
 // Return the number of FDEs.
 
 unsigned int
@@ -1113,18 +1161,19 @@ template<int size, bool big_endian>
 void
 Eh_frame::do_sized_write(unsigned char* oview)
 {
+  uint64_t address = this->address();
   unsigned int addralign = this->addralign();
   section_offset_type o = 0;
   for (Unmergeable_cie_offsets::iterator p =
         this->unmergeable_cie_offsets_.begin();
        p != this->unmergeable_cie_offsets_.end();
        ++p)
-    o = (*p)->write<size, big_endian>(oview, o, addralign,
+    o = (*p)->write<size, big_endian>(oview, o, address, addralign,
                                       this->eh_frame_hdr_);
   for (Cie_offsets::iterator p = this->cie_offsets_.begin();
        p != this->cie_offsets_.end();
        ++p)
-    o = (*p)->write<size, big_endian>(oview, o, addralign,
+    o = (*p)->write<size, big_endian>(oview, o, address, addralign,
                                       this->eh_frame_hdr_);
 }
 
index f626b1f..c3f82e9 100644 (file)
@@ -1,6 +1,6 @@
 // ehframe.h -- handle exception frame sections for gold  -*- C++ -*-
 
-// Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
+// Copyright 2006, 2007, 2008, 2010, 2011 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -45,10 +45,6 @@ class Eh_frame;
 // time and when a shared object is loaded, and the time required to
 // deregister the exception handlers when a shared object is unloaded.
 
-// FIXME: gcc supports using storing a sorted lookup table for the
-// FDEs in the PT_GNU_EH_FRAME segment, but we do not yet generate
-// that.
-
 class Eh_frame_hdr : public Output_section_data
 {
  public:
@@ -170,9 +166,18 @@ class Fde
  public:
   Fde(Relobj* object, unsigned int shndx, section_offset_type input_offset,
       const unsigned char* contents, size_t length)
-    : object_(object), shndx_(shndx), input_offset_(input_offset),
+    : object_(object),
       contents_(reinterpret_cast<const char*>(contents), length)
-  { }
+  {
+    this->u_.from_object.shndx = shndx;
+    this->u_.from_object.input_offset = input_offset;
+  }
+
+  // Create an FDE associated with a PLT.
+  Fde(Output_data* plt, const unsigned char* contents, size_t length)
+    : object_(NULL),
+      contents_(reinterpret_cast<const char*>(contents), length)
+  { this->u_.from_linker.plt = plt; }
 
   // Return the length of this FDE.  Add 4 for the length and 4 for
   // the offset to the CIE.
@@ -180,32 +185,52 @@ class Fde
   length() const
   { return this->contents_.length() + 8; }
 
-  // Add a mapping for this FDE to MERGE_MAP.
+  // Add a mapping for this FDE to MERGE_MAP, so that relocations
+  // against the FDE are applied to right part of the output file.
   void
   add_mapping(section_offset_type output_offset, Merge_map* merge_map) const
   {
-    merge_map->add_mapping(this->object_, this->shndx_,
-                          this->input_offset_, this->length(),
-                          output_offset);
+    if (this->object_ != NULL)
+      merge_map->add_mapping(this->object_, this->u_.from_object.shndx,
+                            this->u_.from_object.input_offset, this->length(),
+                            output_offset);
   }
 
   // Write the FDE to OVIEW starting at OFFSET.  FDE_ENCODING is the
   // encoding, from the CIE.  Round up the bytes to ADDRALIGN if
-  // necessary.  Record the FDE in EH_FRAME_HDR.  Return the new
-  // offset.
+  // necessary.  ADDRESS is the virtual address of OVIEW.  Record the
+  // FDE in EH_FRAME_HDR.  Return the new offset.
   template<int size, bool big_endian>
   section_offset_type
   write(unsigned char* oview, section_offset_type offset,
-       unsigned int addralign, section_offset_type cie_offset,
-        unsigned char fde_encoding, Eh_frame_hdr* eh_frame_hdr);
+       uint64_t address, unsigned int addralign,
+       section_offset_type cie_offset, unsigned char fde_encoding,
+       Eh_frame_hdr* eh_frame_hdr);
 
  private:
-  // The object in which this FDE was seen.
+  // The object in which this FDE was seen.  This will be NULL for a
+  // linker generated FDE.
   Relobj* object_;
-  // Input section index for this FDE.
-  unsigned int shndx_;
-  // Offset within the input section for this FDE.
-  section_offset_type input_offset_;
+  union
+  {
+    // These fields are used if the FDE is from an input object (the
+    // object_ field is not NULL).
+    struct
+    {
+      // Input section index for this FDE.
+      unsigned int shndx;
+      // Offset within the input section for this FDE.
+      section_offset_type input_offset;
+    } from_object;
+    // This field is used if the FDE is generated by the linker (the
+    // object_ field is NULL).
+    struct
+    {
+      // The only linker generated FDEs are for PLT sections, and this
+      // points to the PLT section.
+      Output_data* plt;
+    } from_linker;
+  } u_;
   // FDE data.
   std::string contents_;
 };
@@ -261,10 +286,11 @@ class Cie
 
   // Write the CIE to OVIEW starting at OFFSET.  EH_FRAME_HDR is the
   // exception frame header for FDE recording.  Round up the bytes to
-  // ADDRALIGN.  Return the new offset.
+  // ADDRALIGN.  ADDRESS is the virtual address of OVIEW.  Return the
+  // new offset.
   template<int size, bool big_endian>
   section_offset_type
-  write(unsigned char* oview, section_offset_type offset,
+  write(unsigned char* oview, section_offset_type offset, uint64_t address,
        unsigned int addralign, Eh_frame_hdr* eh_frame_hdr);
 
   friend bool operator<(const Cie&, const Cie&);
@@ -274,11 +300,14 @@ class Cie
   // The class is not assignable.
   Cie& operator=(const Cie&);
 
-  // The object in which this CIE was first seen.
+  // The object in which this CIE was first seen.  This will be NULL
+  // for a linker generated CIE.
   Relobj* object_;
-  // Input section index for this CIE.
+  // Input section index for this CIE.  This will be 0 for a linker
+  // generated CIE.
   unsigned int shndx_;
-  // Offset within the input section for this CIE.
+  // Offset within the input section for this CIE.  This will be 0 for
+  // a linker generated CIE.
   section_offset_type input_offset_;
   // The encoding of the FDE.  This is a DW_EH_PE code.
   unsigned char fde_encoding_;
@@ -324,6 +353,15 @@ class Eh_frame : public Output_section_data
                            unsigned int shndx, unsigned int reloc_shndx,
                            unsigned int reloc_type);
 
+  // Add a CIE and an FDE for a PLT section, to permit unwinding
+  // through a PLT.  The FDE data should start with 8 bytes of zero,
+  // which will be replaced by a 4 byte PC relative reference to the
+  // address of PLT and a 4 byte size of PLT.
+  void
+  add_ehframe_for_plt(Output_data* plt, const unsigned char* cie_data,
+                     size_t cie_length, const unsigned char* fde_data,
+                     size_t fde_length);
+
   // Return the number of FDEs.
   unsigned int
   fde_count() const;
index beec4a8..636dfbb 100644 (file)
@@ -25,6 +25,7 @@
 #include <cstring>
 
 #include "elfcpp.h"
+#include "dwarf.h"
 #include "parameters.h"
 #include "reloc.h"
 #include "i386.h"
@@ -101,16 +102,22 @@ 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
@@ -728,7 +735,7 @@ Target_i386::rel_dyn_section(Layout* layout)
 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),
+  : Output_section_data(16), tls_desc_rel_(NULL), got_plt_(got_plt), count_(0),
     global_ifuncs_(), local_ifuncs_()
 {
   this->rel_ = new Reloc_section(false);
@@ -753,6 +760,11 @@ Output_data_plt_i386::Output_data_plt_i386(Symbol_table* symtab,
                                    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
@@ -857,7 +869,7 @@ Output_data_plt_i386::rel_tls_desc(Layout* layout)
 
 // 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 +880,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 +889,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 +901,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 +911,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+8
+  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.
index 3152622..11dbbef 100644 (file)
@@ -1136,51 +1136,10 @@ Layout::layout_eh_frame(Sized_relobj_file<size, big_endian>* object,
              || shdr.get_sh_type() == elfcpp::SHT_X86_64_UNWIND);
   gold_assert((shdr.get_sh_flags() & elfcpp::SHF_ALLOC) != 0);
 
-  const char* const name = ".eh_frame";
-  Output_section* os = this->choose_output_section(object, name,
-                                                  elfcpp::SHT_PROGBITS,
-                                                  elfcpp::SHF_ALLOC, false,
-                                                  ORDER_EHFRAME, false);
+  Output_section* os = this->make_eh_frame_section(object);
   if (os == NULL)
     return NULL;
 
-  if (this->eh_frame_section_ == NULL)
-    {
-      this->eh_frame_section_ = os;
-      this->eh_frame_data_ = new Eh_frame();
-
-      // For incremental linking, we do not optimize .eh_frame sections
-      // or create a .eh_frame_hdr section.
-      if (parameters->options().eh_frame_hdr() && !parameters->incremental())
-       {
-         Output_section* hdr_os =
-           this->choose_output_section(NULL, ".eh_frame_hdr",
-                                       elfcpp::SHT_PROGBITS,
-                                       elfcpp::SHF_ALLOC, false,
-                                       ORDER_EHFRAME, false);
-
-         if (hdr_os != NULL)
-           {
-             Eh_frame_hdr* hdr_posd = new Eh_frame_hdr(os,
-                                                       this->eh_frame_data_);
-             hdr_os->add_output_section_data(hdr_posd);
-
-             hdr_os->set_after_input_sections();
-
-             if (!this->script_options_->saw_phdrs_clause())
-               {
-                 Output_segment* hdr_oseg;
-                 hdr_oseg = this->make_output_segment(elfcpp::PT_GNU_EH_FRAME,
-                                                      elfcpp::PF_R);
-                 hdr_oseg->add_output_section_to_nonload(hdr_os,
-                                                         elfcpp::PF_R);
-               }
-
-             this->eh_frame_data_->set_eh_frame_hdr(hdr_posd);
-           }
-       }
-    }
-
   gold_assert(this->eh_frame_section_ == os);
 
   elfcpp::Elf_Xword orig_flags = os->flags();
@@ -1222,8 +1181,8 @@ Layout::layout_eh_frame(Sized_relobj_file<size, big_endian>* object,
       // We couldn't handle this .eh_frame section for some reason.
       // Add it as a normal section.
       bool saw_sections_clause = this->script_options_->saw_sections_clause();
-      *off = os->add_input_section(this, object, shndx, name, shdr, reloc_shndx,
-                                  saw_sections_clause);
+      *off = os->add_input_section(this, object, shndx, ".eh_frame", shdr,
+                                  reloc_shndx, saw_sections_clause);
       this->have_added_input_section_ = true;
 
       if ((orig_flags & (elfcpp::SHF_WRITE | elfcpp::SHF_EXECINSTR))
@@ -1234,6 +1193,86 @@ Layout::layout_eh_frame(Sized_relobj_file<size, big_endian>* object,
   return os;
 }
 
+// Create and return the magic .eh_frame section.  Create
+// .eh_frame_hdr also if appropriate.  OBJECT is the object with the
+// input .eh_frame section; it may be NULL.
+
+Output_section*
+Layout::make_eh_frame_section(const Relobj* object)
+{
+  // FIXME: On x86_64, this could use SHT_X86_64_UNWIND rather than
+  // SHT_PROGBITS.
+  Output_section* os = this->choose_output_section(object, ".eh_frame",
+                                                  elfcpp::SHT_PROGBITS,
+                                                  elfcpp::SHF_ALLOC, false,
+                                                  ORDER_EHFRAME, false);
+  if (os == NULL)
+    return NULL;
+
+  if (this->eh_frame_section_ == NULL)
+    {
+      this->eh_frame_section_ = os;
+      this->eh_frame_data_ = new Eh_frame();
+
+      // For incremental linking, we do not optimize .eh_frame sections
+      // or create a .eh_frame_hdr section.
+      if (parameters->options().eh_frame_hdr() && !parameters->incremental())
+       {
+         Output_section* hdr_os =
+           this->choose_output_section(NULL, ".eh_frame_hdr",
+                                       elfcpp::SHT_PROGBITS,
+                                       elfcpp::SHF_ALLOC, false,
+                                       ORDER_EHFRAME, false);
+
+         if (hdr_os != NULL)
+           {
+             Eh_frame_hdr* hdr_posd = new Eh_frame_hdr(os,
+                                                       this->eh_frame_data_);
+             hdr_os->add_output_section_data(hdr_posd);
+
+             hdr_os->set_after_input_sections();
+
+             if (!this->script_options_->saw_phdrs_clause())
+               {
+                 Output_segment* hdr_oseg;
+                 hdr_oseg = this->make_output_segment(elfcpp::PT_GNU_EH_FRAME,
+                                                      elfcpp::PF_R);
+                 hdr_oseg->add_output_section_to_nonload(hdr_os,
+                                                         elfcpp::PF_R);
+               }
+
+             this->eh_frame_data_->set_eh_frame_hdr(hdr_posd);
+           }
+       }
+    }
+
+  return os;
+}
+
+// Add an exception frame for a PLT.  This is called from target code.
+
+void
+Layout::add_eh_frame_for_plt(Output_data* plt, const unsigned char* cie_data,
+                            size_t cie_length, const unsigned char* fde_data,
+                            size_t fde_length)
+{
+  if (parameters->incremental())
+    {
+      // FIXME: Maybe this could work some day....
+      return;
+    }
+  Output_section* os = this->make_eh_frame_section(NULL);
+  if (os == NULL)
+    return;
+  this->eh_frame_data_->add_ehframe_for_plt(plt, cie_data, cie_length,
+                                           fde_data, fde_length);
+  if (!this->added_eh_frame_data_)
+    {
+      os->add_output_section_data(this->eh_frame_data_);
+      this->added_eh_frame_data_ = true;
+    }
+}
+
 // Add POSD to an output section using NAME, TYPE, and FLAGS.  Return
 // the output section.
 
index 4790584..a16bb5c 100644 (file)
@@ -549,6 +549,14 @@ class Layout
                  unsigned int reloc_shndx, unsigned int reloc_type,
                  off_t* offset);
 
+  // Add .eh_frame information for a PLT.  The FDE must start with a
+  // 4-byte PC-relative reference to the start of the PLT, followed by
+  // a 4-byte size of PLT.
+  void
+  add_eh_frame_for_plt(Output_data* plt, const unsigned char* cie_data,
+                      size_t cie_length, const unsigned char* fde_data,
+                      size_t fde_length);
+
   // Handle a GNU stack note.  This is called once per input object
   // file.  SEEN_GNU_STACK is true if the object file has a
   // .note.GNU-stack section.  GNU_STACK_FLAGS is the section flags
@@ -1018,6 +1026,10 @@ class Layout
   void
   attach_allocated_section_to_segment(Output_section*);
 
+  // Make the .eh_frame section.
+  Output_section*
+  make_eh_frame_section(const Relobj*);
+
   // Set the final file offsets of all the segments.
   off_t
   set_segment_offsets(const Target*, Output_segment*, unsigned int* pshndx);
index 4fcbc10..093b6fc 100644 (file)
@@ -1,6 +1,6 @@
 // merge.cc -- handle section merging for gold
 
-// Copyright 2006, 2007, 2008, 2010 Free Software Foundation, Inc.
+// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -242,6 +242,7 @@ Merge_map::add_mapping(Relobj* object, unsigned int shndx,
                       section_offset_type offset, section_size_type length,
                       section_offset_type output_offset)
 {
+  gold_assert(object != NULL);
   Object_merge_map* object_merge_map = object->merge_map();
   if (object_merge_map == NULL)
     {
index da4b6cf..998e850 100644 (file)
@@ -820,6 +820,10 @@ class General_options
               N_("Keep files mapped across passes (default)"),
               N_("Release mapped files after each pass"));
 
+  DEFINE_bool(ld_generated_unwind_info, options::TWO_DASHES, '\0', true,
+             N_("Generate unwind information for PLT (default)"),
+             N_("Do not generate unwind information for PLT"));
+
   DEFINE_special(library, options::TWO_DASHES, 'l',
                  N_("Search for library LIBNAME"), N_("LIBNAME"));
 
index de39cb4..12a5467 100644 (file)
@@ -25,6 +25,7 @@
 #include <cstring>
 
 #include "elfcpp.h"
+#include "dwarf.h"
 #include "parameters.h"
 #include "reloc.h"
 #include "x86_64.h"
@@ -56,7 +57,7 @@ class Output_data_plt_x86_64 : public Output_section_data
   Output_data_plt_x86_64(Symbol_table* symtab, Layout* layout,
                         Output_data_got<64, false>* got,
                         Output_data_space* got_plt)
-    : Output_section_data(8), tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt),
+    : Output_section_data(16), tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt),
       count_(0), tlsdesc_got_offset_(-1U), free_list_()
   { this->init(symtab, layout); }
 
@@ -64,7 +65,7 @@ class Output_data_plt_x86_64 : public Output_section_data
                         Output_data_got<64, false>* got,
                         Output_data_space* got_plt,
                         unsigned int plt_count)
-    : Output_section_data((plt_count + 1) * plt_entry_size, 8, false),
+    : Output_section_data((plt_count + 1) * plt_entry_size, 16, false),
       tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt),
       count_(plt_count), tlsdesc_got_offset_(-1U), free_list_()
   {
@@ -160,13 +161,19 @@ class Output_data_plt_x86_64 : public Output_section_data
   // The first entry in the PLT.
   // From the AMD64 ABI: "Unlike Intel386 ABI, this ABI uses the same
   // procedure linkage table for both programs and shared objects."
-  static unsigned char first_plt_entry[plt_entry_size];
+  static const unsigned char first_plt_entry[plt_entry_size];
 
   // Other entries in the PLT for an executable.
-  static unsigned char plt_entry[plt_entry_size];
+  static const unsigned char plt_entry[plt_entry_size];
 
   // The reserved TLSDESC entry in the PLT for an executable.
-  static unsigned char tlsdesc_plt_entry[plt_entry_size];
+  static const unsigned char tlsdesc_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
@@ -871,6 +878,11 @@ Output_data_plt_x86_64::init(Symbol_table* symtab, Layout* layout)
                                    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
@@ -1004,7 +1016,7 @@ Output_data_plt_x86_64::set_final_data_size()
 
 // The first entry in the PLT for an executable.
 
-unsigned char Output_data_plt_x86_64::first_plt_entry[plt_entry_size] =
+const unsigned char Output_data_plt_x86_64::first_plt_entry[plt_entry_size] =
 {
   // From AMD64 ABI Draft 0.98, page 76
   0xff, 0x35,  // pushq contents of memory address
@@ -1016,7 +1028,7 @@ unsigned char Output_data_plt_x86_64::first_plt_entry[plt_entry_size] =
 
 // Subsequent entries in the PLT for an executable.
 
-unsigned char Output_data_plt_x86_64::plt_entry[plt_entry_size] =
+const unsigned char Output_data_plt_x86_64::plt_entry[plt_entry_size] =
 {
   // From AMD64 ABI Draft 0.98, page 76
   0xff, 0x25,  // jmpq indirect
@@ -1029,7 +1041,7 @@ unsigned char Output_data_plt_x86_64::plt_entry[plt_entry_size] =
 
 // The reserved TLSDESC entry in the PLT for an executable.
 
-unsigned char Output_data_plt_x86_64::tlsdesc_plt_entry[plt_entry_size] =
+const unsigned char Output_data_plt_x86_64::tlsdesc_plt_entry[plt_entry_size] =
 {
   // From Alexandre Oliva, "Thread-Local Storage Descriptors for IA32
   // and AMD64/EM64T", Version 0.9.4 (2005-10-10).
@@ -1041,6 +1053,54 @@ unsigned char Output_data_plt_x86_64::tlsdesc_plt_entry[plt_entry_size] =
   0x40, 0
 };
 
+// The .eh_frame unwind information for the PLT.
+
+const unsigned char 
+Output_data_plt_x86_64::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.
+  0x78,                                // Data alignment factor.
+  16,                          // Return address column.
+  1,                           // Augmentation size.
+  (elfcpp::DW_EH_PE_pcrel      // FDE encoding.
+   | elfcpp::DW_EH_PE_sdata4),
+  elfcpp::DW_CFA_def_cfa, 7, 8,        // DW_CFA_def_cfa: r7 (rsp) ofs 8.
+  elfcpp::DW_CFA_offset + 16, 1,// DW_CFA_offset: r16 (rip) at cfa-8.
+  elfcpp::DW_CFA_nop,          // Align to 16 bytes.
+  elfcpp::DW_CFA_nop
+};
+
+const unsigned char
+Output_data_plt_x86_64::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, 16,   // DW_CFA_def_cfa_offset: 16.
+  elfcpp::DW_CFA_advance_loc + 6,      // Advance 6 to __PLT__ + 6.
+  elfcpp::DW_CFA_def_cfa_offset, 24,   // DW_CFA_def_cfa_offset: 24.
+  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_breg7, 8,              // Push %rsp + 8.
+  elfcpp::DW_OP_breg16, 0,             // Push %rip.
+  elfcpp::DW_OP_lit15,                 // Push 0xf.
+  elfcpp::DW_OP_and,                   // & (%rip & 0xf).
+  elfcpp::DW_OP_lit11,                 // Push 0xb.
+  elfcpp::DW_OP_ge,                    // >= ((%rip & 0xf) >= 0xb)
+  elfcpp::DW_OP_lit3,                  // Push 3.
+  elfcpp::DW_OP_shl,                   // << (((%rip & 0xf) >= 0xb) << 3)
+  elfcpp::DW_OP_plus,                  // + ((((%rip&0xf)>=0xb)<<3)+%rsp+8
+  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 specified by the AMD64 ABI.