[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.
This commit is contained in:
Alan Modra 2017-08-01 14:08:53 +09:30
parent 51b69c74c6
commit be897fb774
6 changed files with 138 additions and 19 deletions

View File

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

View File

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

View File

@ -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;

View File

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

View File

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

View File

@ -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;
// 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;
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_)
{
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;
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;
}
}