diff options
-rw-r--r-- | elfcpp/ChangeLog | 4 | ||||
-rw-r--r-- | elfcpp/arm.h | 7 | ||||
-rw-r--r-- | gold/ChangeLog | 6 | ||||
-rw-r--r-- | gold/arm.cc | 266 |
4 files changed, 283 insertions, 0 deletions
diff --git a/elfcpp/ChangeLog b/elfcpp/ChangeLog index ab3d2df..a6c75cd 100644 --- a/elfcpp/ChangeLog +++ b/elfcpp/ChangeLog @@ -1,3 +1,7 @@ +2010-01-21 Doug Kwan <dougkwan@google.com> + + * arm.h (EXIDX_CANTUNWIND): New enum. + 2010-01-19 Ian Lance Taylor <iant@google.com> * elfcpp.h (PN_XNUM): Define. diff --git a/elfcpp/arm.h b/elfcpp/arm.h index 08a17ad..397d0d0 100644 --- a/elfcpp/arm.h +++ b/elfcpp/arm.h @@ -321,6 +321,13 @@ enum AEABI_enum_forced_wide = 3 }; +// For Exception Index Table. (Exception handling ABI for the ARM +// architectue, Section 5) +enum +{ + EXIDX_CANTUNWIND = 1, +}; + } // End namespace elfcpp. #endif // !defined(ELFCPP_ARM_H) diff --git a/gold/ChangeLog b/gold/ChangeLog index 3d2b181..6cb3d92 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,5 +1,11 @@ 2010-01-21 Doug Kwan <dougkwan@google.com> + * arm.cc (Arm_exidx_cantunwind, Arm_exidx_merged_section): New + classes. + (Arm_exidx_section_offset_map): New type. + +2010-01-21 Doug Kwan <dougkwan@google.com> + * arm.cc (Arm_exidx_input_section): New class. (Arm_relobj::exidx_input_section_by_link, Arm_relobj::exidx_input_section_by_shndx, diff --git a/gold/arm.cc b/gold/arm.cc index b7d369c..1e2b8e6 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -64,6 +64,10 @@ class Stub_table; template<bool big_endian> class Arm_input_section; +class Arm_exidx_cantunwind; + +class Arm_exidx_merged_section; + template<bool big_endian> class Arm_output_section; @@ -1027,6 +1031,97 @@ class Stub_table : public Output_data uint64_t prev_addralign_; }; +// Arm_exidx_cantunwind class. This represents an EXIDX_CANTUNWIND entry +// we add to the end of an EXIDX input section that goes into the output. + +class Arm_exidx_cantunwind : public Output_section_data +{ + public: + Arm_exidx_cantunwind(Relobj* relobj, unsigned int shndx) + : Output_section_data(8, 4, true), relobj_(relobj), shndx_(shndx) + { } + + // Return the object containing the section pointed by this. + Relobj* + relobj() const + { return this->relobj_; } + + // Return the section index of the section pointed by this. + unsigned int + shndx() const + { return this->shndx_; } + + protected: + void + do_write(Output_file* of) + { + if (parameters->target().is_big_endian()) + this->do_fixed_endian_write<true>(of); + else + this->do_fixed_endian_write<false>(of); + } + + private: + // Implement do_write for a given endianity. + template<bool big_endian> + void inline + do_fixed_endian_write(Output_file*); + + // The object containing the section pointed by this. + Relobj* relobj_; + // The section index of the section pointed by this. + unsigned int shndx_; +}; + +// During EXIDX coverage fix-up, we compact an EXIDX section. The +// Offset map is used to map input section offset within the EXIDX section +// to the output offset from the start of this EXIDX section. + +typedef std::map<section_offset_type, section_offset_type> + Arm_exidx_section_offset_map; + +// Arm_exidx_merged_section class. This represents an EXIDX input section +// with some of its entries merged. + +class Arm_exidx_merged_section : public Output_relaxed_input_section +{ + public: + // Constructor for Arm_exidx_merged_section. + // EXIDX_INPUT_SECTION points to the unmodified EXIDX input section. + // SECTION_OFFSET_MAP points to a section offset map describing how + // parts of the input section are mapped to output. DELETED_BYTES is + // the number of bytes deleted from the EXIDX input section. + Arm_exidx_merged_section( + const Arm_exidx_input_section& exidx_input_section, + const Arm_exidx_section_offset_map& section_offset_map, + uint32_t deleted_bytes); + + // Return the original EXIDX input section. + const Arm_exidx_input_section& + exidx_input_section() const + { return this->exidx_input_section_; } + + // Return the section offset map. + const Arm_exidx_section_offset_map& + section_offset_map() const + { return this->section_offset_map_; } + + protected: + // Write merged section into file OF. + void + do_write(Output_file* of); + + bool + do_output_offset(const Relobj*, unsigned int, section_offset_type, + section_offset_type*) const; + + private: + // Original EXIDX input section. + const Arm_exidx_input_section& exidx_input_section_; + // Section offset map. + const Arm_exidx_section_offset_map& section_offset_map_; +}; + // A class to wrap an ordinary input section containing executable code. template<bool big_endian> @@ -4329,6 +4424,177 @@ Arm_input_section<big_endian>::do_reset_address_and_file_offset() this->set_current_data_size(off); } +// Arm_exidx_cantunwind methods. + +// Write this to Output file OF for a fixed endianity. + +template<bool big_endian> +void +Arm_exidx_cantunwind::do_fixed_endian_write(Output_file* of) +{ + off_t offset = this->offset(); + const section_size_type oview_size = 8; + unsigned char* const oview = of->get_output_view(offset, oview_size); + + typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype; + Valtype* wv = reinterpret_cast<Valtype*>(oview); + + Output_section* os = this->relobj_->output_section(this->shndx_); + gold_assert(os != NULL); + + Arm_relobj<big_endian>* arm_relobj = + Arm_relobj<big_endian>::as_arm_relobj(this->relobj_); + Arm_address output_offset = + arm_relobj->get_output_section_offset(this->shndx_); + Arm_address section_start; + if(output_offset != Arm_relobj<big_endian>::invalid_address) + section_start = os->address() + output_offset; + else + { + // Currently this only happens for a relaxed section. + const Output_relaxed_input_section* poris = + os->find_relaxed_input_section(this->relobj_, this->shndx_); + gold_assert(poris != NULL); + section_start = poris->address(); + } + + // We always append this to the end of an EXIDX section. + Arm_address output_address = + section_start + this->relobj_->section_size(this->shndx_); + + // Write out the entry. The first word either points to the beginning + // or after the end of a text section. The second word is the special + // EXIDX_CANTUNWIND value. + elfcpp::Swap<32, big_endian>::writeval(wv, output_address); + elfcpp::Swap<32, big_endian>::writeval(wv + 1, elfcpp::EXIDX_CANTUNWIND); + + of->write_output_view(this->offset(), oview_size, oview); +} + +// Arm_exidx_merged_section methods. + +// Constructor for Arm_exidx_merged_section. +// EXIDX_INPUT_SECTION points to the unmodified EXIDX input section. +// SECTION_OFFSET_MAP points to a section offset map describing how +// parts of the input section are mapped to output. DELETED_BYTES is +// the number of bytes deleted from the EXIDX input section. + +Arm_exidx_merged_section::Arm_exidx_merged_section( + const Arm_exidx_input_section& exidx_input_section, + const Arm_exidx_section_offset_map& section_offset_map, + uint32_t deleted_bytes) + : Output_relaxed_input_section(exidx_input_section.relobj(), + exidx_input_section.shndx(), + exidx_input_section.addralign()), + exidx_input_section_(exidx_input_section), + section_offset_map_(section_offset_map) +{ + // Fix size here so that we do not need to implement set_final_data_size. + this->set_data_size(exidx_input_section.size() - deleted_bytes); + this->fix_data_size(); +} + +// Given an input OBJECT, an input section index SHNDX within that +// object, and an OFFSET relative to the start of that input +// section, return whether or not the corresponding offset within +// the output section is known. If this function returns true, it +// sets *POUTPUT to the output offset. The value -1 indicates that +// this input offset is being discarded. + +bool +Arm_exidx_merged_section::do_output_offset( + const Relobj* relobj, + unsigned int shndx, + section_offset_type offset, + section_offset_type* poutput) const +{ + // We only handle offsets for the original EXIDX input section. + if (relobj != this->exidx_input_section_.relobj() + || shndx != this->exidx_input_section_.shndx()) + return false; + + if (offset < 0 || offset >= this->exidx_input_section_.size()) + // Input offset is out of valid range. + *poutput = -1; + else + { + // We need to look up the section offset map to determine the output + // offset. Find the reference point in map that is first offset + // bigger than or equal to this offset. + Arm_exidx_section_offset_map::const_iterator p = + this->section_offset_map_.lower_bound(offset); + + // The section offset maps are build such that this should not happen if + // input offset is in the valid range. + gold_assert(p != this->section_offset_map_.end()); + + // We need to check if this is dropped. + section_offset_type ref = p->first; + section_offset_type mapped_ref = p->second; + + if (mapped_ref != Arm_exidx_input_section::invalid_offset) + // Offset is present in output. + *poutput = mapped_ref + (offset - ref); + else + // Offset is discarded owing to EXIDX entry merging. + *poutput = -1; + } + + return true; +} + +// Write this to output file OF. + +void +Arm_exidx_merged_section::do_write(Output_file* of) +{ + // If we retain or discard the whole EXIDX input section, we would + // not be here. + gold_assert(this->data_size() != this->exidx_input_section_.size() + && this->data_size() != 0); + + off_t offset = this->offset(); + const section_size_type oview_size = this->data_size(); + unsigned char* const oview = of->get_output_view(offset, oview_size); + + Output_section* os = this->relobj()->output_section(this->shndx()); + gold_assert(os != NULL); + + // Get contents of EXIDX input section. + section_size_type section_size; + const unsigned char* section_contents = + this->relobj()->section_contents(this->shndx(), §ion_size, false); + gold_assert(section_size == this->exidx_input_section_.size()); + + // Go over spans of input offsets and write only those that are not + // discarded. + section_offset_type in_start = 0; + section_offset_type out_start = 0; + for(Arm_exidx_section_offset_map::const_iterator p = + this->section_offset_map_.begin(); + p != this->section_offset_map_.end(); + ++p) + { + section_offset_type in_end = p->first; + gold_assert(in_end >= in_start); + section_offset_type out_end = p->second; + size_t in_chunk_size = convert_types<size_t>(in_end - in_start + 1); + if (out_end != -1) + { + size_t out_chunk_size = + convert_types<size_t>(out_end - out_start + 1); + gold_assert(out_chunk_size == in_chunk_size); + memcpy(oview + out_start, section_contents + in_start, + out_chunk_size); + out_start += out_chunk_size; + } + in_start += in_chunk_size; + } + + gold_assert(convert_to_section_size_type(out_start) == oview_size); + of->write_output_view(this->offset(), oview_size, oview); +} + // Arm_output_section methods. // Create a stub group for input sections from BEGIN to END. OWNER |