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 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.

OK?

	* 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 --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 142dee2..e9cd976 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -3340,6 +3340,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();
@@ -3349,7 +3359,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());
 	}
     }
@@ -4251,25 +4260,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;
       }
   }
 

-- 
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]