[binutils-gdb] [GOLD] PowerPC recreate eh_frame for stubs on each relax pass

Alan Modra amodra@sourceware.org
Tue Aug 1 04:41:00 GMT 2017


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=be897fb774abfb0a44b3b87fe77bccafa336e638

commit be897fb774abfb0a44b3b87fe77bccafa336e638
Author: Alan Modra <amodra@gmail.com>
Date:   Tue Aug 1 14:08:53 2017 +0930

    [GOLD] PowerPC recreate eh_frame for stubs on each relax pass
    
    There is a very small but non-zero probability that a stub group
    contains stubs on one relax pass, but does not on the next.  In that
    case we would get an FDE covering a zero length address range.
    (Actually, it's even worse.  Alignment padding for stubs can mean the
    address for the non-existent stubs is past the end of the original
    section to which stubs are attached, and due to the way
    do_plt_fde_location calculates the length we can get a negative
    length.)  Fixing this properly requires removing the FDE.
    
    Also, I have been implementing the __tls_get_addr_opt support for
    gold, and that stub needs something other than the default FDE.  The
    necessary FDE will depend on the offset to the __tls_get_addr_opt
    stub, which of course can change during relaxation.  That means at the
    very least, rewriting the FDE on each pass, possibly changing the FDE
    size.  I think that is better done by completely recreating PLT
    eh_frame FDEs.
    
    	* ehframe.cc (Fde::operator==): New.
    	(Cie::remove_fde, Eh_frame::remove_ehframe_for_plt): New.
    	* ehframe.h (Fde::operator==): Declare.
    	(Cie::remove_fde, Eh_frame::remove_ehframe_for_plt): Likewise.
    	* layout.cc (Layout::remove_eh_frame_for_plt): New.
    	* layout.h (Layout::remove_eh_frame_for_plt): Declare.
    	* powerpc.cc (Target_powerpc::do_relax): Remove old eh_frame FDEs.
    	(Stub_table::add_eh_frame): Delete eh_frame_added_ condition.
    	Don't add eh_frame for empty stub section.
    	(Stub_table::remove_eh_frame): New.

Diff:
---
 gold/ChangeLog  | 13 +++++++++++++
 gold/ehframe.cc | 46 ++++++++++++++++++++++++++++++++++++++++++++
 gold/ehframe.h  | 13 +++++++++++++
 gold/layout.cc  | 17 +++++++++++++++++
 gold/layout.h   |  7 +++++++
 gold/powerpc.cc | 59 +++++++++++++++++++++++++++++++++++++++------------------
 6 files changed, 137 insertions(+), 18 deletions(-)

diff --git a/gold/ChangeLog b/gold/ChangeLog
index 869a01d..c886cc1 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,3 +1,16 @@
+2017-08-01  Alan Modra  <amodra@gmail.com>
+
+	* ehframe.cc (Fde::operator==): New.
+	(Cie::remove_fde, Eh_frame::remove_ehframe_for_plt): New.
+	* ehframe.h (Fde::operator==): Declare.
+	(Cie::remove_fde, Eh_frame::remove_ehframe_for_plt): Likewise.
+	* layout.cc (Layout::remove_eh_frame_for_plt): New.
+	* layout.h (Layout::remove_eh_frame_for_plt): Declare.
+	* powerpc.cc (Target_powerpc::do_relax): Remove old eh_frame FDEs.
+	(Stub_table::add_eh_frame): Delete eh_frame_added_ condition.
+	Don't add eh_frame for empty stub section.
+	(Stub_table::remove_eh_frame): New.
+
 2017-07-31  Alan Modra  <amodra@gmail.com>
 
 	* options.h (no_tls_optimize): New powerpc option.
diff --git a/gold/ehframe.cc b/gold/ehframe.cc
index fd72a4f..61b5193 100644
--- a/gold/ehframe.cc
+++ b/gold/ehframe.cc
@@ -325,6 +325,21 @@ Eh_frame_hdr::get_fde_addresses(Output_file* of,
 
 // Class Fde.
 
+bool
+Fde::operator==(const Fde& that) const
+{
+  if (this->object_ != that.object_
+      || this->contents_ != that.contents_)
+    return false;
+  if (this->object_ == NULL)
+    return (this->u_.from_linker.plt == that.u_.from_linker.plt
+	    && this->u_.from_linker.post_map == that.u_.from_linker.post_map);
+  else
+    return (this->u_.from_object.shndx == that.u_.from_object.shndx
+	    && (this->u_.from_object.input_offset
+		== that.u_.from_object.input_offset));
+}
+
 // Write the FDE to OVIEW starting at OFFSET.  CIE_OFFSET is the
 // offset of the CIE in OVIEW.  OUTPUT_OFFSET is the offset of the
 // Eh_frame section within the output section.  FDE_ENCODING is the
@@ -443,6 +458,15 @@ Cie::set_output_offset(section_offset_type output_offset,
   return output_offset + length;
 }
 
+// Remove FDE.  Only the last FDE using this CIE may be removed.
+
+void
+Cie::remove_fde(const Fde* fde)
+{
+  gold_assert(*fde == *this->fdes_.back());
+  this->fdes_.pop_back();
+}
+
 // Write the CIE to OVIEW starting at OFFSET.  OUTPUT_OFFSET is the
 // offset of the Eh_frame section within the output section.  Round up
 // the bytes to ADDRALIGN.  ADDRESS is the virtual address of OVIEW.
@@ -1143,6 +1167,28 @@ Eh_frame::add_ehframe_for_plt(Output_data* plt, const unsigned char* cie_data,
     this->final_data_size_ += align_address(fde_length + 8, this->addralign());
 }
 
+// Remove unwind information for a PLT.  Only the last FDE added may be removed.
+
+void
+Eh_frame::remove_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);
+  gold_assert (find_cie != this->cie_offsets_.end());
+  Cie* pcie = *find_cie;
+
+  Fde* fde = new Fde(plt, fde_data, fde_length, this->mappings_are_done_);
+  pcie->remove_fde(fde);
+
+  if (this->mappings_are_done_)
+    this->final_data_size_ -= align_address(fde_length + 8, this->addralign());
+}
+
 // Return the number of FDEs.
 
 unsigned int
diff --git a/gold/ehframe.h b/gold/ehframe.h
index 347ce46..f501634 100644
--- a/gold/ehframe.h
+++ b/gold/ehframe.h
@@ -217,6 +217,8 @@ class Fde
 	section_offset_type cie_offset, unsigned char fde_encoding,
 	Eh_frame_hdr* eh_frame_hdr);
 
+  bool operator==(const Fde&) const;
+
  private:
   // The object in which this FDE was seen.  This will be NULL for a
   // linker generated FDE.
@@ -298,6 +300,10 @@ class Cie
   add_fde(Fde* fde)
   { this->fdes_.push_back(fde); }
 
+  // Remove an FDE associated with this CIE.  Only the last FDE may be removed.
+  void
+  remove_fde(const Fde*);
+
   // Return the number of FDEs.
   unsigned int
   fde_count() const
@@ -405,6 +411,13 @@ class Eh_frame : public Output_section_data
 		      size_t cie_length, const unsigned char* fde_data,
 		      size_t fde_length);
 
+  // Remove unwind information for a PLT.  Only the last FDE added may
+  // be removed.
+  void
+  remove_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;
diff --git a/gold/layout.cc b/gold/layout.cc
index 963ae52..5f25fae 100644
--- a/gold/layout.cc
+++ b/gold/layout.cc
@@ -1581,6 +1581,23 @@ Layout::add_eh_frame_for_plt(Output_data* plt, const unsigned char* cie_data,
     }
 }
 
+// Remove .eh_frame information for a PLT.  FDEs using the CIE must
+// be removed in reverse order to the order they were added.
+
+void
+Layout::remove_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;
+    }
+  this->eh_frame_data_->remove_ehframe_for_plt(plt, cie_data, cie_length,
+					       fde_data, fde_length);
+}
+
 // Scan a .debug_info or .debug_types section, and add summary
 // information to the .gdb_index section.
 
diff --git a/gold/layout.h b/gold/layout.h
index 5f58c2c..15ee924 100644
--- a/gold/layout.h
+++ b/gold/layout.h
@@ -653,6 +653,13 @@ class Layout
 		       size_t cie_length, const unsigned char* fde_data,
 		       size_t fde_length);
 
+  // Remove .eh_frame information for a PLT.  FDEs using the CIE must
+  // be removed in reverse order to the order they were added.
+  void
+  remove_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);
+
   // Scan a .debug_info or .debug_types section, and add summary
   // information to the .gdb_index section.
   template<int size, bool big_endian>
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index 9a200ab..6f610cb 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -3333,6 +3333,16 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
   if (size == 64 && again)
     this->brlt_section_->set_current_size(num_huge_branches);
 
+  for (typename Stub_tables::reverse_iterator p = this->stub_tables_.rbegin();
+       p != this->stub_tables_.rend();
+       ++p)
+    (*p)->remove_eh_frame(layout);
+
+  for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+       p != this->stub_tables_.end();
+       ++p)
+    (*p)->add_eh_frame(layout);
+
   typedef Unordered_set<Output_section*> Output_sections;
   Output_sections os_need_update;
   for (typename Stub_tables::iterator p = this->stub_tables_.begin();
@@ -3342,7 +3352,6 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
       if ((*p)->size_update())
 	{
 	  again = true;
-	  (*p)->add_eh_frame(layout);
 	  os_need_update.insert((*p)->output_section());
 	}
     }
@@ -4244,25 +4253,39 @@ class Stub_table : public Output_relaxed_input_section
   void
   add_eh_frame(Layout* layout)
   {
-    if (!this->eh_frame_added_)
-      {
-	if (!parameters->options().ld_generated_unwind_info())
-	  return;
+    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;
+    // 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;
+    if (this->plt_size_ + this->branch_size_ + this->need_save_res_ == 0)
+      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;
+  }
+
+  void
+  remove_eh_frame(Layout* layout)
+  {
+    if (this->eh_frame_added_)
+      {
+	layout->remove_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_ = false;
       }
   }



More information about the Binutils-cvs mailing list