2010-01-26 Doug Kwan <dougkwan@google.com>

* arm.cc (set): Include.
	(class Arm_exidx_fixup): Change type of last_input_section_ to const
	pointer type.
 	(Arm_output_section::Text_section_list): New type.
	(Arm_output_section::append_text_sections_to_list): New method.
	(Arm_output_section::fix_exidx_coverage): Ditto.
	(Arm_relobj::Arm_relobj): Initialize exidx_section_map_.
   	(Arm_relobj::convert_input_section_to_relaxed_section): Use
	Relobj::set_section_offset() instead of
	Sized_relobj::invalidate_section_offset().
   	(Arm_relobj::section_needs_reloc_stub_scanning): Add an extra
	parameter for section headers. Ignore relocation sections for
	unallocated sections and EXIDX sections.
	(Target_arm::fix_exidx_coverage): New method.
	(Target_arm::output_section_address_less_than): New type.
	(Arm_exidx_fixup::add_exidx_cantunwind_as_needed): Use index of the
	linked text section instead of the EXIDX section.
	(Arm_output_section::create_stub_group): Add an assertion to check
	that this is not an EXIDX output section.
	(Arm_output_section::append_text_sections_to_list): New method.
	(Arm_output_section::fix_exidx_coverage): Ditto.
	(Arm_relobj::scan_sections_for_stubs): Adjust call to
   	Arm_relobj::section_needs_reloc_stub_scanning.
	(Target_arm::do_relax): Fix EXIDX output section coverage in the
	first pass.
	(Target_arm::fix_exidx_coverage): New method.
	* object.h (Relobj::set_output_section): New method.
	(Sized_relobj::invalidate_section_offset): Remove method.
	(Sized_relobj::do_invalidate_section_offset): Remove method.
	(Sized_relobj::do_set_section_offset): Handle offset value -1.
This commit is contained in:
Doug Kwan 2010-01-26 21:29:10 +00:00
parent 708ead4e05
commit 2b328d4ec5
3 changed files with 325 additions and 24 deletions

View File

@ -1,3 +1,36 @@
2010-01-26 Doug Kwan <dougkwan@google.com>
* arm.cc (set): Include.
(class Arm_exidx_fixup): Change type of last_input_section_ to const
pointer type.
(Arm_output_section::Text_section_list): New type.
(Arm_output_section::append_text_sections_to_list): New method.
(Arm_output_section::fix_exidx_coverage): Ditto.
(Arm_relobj::Arm_relobj): Initialize exidx_section_map_.
(Arm_relobj::convert_input_section_to_relaxed_section): Use
Relobj::set_section_offset() instead of
Sized_relobj::invalidate_section_offset().
(Arm_relobj::section_needs_reloc_stub_scanning): Add an extra
parameter for section headers. Ignore relocation sections for
unallocated sections and EXIDX sections.
(Target_arm::fix_exidx_coverage): New method.
(Target_arm::output_section_address_less_than): New type.
(Arm_exidx_fixup::add_exidx_cantunwind_as_needed): Use index of the
linked text section instead of the EXIDX section.
(Arm_output_section::create_stub_group): Add an assertion to check
that this is not an EXIDX output section.
(Arm_output_section::append_text_sections_to_list): New method.
(Arm_output_section::fix_exidx_coverage): Ditto.
(Arm_relobj::scan_sections_for_stubs): Adjust call to
Arm_relobj::section_needs_reloc_stub_scanning.
(Target_arm::do_relax): Fix EXIDX output section coverage in the
first pass.
(Target_arm::fix_exidx_coverage): New method.
* object.h (Relobj::set_output_section): New method.
(Sized_relobj::invalidate_section_offset): Remove method.
(Sized_relobj::do_invalidate_section_offset): Remove method.
(Sized_relobj::do_set_section_offset): Handle offset value -1.
2010-01-25 Doug Kwan <dougkwan@google.com>
* arm.cc (Arm_exidx_merged_section::do_output_offset):

View File

@ -32,6 +32,7 @@
#include <algorithm>
#include <map>
#include <utility>
#include <set>
#include "elfcpp.h"
#include "parameters.h"
@ -1288,7 +1289,7 @@ class Arm_exidx_fixup
// Last seen inlined EXIDX entry.
uint32_t last_inlined_entry_;
// Last processed EXIDX input section.
Arm_exidx_input_section* last_input_section_;
const Arm_exidx_input_section* last_input_section_;
// Section offset map created in process_exidx_section.
Arm_exidx_section_offset_map* section_offset_map_;
};
@ -1300,6 +1301,8 @@ template<bool big_endian>
class Arm_output_section : public Output_section
{
public:
typedef std::vector<std::pair<Relobj*, unsigned int> > Text_section_list;
Arm_output_section(const char* name, elfcpp::Elf_Word type,
elfcpp::Elf_Xword flags)
: Output_section(name, type, flags)
@ -1318,6 +1321,17 @@ class Arm_output_section : public Output_section
as_arm_output_section(Output_section* os)
{ return static_cast<Arm_output_section<big_endian>*>(os); }
// Append all input text sections in this into LIST.
void
append_text_sections_to_list(Text_section_list* list);
// Fix EXIDX coverage of this EXIDX output section. SORTED_TEXT_SECTION
// is a list of text input sections sorted in ascending order of their
// output addresses.
void
fix_exidx_coverage(const Text_section_list& sorted_text_section,
Symbol_table* symtab);
private:
// For convenience.
typedef Output_section::Input_section Input_section;
@ -1401,7 +1415,7 @@ class Arm_relobj : public Sized_relobj<32, big_endian>
: Sized_relobj<32, big_endian>(name, input_file, offset, ehdr),
stub_tables_(), local_symbol_is_thumb_function_(),
attributes_section_data_(NULL), mapping_symbols_info_(),
section_has_cortex_a8_workaround_(NULL)
section_has_cortex_a8_workaround_(NULL), exidx_section_map_()
{ }
~Arm_relobj()
@ -1443,7 +1457,7 @@ class Arm_relobj : public Sized_relobj<32, big_endian>
{
// The stubs have relocations and we need to process them after writing
// out the stubs. So relocation now must follow section write.
this->invalidate_section_offset(shndx);
this->set_section_offset(shndx, -1ULL);
this->set_relocs_must_follow_section_writes();
}
@ -1563,7 +1577,7 @@ class Arm_relobj : public Sized_relobj<32, big_endian>
bool
section_needs_reloc_stub_scanning(const elfcpp::Shdr<32, big_endian>&,
const Relobj::Output_sections&,
const Symbol_table *);
const Symbol_table *, const unsigned char*);
// Whether a section needs to be scanned for the Cortex-A8 erratum.
bool
@ -2365,6 +2379,18 @@ class Target_arm : public Sized_target<32, big_endian>
elfcpp::Elf_types<32>::Elf_Addr view_address,
section_size_type);
// Fix .ARM.exidx section coverage.
void
fix_exidx_coverage(Layout*, Arm_output_section<big_endian>*, Symbol_table*);
// Functors for STL set.
struct output_section_address_less_than
{
bool
operator()(const Output_section* s1, const Output_section* s2) const
{ return s1->address() < s2->address(); }
};
// Information about this specific target which we pass to the
// general Target structure.
static const Target::Target_info arm_info;
@ -4684,9 +4710,9 @@ Arm_exidx_fixup::add_exidx_cantunwind_as_needed()
&& this->last_input_section_ != NULL)
{
Relobj* relobj = this->last_input_section_->relobj();
unsigned int shndx = this->last_input_section_->shndx();
unsigned int text_shndx = this->last_input_section_->link();
Arm_exidx_cantunwind* cantunwind =
new Arm_exidx_cantunwind(relobj, shndx);
new Arm_exidx_cantunwind(relobj, text_shndx);
this->exidx_output_section_->add_output_section_data(cantunwind);
this->last_unwind_type_ = UT_EXIDX_CANTUNWIND;
}
@ -4830,6 +4856,12 @@ Arm_output_section<big_endian>::create_stub_group(
Target_arm<big_endian>* target,
std::vector<Output_relaxed_input_section*>* new_relaxed_sections)
{
// We use a different kind of relaxed section in an EXIDX section.
// The static casting from Output_relaxed_input_section to
// Arm_input_section is invalid in an EXIDX section. We are okay
// because we should not be calling this for an EXIDX section.
gold_assert(this->type() != elfcpp::SHT_ARM_EXIDX);
// Currently we convert ordinary input sections into relaxed sections only
// at this point but we may want to support creating relaxed input section
// very early. So we check here to see if owner is already a relaxed
@ -5025,6 +5057,172 @@ Arm_output_section<big_endian>::group_sections(
}
}
// Append non empty text sections in this to LIST in ascending
// order of their position in this.
template<bool big_endian>
void
Arm_output_section<big_endian>::append_text_sections_to_list(
Text_section_list* list)
{
// We only care about text sections.
if ((this->flags() & elfcpp::SHF_EXECINSTR) == 0)
return;
gold_assert((this->flags() & elfcpp::SHF_ALLOC) != 0);
for (Input_section_list::const_iterator p = this->input_sections().begin();
p != this->input_sections().end();
++p)
{
// We only care about plain or relaxed input sections. We also
// ignore any merged sections.
if ((p->is_input_section() || p->is_relaxed_input_section())
&& p->data_size() != 0)
list->push_back(Text_section_list::value_type(p->relobj(),
p->shndx()));
}
}
template<bool big_endian>
void
Arm_output_section<big_endian>::fix_exidx_coverage(
const Text_section_list& sorted_text_sections,
Symbol_table* symtab)
{
// We should only do this for the EXIDX output section.
gold_assert(this->type() == elfcpp::SHT_ARM_EXIDX);
// We don't want the relaxation loop to undo these changes, so we discard
// the current saved states and take another one after the fix-up.
this->discard_states();
// Remove all input sections.
uint64_t address = this->address();
typedef std::list<Simple_input_section> Simple_input_section_list;
Simple_input_section_list input_sections;
this->reset_address_and_file_offset();
this->get_input_sections(address, std::string(""), &input_sections);
if (!this->input_sections().empty())
gold_error(_("Found non-EXIDX input sections in EXIDX output section"));
// Go through all the known input sections and record them.
typedef Unordered_set<Section_id, Section_id_hash> Section_id_set;
Section_id_set known_input_sections;
for (Simple_input_section_list::const_iterator p = input_sections.begin();
p != input_sections.end();
++p)
{
// This should never happen. At this point, we should only see
// plain EXIDX input sections.
gold_assert(!p->is_relaxed_input_section());
known_input_sections.insert(Section_id(p->relobj(), p->shndx()));
}
Arm_exidx_fixup exidx_fixup(this);
// Go over the sorted text sections.
Section_id_set processed_input_sections;
for (Text_section_list::const_iterator p = sorted_text_sections.begin();
p != sorted_text_sections.end();
++p)
{
Relobj* relobj = p->first;
unsigned int shndx = p->second;
Arm_relobj<big_endian>* arm_relobj =
Arm_relobj<big_endian>::as_arm_relobj(relobj);
const Arm_exidx_input_section* exidx_input_section =
arm_relobj->exidx_input_section_by_link(shndx);
// If this text section has no EXIDX section, force an EXIDX_CANTUNWIND
// entry pointing to the end of the last seen EXIDX section.
if (exidx_input_section == NULL)
{
exidx_fixup.add_exidx_cantunwind_as_needed();
continue;
}
Relobj* exidx_relobj = exidx_input_section->relobj();
unsigned int exidx_shndx = exidx_input_section->shndx();
Section_id sid(exidx_relobj, exidx_shndx);
if (known_input_sections.find(sid) == known_input_sections.end())
{
// This is odd. We have not seen this EXIDX input section before.
// We cannot do fix-up.
gold_error(_("EXIDX section %u of %s is not in EXIDX output section"),
exidx_shndx, exidx_relobj->name().c_str());
exidx_fixup.add_exidx_cantunwind_as_needed();
continue;
}
// Fix up coverage and append input section to output data list.
Arm_exidx_section_offset_map* section_offset_map = NULL;
uint32_t deleted_bytes =
exidx_fixup.process_exidx_section<big_endian>(exidx_input_section,
&section_offset_map);
if (deleted_bytes == exidx_input_section->size())
{
// The whole EXIDX section got merged. Remove it from output.
gold_assert(section_offset_map == NULL);
exidx_relobj->set_output_section(exidx_shndx, NULL);
}
else if (deleted_bytes > 0)
{
// Some entries are merged. We need to convert this EXIDX input
// section into a relaxed section.
gold_assert(section_offset_map != NULL);
Arm_exidx_merged_section* merged_section =
new Arm_exidx_merged_section(*exidx_input_section,
*section_offset_map, deleted_bytes);
this->add_relaxed_input_section(merged_section);
arm_relobj->convert_input_section_to_relaxed_section(exidx_shndx);
}
else
{
// Just add back the EXIDX input section.
gold_assert(section_offset_map == NULL);
Output_section::Simple_input_section sis(exidx_relobj, exidx_shndx);
this->add_simple_input_section(sis, exidx_input_section->size(),
exidx_input_section->addralign());
}
processed_input_sections.insert(Section_id(exidx_relobj, exidx_shndx));
}
// Insert an EXIDX_CANTUNWIND entry at the end of output if necessary.
exidx_fixup.add_exidx_cantunwind_as_needed();
// Remove any known EXIDX input sections that are not processed.
for (Simple_input_section_list::const_iterator p = input_sections.begin();
p != input_sections.end();
++p)
{
if (processed_input_sections.find(Section_id(p->relobj(), p->shndx()))
== processed_input_sections.end())
{
// We only discard a known EXIDX section because its linked
// text section has been folded by ICF.
Arm_relobj<big_endian>* arm_relobj =
Arm_relobj<big_endian>::as_arm_relobj(p->relobj());
const Arm_exidx_input_section* exidx_input_section =
arm_relobj->exidx_input_section_by_shndx(p->shndx());
gold_assert(exidx_input_section != NULL);
unsigned int text_shndx = exidx_input_section->link();
gold_assert(symtab->is_section_folded(p->relobj(), text_shndx));
// Remove this from link.
p->relobj()->set_output_section(p->shndx(), NULL);
}
}
// Make changes permanent.
this->save_states();
this->set_section_offsets_need_adjustment();
}
// Arm_relobj methods.
// Determine if we want to scan the SHNDX-th section for relocation stubs.
@ -5035,7 +5233,8 @@ bool
Arm_relobj<big_endian>::section_needs_reloc_stub_scanning(
const elfcpp::Shdr<32, big_endian>& shdr,
const Relobj::Output_sections& out_sections,
const Symbol_table *symtab)
const Symbol_table *symtab,
const unsigned char* pshdrs)
{
unsigned int sh_type = shdr.get_sh_type();
if (sh_type != elfcpp::SHT_REL && sh_type != elfcpp::SHT_RELA)
@ -5058,6 +5257,14 @@ Arm_relobj<big_endian>::section_needs_reloc_stub_scanning(
if (out_sections[index] == NULL || symtab->is_section_folded(this, index))
return false;
// Check the section to which relocations are applied. Ignore relocations
// to unallocated sections or EXIDX sections.
const unsigned int shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
const elfcpp::Shdr<32, big_endian> data_shdr(pshdrs + index * shdr_size);
if ((data_shdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0
|| data_shdr.get_sh_type() == elfcpp::SHT_ARM_EXIDX)
return false;
// Ignore reloc section with unexpected symbol table. The
// error will be reported in the final link.
if (this->adjust_shndx(shdr.get_sh_link()) != this->symtab_shndx())
@ -5211,7 +5418,8 @@ Arm_relobj<big_endian>::scan_sections_for_stubs(
for (unsigned int i = 1; i < shnum; ++i, p += shdr_size)
{
const elfcpp::Shdr<32, big_endian> shdr(p);
if (this->section_needs_reloc_stub_scanning(shdr, out_sections, symtab))
if (this->section_needs_reloc_stub_scanning(shdr, out_sections, symtab,
pshdrs))
{
unsigned int index = this->adjust_shndx(shdr.get_sh_info());
Arm_address output_offset = this->get_output_section_offset(index);
@ -8647,6 +8855,7 @@ Target_arm<big_endian>::do_relax(
// If this is the first pass, we need to group input sections into
// stub groups.
bool done_exidx_fixup = false;
if (pass == 1)
{
// Determine the stub group size. The group size is the absolute
@ -8679,6 +8888,16 @@ Target_arm<big_endian>::do_relax(
}
group_sections(layout, stub_group_size, stubs_always_after_branch);
// Also fix .ARM.exidx section coverage.
Output_section* os = layout->find_output_section(".ARM.exidx");
if (os != NULL && os->type() == elfcpp::SHT_ARM_EXIDX)
{
Arm_output_section<big_endian>* exidx_output_section =
Arm_output_section<big_endian>::as_arm_output_section(os);
this->fix_exidx_coverage(layout, exidx_output_section, symtab);
done_exidx_fixup = true;
}
}
// The Cortex-A8 stubs are sensitive to layout of code sections. At the
@ -8750,14 +8969,17 @@ Target_arm<big_endian>::do_relax(
(*p)->set_section_offsets_need_adjustment();
}
// Stop relaxation if no EXIDX fix-up and no stub table change.
bool continue_relaxation = done_exidx_fixup || any_stub_table_changed;
// Finalize the stubs in the last relaxation pass.
if (!any_stub_table_changed)
if (!continue_relaxation)
for (Stub_table_iterator sp = this->stub_tables_.begin();
(sp != this->stub_tables_.end()) && !any_stub_table_changed;
++sp)
(*sp)->finalize_stubs();
return any_stub_table_changed;
return continue_relaxation;
}
// Relocate a stub.
@ -9089,6 +9311,52 @@ class Target_selector_arm : public Target_selector
{ return new Target_arm<big_endian>(); }
};
// Fix .ARM.exidx section coverage.
template<bool big_endian>
void
Target_arm<big_endian>::fix_exidx_coverage(
Layout* layout,
Arm_output_section<big_endian>* exidx_section,
Symbol_table* symtab)
{
// We need to look at all the input sections in output in ascending
// order of of output address. We do that by building a sorted list
// of output sections by addresses. Then we looks at the output sections
// in order. The input sections in an output section are already sorted
// by addresses within the output section.
typedef std::set<Output_section*, output_section_address_less_than>
Sorted_output_section_list;
Sorted_output_section_list sorted_output_sections;
Layout::Section_list section_list;
layout->get_allocated_sections(&section_list);
for (Layout::Section_list::const_iterator p = section_list.begin();
p != section_list.end();
++p)
{
// We only care about output sections that contain executable code.
if (((*p)->flags() & elfcpp::SHF_EXECINSTR) != 0)
sorted_output_sections.insert(*p);
}
// Go over the output sections in ascending order of output addresses.
typedef typename Arm_output_section<big_endian>::Text_section_list
Text_section_list;
Text_section_list sorted_text_sections;
for(typename Sorted_output_section_list::iterator p =
sorted_output_sections.begin();
p != sorted_output_sections.end();
++p)
{
Arm_output_section<big_endian>* arm_output_section =
Arm_output_section<big_endian>::as_arm_output_section(*p);
arm_output_section->append_text_sections_to_list(&sorted_text_sections);
}
exidx_section->fix_exidx_coverage(sorted_text_sections, symtab);
}
Target_selector_arm<false> target_selector_arm;
Target_selector_arm<true> target_selector_armbe;

View File

@ -747,6 +747,16 @@ class Relobj : public Object
return this->output_sections_[shndx];
}
// The the output section of the input section with index SHNDX.
// This is only used currently to remove a section from the link in
// relaxation.
void
set_output_section(unsigned int shndx, Output_section* os)
{
gold_assert(shndx < this->output_sections_.size());
this->output_sections_[shndx] = os;
}
// Given a section index, return the offset in the Output_section.
// The return value will be -1U if the section is specially mapped,
// such as a merge section.
@ -1484,11 +1494,6 @@ class Sized_relobj : public Relobj
Address
map_to_kept_section(unsigned int shndx, bool* found) const;
// Make section offset invalid. This is needed for relaxation.
void
invalidate_section_offset(unsigned int shndx)
{ this->do_invalidate_section_offset(shndx); }
protected:
// Set up.
virtual void
@ -1626,15 +1631,10 @@ class Sized_relobj : public Relobj
do_set_section_offset(unsigned int shndx, uint64_t off)
{
gold_assert(shndx < this->section_offsets_.size());
this->section_offsets_[shndx] = convert_types<Address, uint64_t>(off);
}
// Set the offset of a section to invalid_address.
virtual void
do_invalidate_section_offset(unsigned int shndx)
{
gold_assert(shndx < this->section_offsets_.size());
this->section_offsets_[shndx] = invalid_address;
this->section_offsets_[shndx] =
(off == static_cast<uint64_t>(-1)
? invalid_address
: convert_types<Address, uint64_t>(off));
}
// Adjust a section index if necessary.