aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gold/ChangeLog33
-rw-r--r--gold/arm.cc288
-rw-r--r--gold/object.h28
3 files changed, 325 insertions, 24 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog
index a29b25b..320eb3e 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -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):
diff --git a/gold/arm.cc b/gold/arm.cc
index 36ff359..92d8a7a 100644
--- a/gold/arm.cc
+++ b/gold/arm.cc
@@ -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;
diff --git a/gold/object.h b/gold/object.h
index d006856..3f9aaae 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -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.