2010-01-22 Doug Kwan <dougkwan@google.com>
* arm.cc (Arm_exidx_fixup): New class.
This commit is contained in:
parent
0b92b5bb46
commit
80d0d023f5
|
@ -1,3 +1,7 @@
|
||||||
|
2010-01-22 Doug Kwan <dougkwan@google.com>
|
||||||
|
|
||||||
|
* arm.cc (Arm_exidx_fixup): New class.
|
||||||
|
|
||||||
2010-01-21 Doug Kwan <dougkwan@google.com>
|
2010-01-21 Doug Kwan <dougkwan@google.com>
|
||||||
|
|
||||||
* arm.cc (Arm_exidx_cantunwind, Arm_exidx_merged_section): New
|
* arm.cc (Arm_exidx_cantunwind, Arm_exidx_merged_section): New
|
||||||
|
|
221
gold/arm.cc
221
gold/arm.cc
|
@ -68,6 +68,8 @@ class Arm_exidx_cantunwind;
|
||||||
|
|
||||||
class Arm_exidx_merged_section;
|
class Arm_exidx_merged_section;
|
||||||
|
|
||||||
|
class Arm_exidx_fixup;
|
||||||
|
|
||||||
template<bool big_endian>
|
template<bool big_endian>
|
||||||
class Arm_output_section;
|
class Arm_output_section;
|
||||||
|
|
||||||
|
@ -1217,6 +1219,80 @@ class Arm_input_section : public Output_relaxed_input_section
|
||||||
Stub_table<big_endian>* stub_table_;
|
Stub_table<big_endian>* stub_table_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Arm_exidx_fixup class. This is used to define a number of methods
|
||||||
|
// and keep states for fixing up EXIDX coverage.
|
||||||
|
|
||||||
|
class Arm_exidx_fixup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Arm_exidx_fixup(Output_section* exidx_output_section)
|
||||||
|
: exidx_output_section_(exidx_output_section), last_unwind_type_(UT_NONE),
|
||||||
|
last_inlined_entry_(0), last_input_section_(NULL),
|
||||||
|
section_offset_map_(NULL)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~Arm_exidx_fixup()
|
||||||
|
{ delete this->section_offset_map_; }
|
||||||
|
|
||||||
|
// Process an EXIDX section for entry merging. Return number of bytes to
|
||||||
|
// be deleted in output. If parts of the input EXIDX section are merged
|
||||||
|
// a heap allocated Arm_exidx_section_offset_map is store in the located
|
||||||
|
// PSECTION_OFFSET_MAP. The caller owns the map and is reponsible for
|
||||||
|
// releasing it.
|
||||||
|
template<bool big_endian>
|
||||||
|
uint32_t
|
||||||
|
process_exidx_section(const Arm_exidx_input_section* exidx_input_section,
|
||||||
|
Arm_exidx_section_offset_map** psection_offset_map);
|
||||||
|
|
||||||
|
// Append an EXIDX_CANTUNWIND entry pointing at the end of the last
|
||||||
|
// input section, if there is not one already.
|
||||||
|
void
|
||||||
|
add_exidx_cantunwind_as_needed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Copying is not allowed.
|
||||||
|
Arm_exidx_fixup(const Arm_exidx_fixup&);
|
||||||
|
Arm_exidx_fixup& operator=(const Arm_exidx_fixup&);
|
||||||
|
|
||||||
|
// Type of EXIDX unwind entry.
|
||||||
|
enum Unwind_type
|
||||||
|
{
|
||||||
|
// No type.
|
||||||
|
UT_NONE,
|
||||||
|
// EXIDX_CANTUNWIND.
|
||||||
|
UT_EXIDX_CANTUNWIND,
|
||||||
|
// Inlined entry.
|
||||||
|
UT_INLINED_ENTRY,
|
||||||
|
// Normal entry.
|
||||||
|
UT_NORMAL_ENTRY,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Process an EXIDX entry. We only care about the second word of the
|
||||||
|
// entry. Return true if the entry can be deleted.
|
||||||
|
bool
|
||||||
|
process_exidx_entry(uint32_t second_word);
|
||||||
|
|
||||||
|
// Update the current section offset map during EXIDX section fix-up.
|
||||||
|
// If there is no map, create one. INPUT_OFFSET is the offset of a
|
||||||
|
// reference point, DELETED_BYTES is the number of deleted by in the
|
||||||
|
// section so far. If DELETE_ENTRY is true, the reference point and
|
||||||
|
// all offsets after the previous reference point are discarded.
|
||||||
|
void
|
||||||
|
update_offset_map(section_offset_type input_offset,
|
||||||
|
section_size_type deleted_bytes, bool delete_entry);
|
||||||
|
|
||||||
|
// EXIDX output section.
|
||||||
|
Output_section* exidx_output_section_;
|
||||||
|
// Unwind type of the last EXIDX entry processed.
|
||||||
|
Unwind_type last_unwind_type_;
|
||||||
|
// Last seen inlined EXIDX entry.
|
||||||
|
uint32_t last_inlined_entry_;
|
||||||
|
// Last processed EXIDX input section.
|
||||||
|
Arm_exidx_input_section* last_input_section_;
|
||||||
|
// Section offset map created in process_exidx_section.
|
||||||
|
Arm_exidx_section_offset_map* section_offset_map_;
|
||||||
|
};
|
||||||
|
|
||||||
// Arm output section class. This is defined mainly to add a number of
|
// Arm output section class. This is defined mainly to add a number of
|
||||||
// stub generation methods.
|
// stub generation methods.
|
||||||
|
|
||||||
|
@ -4595,6 +4671,151 @@ Arm_exidx_merged_section::do_write(Output_file* of)
|
||||||
of->write_output_view(this->offset(), oview_size, oview);
|
of->write_output_view(this->offset(), oview_size, oview);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Arm_exidx_fixup methods.
|
||||||
|
|
||||||
|
// Append an EXIDX_CANTUNWIND in the current output section if the last entry
|
||||||
|
// is not an EXIDX_CANTUNWIND entry already. The new EXIDX_CANTUNWIND entry
|
||||||
|
// points to the end of the last seen EXIDX section.
|
||||||
|
|
||||||
|
void
|
||||||
|
Arm_exidx_fixup::add_exidx_cantunwind_as_needed()
|
||||||
|
{
|
||||||
|
if (this->last_unwind_type_ != UT_EXIDX_CANTUNWIND
|
||||||
|
&& this->last_input_section_ != NULL)
|
||||||
|
{
|
||||||
|
Relobj* relobj = this->last_input_section_->relobj();
|
||||||
|
unsigned int shndx = this->last_input_section_->shndx();
|
||||||
|
Arm_exidx_cantunwind* cantunwind =
|
||||||
|
new Arm_exidx_cantunwind(relobj, shndx);
|
||||||
|
this->exidx_output_section_->add_output_section_data(cantunwind);
|
||||||
|
this->last_unwind_type_ = UT_EXIDX_CANTUNWIND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process an EXIDX section entry in input. Return whether this entry
|
||||||
|
// can be deleted in the output. SECOND_WORD in the second word of the
|
||||||
|
// EXIDX entry.
|
||||||
|
|
||||||
|
bool
|
||||||
|
Arm_exidx_fixup::process_exidx_entry(uint32_t second_word)
|
||||||
|
{
|
||||||
|
bool delete_entry;
|
||||||
|
if (second_word == elfcpp::EXIDX_CANTUNWIND)
|
||||||
|
{
|
||||||
|
// Merge if previous entry is also an EXIDX_CANTUNWIND.
|
||||||
|
delete_entry = this->last_unwind_type_ == UT_EXIDX_CANTUNWIND;
|
||||||
|
this->last_unwind_type_ = UT_EXIDX_CANTUNWIND;
|
||||||
|
}
|
||||||
|
else if ((second_word & 0x80000000) != 0)
|
||||||
|
{
|
||||||
|
// Inlined unwinding data. Merge if equal to previous.
|
||||||
|
delete_entry = (this->last_unwind_type_ == UT_INLINED_ENTRY
|
||||||
|
&& this->last_inlined_entry_ == second_word);
|
||||||
|
this->last_unwind_type_ = UT_INLINED_ENTRY;
|
||||||
|
this->last_inlined_entry_ = second_word;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Normal table entry. In theory we could merge these too,
|
||||||
|
// but duplicate entries are likely to be much less common.
|
||||||
|
delete_entry = false;
|
||||||
|
this->last_unwind_type_ = UT_NORMAL_ENTRY;
|
||||||
|
}
|
||||||
|
return delete_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the current section offset map during EXIDX section fix-up.
|
||||||
|
// If there is no map, create one. INPUT_OFFSET is the offset of a
|
||||||
|
// reference point, DELETED_BYTES is the number of deleted by in the
|
||||||
|
// section so far. If DELETE_ENTRY is true, the reference point and
|
||||||
|
// all offsets after the previous reference point are discarded.
|
||||||
|
|
||||||
|
void
|
||||||
|
Arm_exidx_fixup::update_offset_map(
|
||||||
|
section_offset_type input_offset,
|
||||||
|
section_size_type deleted_bytes,
|
||||||
|
bool delete_entry)
|
||||||
|
{
|
||||||
|
if (this->section_offset_map_ == NULL)
|
||||||
|
this->section_offset_map_ = new Arm_exidx_section_offset_map();
|
||||||
|
section_offset_type output_offset = (delete_entry
|
||||||
|
? -1
|
||||||
|
: input_offset - deleted_bytes);
|
||||||
|
(*this->section_offset_map_)[input_offset] = output_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process EXIDX_INPUT_SECTION for EXIDX entry merging. Return the number of
|
||||||
|
// bytes deleted. If some entries are merged, also store a pointer to a newly
|
||||||
|
// created Arm_exidx_section_offset_map object in *PSECTION_OFFSET_MAP. The
|
||||||
|
// caller owns the map and is responsible for releasing it after use.
|
||||||
|
|
||||||
|
template<bool big_endian>
|
||||||
|
uint32_t
|
||||||
|
Arm_exidx_fixup::process_exidx_section(
|
||||||
|
const Arm_exidx_input_section* exidx_input_section,
|
||||||
|
Arm_exidx_section_offset_map** psection_offset_map)
|
||||||
|
{
|
||||||
|
Relobj* relobj = exidx_input_section->relobj();
|
||||||
|
unsigned shndx = exidx_input_section->shndx();
|
||||||
|
section_size_type section_size;
|
||||||
|
const unsigned char* section_contents =
|
||||||
|
relobj->section_contents(shndx, §ion_size, false);
|
||||||
|
|
||||||
|
if ((section_size % 8) != 0)
|
||||||
|
{
|
||||||
|
// Something is wrong with this section. Better not touch it.
|
||||||
|
gold_error(_("uneven .ARM.exidx section size in %s section %u"),
|
||||||
|
relobj->name().c_str(), shndx);
|
||||||
|
this->last_input_section_ = exidx_input_section;
|
||||||
|
this->last_unwind_type_ = UT_NONE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t deleted_bytes = 0;
|
||||||
|
bool prev_delete_entry = false;
|
||||||
|
gold_assert(this->section_offset_map_ == NULL);
|
||||||
|
|
||||||
|
for (section_size_type i = 0; i < section_size; i += 8)
|
||||||
|
{
|
||||||
|
typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
|
||||||
|
const Valtype* wv =
|
||||||
|
reinterpret_cast<const Valtype*>(section_contents + i + 4);
|
||||||
|
uint32_t second_word = elfcpp::Swap<32, big_endian>::readval(wv);
|
||||||
|
|
||||||
|
bool delete_entry = this->process_exidx_entry(second_word);
|
||||||
|
|
||||||
|
// Entry deletion causes changes in output offsets. We use a std::map
|
||||||
|
// to record these. And entry (x, y) means input offset x
|
||||||
|
// is mapped to output offset y. If y is invalid_offset, then x is
|
||||||
|
// dropped in the output. Because of the way std::map::lower_bound
|
||||||
|
// works, we record the last offset in a region w.r.t to keeping or
|
||||||
|
// dropping. If there is no entry (x0, y0) for an input offset x0,
|
||||||
|
// the output offset y0 of it is determined by the output offset y1 of
|
||||||
|
// the smallest input offset x1 > x0 that there is an (x1, y1) entry
|
||||||
|
// in the map. If y1 is not -1, then y0 = y1 + x0 - x1. Othewise, y1
|
||||||
|
// y0 is also -1.
|
||||||
|
if (delete_entry != prev_delete_entry && i != 0)
|
||||||
|
this->update_offset_map(i - 1, deleted_bytes, prev_delete_entry);
|
||||||
|
|
||||||
|
// Update total deleted bytes for this entry.
|
||||||
|
if (delete_entry)
|
||||||
|
deleted_bytes += 8;
|
||||||
|
|
||||||
|
prev_delete_entry = delete_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If section offset map is not NULL, make an entry for the end of
|
||||||
|
// section.
|
||||||
|
if (this->section_offset_map_ != NULL)
|
||||||
|
update_offset_map(section_size - 1, deleted_bytes, prev_delete_entry);
|
||||||
|
|
||||||
|
*psection_offset_map = this->section_offset_map_;
|
||||||
|
this->section_offset_map_ = NULL;
|
||||||
|
this->last_input_section_ = exidx_input_section;
|
||||||
|
|
||||||
|
return deleted_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
// Arm_output_section methods.
|
// Arm_output_section methods.
|
||||||
|
|
||||||
// Create a stub group for input sections from BEGIN to END. OWNER
|
// Create a stub group for input sections from BEGIN to END. OWNER
|
||||||
|
|
Loading…
Reference in New Issue