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] powerpc linker generated .eh_frame


This teaches powerpc gold to provide linker generated .eh_frame info
covering plt call stubs and other stubs.  Some infrastructure changes
were necessary.  The first problem I hit was that since powerpc plt
call stubs are held in a relaxed input section, Fde::write use of
plt->address() and plt->data_size() is incorrect.  We don't want the
whole relaxed input section, just the stubs tacked onto the end.
Target::plt_fde_location was invented to handle this difference, and
oddities like ppc32 PIC .glink section either needing two FDEs (as I
use here) or eh_frame info depending on the size of .glink
(bfd/elf32-ppc.c).

The second, more difficult problem is that a powerpc stub section may
be empty.  I discovered this on hitting assertions in gold on small
statically linked binaries, but the same thing can occur with large
dynamically linked binaries.  A large binary having multiple stub
sections might not make any plt calls from code serviced by one of the
stub sections.  In any case, we don't need or want .eh_frame FDEs
covering zero length address ranges.  This means removing unwanted
FDEs or creating them only when we discover stubs are needed.
Both approaches run into difficulty with merge maps created to map
input .eh_frame FDEs to the output .eh_frame section.  If we modify
the layout of input .eh_frame sections, then the mapping needs to be
updated.  I decided the easiest approach was late creation coupled
with placing post mapping FDEs at the end of .eh_frame.  That way
input .eh_frame section layout is preserved.  OK to apply?

	* target.h (Target::plt_fde_location, do_plt_fde_location): Declare.
	* target.cc (Target::do_plt_fde_location): New function.
	* ehframe.h (class FDE): Add post_map field to u_.from_linker,
	accessor function, and constructor param.
	(class Post_fde, Post_fdes): Declare.
	(Cie::write): Add post_fdes param.
	* ehframe.cc (Fde::write): Use plt_fde_location.
	(class Post_fde): Define.
	(Cie::write): Stash FDEs added post merge mapping.
	(Eh_frame::add_ehframe_for_plt): Assert no new CIEs after mapping.
	Adjust Fde constructor call.  Bump final_data_size_ for post map FDEs.
	(Eh_frame::do_sized_write): Arrange to write post map FDES after
	other FDEs.
	* powerpc.cc (Target_powerpc::do_plt_fde_location): New function.
	(Target_powerpc::has_glink): New function.
	(Target_powerpc::do_relax): Add eh_frame info for stubs.
	(struct Eh_cie, eh_frame_cie, glink_eh_frame_fde_64,
	glink_eh_frame_fde_32, default_fde): New data.
	(Stub_table::eh_frame_added_): New var.
	(Stub_table::find_long_branch_entry, stub_address, stub_offset):
	Make const.
	(Stub_table::add_eh_frame): New function.
	(Output_data_glink::add_eh_frame): New function.
	(Target_powerpc::make_glink_section): Call add_eh_frame.

Index: gold/target.h
===================================================================
RCS file: /cvs/src/src/gold/target.h,v
retrieving revision 1.72
diff -u -p -r1.72 target.h
--- gold/target.h	1 Nov 2012 23:27:00 -0000	1.72
+++ gold/target.h	27 Feb 2013 02:47:07 -0000
@@ -240,6 +240,12 @@ class Target
   adjust_elf_header(unsigned char* view, int len) const
   { return this->do_adjust_elf_header(view, len); }
 
+  // Return address and size to plug into eh_frame FDEs associated with a PLT.
+  void
+  plt_fde_location(const Output_data* plt, unsigned char* oview,
+		   uint64_t* address, off_t* len) const
+  { return this->do_plt_fde_location(plt, oview, address, len); }
+
   // Return whether NAME is a local label name.  This is used to implement the
   // --discard-locals options.
   bool
@@ -530,6 +536,11 @@ class Target
   virtual void
   do_adjust_elf_header(unsigned char*, int) const = 0;
 
+  // Return address and size to plug into eh_frame FDEs associated with a PLT.
+  virtual void
+  do_plt_fde_location(const Output_data* plt, unsigned char* oview,
+		      uint64_t* address, off_t* len) const;
+
   // Virtual function which may be overridden by the child class.
   virtual bool
   do_is_local_label_name(const char*) const;
Index: gold/target.cc
===================================================================
RCS file: /cvs/src/src/gold/target.cc,v
retrieving revision 1.13
diff -u -p -r1.13 target.cc
--- gold/target.cc	20 Apr 2012 22:23:47 -0000	1.13
+++ gold/target.cc	27 Feb 2013 02:47:07 -0000
@@ -203,6 +203,15 @@ Target::set_view_to_nop(unsigned char* v
     }
 }
 
+// Return address and size to plug into eh_frame FDEs associated with a PLT.
+void
+Target::do_plt_fde_location(const Output_data* plt, unsigned char*,
+			    uint64_t* address, off_t* len) const
+{
+  *address = plt->address();
+  *len = plt->data_size();
+}
+
 // Class Sized_target.
 
 // Set the EI_OSABI field of the ELF header if requested.
Index: gold/ehframe.h
===================================================================
RCS file: /cvs/src/src/gold/ehframe.h,v
retrieving revision 1.17
diff -u -p -r1.17 ehframe.h
--- gold/ehframe.h	1 Jul 2011 22:05:01 -0000	1.17
+++ gold/ehframe.h	27 Feb 2013 02:47:06 -0000
@@ -174,10 +174,14 @@ class Fde
   }
 
   // Create an FDE associated with a PLT.
-  Fde(Output_data* plt, const unsigned char* contents, size_t length)
+  Fde(Output_data* plt, const unsigned char* contents, size_t length,
+      bool post_map)
     : object_(NULL),
       contents_(reinterpret_cast<const char*>(contents), length)
-  { this->u_.from_linker.plt = plt; }
+  {
+    this->u_.from_linker.plt = plt;
+    this->u_.from_linker.post_map = post_map;
+  }
 
   // Return the length of this FDE.  Add 4 for the length and 4 for
   // the offset to the CIE.
@@ -196,6 +200,11 @@ class Fde
 			     output_offset);
   }
 
+  // Return whether this FDE was added after merge mapping.
+  bool
+  post_map()
+  { return this->object_ == NULL && this->u_.from_linker.post_map; }
+
   // Write the FDE to OVIEW starting at OFFSET.  FDE_ENCODING is the
   // encoding, from the CIE.  Round up the bytes to ADDRALIGN if
   // necessary.  ADDRESS is the virtual address of OVIEW.  Record the
@@ -229,12 +238,17 @@ class Fde
       // The only linker generated FDEs are for PLT sections, and this
       // points to the PLT section.
       Output_data* plt;
+      // Set if the FDE was added after merge mapping.
+      bool post_map;
     } from_linker;
   } u_;
   // FDE data.
   std::string contents_;
 };
 
+class Post_fde;
+typedef std::vector<Post_fde*> Post_fdes;
+
 // This class holds a CIE.
 
 class Cie
@@ -284,14 +298,16 @@ class Cie
   set_output_offset(section_offset_type output_offset, unsigned int addralign,
 		    Merge_map*);
 
-  // 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.  ADDRESS is the virtual address of OVIEW.  Return the
-  // new offset.
+  // Write the CIE to OVIEW starting at OFFSET.  Round up the bytes to
+  // ADDRALIGN.  ADDRESS is the virtual address of OVIEW.
+  // EH_FRAME_HDR is the exception frame header for FDE recording.
+  // POST_FDES stashes FDEs created after mappings were done, for later
+  // writing.  Return the new offset.
   template<int size, bool big_endian>
   section_offset_type
   write(unsigned char* oview, section_offset_type offset, uint64_t address,
-	unsigned int addralign, Eh_frame_hdr* eh_frame_hdr);
+	unsigned int addralign, Eh_frame_hdr* eh_frame_hdr,
+	Post_fdes* post_fdes);
 
   friend bool operator<(const Cie&, const Cie&);
   friend bool operator==(const Cie&, const Cie&);
Index: gold/ehframe.cc
===================================================================
RCS file: /cvs/src/src/gold/ehframe.cc,v
retrieving revision 1.22
diff -u -p -r1.22 ehframe.cc
--- gold/ehframe.cc	11 Jul 2012 14:18:40 -0000	1.22
+++ gold/ehframe.cc	27 Feb 2013 02:47:06 -0000
@@ -368,10 +368,13 @@ Fde::write(unsigned char* oview, section
   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);
+      uint64_t paddress;
+      off_t psize;
+      parameters->target().plt_fde_location(this->u_.from_linker.plt,
+					    oview + offset + 8,
+					    &paddress, &psize);
+      uint64_t poffset = paddress - (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)
@@ -438,15 +441,31 @@ Cie::set_output_offset(section_offset_ty
   return output_offset + length;
 }
 
-// Write the CIE to OVIEW starting at OFFSET.  EH_FRAME_HDR is for FDE
-// recording.  Round up the bytes to ADDRALIGN.  Return the new
-// offset.
+class Post_fde
+{
+ public:
+  Post_fde(Fde* fde, section_offset_type cie_offset, unsigned char fde_encoding)
+    : fde_(fde), cie_offset_(cie_offset), fde_encoding_(fde_encoding)
+  { }
+
+  ~Post_fde();
+
+  Fde* fde_;
+  section_offset_type cie_offset_;
+  unsigned char fde_encoding_;
+};
+
+// Write the CIE to OVIEW starting at OFFSET.  Round up the bytes to
+// ADDRALIGN.  ADDRESS is the virtual address of OVIEW.
+// EH_FRAME_HDR is the exception frame header for FDE recording.
+// POST_FDES stashes FDEs created after mappings were done, for later
+// writing.  Return the new offset.
 
 template<int size, bool big_endian>
 section_offset_type
 Cie::write(unsigned char* oview, section_offset_type offset,
 	   uint64_t address, unsigned int addralign,
-	   Eh_frame_hdr* eh_frame_hdr)
+	   Eh_frame_hdr* eh_frame_hdr, Post_fdes* post_fdes)
 {
   gold_assert((offset & (addralign - 1)) == 0);
 
@@ -479,9 +498,14 @@ 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, address, addralign,
-                                           cie_offset, fde_encoding,
-                                           eh_frame_hdr);
+    {
+      if ((*p)->post_map())
+	post_fdes->push_back(new Post_fde(*p, cie_offset, fde_encoding));
+      else
+	offset = (*p)->write<size, big_endian>(oview, offset, address,
+					       addralign, cie_offset,
+					       fde_encoding, eh_frame_hdr);
+    }
 
   return offset;
 }
@@ -1039,12 +1063,16 @@ Eh_frame::add_ehframe_for_plt(Output_dat
     pcie = *find_cie;
   else
     {
+      gold_assert(!this->mappings_are_done_);
       pcie = new Cie(cie);
       this->cie_offsets_.insert(pcie);
     }
 
-  Fde* fde = new Fde(plt, fde_data, fde_length);
+  Fde* fde = new Fde(plt, fde_data, fde_length, this->mappings_are_done_);
   pcie->add_fde(fde);
+
+  if (this->mappings_are_done_)
+    this->final_data_size_ += align_address(fde_length + 8, this->addralign());
 }
 
 // Return the number of FDEs.
@@ -1169,17 +1197,25 @@ Eh_frame::do_sized_write(unsigned char* 
   uint64_t address = this->address();
   unsigned int addralign = this->addralign();
   section_offset_type o = 0;
+  Post_fdes post_fdes;
   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, address, addralign,
-                                      this->eh_frame_hdr_);
+                                      this->eh_frame_hdr_, &post_fdes);
   for (Cie_offsets::iterator p = this->cie_offsets_.begin();
        p != this->cie_offsets_.end();
        ++p)
     o = (*p)->write<size, big_endian>(oview, o, address, addralign,
-                                      this->eh_frame_hdr_);
+                                      this->eh_frame_hdr_, &post_fdes);
+  for (Post_fdes::iterator p = post_fdes.begin();
+       p != post_fdes.end();
+       ++p)
+    o = (*p)->fde_->write<size, big_endian>(oview, o, address, addralign,
+					    (*p)->cie_offset_,
+					    (*p)->fde_encoding_,
+					    this->eh_frame_hdr_);
 }
 
 #ifdef HAVE_TARGET_32_LITTLE
Index: gold/powerpc.cc
===================================================================
RCS file: /cvs/src/src/gold/powerpc.cc,v
retrieving revision 1.83
diff -u -p -r1.83 powerpc.cc
--- gold/powerpc.cc	15 Feb 2013 12:59:15 -0000	1.83
+++ gold/powerpc.cc	27 Feb 2013 02:47:07 -0000
@@ -25,6 +25,7 @@
 
 #include <algorithm>
 #include "elfcpp.h"
+#include "dwarf.h"
 #include "parameters.h"
 #include "reloc.h"
 #include "powerpc.h"
@@ -394,6 +395,10 @@ class Target_powerpc : public Sized_targ
   bool
   do_relax(int, const Input_objects*, Symbol_table*, Layout*, const Task*);
 
+  void
+  do_plt_fde_location(const Output_data*, unsigned char*,
+		      uint64_t*, off_t*) const;
+
   // Stash info about branches, for stub generation.
   void
   push_branch(Powerpc_relobj<size, big_endian>* ppc_object,
@@ -526,6 +531,9 @@ class Target_powerpc : public Sized_targ
     return this->glink_;
   }
 
+  bool has_glink() const
+  { return this->glink_ != NULL; }
+
   // Get the GOT section.
   const Output_data_got_powerpc<size, big_endian>*
   got_section() const
@@ -2277,6 +2285,7 @@ Target_powerpc<size, big_endian>::do_rel
       if ((*p)->size_update())
 	{
 	  again = true;
+	  (*p)->add_eh_frame(layout);
 	  os_need_update.insert((*p)->output_section());
 	}
     }
@@ -2332,6 +2341,54 @@ Target_powerpc<size, big_endian>::do_rel
   return again;
 }
 
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::do_plt_fde_location(const Output_data* plt,
+						      unsigned char* oview,
+						      uint64_t* paddress,
+						      off_t* plen) const
+{
+  uint64_t address = plt->address();
+  off_t len = plt->data_size();
+
+  if (plt == this->glink_)
+    {
+      // See Output_data_glink::do_write() for glink contents.
+      if (size == 64)
+	{
+	  // There is one word before __glink_PLTresolve
+	  address += 8;
+	  len -= 8;
+	}
+      else if (parameters->options().output_is_position_independent())
+	{
+	  // There are two FDEs for a position independent glink.
+	  // The first covers the branch table, the second
+	  // __glink_PLTresolve at the end of glink.
+	  off_t resolve_size = this->glink_->pltresolve_size;
+	  if (oview[9] == 0)
+	    len -= resolve_size;
+	  else
+	    {
+	      address += len - resolve_size;
+	      len = resolve_size;
+	    }
+	}
+    }
+  else
+    {
+      // Must be a stub table.
+      const Stub_table<size, big_endian>* stub_table
+	= static_cast<const Stub_table<size, big_endian>*>(plt);
+      uint64_t stub_address = stub_table->stub_address();
+      len -= stub_address - address;
+      address = stub_address;
+    }
+
+  *paddress = address;
+  *plen = len;
+}
+
 // A class to handle the PLT data.
 
 template<int size, bool big_endian>
@@ -2774,6 +2831,60 @@ ha(uint32_t a)
   return hi(a + 0x8000);
 }
 
+template<int size>
+struct Eh_cie
+{
+  static const unsigned char eh_frame_cie[12];
+};
+
+template<int size>
+const unsigned char Eh_cie<size>::eh_frame_cie[] =
+{
+  1,					// CIE version.
+  'z', 'R', 0,				// Augmentation string.
+  4,					// Code alignment.
+  0x80 - size / 8 ,			// Data alignment.
+  65,					// RA reg.
+  1,					// Augmentation size.
+  (elfcpp::DW_EH_PE_pcrel
+   | elfcpp::DW_EH_PE_sdata4),		// FDE encoding.
+  elfcpp::DW_CFA_def_cfa, 1, 0		// def_cfa: r1 offset 0.
+};
+
+// Describe __glink_PLTresolve use of LR, 64-bit version.
+static const unsigned char glink_eh_frame_fde_64[] =
+{
+  0, 0, 0, 0,				// Replaced with offset to .glink.
+  0, 0, 0, 0,				// Replaced with size of .glink.
+  0,					// Augmentation size.
+  elfcpp::DW_CFA_advance_loc + 1,
+  elfcpp::DW_CFA_register, 65, 12,
+  elfcpp::DW_CFA_advance_loc + 4,
+  elfcpp::DW_CFA_restore_extended, 65
+};
+
+// Describe __glink_PLTresolve use of LR, 32-bit version.
+static const unsigned char glink_eh_frame_fde_32[] =
+{
+  0, 0, 0, 0,				// Replaced with offset to .glink.
+  0, 0, 0, 0,				// Replaced with size of .glink.
+  0,					// Augmentation size.
+  elfcpp::DW_CFA_advance_loc + 2,
+  elfcpp::DW_CFA_register, 65, 0,
+  elfcpp::DW_CFA_advance_loc + 4,
+  elfcpp::DW_CFA_restore_extended, 65
+};
+
+static const unsigned char default_fde[] =
+{
+  0, 0, 0, 0,				// Replaced with offset to stubs.
+  0, 0, 0, 0,				// Replaced with size of stubs.
+  0,					// Augmentation size.
+  elfcpp::DW_CFA_nop,			// Pad.
+  elfcpp::DW_CFA_nop,
+  elfcpp::DW_CFA_nop
+};
+
 template<bool big_endian>
 static inline void
 write_insn(unsigned char* p, uint32_t v)
@@ -2797,7 +2908,7 @@ class Stub_table : public Output_relaxed
     : Output_relaxed_input_section(NULL, 0, 0),
       targ_(targ), plt_call_stubs_(), long_branch_stubs_(),
       orig_data_size_(0), plt_size_(0), last_plt_size_(0),
-      branch_size_(0), last_branch_size_(0)
+      branch_size_(0), last_branch_size_(0), eh_frame_added_(false)
   { }
 
   // Delayed Output_relaxed_input_section init.
@@ -2842,7 +2953,8 @@ class Stub_table : public Output_relaxed
   add_long_branch_entry(const Powerpc_relobj<size, big_endian>*, Address);
 
   Address
-  find_long_branch_entry(const Powerpc_relobj<size, big_endian>*, Address);
+  find_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
+			 Address) const;
 
   void
   clear_stubs()
@@ -2871,14 +2983,14 @@ class Stub_table : public Output_relaxed
   }
 
   Address
-  stub_address()
+  stub_address() const
   {
     return align_address(this->address() + this->orig_data_size_,
 			 this->stub_align());
   }
 
   Address
-  stub_offset()
+  stub_offset() const
   {
     return align_address(this->offset() + this->orig_data_size_,
 			 this->stub_align());
@@ -2919,6 +3031,35 @@ class Stub_table : public Output_relaxed
     return false;
   }
 
+  // Add .eh_frame info for this stub section.  Unlike other linker
+  // generated .eh_frame this is added late in the link, because we
+  // only want the .eh_frame info if this particular stub section is
+  // non-empty.
+  void
+  add_eh_frame(Layout* layout)
+  {
+    if (!this->eh_frame_added_)
+      {
+	if (!parameters->options().ld_generated_unwind_info())
+	  return;
+
+	// Since we add stub .eh_frame info late, it must be placed
+	// after all other linker generated .eh_frame info so that
+	// merge mapping need not be updated for input sections.
+	// There is no provision to use a different CIE to that used
+	// by .glink.
+	if (!this->targ_->has_glink())
+	  return;
+
+	layout->add_eh_frame_for_plt(this,
+				     Eh_cie<size>::eh_frame_cie,
+				     sizeof (Eh_cie<size>::eh_frame_cie),
+				     default_fde,
+				     sizeof (default_fde));
+	this->eh_frame_added_ = true;
+      }
+  }
+
   Target_powerpc<size, big_endian>*
   targ() const
   { return targ_; }
@@ -3118,6 +3259,8 @@ class Stub_table : public Output_relaxed
   section_size_type orig_data_size_;
   // size of stubs
   section_size_type plt_size_, last_plt_size_, branch_size_, last_branch_size_;
+  // Whether .eh_frame info has been created for this stub section.
+  bool eh_frame_added_;
 };
 
 // Make a new stub table, and record.
@@ -3261,7 +3404,7 @@ template<int size, bool big_endian>
 typename Stub_table<size, big_endian>::Address
 Stub_table<size, big_endian>::find_long_branch_entry(
     const Powerpc_relobj<size, big_endian>* object,
-    Address to)
+    Address to) const
 {
   Branch_stub_ent ent(object, to);
   typename Branch_stub_entries::const_iterator p
@@ -3281,6 +3424,37 @@ class Output_data_glink : public Output_
     : Output_section_data(16), targ_(targ)
   { }
 
+  void
+  add_eh_frame(Layout* layout)
+  {
+    if (!parameters->options().ld_generated_unwind_info())
+      return;
+
+    if (size == 64)
+      layout->add_eh_frame_for_plt(this,
+				   Eh_cie<64>::eh_frame_cie,
+				   sizeof (Eh_cie<64>::eh_frame_cie),
+				   glink_eh_frame_fde_64,
+				   sizeof (glink_eh_frame_fde_64));
+    else
+      {
+	// 32-bit .glink can use the default since the CIE return
+	// address reg, LR, is valid.
+	layout->add_eh_frame_for_plt(this,
+				     Eh_cie<32>::eh_frame_cie,
+				     sizeof (Eh_cie<32>::eh_frame_cie),
+				     default_fde,
+				     sizeof (default_fde));
+	// Except where LR is used in a PIC __glink_PLTresolve.
+	if (parameters->options().output_is_position_independent())
+	  layout->add_eh_frame_for_plt(this,
+				       Eh_cie<32>::eh_frame_cie,
+				       sizeof (Eh_cie<32>::eh_frame_cie),
+				       glink_eh_frame_fde_32,
+				       sizeof (glink_eh_frame_fde_32));
+      }
+  }
+
  protected:
   // Write to a map file.
   void
@@ -4086,6 +4260,7 @@ Target_powerpc<size, big_endian>::make_g
   if (this->glink_ == NULL)
     {
       this->glink_ = new Output_data_glink<size, big_endian>(this);
+      this->glink_->add_eh_frame(layout);
       layout->add_output_section_data(".text", elfcpp::SHT_PROGBITS,
 				      elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR,
 				      this->glink_, ORDER_TEXT, false);

-- 
Alan Modra
Australia Development Lab, IBM


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