From: Ian Lance Taylor Date: Fri, 1 Jul 2011 22:05:01 +0000 (+0000) Subject: PR gold/12571 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=07a60597350488f098508ee67717d549a8a0b579;p=binutils-gdb.git PR gold/12571 * options.h (class General_options): Add --ld-generated-unwind-info. * ehframe.cc (Fde::write): Add address parameter. Change all callers. If associated with PLT, fill in address and size. (Cie::set_output_offset): Only add merge mapping if there is an object. (Cie::write): Add address parameter. Change all callers. (Eh_frame::add_ehframe_for_plt): New function. * ehframe.h (class Fde): Update declarations. Move shndx_ and input_offset_ fields into union u_, with new plt field. (Fde::Fde): Adjust for new union field. (Fde::Fde) [Output_data version]: New constructor. (Fde::add_mapping): Only add merge mapping if there is an object. (class Cie): Update declarations. (class Eh_frame): Declare add_ehframe_for_plt. * layout.cc (Layout::layout_eh_frame): Break out code into make_eh_frame_section, and call it. (Layout::make_eh_frame_section): New function. (Layout::add_eh_frame_for_plt): New function. * layout.h (class Layout): Update declarations. * merge.cc (Merge_map::add_mapping): Add assertion. * i386.cc: Include "dwarf.h". (class Output_data_plt_i386): Make first_plt_entry, dyn_first_plt_entry, exec_plt_entry, and dyn_plt_entry const. Add plt_eh_frame_cie_size, plt_eh_frame_fde_size, plt_eh_frame_cie, and plt_eh_frame_fde. (Output_data_plt_i386::Output_data_plt_i386): Align to 16-byte boundary. Call add_eh_frame_for_plt if appropriate. * x86_64.cc: Include "dwarf.h". (class Output_data_plt_x86_64): Align to 16-byte boundary. Make first_plt_entry, plt_entry and tlsdesc_plt_entry const. Add plt_eh_frame_cie_size, plt_eh_frame_fde_size, plt_eh_frame_cie, and plt_eh_frame_fde. (Output_data_plt_x86_64::init): Call add_eh_frame_for_plt if appropriate. --- diff --git a/gold/ChangeLog b/gold/ChangeLog index 62ee3cad8be..8b8112dc320 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,42 @@ +2011-07-01 Ian Lance Taylor + + PR gold/12571 + * options.h (class General_options): Add + --ld-generated-unwind-info. + * ehframe.cc (Fde::write): Add address parameter. Change all + callers. If associated with PLT, fill in address and size. + (Cie::set_output_offset): Only add merge mapping if there is an + object. + (Cie::write): Add address parameter. Change all callers. + (Eh_frame::add_ehframe_for_plt): New function. + * ehframe.h (class Fde): Update declarations. Move shndx_ and + input_offset_ fields into union u_, with new plt field. + (Fde::Fde): Adjust for new union field. + (Fde::Fde) [Output_data version]: New constructor. + (Fde::add_mapping): Only add merge mapping if there is an object. + (class Cie): Update declarations. + (class Eh_frame): Declare add_ehframe_for_plt. + * layout.cc (Layout::layout_eh_frame): Break out code into + make_eh_frame_section, and call it. + (Layout::make_eh_frame_section): New function. + (Layout::add_eh_frame_for_plt): New function. + * layout.h (class Layout): Update declarations. + * merge.cc (Merge_map::add_mapping): Add assertion. + * i386.cc: Include "dwarf.h". + (class Output_data_plt_i386): Make first_plt_entry, + dyn_first_plt_entry, exec_plt_entry, and dyn_plt_entry const. Add + plt_eh_frame_cie_size, plt_eh_frame_fde_size, plt_eh_frame_cie, + and plt_eh_frame_fde. + (Output_data_plt_i386::Output_data_plt_i386): Align to 16-byte + boundary. Call add_eh_frame_for_plt if appropriate. + * x86_64.cc: Include "dwarf.h". + (class Output_data_plt_x86_64): Align to 16-byte boundary. Make + first_plt_entry, plt_entry and tlsdesc_plt_entry const. Add + plt_eh_frame_cie_size, plt_eh_frame_fde_size, plt_eh_frame_cie, + and plt_eh_frame_fde. + (Output_data_plt_x86_64::init): Call add_eh_frame_for_plt if + appropriate. + 2011-06-29 Ian Lance Taylor PR gold/12629 diff --git a/gold/ehframe.cc b/gold/ehframe.cc index fbeb8a3787f..80b00358d22 100644 --- a/gold/ehframe.cc +++ b/gold/ehframe.cc @@ -1,6 +1,6 @@ // ehframe.cc -- handle exception frame sections for gold -// Copyright 2006, 2007, 2008, 2010 Free Software Foundation, Inc. +// Copyright 2006, 2007, 2008, 2010, 2011 Free Software Foundation, Inc. // Written by Ian Lance Taylor . // This file is part of gold. @@ -321,14 +321,16 @@ Eh_frame_hdr::get_fde_addresses(Output_file* of, // Write the FDE to OVIEW starting at OFFSET. CIE_OFFSET is the // offset of the CIE in OVIEW. FDE_ENCODING is the encoding, from the -// CIE. ADDRALIGN is the required alignment. Record the FDE pc for -// EH_FRAME_HDR. Return the new offset. +// CIE. ADDRALIGN is the required alignment. ADDRESS is the virtual +// address of OVIEW. Record the FDE pc for EH_FRAME_HDR. Return the +// new offset. template section_offset_type Fde::write(unsigned char* oview, section_offset_type offset, - unsigned int addralign, section_offset_type cie_offset, - unsigned char fde_encoding, Eh_frame_hdr* eh_frame_hdr) + uint64_t address, unsigned int addralign, + section_offset_type cie_offset, unsigned char fde_encoding, + Eh_frame_hdr* eh_frame_hdr) { gold_assert((offset & (addralign - 1)) == 0); @@ -355,6 +357,24 @@ Fde::write(unsigned char* oview, section_offset_type offset, // will later be applied to the FDE data. memcpy(oview + offset + 8, this->contents_.data(), length); + // If this FDE is associated with a PLT, fill in the PLT's address + // and size. + 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); + int32_t spoffset = static_cast(poffset); + off_t psize = plt->data_size(); + uint32_t upsize = static_cast(psize); + if (static_cast(static_cast(spoffset)) != poffset + || static_cast(upsize) != psize) + gold_warning(_("overflow in PLT unwind data; " + "unwinding through PLT may fail")); + elfcpp::Swap<32, big_endian>::writeval(oview + offset + 8, spoffset); + elfcpp::Swap<32, big_endian>::writeval(oview + offset + 12, upsize); + } + if (aligned_full_length > length + 8) memset(oview + offset + length + 8, 0, aligned_full_length - (length + 8)); @@ -389,8 +409,12 @@ Cie::set_output_offset(section_offset_type output_offset, // Add 4 for length and 4 for zero CIE identifier tag. length += 8; - merge_map->add_mapping(this->object_, this->shndx_, this->input_offset_, - length, output_offset); + if (this->object_ != NULL) + { + // Add a mapping so that relocations are applied correctly. + merge_map->add_mapping(this->object_, this->shndx_, this->input_offset_, + length, output_offset); + } length = align_address(length, addralign); @@ -415,7 +439,8 @@ Cie::set_output_offset(section_offset_type output_offset, template section_offset_type Cie::write(unsigned char* oview, section_offset_type offset, - unsigned int addralign, Eh_frame_hdr* eh_frame_hdr) + uint64_t address, unsigned int addralign, + Eh_frame_hdr* eh_frame_hdr) { gold_assert((offset & (addralign - 1)) == 0); @@ -448,7 +473,7 @@ Cie::write(unsigned char* oview, section_offset_type offset, for (std::vector::const_iterator p = this->fdes_.begin(); p != this->fdes_.end(); ++p) - offset = (*p)->write(oview, offset, addralign, + offset = (*p)->write(oview, offset, address, addralign, cie_offset, fde_encoding, eh_frame_hdr); @@ -994,6 +1019,29 @@ Eh_frame::read_fde(Sized_relobj_file* object, return true; } +// Add unwind information for a PLT. + +void +Eh_frame::add_ehframe_for_plt(Output_data* plt, const unsigned char* cie_data, + size_t cie_length, const unsigned char* fde_data, + size_t fde_length) +{ + Cie cie(NULL, 0, 0, elfcpp::DW_EH_PE_pcrel | elfcpp::DW_EH_PE_sdata4, "", + cie_data, cie_length); + Cie_offsets::iterator find_cie = this->cie_offsets_.find(&cie); + Cie* pcie; + if (find_cie != this->cie_offsets_.end()) + pcie = *find_cie; + else + { + pcie = new Cie(cie); + this->cie_offsets_.insert(pcie); + } + + Fde* fde = new Fde(plt, fde_data, fde_length); + pcie->add_fde(fde); +} + // Return the number of FDEs. unsigned int @@ -1113,18 +1161,19 @@ template void Eh_frame::do_sized_write(unsigned char* oview) { + uint64_t address = this->address(); unsigned int addralign = this->addralign(); section_offset_type o = 0; for (Unmergeable_cie_offsets::iterator p = this->unmergeable_cie_offsets_.begin(); p != this->unmergeable_cie_offsets_.end(); ++p) - o = (*p)->write(oview, o, addralign, + o = (*p)->write(oview, o, address, addralign, this->eh_frame_hdr_); for (Cie_offsets::iterator p = this->cie_offsets_.begin(); p != this->cie_offsets_.end(); ++p) - o = (*p)->write(oview, o, addralign, + o = (*p)->write(oview, o, address, addralign, this->eh_frame_hdr_); } diff --git a/gold/ehframe.h b/gold/ehframe.h index f626b1f1696..c3f82e937e1 100644 --- a/gold/ehframe.h +++ b/gold/ehframe.h @@ -1,6 +1,6 @@ // ehframe.h -- handle exception frame sections for gold -*- C++ -*- -// Copyright 2006, 2007, 2008 Free Software Foundation, Inc. +// Copyright 2006, 2007, 2008, 2010, 2011 Free Software Foundation, Inc. // Written by Ian Lance Taylor . // This file is part of gold. @@ -45,10 +45,6 @@ class Eh_frame; // time and when a shared object is loaded, and the time required to // deregister the exception handlers when a shared object is unloaded. -// FIXME: gcc supports using storing a sorted lookup table for the -// FDEs in the PT_GNU_EH_FRAME segment, but we do not yet generate -// that. - class Eh_frame_hdr : public Output_section_data { public: @@ -170,9 +166,18 @@ class Fde public: Fde(Relobj* object, unsigned int shndx, section_offset_type input_offset, const unsigned char* contents, size_t length) - : object_(object), shndx_(shndx), input_offset_(input_offset), + : object_(object), contents_(reinterpret_cast(contents), length) - { } + { + this->u_.from_object.shndx = shndx; + this->u_.from_object.input_offset = input_offset; + } + + // Create an FDE associated with a PLT. + Fde(Output_data* plt, const unsigned char* contents, size_t length) + : object_(NULL), + contents_(reinterpret_cast(contents), length) + { this->u_.from_linker.plt = plt; } // Return the length of this FDE. Add 4 for the length and 4 for // the offset to the CIE. @@ -180,32 +185,52 @@ class Fde length() const { return this->contents_.length() + 8; } - // Add a mapping for this FDE to MERGE_MAP. + // Add a mapping for this FDE to MERGE_MAP, so that relocations + // against the FDE are applied to right part of the output file. void add_mapping(section_offset_type output_offset, Merge_map* merge_map) const { - merge_map->add_mapping(this->object_, this->shndx_, - this->input_offset_, this->length(), - output_offset); + if (this->object_ != NULL) + merge_map->add_mapping(this->object_, this->u_.from_object.shndx, + this->u_.from_object.input_offset, this->length(), + output_offset); } // Write the FDE to OVIEW starting at OFFSET. FDE_ENCODING is the // encoding, from the CIE. Round up the bytes to ADDRALIGN if - // necessary. Record the FDE in EH_FRAME_HDR. Return the new - // offset. + // necessary. ADDRESS is the virtual address of OVIEW. Record the + // FDE in EH_FRAME_HDR. Return the new offset. template section_offset_type write(unsigned char* oview, section_offset_type offset, - unsigned int addralign, section_offset_type cie_offset, - unsigned char fde_encoding, Eh_frame_hdr* eh_frame_hdr); + uint64_t address, unsigned int addralign, + section_offset_type cie_offset, unsigned char fde_encoding, + Eh_frame_hdr* eh_frame_hdr); private: - // The object in which this FDE was seen. + // The object in which this FDE was seen. This will be NULL for a + // linker generated FDE. Relobj* object_; - // Input section index for this FDE. - unsigned int shndx_; - // Offset within the input section for this FDE. - section_offset_type input_offset_; + union + { + // These fields are used if the FDE is from an input object (the + // object_ field is not NULL). + struct + { + // Input section index for this FDE. + unsigned int shndx; + // Offset within the input section for this FDE. + section_offset_type input_offset; + } from_object; + // This field is used if the FDE is generated by the linker (the + // object_ field is NULL). + struct + { + // The only linker generated FDEs are for PLT sections, and this + // points to the PLT section. + Output_data* plt; + } from_linker; + } u_; // FDE data. std::string contents_; }; @@ -261,10 +286,11 @@ class Cie // 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. Return the new offset. + // ADDRALIGN. ADDRESS is the virtual address of OVIEW. Return the + // new offset. template section_offset_type - write(unsigned char* oview, section_offset_type offset, + write(unsigned char* oview, section_offset_type offset, uint64_t address, unsigned int addralign, Eh_frame_hdr* eh_frame_hdr); friend bool operator<(const Cie&, const Cie&); @@ -274,11 +300,14 @@ class Cie // The class is not assignable. Cie& operator=(const Cie&); - // The object in which this CIE was first seen. + // The object in which this CIE was first seen. This will be NULL + // for a linker generated CIE. Relobj* object_; - // Input section index for this CIE. + // Input section index for this CIE. This will be 0 for a linker + // generated CIE. unsigned int shndx_; - // Offset within the input section for this CIE. + // Offset within the input section for this CIE. This will be 0 for + // a linker generated CIE. section_offset_type input_offset_; // The encoding of the FDE. This is a DW_EH_PE code. unsigned char fde_encoding_; @@ -324,6 +353,15 @@ class Eh_frame : public Output_section_data unsigned int shndx, unsigned int reloc_shndx, unsigned int reloc_type); + // Add a CIE and an FDE for a PLT section, to permit unwinding + // through a PLT. The FDE data should start with 8 bytes of zero, + // which will be replaced by a 4 byte PC relative reference to the + // address of PLT and a 4 byte size of PLT. + void + add_ehframe_for_plt(Output_data* plt, const unsigned char* cie_data, + size_t cie_length, const unsigned char* fde_data, + size_t fde_length); + // Return the number of FDEs. unsigned int fde_count() const; diff --git a/gold/i386.cc b/gold/i386.cc index beec4a8e314..636dfbb8403 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -25,6 +25,7 @@ #include #include "elfcpp.h" +#include "dwarf.h" #include "parameters.h" #include "reloc.h" #include "i386.h" @@ -101,16 +102,22 @@ class Output_data_plt_i386 : public Output_section_data static const int plt_entry_size = 16; // The first entry in the PLT for an executable. - static unsigned char exec_first_plt_entry[plt_entry_size]; + static const unsigned char exec_first_plt_entry[plt_entry_size]; // The first entry in the PLT for a shared object. - static unsigned char dyn_first_plt_entry[plt_entry_size]; + static const unsigned char dyn_first_plt_entry[plt_entry_size]; // Other entries in the PLT for an executable. - static unsigned char exec_plt_entry[plt_entry_size]; + static const unsigned char exec_plt_entry[plt_entry_size]; // Other entries in the PLT for a shared object. - static unsigned char dyn_plt_entry[plt_entry_size]; + static const unsigned char dyn_plt_entry[plt_entry_size]; + + // The .eh_frame unwind information for the PLT. + static const int plt_eh_frame_cie_size = 16; + static const int plt_eh_frame_fde_size = 32; + static const unsigned char plt_eh_frame_cie[plt_eh_frame_cie_size]; + static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size]; // Set the final size. void @@ -728,7 +735,7 @@ Target_i386::rel_dyn_section(Layout* layout) Output_data_plt_i386::Output_data_plt_i386(Symbol_table* symtab, Layout* layout, Output_data_space* got_plt) - : Output_section_data(4), tls_desc_rel_(NULL), got_plt_(got_plt), count_(0), + : Output_section_data(16), tls_desc_rel_(NULL), got_plt_(got_plt), count_(0), global_ifuncs_(), local_ifuncs_() { this->rel_ = new Reloc_section(false); @@ -753,6 +760,11 @@ Output_data_plt_i386::Output_data_plt_i386(Symbol_table* symtab, elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, 0, true, true); } + + // Add unwind information if requested. + if (parameters->options().ld_generated_unwind_info()) + layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size, + plt_eh_frame_fde, plt_eh_frame_fde_size); } void @@ -857,7 +869,7 @@ Output_data_plt_i386::rel_tls_desc(Layout* layout) // The first entry in the PLT for an executable. -unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] = { 0xff, 0x35, // pushl contents of memory address 0, 0, 0, 0, // replaced with address of .got + 4 @@ -868,7 +880,7 @@ unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] = // The first entry in the PLT for a shared object. -unsigned char Output_data_plt_i386::dyn_first_plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_i386::dyn_first_plt_entry[plt_entry_size] = { 0xff, 0xb3, 4, 0, 0, 0, // pushl 4(%ebx) 0xff, 0xa3, 8, 0, 0, 0, // jmp *8(%ebx) @@ -877,7 +889,7 @@ unsigned char Output_data_plt_i386::dyn_first_plt_entry[plt_entry_size] = // Subsequent entries in the PLT for an executable. -unsigned char Output_data_plt_i386::exec_plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_i386::exec_plt_entry[plt_entry_size] = { 0xff, 0x25, // jmp indirect 0, 0, 0, 0, // replaced with address of symbol in .got @@ -889,7 +901,7 @@ unsigned char Output_data_plt_i386::exec_plt_entry[plt_entry_size] = // Subsequent entries in the PLT for a shared object. -unsigned char Output_data_plt_i386::dyn_plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_i386::dyn_plt_entry[plt_entry_size] = { 0xff, 0xa3, // jmp *offset(%ebx) 0, 0, 0, 0, // replaced with offset of symbol in .got @@ -899,6 +911,54 @@ unsigned char Output_data_plt_i386::dyn_plt_entry[plt_entry_size] = 0, 0, 0, 0 // replaced with offset to start of .plt }; +// The .eh_frame unwind information for the PLT. + +const unsigned char +Output_data_plt_i386::plt_eh_frame_cie[plt_eh_frame_cie_size] = +{ + 1, // CIE version. + 'z', // Augmentation: augmentation size included. + 'R', // Augmentation: FDE encoding included. + '\0', // End of augmentation string. + 1, // Code alignment factor. + 0x7c, // Data alignment factor. + 8, // Return address column. + 1, // Augmentation size. + (elfcpp::DW_EH_PE_pcrel // FDE encoding. + | elfcpp::DW_EH_PE_sdata4), + elfcpp::DW_CFA_def_cfa, 4, 4, // DW_CFA_def_cfa: r4 (esp) ofs 4. + elfcpp::DW_CFA_offset + 8, 1, // DW_CFA_offset: r8 (eip) at cfa-4. + elfcpp::DW_CFA_nop, // Align to 16 bytes. + elfcpp::DW_CFA_nop +}; + +const unsigned char +Output_data_plt_i386::plt_eh_frame_fde[plt_eh_frame_fde_size] = +{ + 0, 0, 0, 0, // Replaced with offset to .plt. + 0, 0, 0, 0, // Replaced with size of .plt. + 0, // Augmentation size. + elfcpp::DW_CFA_def_cfa_offset, 8, // DW_CFA_def_cfa_offset: 8. + elfcpp::DW_CFA_advance_loc + 6, // Advance 6 to __PLT__ + 6. + elfcpp::DW_CFA_def_cfa_offset, 12, // DW_CFA_def_cfa_offset: 12. + elfcpp::DW_CFA_advance_loc + 10, // Advance 10 to __PLT__ + 16. + elfcpp::DW_CFA_def_cfa_expression, // DW_CFA_def_cfa_expression. + 11, // Block length. + elfcpp::DW_OP_breg4, 4, // Push %esp + 4. + elfcpp::DW_OP_breg8, 0, // Push %eip. + elfcpp::DW_OP_lit15, // Push 0xf. + elfcpp::DW_OP_and, // & (%eip & 0xf). + elfcpp::DW_OP_lit11, // Push 0xb. + elfcpp::DW_OP_ge, // >= ((%eip & 0xf) >= 0xb) + elfcpp::DW_OP_lit2, // Push 2. + elfcpp::DW_OP_shl, // << (((%eip & 0xf) >= 0xb) << 2) + elfcpp::DW_OP_plus, // + ((((%eip&0xf)>=0xb)<<2)+%esp+8 + elfcpp::DW_CFA_nop, // Align to 32 bytes. + elfcpp::DW_CFA_nop, + elfcpp::DW_CFA_nop, + elfcpp::DW_CFA_nop +}; + // Write out the PLT. This uses the hand-coded instructions above, // and adjusts them as needed. This is all specified by the i386 ELF // Processor Supplement. diff --git a/gold/layout.cc b/gold/layout.cc index 3152622fb13..11dbbef6e82 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -1136,51 +1136,10 @@ Layout::layout_eh_frame(Sized_relobj_file* object, || shdr.get_sh_type() == elfcpp::SHT_X86_64_UNWIND); gold_assert((shdr.get_sh_flags() & elfcpp::SHF_ALLOC) != 0); - const char* const name = ".eh_frame"; - Output_section* os = this->choose_output_section(object, name, - elfcpp::SHT_PROGBITS, - elfcpp::SHF_ALLOC, false, - ORDER_EHFRAME, false); + Output_section* os = this->make_eh_frame_section(object); if (os == NULL) return NULL; - if (this->eh_frame_section_ == NULL) - { - this->eh_frame_section_ = os; - this->eh_frame_data_ = new Eh_frame(); - - // For incremental linking, we do not optimize .eh_frame sections - // or create a .eh_frame_hdr section. - if (parameters->options().eh_frame_hdr() && !parameters->incremental()) - { - Output_section* hdr_os = - this->choose_output_section(NULL, ".eh_frame_hdr", - elfcpp::SHT_PROGBITS, - elfcpp::SHF_ALLOC, false, - ORDER_EHFRAME, false); - - if (hdr_os != NULL) - { - Eh_frame_hdr* hdr_posd = new Eh_frame_hdr(os, - this->eh_frame_data_); - hdr_os->add_output_section_data(hdr_posd); - - hdr_os->set_after_input_sections(); - - if (!this->script_options_->saw_phdrs_clause()) - { - Output_segment* hdr_oseg; - hdr_oseg = this->make_output_segment(elfcpp::PT_GNU_EH_FRAME, - elfcpp::PF_R); - hdr_oseg->add_output_section_to_nonload(hdr_os, - elfcpp::PF_R); - } - - this->eh_frame_data_->set_eh_frame_hdr(hdr_posd); - } - } - } - gold_assert(this->eh_frame_section_ == os); elfcpp::Elf_Xword orig_flags = os->flags(); @@ -1222,8 +1181,8 @@ Layout::layout_eh_frame(Sized_relobj_file* object, // We couldn't handle this .eh_frame section for some reason. // Add it as a normal section. bool saw_sections_clause = this->script_options_->saw_sections_clause(); - *off = os->add_input_section(this, object, shndx, name, shdr, reloc_shndx, - saw_sections_clause); + *off = os->add_input_section(this, object, shndx, ".eh_frame", shdr, + reloc_shndx, saw_sections_clause); this->have_added_input_section_ = true; if ((orig_flags & (elfcpp::SHF_WRITE | elfcpp::SHF_EXECINSTR)) @@ -1234,6 +1193,86 @@ Layout::layout_eh_frame(Sized_relobj_file* object, return os; } +// Create and return the magic .eh_frame section. Create +// .eh_frame_hdr also if appropriate. OBJECT is the object with the +// input .eh_frame section; it may be NULL. + +Output_section* +Layout::make_eh_frame_section(const Relobj* object) +{ + // FIXME: On x86_64, this could use SHT_X86_64_UNWIND rather than + // SHT_PROGBITS. + Output_section* os = this->choose_output_section(object, ".eh_frame", + elfcpp::SHT_PROGBITS, + elfcpp::SHF_ALLOC, false, + ORDER_EHFRAME, false); + if (os == NULL) + return NULL; + + if (this->eh_frame_section_ == NULL) + { + this->eh_frame_section_ = os; + this->eh_frame_data_ = new Eh_frame(); + + // For incremental linking, we do not optimize .eh_frame sections + // or create a .eh_frame_hdr section. + if (parameters->options().eh_frame_hdr() && !parameters->incremental()) + { + Output_section* hdr_os = + this->choose_output_section(NULL, ".eh_frame_hdr", + elfcpp::SHT_PROGBITS, + elfcpp::SHF_ALLOC, false, + ORDER_EHFRAME, false); + + if (hdr_os != NULL) + { + Eh_frame_hdr* hdr_posd = new Eh_frame_hdr(os, + this->eh_frame_data_); + hdr_os->add_output_section_data(hdr_posd); + + hdr_os->set_after_input_sections(); + + if (!this->script_options_->saw_phdrs_clause()) + { + Output_segment* hdr_oseg; + hdr_oseg = this->make_output_segment(elfcpp::PT_GNU_EH_FRAME, + elfcpp::PF_R); + hdr_oseg->add_output_section_to_nonload(hdr_os, + elfcpp::PF_R); + } + + this->eh_frame_data_->set_eh_frame_hdr(hdr_posd); + } + } + } + + return os; +} + +// Add an exception frame for a PLT. This is called from target code. + +void +Layout::add_eh_frame_for_plt(Output_data* plt, const unsigned char* cie_data, + size_t cie_length, const unsigned char* fde_data, + size_t fde_length) +{ + if (parameters->incremental()) + { + // FIXME: Maybe this could work some day.... + return; + } + Output_section* os = this->make_eh_frame_section(NULL); + if (os == NULL) + return; + this->eh_frame_data_->add_ehframe_for_plt(plt, cie_data, cie_length, + fde_data, fde_length); + if (!this->added_eh_frame_data_) + { + os->add_output_section_data(this->eh_frame_data_); + this->added_eh_frame_data_ = true; + } +} + // Add POSD to an output section using NAME, TYPE, and FLAGS. Return // the output section. diff --git a/gold/layout.h b/gold/layout.h index 4790584436e..a16bb5ca6b6 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -549,6 +549,14 @@ class Layout unsigned int reloc_shndx, unsigned int reloc_type, off_t* offset); + // Add .eh_frame information for a PLT. The FDE must start with a + // 4-byte PC-relative reference to the start of the PLT, followed by + // a 4-byte size of PLT. + void + add_eh_frame_for_plt(Output_data* plt, const unsigned char* cie_data, + size_t cie_length, const unsigned char* fde_data, + size_t fde_length); + // Handle a GNU stack note. This is called once per input object // file. SEEN_GNU_STACK is true if the object file has a // .note.GNU-stack section. GNU_STACK_FLAGS is the section flags @@ -1018,6 +1026,10 @@ class Layout void attach_allocated_section_to_segment(Output_section*); + // Make the .eh_frame section. + Output_section* + make_eh_frame_section(const Relobj*); + // Set the final file offsets of all the segments. off_t set_segment_offsets(const Target*, Output_segment*, unsigned int* pshndx); diff --git a/gold/merge.cc b/gold/merge.cc index 4fcbc1092a9..093b6fc692f 100644 --- a/gold/merge.cc +++ b/gold/merge.cc @@ -1,6 +1,6 @@ // merge.cc -- handle section merging for gold -// Copyright 2006, 2007, 2008, 2010 Free Software Foundation, Inc. +// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. // Written by Ian Lance Taylor . // This file is part of gold. @@ -242,6 +242,7 @@ Merge_map::add_mapping(Relobj* object, unsigned int shndx, section_offset_type offset, section_size_type length, section_offset_type output_offset) { + gold_assert(object != NULL); Object_merge_map* object_merge_map = object->merge_map(); if (object_merge_map == NULL) { diff --git a/gold/options.h b/gold/options.h index da4b6cfa8a0..998e8505b31 100644 --- a/gold/options.h +++ b/gold/options.h @@ -820,6 +820,10 @@ class General_options N_("Keep files mapped across passes (default)"), N_("Release mapped files after each pass")); + DEFINE_bool(ld_generated_unwind_info, options::TWO_DASHES, '\0', true, + N_("Generate unwind information for PLT (default)"), + N_("Do not generate unwind information for PLT")); + DEFINE_special(library, options::TWO_DASHES, 'l', N_("Search for library LIBNAME"), N_("LIBNAME")); diff --git a/gold/x86_64.cc b/gold/x86_64.cc index de39cb42887..12a5467f3f5 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -25,6 +25,7 @@ #include #include "elfcpp.h" +#include "dwarf.h" #include "parameters.h" #include "reloc.h" #include "x86_64.h" @@ -56,7 +57,7 @@ class Output_data_plt_x86_64 : public Output_section_data Output_data_plt_x86_64(Symbol_table* symtab, Layout* layout, Output_data_got<64, false>* got, Output_data_space* got_plt) - : Output_section_data(8), tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt), + : Output_section_data(16), tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt), count_(0), tlsdesc_got_offset_(-1U), free_list_() { this->init(symtab, layout); } @@ -64,7 +65,7 @@ class Output_data_plt_x86_64 : public Output_section_data Output_data_got<64, false>* got, Output_data_space* got_plt, unsigned int plt_count) - : Output_section_data((plt_count + 1) * plt_entry_size, 8, false), + : Output_section_data((plt_count + 1) * plt_entry_size, 16, false), tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt), count_(plt_count), tlsdesc_got_offset_(-1U), free_list_() { @@ -160,13 +161,19 @@ class Output_data_plt_x86_64 : public Output_section_data // The first entry in the PLT. // From the AMD64 ABI: "Unlike Intel386 ABI, this ABI uses the same // procedure linkage table for both programs and shared objects." - static unsigned char first_plt_entry[plt_entry_size]; + static const unsigned char first_plt_entry[plt_entry_size]; // Other entries in the PLT for an executable. - static unsigned char plt_entry[plt_entry_size]; + static const unsigned char plt_entry[plt_entry_size]; // The reserved TLSDESC entry in the PLT for an executable. - static unsigned char tlsdesc_plt_entry[plt_entry_size]; + static const unsigned char tlsdesc_plt_entry[plt_entry_size]; + + // The .eh_frame unwind information for the PLT. + static const int plt_eh_frame_cie_size = 16; + static const int plt_eh_frame_fde_size = 32; + static const unsigned char plt_eh_frame_cie[plt_eh_frame_cie_size]; + static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size]; // Set the final size. void @@ -871,6 +878,11 @@ Output_data_plt_x86_64::init(Symbol_table* symtab, Layout* layout) elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, 0, true, true); } + + // Add unwind information if requested. + if (parameters->options().ld_generated_unwind_info()) + layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size, + plt_eh_frame_fde, plt_eh_frame_fde_size); } void @@ -1004,7 +1016,7 @@ Output_data_plt_x86_64::set_final_data_size() // The first entry in the PLT for an executable. -unsigned char Output_data_plt_x86_64::first_plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_x86_64::first_plt_entry[plt_entry_size] = { // From AMD64 ABI Draft 0.98, page 76 0xff, 0x35, // pushq contents of memory address @@ -1016,7 +1028,7 @@ unsigned char Output_data_plt_x86_64::first_plt_entry[plt_entry_size] = // Subsequent entries in the PLT for an executable. -unsigned char Output_data_plt_x86_64::plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_x86_64::plt_entry[plt_entry_size] = { // From AMD64 ABI Draft 0.98, page 76 0xff, 0x25, // jmpq indirect @@ -1029,7 +1041,7 @@ unsigned char Output_data_plt_x86_64::plt_entry[plt_entry_size] = // The reserved TLSDESC entry in the PLT for an executable. -unsigned char Output_data_plt_x86_64::tlsdesc_plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_x86_64::tlsdesc_plt_entry[plt_entry_size] = { // From Alexandre Oliva, "Thread-Local Storage Descriptors for IA32 // and AMD64/EM64T", Version 0.9.4 (2005-10-10). @@ -1041,6 +1053,54 @@ unsigned char Output_data_plt_x86_64::tlsdesc_plt_entry[plt_entry_size] = 0x40, 0 }; +// The .eh_frame unwind information for the PLT. + +const unsigned char +Output_data_plt_x86_64::plt_eh_frame_cie[plt_eh_frame_cie_size] = +{ + 1, // CIE version. + 'z', // Augmentation: augmentation size included. + 'R', // Augmentation: FDE encoding included. + '\0', // End of augmentation string. + 1, // Code alignment factor. + 0x78, // Data alignment factor. + 16, // Return address column. + 1, // Augmentation size. + (elfcpp::DW_EH_PE_pcrel // FDE encoding. + | elfcpp::DW_EH_PE_sdata4), + elfcpp::DW_CFA_def_cfa, 7, 8, // DW_CFA_def_cfa: r7 (rsp) ofs 8. + elfcpp::DW_CFA_offset + 16, 1,// DW_CFA_offset: r16 (rip) at cfa-8. + elfcpp::DW_CFA_nop, // Align to 16 bytes. + elfcpp::DW_CFA_nop +}; + +const unsigned char +Output_data_plt_x86_64::plt_eh_frame_fde[plt_eh_frame_fde_size] = +{ + 0, 0, 0, 0, // Replaced with offset to .plt. + 0, 0, 0, 0, // Replaced with size of .plt. + 0, // Augmentation size. + elfcpp::DW_CFA_def_cfa_offset, 16, // DW_CFA_def_cfa_offset: 16. + elfcpp::DW_CFA_advance_loc + 6, // Advance 6 to __PLT__ + 6. + elfcpp::DW_CFA_def_cfa_offset, 24, // DW_CFA_def_cfa_offset: 24. + elfcpp::DW_CFA_advance_loc + 10, // Advance 10 to __PLT__ + 16. + elfcpp::DW_CFA_def_cfa_expression, // DW_CFA_def_cfa_expression. + 11, // Block length. + elfcpp::DW_OP_breg7, 8, // Push %rsp + 8. + elfcpp::DW_OP_breg16, 0, // Push %rip. + elfcpp::DW_OP_lit15, // Push 0xf. + elfcpp::DW_OP_and, // & (%rip & 0xf). + elfcpp::DW_OP_lit11, // Push 0xb. + elfcpp::DW_OP_ge, // >= ((%rip & 0xf) >= 0xb) + elfcpp::DW_OP_lit3, // Push 3. + elfcpp::DW_OP_shl, // << (((%rip & 0xf) >= 0xb) << 3) + elfcpp::DW_OP_plus, // + ((((%rip&0xf)>=0xb)<<3)+%rsp+8 + elfcpp::DW_CFA_nop, // Align to 32 bytes. + elfcpp::DW_CFA_nop, + elfcpp::DW_CFA_nop, + elfcpp::DW_CFA_nop +}; + // Write out the PLT. This uses the hand-coded instructions above, // and adjusts them as needed. This is specified by the AMD64 ABI.