diff options
author | Alan Modra <amodra@gmail.com> | 2013-02-27 23:11:56 +0000 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2013-02-27 23:11:56 +0000 |
commit | 9d5781f8a245086dcdd3cd8fcddcd5b19c3a1e49 (patch) | |
tree | e53d7aa78570b4f92a31db6d49bb0e0a31374b61 | |
parent | 71091f23fd90c0f1d04a0471ec561f07120b702c (diff) | |
download | gdb-9d5781f8a245086dcdd3cd8fcddcd5b19c3a1e49.zip gdb-9d5781f8a245086dcdd3cd8fcddcd5b19c3a1e49.tar.gz gdb-9d5781f8a245086dcdd3cd8fcddcd5b19c3a1e49.tar.bz2 |
* target.h (Target::plt_fde_location, do_plt_fde_location): Declare.
* target.cc (Target::do_plt_fde_location): New function.
* ehframe.h (class FDE): Add post_map field to u_.from_linker,
accessor function, and constructor param.
(struct Post_fde, Post_fdes): Declare.
(Cie::write): Add post_fdes param.
* ehframe.cc (Fde::write): Use plt_fde_location.
(struct Post_fde): Define.
(Cie::write): Stash FDEs added post merge mapping.
(Eh_frame::add_ehframe_for_plt): Assert no new CIEs after mapping.
Adjust Fde constructor call. Bump final_data_size_ for post map FDEs.
(Eh_frame::do_sized_write): Arrange to write post map FDES after
other FDEs.
* powerpc.cc (Target_powerpc::do_plt_fde_location): New function.
(Target_powerpc::has_glink): New function.
(Target_powerpc::do_relax): Add eh_frame info for stubs.
(struct Eh_cie, eh_frame_cie, glink_eh_frame_fde_64,
glink_eh_frame_fde_32, default_fde): New data.
(Stub_table::eh_frame_added_): New var.
(Stub_table::find_long_branch_entry, stub_address, stub_offset):
Make const.
(Stub_table::add_eh_frame): New function.
(Output_data_glink::add_eh_frame): New function.
(Target_powerpc::make_glink_section): Call add_eh_frame.
-rw-r--r-- | gold/ChangeLog | 27 | ||||
-rw-r--r-- | gold/ehframe.cc | 64 | ||||
-rw-r--r-- | gold/ehframe.h | 32 | ||||
-rw-r--r-- | gold/powerpc.cc | 185 | ||||
-rw-r--r-- | gold/target.cc | 9 | ||||
-rw-r--r-- | gold/target.h | 11 |
6 files changed, 303 insertions, 25 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index e9de821..0aa655e 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,30 @@ +2013-02-28 Alan Modra <amodra@gmail.com> + + * target.h (Target::plt_fde_location, do_plt_fde_location): Declare. + * target.cc (Target::do_plt_fde_location): New function. + * ehframe.h (class FDE): Add post_map field to u_.from_linker, + accessor function, and constructor param. + (struct Post_fde, Post_fdes): Declare. + (Cie::write): Add post_fdes param. + * ehframe.cc (Fde::write): Use plt_fde_location. + (struct Post_fde): Define. + (Cie::write): Stash FDEs added post merge mapping. + (Eh_frame::add_ehframe_for_plt): Assert no new CIEs after mapping. + Adjust Fde constructor call. Bump final_data_size_ for post map FDEs. + (Eh_frame::do_sized_write): Arrange to write post map FDES after + other FDEs. + * powerpc.cc (Target_powerpc::do_plt_fde_location): New function. + (Target_powerpc::has_glink): New function. + (Target_powerpc::do_relax): Add eh_frame info for stubs. + (struct Eh_cie, eh_frame_cie, glink_eh_frame_fde_64, + glink_eh_frame_fde_32, default_fde): New data. + (Stub_table::eh_frame_added_): New var. + (Stub_table::find_long_branch_entry, stub_address, stub_offset): + Make const. + (Stub_table::add_eh_frame): New function. + (Output_data_glink::add_eh_frame): New function. + (Target_powerpc::make_glink_section): Call add_eh_frame. + 2013-02-15 Ian Lance Taylor <iant@google.com> * options.h (DEFINE_uint64_alias): Define. diff --git a/gold/ehframe.cc b/gold/ehframe.cc index 1aaf346..82a6d6e 100644 --- a/gold/ehframe.cc +++ b/gold/ehframe.cc @@ -368,10 +368,13 @@ Fde::write(unsigned char* oview, section_offset_type offset, if (this->object_ == NULL) { gold_assert(memcmp(oview + offset + 8, "\0\0\0\0\0\0\0\0", 8) == 0); - Output_data* plt = this->u_.from_linker.plt; - uint64_t poffset = plt->address() - (address + offset + 8); + uint64_t paddress; + off_t psize; + parameters->target().plt_fde_location(this->u_.from_linker.plt, + oview + offset + 8, + &paddress, &psize); + uint64_t poffset = paddress - (address + offset + 8); int32_t spoffset = static_cast<int32_t>(poffset); - off_t psize = plt->data_size(); uint32_t upsize = static_cast<uint32_t>(psize); if (static_cast<uint64_t>(static_cast<int64_t>(spoffset)) != poffset || static_cast<off_t>(upsize) != psize) @@ -438,15 +441,30 @@ Cie::set_output_offset(section_offset_type output_offset, return output_offset + length; } -// Write the CIE to OVIEW starting at OFFSET. EH_FRAME_HDR is for FDE -// recording. Round up the bytes to ADDRALIGN. Return the new -// offset. +// A FDE plus some info from a CIE to allow later writing of the FDE. + +struct Post_fde +{ + Post_fde(Fde* f, section_offset_type cie_off, unsigned char encoding) + : fde(f), cie_offset(cie_off), fde_encoding(encoding) + { } + + Fde* fde; + section_offset_type cie_offset; + unsigned char fde_encoding; +}; + +// Write the CIE to OVIEW starting at OFFSET. Round up the bytes to +// ADDRALIGN. ADDRESS is the virtual address of OVIEW. +// EH_FRAME_HDR is the exception frame header for FDE recording. +// POST_FDES stashes FDEs created after mappings were done, for later +// writing. Return the new offset. template<int size, bool big_endian> section_offset_type Cie::write(unsigned char* oview, section_offset_type offset, uint64_t address, unsigned int addralign, - Eh_frame_hdr* eh_frame_hdr) + Eh_frame_hdr* eh_frame_hdr, Post_fdes* post_fdes) { gold_assert((offset & (addralign - 1)) == 0); @@ -479,9 +497,14 @@ Cie::write(unsigned char* oview, section_offset_type offset, for (std::vector<Fde*>::const_iterator p = this->fdes_.begin(); p != this->fdes_.end(); ++p) - offset = (*p)->write<size, big_endian>(oview, offset, address, addralign, - cie_offset, fde_encoding, - eh_frame_hdr); + { + if ((*p)->post_map()) + post_fdes->push_back(new Post_fde(*p, cie_offset, fde_encoding)); + else + offset = (*p)->write<size, big_endian>(oview, offset, address, + addralign, cie_offset, + fde_encoding, eh_frame_hdr); + } return offset; } @@ -1039,12 +1062,16 @@ Eh_frame::add_ehframe_for_plt(Output_data* plt, const unsigned char* cie_data, pcie = *find_cie; else { + gold_assert(!this->mappings_are_done_); pcie = new Cie(cie); this->cie_offsets_.insert(pcie); } - Fde* fde = new Fde(plt, fde_data, fde_length); + Fde* fde = new Fde(plt, fde_data, fde_length, this->mappings_are_done_); pcie->add_fde(fde); + + if (this->mappings_are_done_) + this->final_data_size_ += align_address(fde_length + 8, this->addralign()); } // Return the number of FDEs. @@ -1169,17 +1196,28 @@ Eh_frame::do_sized_write(unsigned char* oview) uint64_t address = this->address(); unsigned int addralign = this->addralign(); section_offset_type o = 0; + Post_fdes post_fdes; for (Unmergeable_cie_offsets::iterator p = this->unmergeable_cie_offsets_.begin(); p != this->unmergeable_cie_offsets_.end(); ++p) o = (*p)->write<size, big_endian>(oview, o, address, addralign, - this->eh_frame_hdr_); + this->eh_frame_hdr_, &post_fdes); for (Cie_offsets::iterator p = this->cie_offsets_.begin(); p != this->cie_offsets_.end(); ++p) o = (*p)->write<size, big_endian>(oview, o, address, addralign, - this->eh_frame_hdr_); + this->eh_frame_hdr_, &post_fdes); + for (Post_fdes::iterator p = post_fdes.begin(); + p != post_fdes.end(); + ++p) + { + o = (*p)->fde->write<size, big_endian>(oview, o, address, addralign, + (*p)->cie_offset, + (*p)->fde_encoding, + this->eh_frame_hdr_); + delete *p; + } } #ifdef HAVE_TARGET_32_LITTLE diff --git a/gold/ehframe.h b/gold/ehframe.h index c3f82e9..de7c109 100644 --- a/gold/ehframe.h +++ b/gold/ehframe.h @@ -174,10 +174,14 @@ class Fde } // Create an FDE associated with a PLT. - Fde(Output_data* plt, const unsigned char* contents, size_t length) + Fde(Output_data* plt, const unsigned char* contents, size_t length, + bool post_map) : object_(NULL), contents_(reinterpret_cast<const char*>(contents), length) - { this->u_.from_linker.plt = plt; } + { + this->u_.from_linker.plt = plt; + this->u_.from_linker.post_map = post_map; + } // Return the length of this FDE. Add 4 for the length and 4 for // the offset to the CIE. @@ -196,6 +200,11 @@ class Fde output_offset); } + // Return whether this FDE was added after merge mapping. + bool + post_map() + { return this->object_ == NULL && this->u_.from_linker.post_map; } + // Write the FDE to OVIEW starting at OFFSET. FDE_ENCODING is the // encoding, from the CIE. Round up the bytes to ADDRALIGN if // necessary. ADDRESS is the virtual address of OVIEW. Record the @@ -229,12 +238,19 @@ class Fde // The only linker generated FDEs are for PLT sections, and this // points to the PLT section. Output_data* plt; + // Set if the FDE was added after merge mapping. + bool post_map; } from_linker; } u_; // FDE data. std::string contents_; }; +// FDEs stashed for later processing. + +struct Post_fde; +typedef std::vector<Post_fde*> Post_fdes; + // This class holds a CIE. class Cie @@ -284,14 +300,16 @@ class Cie set_output_offset(section_offset_type output_offset, unsigned int addralign, Merge_map*); - // Write the CIE to OVIEW starting at OFFSET. EH_FRAME_HDR is the - // exception frame header for FDE recording. Round up the bytes to - // ADDRALIGN. ADDRESS is the virtual address of OVIEW. Return the - // new offset. + // Write the CIE to OVIEW starting at OFFSET. Round up the bytes to + // ADDRALIGN. ADDRESS is the virtual address of OVIEW. + // EH_FRAME_HDR is the exception frame header for FDE recording. + // POST_FDES stashes FDEs created after mappings were done, for later + // writing. Return the new offset. template<int size, bool big_endian> section_offset_type write(unsigned char* oview, section_offset_type offset, uint64_t address, - unsigned int addralign, Eh_frame_hdr* eh_frame_hdr); + unsigned int addralign, Eh_frame_hdr* eh_frame_hdr, + Post_fdes* post_fdes); friend bool operator<(const Cie&, const Cie&); friend bool operator==(const Cie&, const Cie&); diff --git a/gold/powerpc.cc b/gold/powerpc.cc index e192885..4521fc7 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -25,6 +25,7 @@ #include <algorithm> #include "elfcpp.h" +#include "dwarf.h" #include "parameters.h" #include "reloc.h" #include "powerpc.h" @@ -394,6 +395,10 @@ class Target_powerpc : public Sized_target<size, big_endian> bool do_relax(int, const Input_objects*, Symbol_table*, Layout*, const Task*); + void + do_plt_fde_location(const Output_data*, unsigned char*, + uint64_t*, off_t*) const; + // Stash info about branches, for stub generation. void push_branch(Powerpc_relobj<size, big_endian>* ppc_object, @@ -526,6 +531,9 @@ class Target_powerpc : public Sized_target<size, big_endian> return this->glink_; } + bool has_glink() const + { return this->glink_ != NULL; } + // Get the GOT section. const Output_data_got_powerpc<size, big_endian>* got_section() const @@ -2277,6 +2285,7 @@ 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()); } } @@ -2332,6 +2341,54 @@ Target_powerpc<size, big_endian>::do_relax(int pass, return again; } +template<int size, bool big_endian> +void +Target_powerpc<size, big_endian>::do_plt_fde_location(const Output_data* plt, + unsigned char* oview, + uint64_t* paddress, + off_t* plen) const +{ + uint64_t address = plt->address(); + off_t len = plt->data_size(); + + if (plt == this->glink_) + { + // See Output_data_glink::do_write() for glink contents. + if (size == 64) + { + // There is one word before __glink_PLTresolve + address += 8; + len -= 8; + } + else if (parameters->options().output_is_position_independent()) + { + // There are two FDEs for a position independent glink. + // The first covers the branch table, the second + // __glink_PLTresolve at the end of glink. + off_t resolve_size = this->glink_->pltresolve_size; + if (oview[9] == 0) + len -= resolve_size; + else + { + address += len - resolve_size; + len = resolve_size; + } + } + } + else + { + // Must be a stub table. + const Stub_table<size, big_endian>* stub_table + = static_cast<const Stub_table<size, big_endian>*>(plt); + uint64_t stub_address = stub_table->stub_address(); + len -= stub_address - address; + address = stub_address; + } + + *paddress = address; + *plen = len; +} + // A class to handle the PLT data. template<int size, bool big_endian> @@ -2774,6 +2831,60 @@ ha(uint32_t a) return hi(a + 0x8000); } +template<int size> +struct Eh_cie +{ + static const unsigned char eh_frame_cie[12]; +}; + +template<int size> +const unsigned char Eh_cie<size>::eh_frame_cie[] = +{ + 1, // CIE version. + 'z', 'R', 0, // Augmentation string. + 4, // Code alignment. + 0x80 - size / 8 , // Data alignment. + 65, // RA reg. + 1, // Augmentation size. + (elfcpp::DW_EH_PE_pcrel + | elfcpp::DW_EH_PE_sdata4), // FDE encoding. + elfcpp::DW_CFA_def_cfa, 1, 0 // def_cfa: r1 offset 0. +}; + +// Describe __glink_PLTresolve use of LR, 64-bit version. +static const unsigned char glink_eh_frame_fde_64[] = +{ + 0, 0, 0, 0, // Replaced with offset to .glink. + 0, 0, 0, 0, // Replaced with size of .glink. + 0, // Augmentation size. + elfcpp::DW_CFA_advance_loc + 1, + elfcpp::DW_CFA_register, 65, 12, + elfcpp::DW_CFA_advance_loc + 4, + elfcpp::DW_CFA_restore_extended, 65 +}; + +// Describe __glink_PLTresolve use of LR, 32-bit version. +static const unsigned char glink_eh_frame_fde_32[] = +{ + 0, 0, 0, 0, // Replaced with offset to .glink. + 0, 0, 0, 0, // Replaced with size of .glink. + 0, // Augmentation size. + elfcpp::DW_CFA_advance_loc + 2, + elfcpp::DW_CFA_register, 65, 0, + elfcpp::DW_CFA_advance_loc + 4, + elfcpp::DW_CFA_restore_extended, 65 +}; + +static const unsigned char default_fde[] = +{ + 0, 0, 0, 0, // Replaced with offset to stubs. + 0, 0, 0, 0, // Replaced with size of stubs. + 0, // Augmentation size. + elfcpp::DW_CFA_nop, // Pad. + elfcpp::DW_CFA_nop, + elfcpp::DW_CFA_nop +}; + template<bool big_endian> static inline void write_insn(unsigned char* p, uint32_t v) @@ -2797,7 +2908,7 @@ class Stub_table : public Output_relaxed_input_section : Output_relaxed_input_section(NULL, 0, 0), targ_(targ), plt_call_stubs_(), long_branch_stubs_(), orig_data_size_(0), plt_size_(0), last_plt_size_(0), - branch_size_(0), last_branch_size_(0) + branch_size_(0), last_branch_size_(0), eh_frame_added_(false) { } // Delayed Output_relaxed_input_section init. @@ -2842,7 +2953,8 @@ class Stub_table : public Output_relaxed_input_section add_long_branch_entry(const Powerpc_relobj<size, big_endian>*, Address); Address - find_long_branch_entry(const Powerpc_relobj<size, big_endian>*, Address); + find_long_branch_entry(const Powerpc_relobj<size, big_endian>*, + Address) const; void clear_stubs() @@ -2871,14 +2983,14 @@ class Stub_table : public Output_relaxed_input_section } Address - stub_address() + stub_address() const { return align_address(this->address() + this->orig_data_size_, this->stub_align()); } Address - stub_offset() + stub_offset() const { return align_address(this->offset() + this->orig_data_size_, this->stub_align()); @@ -2919,6 +3031,35 @@ class Stub_table : public Output_relaxed_input_section return false; } + // Add .eh_frame info for this stub section. Unlike other linker + // generated .eh_frame this is added late in the link, because we + // only want the .eh_frame info if this particular stub section is + // non-empty. + 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; + + 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; + } + } + Target_powerpc<size, big_endian>* targ() const { return targ_; } @@ -3118,6 +3259,8 @@ class Stub_table : public Output_relaxed_input_section section_size_type orig_data_size_; // size of stubs section_size_type plt_size_, last_plt_size_, branch_size_, last_branch_size_; + // Whether .eh_frame info has been created for this stub section. + bool eh_frame_added_; }; // Make a new stub table, and record. @@ -3261,7 +3404,7 @@ template<int size, bool big_endian> typename Stub_table<size, big_endian>::Address Stub_table<size, big_endian>::find_long_branch_entry( const Powerpc_relobj<size, big_endian>* object, - Address to) + Address to) const { Branch_stub_ent ent(object, to); typename Branch_stub_entries::const_iterator p @@ -3281,6 +3424,37 @@ class Output_data_glink : public Output_section_data : Output_section_data(16), targ_(targ) { } + void + add_eh_frame(Layout* layout) + { + if (!parameters->options().ld_generated_unwind_info()) + return; + + if (size == 64) + layout->add_eh_frame_for_plt(this, + Eh_cie<64>::eh_frame_cie, + sizeof (Eh_cie<64>::eh_frame_cie), + glink_eh_frame_fde_64, + sizeof (glink_eh_frame_fde_64)); + else + { + // 32-bit .glink can use the default since the CIE return + // address reg, LR, is valid. + layout->add_eh_frame_for_plt(this, + Eh_cie<32>::eh_frame_cie, + sizeof (Eh_cie<32>::eh_frame_cie), + default_fde, + sizeof (default_fde)); + // Except where LR is used in a PIC __glink_PLTresolve. + if (parameters->options().output_is_position_independent()) + layout->add_eh_frame_for_plt(this, + Eh_cie<32>::eh_frame_cie, + sizeof (Eh_cie<32>::eh_frame_cie), + glink_eh_frame_fde_32, + sizeof (glink_eh_frame_fde_32)); + } + } + protected: // Write to a map file. void @@ -4086,6 +4260,7 @@ Target_powerpc<size, big_endian>::make_glink_section(Layout* layout) if (this->glink_ == NULL) { this->glink_ = new Output_data_glink<size, big_endian>(this); + this->glink_->add_eh_frame(layout); layout->add_output_section_data(".text", elfcpp::SHT_PROGBITS, elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR, this->glink_, ORDER_TEXT, false); diff --git a/gold/target.cc b/gold/target.cc index a47d74f..cc508c5 100644 --- a/gold/target.cc +++ b/gold/target.cc @@ -203,6 +203,15 @@ Target::set_view_to_nop(unsigned char* view, section_size_type view_size, } } +// Return address and size to plug into eh_frame FDEs associated with a PLT. +void +Target::do_plt_fde_location(const Output_data* plt, unsigned char*, + uint64_t* address, off_t* len) const +{ + *address = plt->address(); + *len = plt->data_size(); +} + // Class Sized_target. // Set the EI_OSABI field of the ELF header if requested. diff --git a/gold/target.h b/gold/target.h index d4b9d0f..d75711e 100644 --- a/gold/target.h +++ b/gold/target.h @@ -240,6 +240,12 @@ class Target adjust_elf_header(unsigned char* view, int len) const { return this->do_adjust_elf_header(view, len); } + // Return address and size to plug into eh_frame FDEs associated with a PLT. + void + plt_fde_location(const Output_data* plt, unsigned char* oview, + uint64_t* address, off_t* len) const + { return this->do_plt_fde_location(plt, oview, address, len); } + // Return whether NAME is a local label name. This is used to implement the // --discard-locals options. bool @@ -530,6 +536,11 @@ class Target virtual void do_adjust_elf_header(unsigned char*, int) const = 0; + // Return address and size to plug into eh_frame FDEs associated with a PLT. + virtual void + do_plt_fde_location(const Output_data* plt, unsigned char* oview, + uint64_t* address, off_t* len) const; + // Virtual function which may be overridden by the child class. virtual bool do_is_local_label_name(const char*) const; |