This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

gold patch committed: Add unwind descriptors for PLT entries


This patch changes gold to unwind descriptors for PLT entries.  I just
copied Jakub's approach for GNU ld.  Committed to mainline.

Ian


	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.


Index: ehframe.cc
===================================================================
RCS file: /cvs/src/src/gold/ehframe.cc,v
retrieving revision 1.19
diff -p -u -r1.19 ehframe.cc
--- ehframe.cc	24 May 2011 21:41:10 -0000	1.19
+++ ehframe.cc	1 Jul 2011 21:53:58 -0000
@@ -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_f
 
 // 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
   // 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_ty
   // 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_ty
 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
   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<siz
   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: ehframe.h
===================================================================
RCS file: /cvs/src/src/gold/ehframe.h,v
retrieving revision 1.16
diff -p -u -r1.16 ehframe.h
--- ehframe.h	24 May 2011 21:41:10 -0000	1.16
+++ ehframe.h	1 Jul 2011 21:53:58 -0000
@@ -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_d
 			    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: i386.cc
===================================================================
RCS file: /cvs/src/src/gold/i386.cc,v
retrieving revision 1.131
diff -p -u -r1.131 i386.cc
--- i386.cc	28 Jun 2011 23:12:31 -0000	1.131
+++ i386.cc	1 Jul 2011 21:53:58 -0000
@@ -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 Outp
   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* lay
 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_i3
 				    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(Layou
 
 // 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
 
 // 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_
 
 // 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
 
 // 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_
   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: layout.cc
===================================================================
RCS file: /cvs/src/src/gold/layout.cc,v
retrieving revision 1.209
diff -p -u -r1.209 layout.cc
--- layout.cc	29 Jun 2011 21:39:19 -0000	1.209
+++ layout.cc	1 Jul 2011 21:53:58 -0000
@@ -1136,51 +1136,10 @@ Layout::layout_eh_frame(Sized_relobj_fil
 	      || 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_fil
       // 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_fil
   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: layout.h
===================================================================
RCS file: /cvs/src/src/gold/layout.h,v
retrieving revision 1.96
diff -p -u -r1.96 layout.h
--- layout.h	29 Jun 2011 21:26:40 -0000	1.96
+++ layout.h	1 Jul 2011 21:53:58 -0000
@@ -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: merge.cc
===================================================================
RCS file: /cvs/src/src/gold/merge.cc,v
retrieving revision 1.39
diff -p -u -r1.39 merge.cc
--- merge.cc	25 Aug 2010 08:36:54 -0000	1.39
+++ merge.cc	1 Jul 2011 21:53:58 -0000
@@ -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, u
 		       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: options.h
===================================================================
RCS file: /cvs/src/src/gold/options.h,v
retrieving revision 1.161
diff -p -u -r1.161 options.h
--- options.h	28 Jun 2011 23:12:31 -0000	1.161
+++ options.h	1 Jul 2011 21:53:58 -0000
@@ -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: x86_64.cc
===================================================================
RCS file: /cvs/src/src/gold/x86_64.cc,v
retrieving revision 1.130
diff -p -u -r1.130 x86_64.cc
--- x86_64.cc	28 Jun 2011 23:12:31 -0000	1.130
+++ x86_64.cc	1 Jul 2011 21:53:58 -0000
@@ -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 Ou
   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 Ou
 			 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 Ou
   // 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_tabl
 				    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_s
 
 // 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::fi
 
 // 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::pl
 
 // 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::tl
   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.
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]