aboutsummaryrefslogtreecommitdiff
path: root/gold
diff options
context:
space:
mode:
Diffstat (limited to 'gold')
-rw-r--r--gold/ChangeLog39
-rw-r--r--gold/ehframe.cc71
-rw-r--r--gold/ehframe.h88
-rw-r--r--gold/i386.cc78
-rw-r--r--gold/layout.cc127
-rw-r--r--gold/layout.h12
-rw-r--r--gold/merge.cc3
-rw-r--r--gold/options.h4
-rw-r--r--gold/x86_64.cc76
9 files changed, 400 insertions, 98 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog
index 62ee3ca..8b8112d 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,3 +1,42 @@
+2011-07-01 Ian Lance Taylor <iant@google.com>
+
+ 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 <iant@google.com>
PR gold/12629
diff --git a/gold/ehframe.cc b/gold/ehframe.cc
index fbeb8a3..80b0035 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 <iant@google.com>.
// 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<int size, bool big_endian>
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<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)
+ 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<int size, bool big_endian>
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<Fde*>::const_iterator p = this->fdes_.begin();
p != this->fdes_.end();
++p)
- offset = (*p)->write<size, big_endian>(oview, offset, addralign,
+ offset = (*p)->write<size, big_endian>(oview, offset, address, addralign,
cie_offset, fde_encoding,
eh_frame_hdr);
@@ -994,6 +1019,29 @@ Eh_frame::read_fde(Sized_relobj_file<size, big_endian>* 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<int size, bool big_endian>
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<size, big_endian>(oview, o, addralign,
+ o = (*p)->write<size, big_endian>(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<size, big_endian>(oview, o, addralign,
+ o = (*p)->write<size, big_endian>(oview, o, address, addralign,
this->eh_frame_hdr_);
}
diff --git a/gold/ehframe.h b/gold/ehframe.h
index f626b1f..c3f82e9 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 <iant@google.com>.
// 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<const char*>(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<const char*>(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<int size, bool big_endian>
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<int size, bool big_endian>
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 beec4a8..636dfbb 100644
--- a/gold/i386.cc
+++ b/gold/i386.cc
@@ -25,6 +25,7 @@
#include <cstring>
#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 3152622..11dbbef 100644
--- a/gold/layout.cc
+++ b/gold/layout.cc
@@ -1136,51 +1136,10 @@ Layout::layout_eh_frame(Sized_relobj_file<size, big_endian>* 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<size, big_endian>* 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<size, big_endian>* 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 4790584..a16bb5c 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 4fcbc10..093b6fc 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 <iant@google.com>.
// 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 da4b6cf..998e850 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 de39cb4..12a5467 100644
--- a/gold/x86_64.cc
+++ b/gold/x86_64.cc
@@ -25,6 +25,7 @@
#include <cstring>
#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.