aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2007-11-09 07:00:15 +0000
committerIan Lance Taylor <iant@google.com>2007-11-09 07:00:15 +0000
commit730cdc88f70c0804b5daf2259d3bd8ad29b6411b (patch)
treebdc5f06993a5579fea47d658ecb900c9586727f2
parent0abe36f50df0f2475fec735f5c907bb7af584ab0 (diff)
downloadgdb-730cdc88f70c0804b5daf2259d3bd8ad29b6411b.zip
gdb-730cdc88f70c0804b5daf2259d3bd8ad29b6411b.tar.gz
gdb-730cdc88f70c0804b5daf2259d3bd8ad29b6411b.tar.bz2
Generate a complete exception frame header. Discard duplicate
exception frame information.
-rw-r--r--gold/dynobj.cc2
-rw-r--r--gold/dynobj.h5
-rw-r--r--gold/ehframe.cc1047
-rw-r--r--gold/ehframe.h359
-rw-r--r--gold/gold.cc24
-rw-r--r--gold/i386.cc21
-rw-r--r--gold/layout.cc278
-rw-r--r--gold/layout.h130
-rw-r--r--gold/merge.cc85
-rw-r--r--gold/merge.h92
-rw-r--r--gold/object.cc210
-rw-r--r--gold/object.h120
-rw-r--r--gold/output.cc153
-rw-r--r--gold/output.h119
-rw-r--r--gold/po/gold.pot155
-rw-r--r--gold/reloc.cc218
-rw-r--r--gold/reloc.h58
-rw-r--r--gold/stringpool.cc2
-rw-r--r--gold/symtab.cc12
-rw-r--r--gold/symtab.h2
-rw-r--r--gold/target-reloc.h44
-rw-r--r--gold/target.h31
-rw-r--r--gold/testsuite/testfile.cc9
-rw-r--r--gold/x86_64.cc21
24 files changed, 2830 insertions, 367 deletions
diff --git a/gold/dynobj.cc b/gold/dynobj.cc
index 95ffe08..9845194 100644
--- a/gold/dynobj.cc
+++ b/gold/dynobj.cc
@@ -288,6 +288,7 @@ Sized_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
sd->symbols = NULL;
sd->symbols_size = 0;
+ sd->external_symbols_offset = 0;
sd->symbol_names = NULL;
sd->symbol_names_size = 0;
@@ -606,6 +607,7 @@ Sized_dynobj<size, big_endian>::do_add_symbols(Symbol_table* symtab,
const int sym_size = This::sym_size;
const size_t symcount = sd->symbols_size / sym_size;
+ gold_assert(sd->external_symbols_offset == 0);
if (static_cast<off_t>(symcount * sym_size) != sd->symbols_size)
{
this->error(_("size of dynamic symbols is not multiple of symbol size"));
diff --git a/gold/dynobj.h b/gold/dynobj.h
index d9c3e10..7895dda 100644
--- a/gold/dynobj.h
+++ b/gold/dynobj.h
@@ -146,6 +146,11 @@ class Sized_dynobj : public Dynobj
do_section_flags(unsigned int shndx)
{ return this->elf_file_.section_flags(shndx); }
+ // Return section type.
+ unsigned int
+ do_section_type(unsigned int shndx)
+ { return this->elf_file_.section_type(shndx); }
+
// Return the section link field.
unsigned int
do_section_link(unsigned int shndx)
diff --git a/gold/ehframe.cc b/gold/ehframe.cc
index 607654b..1c7b714 100644
--- a/gold/ehframe.cc
+++ b/gold/ehframe.cc
@@ -22,8 +22,13 @@
#include "gold.h"
+#include <cstring>
+#include <algorithm>
+
#include "elfcpp.h"
#include "dwarf.h"
+#include "symtab.h"
+#include "reloc.h"
#include "ehframe.h"
namespace gold
@@ -31,7 +36,8 @@ namespace gold
// This file handles generation of the exception frame header that
// gcc's runtime support libraries use to find unwind information at
-// runtime.
+// runtime. This file also handles discarding duplicate exception
+// frame information.
// The exception frame header starts with four bytes:
@@ -68,16 +74,17 @@ namespace gold
// the start of the exception frame header information. The entries
// are in sorted order by starting PC.
-// FIXME: We currently always generate an empty exception frame
-// header.
-
const int eh_frame_hdr_size = 4;
// Construct the exception frame header.
-Eh_frame_hdr::Eh_frame_hdr(Output_section* eh_frame_section)
+Eh_frame_hdr::Eh_frame_hdr(Output_section* eh_frame_section,
+ const Eh_frame* eh_frame_data)
: Output_section_data(4),
- eh_frame_section_(eh_frame_section)
+ eh_frame_section_(eh_frame_section),
+ eh_frame_data_(eh_frame_data),
+ fde_offsets_(),
+ any_unrecognized_eh_frame_sections_(false)
{
}
@@ -86,7 +93,15 @@ Eh_frame_hdr::Eh_frame_hdr(Output_section* eh_frame_section)
void
Eh_frame_hdr::do_set_address(uint64_t, off_t)
{
- this->set_data_size(eh_frame_hdr_size + 4);
+ unsigned int data_size = eh_frame_hdr_size + 4;
+ if (!this->any_unrecognized_eh_frame_sections_)
+ {
+ unsigned int fde_count = this->eh_frame_data_->fde_count();
+ if (fde_count != 0)
+ data_size += 4 + 8 * fde_count;
+ this->fde_offsets_.reserve(fde_count);
+ }
+ this->set_data_size(data_size);
}
// Write the data to the flie.
@@ -94,6 +109,54 @@ Eh_frame_hdr::do_set_address(uint64_t, off_t)
void
Eh_frame_hdr::do_write(Output_file* of)
{
+ if (parameters->get_size() == 32)
+ {
+ if (!parameters->is_big_endian())
+ {
+#ifdef HAVE_TARGET_32_LITTLE
+ this->do_sized_write<32, false>(of);
+#else
+ gold_unreachable();
+#endif
+ }
+ else
+ {
+#ifdef HAVE_TARGET_32_BIG
+ this->do_sized_write<32, true>(of);
+#else
+ gold_unreachable();
+#endif
+ }
+ }
+ else if (parameters->get_size() == 64)
+ {
+ if (!parameters->is_big_endian())
+ {
+#ifdef HAVE_TARGET_64_LITTLE
+ this->do_sized_write<64, false>(of);
+#else
+ gold_unreachable();
+#endif
+ }
+ else
+ {
+#ifdef HAVE_TARGET_64_BIG
+ this->do_sized_write<64, true>(of);
+#else
+ gold_unreachable();
+#endif
+ }
+ }
+ else
+ gold_unreachable();
+}
+
+// Write the data to the file with the right endianness.
+
+template<int size, bool big_endian>
+void
+Eh_frame_hdr::do_sized_write(Output_file* of)
+{
const off_t off = this->offset();
const off_t oview_size = this->data_size();
unsigned char* const oview = of->get_output_view(off, oview_size);
@@ -108,18 +171,974 @@ Eh_frame_hdr::do_write(Output_file* of)
uint64_t eh_frame_hdr_address = this->address();
uint64_t eh_frame_offset = (eh_frame_address -
(eh_frame_hdr_address + 4));
- if (parameters->is_big_endian())
- elfcpp::Swap<32, true>::writeval(oview + 4, eh_frame_offset);
+ elfcpp::Swap<32, big_endian>::writeval(oview + 4, eh_frame_offset);
+
+ if (this->any_unrecognized_eh_frame_sections_
+ || this->fde_offsets_.empty())
+ {
+ // There are no FDEs, or we didn't recognize the format of the
+ // some of the .eh_frame sections, so we can't write out the
+ // sorted table.
+ oview[2] = elfcpp::DW_EH_PE_omit;
+ oview[3] = elfcpp::DW_EH_PE_omit;
+
+ gold_assert(oview_size == 8);
+ }
else
- elfcpp::Swap<32, false>::writeval(oview + 4, eh_frame_offset);
+ {
+ oview[2] = elfcpp::DW_EH_PE_udata4;
+ oview[3] = elfcpp::DW_EH_PE_datarel | elfcpp::DW_EH_PE_sdata4;
+
+ elfcpp::Swap<32, big_endian>::writeval(oview + 8,
+ this->fde_offsets_.size());
+
+ // We have the offsets of the FDEs in the .eh_frame section. We
+ // couldn't easily get the PC values before, as they depend on
+ // relocations which are, of course, target specific. This code
+ // is run after all those relocations have been applied to the
+ // output file. Here we read the output file again to find the
+ // PC values. Then we sort the list and write it out.
- // We don't currently write out the sorted table.
- oview[2] = elfcpp::DW_EH_PE_omit;
- oview[3] = elfcpp::DW_EH_PE_omit;
+ Fde_addresses<size> fde_addresses(this->fde_offsets_.size());
+ this->get_fde_addresses<size, big_endian>(of, &this->fde_offsets_,
+ &fde_addresses);
- gold_assert(oview_size == 8);
+ std::sort(fde_addresses.begin(), fde_addresses.end(),
+ Fde_address_compare<size>());
+
+ typename elfcpp::Elf_types<size>::Elf_Addr output_address;
+ output_address = this->address();
+
+ unsigned char* pfde = oview + 12;
+ for (typename Fde_addresses<size>::iterator p = fde_addresses.begin();
+ p != fde_addresses.end();
+ ++p)
+ {
+ elfcpp::Swap<32, big_endian>::writeval(pfde,
+ p->first - output_address);
+ elfcpp::Swap<32, big_endian>::writeval(pfde + 4,
+ p->second - output_address);
+ pfde += 8;
+ }
+
+ gold_assert(pfde - oview == oview_size);
+ }
of->write_output_view(off, oview_size, oview);
}
+// Given the offset FDE_OFFSET of an FDE in the .eh_frame section, and
+// the contents of the .eh_frame section EH_FRAME_CONTENTS, where the
+// FDE's encoding is FDE_ENCODING, return the output address of the
+// FDE's PC.
+
+template<int size, bool big_endian>
+typename elfcpp::Elf_types<size>::Elf_Addr
+Eh_frame_hdr::get_fde_pc(const unsigned char* eh_frame_contents,
+ off_t fde_offset, unsigned char fde_encoding)
+{
+ // The FDE starts with a 4 byte length and a 4 byte offset to the
+ // CIE. The PC follows.
+ const unsigned char* p = eh_frame_contents + fde_offset + 8;
+
+ typename elfcpp::Elf_types<size>::Elf_Addr pc;
+ bool is_signed = (fde_encoding & elfcpp::DW_EH_PE_signed) != 0;
+ int pc_size = fde_encoding & 7;
+ if (pc_size == elfcpp::DW_EH_PE_absptr)
+ {
+ if (size == 32)
+ pc_size = elfcpp::DW_EH_PE_udata4;
+ else if (size == 64)
+ pc_size = elfcpp::DW_EH_PE_udata8;
+ else
+ gold_unreachable();
+ }
+
+ switch (pc_size)
+ {
+ case elfcpp::DW_EH_PE_udata2:
+ pc = elfcpp::Swap<16, big_endian>::readval(p);
+ if (is_signed)
+ pc = (pc ^ 0x8000) - 0x8000;
+ break;
+
+ case elfcpp::DW_EH_PE_udata4:
+ pc = elfcpp::Swap<32, big_endian>::readval(p);
+ if (size > 32 && is_signed)
+ pc = (pc ^ 0x80000000) - 0x80000000;
+ break;
+
+ case elfcpp::DW_EH_PE_udata8:
+ gold_assert(size == 64);
+ pc = elfcpp::Swap_unaligned<64, big_endian>::readval(p);
+ break;
+
+ default:
+ gold_unreachable();
+ }
+
+ return pc;
+}
+
+// Given an array of FDE offsets in the .eh_frame section, return an
+// array of offsets from the exception frame header to the FDE's
+// output PC and to the output address of the FDE itself. We get the
+// FDE's PC by actually looking in the .eh_frame section we just wrote
+// to the output file.
+
+template<int size, bool big_endian>
+void
+Eh_frame_hdr::get_fde_addresses(Output_file* of,
+ const Fde_offsets* fde_offsets,
+ Fde_addresses<size>* fde_addresses)
+{
+ typename elfcpp::Elf_types<size>::Elf_Addr eh_frame_address;
+ eh_frame_address = this->eh_frame_section_->address();
+ off_t eh_frame_offset = this->eh_frame_section_->offset();
+ off_t eh_frame_size = this->eh_frame_section_->data_size();
+ const unsigned char* eh_frame_contents = of->get_input_view(eh_frame_offset,
+ eh_frame_size);
+
+ for (Fde_offsets::const_iterator p = fde_offsets->begin();
+ p != fde_offsets->end();
+ ++p)
+ {
+ typename elfcpp::Elf_types<size>::Elf_Addr fde_pc;
+ fde_pc = this->get_fde_pc<size, big_endian>(eh_frame_contents,
+ p->first, p->second);
+ fde_addresses->push_back(fde_pc, eh_frame_address + p->first);
+ }
+
+ of->free_input_view(eh_frame_offset, eh_frame_size, eh_frame_contents);
+}
+
+// Class Fde.
+
+// 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. Record the FDE pc for EH_FRAME_HDR. Return the new offset.
+
+template<int size, bool big_endian>
+off_t
+Fde::write(unsigned char* oview, off_t offset, off_t cie_offset,
+ unsigned char fde_encoding, Eh_frame_hdr* eh_frame_hdr)
+{
+ size_t length = this->contents_.length();
+
+ // Write the length of the FDE as a 32-bit word. The length word
+ // does not include the four bytes of the length word itself, but it
+ // does include the offset to the CIE.
+ elfcpp::Swap<32, big_endian>::writeval(oview + offset,
+ length + 4);
+
+ // Write the offset to the CIE as a 32-bit word. This is the
+ // difference between the address of the offset word itself and the
+ // CIE address.
+ elfcpp::Swap<32, big_endian>::writeval(oview + offset + 4,
+ offset + 4 - cie_offset);
+
+ // Copy the rest of the FDE. Note that this is run before
+ // relocation processing is done on this section, so the relocations
+ // will later be applied to the FDE data.
+ memcpy(oview + offset + 8, this->contents_.data(), length);
+
+ // Tell the exception frame header about this FDE.
+ if (eh_frame_hdr != NULL)
+ eh_frame_hdr->record_fde(offset, fde_encoding);
+
+ return offset + length + 8;
+}
+
+// Class Cie.
+
+// Destructor.
+
+Cie::~Cie()
+{
+ for (std::vector<Fde*>::iterator p = this->fdes_.begin();
+ p != this->fdes_.end();
+ ++p)
+ delete *p;
+}
+
+// Set the output offset of a CIE. Return the new output offset.
+
+off_t
+Cie::set_output_offset(off_t output_offset, unsigned int addralign,
+ Merge_map* merge_map)
+{
+ size_t length = this->contents_.length();
+ gold_assert((length & (addralign - 1)) == 0);
+ // 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);
+
+ for (std::vector<Fde*>::const_iterator p = this->fdes_.begin();
+ p != this->fdes_.end();
+ ++p)
+ {
+ (*p)->add_mapping(output_offset + length, merge_map);
+
+ size_t fde_length = (*p)->length();
+ gold_assert((fde_length & (addralign - 1)) == 0);
+ length += fde_length;
+ }
+
+ return output_offset + length;
+}
+
+// Write the CIE to OVIEW starting at OFFSET. EH_FRAME_HDR is for FDE
+// recording. Return the new offset.
+
+template<int size, bool big_endian>
+off_t
+Cie::write(unsigned char* oview, off_t offset, Eh_frame_hdr* eh_frame_hdr)
+{
+ off_t cie_offset = offset;
+
+ size_t length = this->contents_.length();
+
+ // Write the length of the CIE as a 32-bit word. The length word
+ // does not include the four bytes of the length word itself.
+ elfcpp::Swap<32, big_endian>::writeval(oview + offset, length + 4);
+
+ // Write the tag which marks this as a CIE: a 32-bit zero.
+ elfcpp::Swap<32, big_endian>::writeval(oview + offset + 4, 0);
+
+ // Write out the CIE data.
+ memcpy(oview + offset + 8, this->contents_.data(), length);
+ offset += length + 8;
+
+ // Write out the associated FDEs.
+ unsigned char fde_encoding = this->fde_encoding_;
+ for (std::vector<Fde*>::const_iterator p = this->fdes_.begin();
+ p != this->fdes_.end();
+ ++p)
+ offset = (*p)->write<size, big_endian>(oview, offset, cie_offset,
+ fde_encoding, eh_frame_hdr);
+
+ return offset;
+}
+
+// We track all the CIEs we see, and merge them when possible. This
+// works because each FDE holds an offset to the relevant CIE: we
+// rewrite the FDEs to point to the merged CIE. This is worthwhile
+// because in a typical C++ program many FDEs in many different object
+// files will use the same CIE.
+
+// An equality operator for Cie.
+
+bool
+operator==(const Cie& cie1, const Cie& cie2)
+{
+ return (cie1.personality_name_ == cie2.personality_name_
+ && cie1.contents_ == cie2.contents_);
+}
+
+// A less-than operator for Cie.
+
+bool
+operator<(const Cie& cie1, const Cie& cie2)
+{
+ if (cie1.personality_name_ != cie2.personality_name_)
+ return cie1.personality_name_ < cie2.personality_name_;
+ return cie1.contents_ < cie2.contents_;
+}
+
+// Class Eh_frame.
+
+Eh_frame::Eh_frame()
+ : Output_section_data(Output_data::default_alignment()),
+ eh_frame_hdr_(NULL),
+ cie_offsets_(),
+ unmergeable_cie_offsets_(),
+ merge_map_()
+{
+}
+
+// Skip an LEB128, updating *PP to point to the next character.
+// Return false if we ran off the end of the string.
+
+bool
+Eh_frame::skip_leb128(const unsigned char** pp, const unsigned char* pend)
+{
+ const unsigned char* p;
+ for (p = *pp; p < pend; ++p)
+ {
+ if ((*p & 0x80) == 0)
+ {
+ *pp = p + 1;
+ return true;
+ }
+ }
+ return false;
+}
+
+// Add input section SHNDX in OBJECT to an exception frame section.
+// SYMBOLS is the contents of the symbol table section (size
+// SYMBOLS_SIZE), SYMBOL_NAMES is the symbol names section (size
+// SYMBOL_NAMES_SIZE). RELOC_SHNDX is the index of a relocation
+// section applying to SHNDX, or 0 if none, or -1U if more than one.
+// RELOC_TYPE is the type of the reloc section if there is one, either
+// SHT_REL or SHT_RELA. We try to parse the input exception frame
+// data into our data structures. If we can't do it, we return false
+// to mean that the section should be handled as a normal input
+// section.
+
+template<int size, bool big_endian>
+bool
+Eh_frame::add_ehframe_input_section(
+ Sized_relobj<size, big_endian>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type)
+{
+ // Get the section contents.
+ off_t contents_len;
+ const unsigned char* pcontents = object->section_contents(shndx,
+ &contents_len,
+ false);
+ if (contents_len == 0)
+ return false;
+
+ // If this is the marker section for the end of the data, then
+ // return false to force it to be handled as an ordinary input
+ // section. If we don't do this, we won't correctly handle the case
+ // of unrecognized .eh_frame sections.
+ if (contents_len == 4
+ && elfcpp::Swap<32, big_endian>::readval(pcontents) == 0)
+ return false;
+
+ New_cies new_cies;
+ if (!this->do_add_ehframe_input_section(object, symbols, symbols_size,
+ symbol_names, symbol_names_size,
+ shndx, reloc_shndx,
+ reloc_type, pcontents,
+ contents_len, &new_cies))
+ {
+ this->eh_frame_hdr_->found_unrecognized_eh_frame_section();
+
+ for (New_cies::iterator p = new_cies.begin();
+ p != new_cies.end();
+ ++p)
+ delete p->first;
+
+ return false;
+ }
+
+ // Now that we know we are using this section, record any new CIEs
+ // that we found.
+ for (New_cies::const_iterator p = new_cies.begin();
+ p != new_cies.end();
+ ++p)
+ {
+ uint64_t zero = 0;
+ if (p->second)
+ this->cie_offsets_.insert(std::make_pair(p->first, zero));
+ else
+ this->unmergeable_cie_offsets_.push_back(std::make_pair(p->first,
+ zero));
+ }
+
+ return true;
+}
+
+// The bulk of the implementation of add_ehframe_input_section.
+
+template<int size, bool big_endian>
+bool
+Eh_frame::do_add_ehframe_input_section(
+ Sized_relobj<size, big_endian>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type,
+ const unsigned char* pcontents,
+ off_t contents_len,
+ New_cies* new_cies)
+{
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+ Track_relocs<size, big_endian> relocs;
+
+ const unsigned char* p = pcontents;
+ const unsigned char* pend = p + contents_len;
+
+ // Get the contents of the reloc section if any.
+ if (!relocs.initialize(object, reloc_shndx, reloc_type))
+ return false;
+
+ // Keep track of which CIEs are at which offsets.
+ Offsets_to_cie cies;
+
+ while (p < pend)
+ {
+ if (pend - p < 4)
+ return false;
+
+ // There shouldn't be any relocations here.
+ if (relocs.advance(p + 4 - pcontents) > 0)
+ return false;
+
+ unsigned int len = elfcpp::Swap<32, big_endian>::readval(p);
+ p += 4;
+ if (len == 0)
+ {
+ // We should only find a zero-length entry at the end of the
+ // section.
+ if (p < pend)
+ return false;
+ break;
+ }
+ // We don't support a 64-bit .eh_frame.
+ if (len == 0xffffffff)
+ return false;
+ if (static_cast<unsigned int>(pend - p) < len)
+ return false;
+
+ const unsigned char* const pentend = p + len;
+
+ if (pend - p < 4)
+ return false;
+ if (relocs.advance(p + 4 - pcontents) > 0)
+ return false;
+
+ unsigned int id = elfcpp::Swap<32, big_endian>::readval(p);
+ p += 4;
+
+ if (id == 0)
+ {
+ // CIE.
+ if (!this->read_cie(object, shndx, symbols, symbols_size,
+ symbol_names, symbol_names_size,
+ pcontents, p, pentend, &relocs, &cies,
+ new_cies))
+ return false;
+ }
+ else
+ {
+ // FDE.
+ if (!this->read_fde(object, shndx, symbols, symbols_size,
+ pcontents, id, p, pentend, &relocs, &cies))
+ return false;
+ }
+
+ p = pentend;
+ }
+
+ return true;
+}
+
+// Read a CIE. Return false if we can't parse the information.
+
+template<int size, bool big_endian>
+bool
+Eh_frame::read_cie(Sized_relobj<size, big_endian>* object,
+ unsigned int shndx,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ const unsigned char* pcontents,
+ const unsigned char* pcie,
+ const unsigned char *pcieend,
+ Track_relocs<size, big_endian>* relocs,
+ Offsets_to_cie* cies,
+ New_cies* new_cies)
+{
+ bool mergeable = true;
+
+ // We need to find the personality routine if there is one, since we
+ // can only merge CIEs which use the same routine. We also need to
+ // find the FDE encoding if there is one, so that we can read the PC
+ // from the FDE.
+
+ const unsigned char* p = pcie;
+
+ if (pcieend - p < 1)
+ return false;
+ unsigned char version = *p++;
+ if (version != 1 && version != 3)
+ return false;
+
+ const unsigned char* paug = p;
+ const void* paugendv = memchr(p, '\0', pcieend - p);
+ const unsigned char* paugend = static_cast<const unsigned char*>(paugendv);
+ if (paugend == NULL)
+ return false;
+ p = paugend + 1;
+
+ if (paug[0] == 'e' && paug[1] == 'h')
+ {
+ // This is a CIE from gcc before version 3.0. We can't merge
+ // these. We can still read the FDEs.
+ mergeable = false;
+ paug += 2;
+ if (*paug != '\0')
+ return false;
+ if (pcieend - p < size / 8)
+ return false;
+ p += size / 8;
+ }
+
+ // Skip the code alignment.
+ if (!skip_leb128(&p, pcieend))
+ return false;
+
+ // Skip the data alignment.
+ if (!skip_leb128(&p, pcieend))
+ return false;
+
+ // Skip the return column.
+ if (version == 1)
+ {
+ if (pcieend - p < 1)
+ return false;
+ ++p;
+ }
+ else
+ {
+ if (!skip_leb128(&p, pcieend))
+ return false;
+ }
+
+ if (*paug == 'z')
+ {
+ ++paug;
+ // Skip the augmentation size.
+ if (!skip_leb128(&p, pcieend))
+ return false;
+ }
+
+ unsigned char fde_encoding = elfcpp::DW_EH_PE_absptr;
+ int per_offset = -1;
+ while (*paug != '\0')
+ {
+ switch (*paug)
+ {
+ case 'L': // LSDA encoding.
+ if (pcieend - p < 1)
+ return false;
+ ++p;
+ break;
+
+ case 'R': // FDE encoding.
+ if (pcieend - p < 1)
+ return false;
+ fde_encoding = *p;
+ switch (fde_encoding & 7)
+ {
+ case elfcpp::DW_EH_PE_absptr:
+ case elfcpp::DW_EH_PE_udata2:
+ case elfcpp::DW_EH_PE_udata4:
+ case elfcpp::DW_EH_PE_udata8:
+ break;
+ default:
+ return false;
+ }
+ ++p;
+ break;
+
+ case 'S':
+ break;
+
+ case 'P':
+ // Personality encoding.
+ {
+ if (pcieend - p < 1)
+ return false;
+ unsigned char per_encoding = *p;
+ ++p;
+
+ if ((per_encoding & 0x60) == 0x60)
+ return false;
+ unsigned int per_width;
+ switch (per_encoding & 7)
+ {
+ case elfcpp::DW_EH_PE_udata2:
+ per_width = 2;
+ break;
+ case elfcpp::DW_EH_PE_udata4:
+ per_width = 4;
+ break;
+ case elfcpp::DW_EH_PE_udata8:
+ per_width = 8;
+ break;
+ case elfcpp::DW_EH_PE_absptr:
+ per_width = size / 8;
+ break;
+ default:
+ return false;
+ }
+
+ if ((per_encoding & 0xf0) == elfcpp::DW_EH_PE_aligned)
+ {
+ unsigned int len = p - pcie;
+ len += per_width - 1;
+ len &= ~ (per_width - 1);
+ if (static_cast<unsigned int>(pcieend - p) < len)
+ return false;
+ p += len;
+ }
+
+ per_offset = p - pcontents;
+
+ if (static_cast<unsigned int>(pcieend - p) < per_width)
+ return false;
+ p += per_width;
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ ++paug;
+ }
+
+ const char* personality_name = "";
+ if (per_offset != -1)
+ {
+ if (relocs->advance(per_offset) > 0)
+ return false;
+ if (relocs->next_offset() != per_offset)
+ return false;
+
+ unsigned int personality_symndx = relocs->next_symndx();
+ if (personality_symndx == -1U)
+ return false;
+
+ if (personality_symndx < object->local_symbol_count())
+ {
+ // We can only merge this CIE if the personality routine is
+ // a global symbol. We can still read the FDEs.
+ mergeable = false;
+ }
+ else
+ {
+ const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
+ if (personality_symndx >= symbols_size / sym_size)
+ return false;
+ elfcpp::Sym<size, big_endian> sym(symbols
+ + (personality_symndx * sym_size));
+ unsigned int name_offset = sym.get_st_name();
+ if (name_offset >= symbol_names_size)
+ return false;
+ personality_name = (reinterpret_cast<const char*>(symbol_names)
+ + name_offset);
+ }
+
+ int r = relocs->advance(per_offset + 1);
+ gold_assert(r == 1);
+ }
+
+ if (relocs->advance(pcieend - pcontents) > 0)
+ return false;
+
+ Cie cie(object, shndx, (pcie - 8) - pcontents, fde_encoding,
+ personality_name, pcie, pcieend - pcie);
+ Cie* cie_pointer = NULL;
+ if (mergeable)
+ {
+ Cie_offsets::iterator find_cie = this->cie_offsets_.find(&cie);
+ if (find_cie != this->cie_offsets_.end())
+ cie_pointer = find_cie->first;
+ else
+ {
+ // See if we already saw this CIE in this object file.
+ for (New_cies::const_iterator pc = new_cies->begin();
+ pc != new_cies->end();
+ ++pc)
+ {
+ if (*(pc->first) == cie)
+ {
+ cie_pointer = pc->first;
+ break;
+ }
+ }
+ }
+ }
+
+ if (cie_pointer == NULL)
+ {
+ cie_pointer = new Cie(cie);
+ new_cies->push_back(std::make_pair(cie_pointer, mergeable));
+ }
+ else
+ {
+ // We are deleting this CIE. Record that in our mapping from
+ // input sections to the output section. At this point we don't
+ // know for sure that we are doing a special mapping for this
+ // input section, but that's OK--if we don't do a special
+ // mapping, nobody will ever ask for the mapping we add here.
+ this->merge_map_.add_mapping(object, shndx, (pcie - 8) - pcontents,
+ pcieend - (pcie - 8), -1);
+ }
+
+ // Record this CIE plus the offset in the input section.
+ cies->insert(std::make_pair(pcie - pcontents, cie_pointer));
+
+ return true;
+}
+
+// Read an FDE. Return false if we can't parse the information.
+
+template<int size, bool big_endian>
+bool
+Eh_frame::read_fde(Sized_relobj<size, big_endian>* object,
+ unsigned int shndx,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* pcontents,
+ unsigned int offset,
+ const unsigned char* pfde,
+ const unsigned char *pfdeend,
+ Track_relocs<size, big_endian>* relocs,
+ Offsets_to_cie* cies)
+{
+ // OFFSET is the distance between the 4 bytes before PFDE to the
+ // start of the CIE. The offset we recorded for the CIE is 8 bytes
+ // after the start of the CIE--after the length and the zero tag.
+ unsigned int cie_offset = (pfde - 4 - pcontents) - offset + 8;
+ Offsets_to_cie::const_iterator pcie = cies->find(cie_offset);
+ if (pcie == cies->end())
+ return false;
+ Cie* cie = pcie->second;
+
+ // The FDE should start with a reloc to the start of the code which
+ // it describes.
+ if (relocs->advance(pfde - pcontents) > 0)
+ return false;
+
+ if (relocs->next_offset() != pfde - pcontents)
+ return false;
+
+ unsigned int symndx = relocs->next_symndx();
+ if (symndx == -1U)
+ return false;
+
+ // There can be another reloc in the FDE, if the CIE specifies an
+ // LSDA (language specific data area). We currently don't care. We
+ // will care later if we want to optimize the LSDA from an absolute
+ // pointer to a PC relative offset when generating a shared library.
+ relocs->advance(pfdeend - pcontents);
+
+ unsigned int fde_shndx;
+ const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
+ if (symndx >= symbols_size / sym_size)
+ return false;
+ elfcpp::Sym<size, big_endian> sym(symbols + symndx * sym_size);
+ fde_shndx = sym.get_st_shndx();
+
+ if (fde_shndx != elfcpp::SHN_UNDEF
+ && fde_shndx < object->shnum()
+ && !object->is_section_included(fde_shndx))
+ {
+ // This FDE applies to a section which we are discarding. We
+ // can discard this FDE.
+ this->merge_map_.add_mapping(object, shndx, (pfde - 8) - pcontents,
+ pfdeend - (pfde - 8), -1);
+ return true;
+ }
+
+ cie->add_fde(new Fde(object, shndx, (pfde - 8) - pcontents,
+ pfde, pfdeend - pfde));
+
+ return true;
+}
+
+// Return the number of FDEs.
+
+unsigned int
+Eh_frame::fde_count() const
+{
+ unsigned int ret = 0;
+ for (Unmergeable_cie_offsets::const_iterator p =
+ this->unmergeable_cie_offsets_.begin();
+ p != this->unmergeable_cie_offsets_.end();
+ ++p)
+ ret += p->first->fde_count();
+ for (Cie_offsets::const_iterator p = this->cie_offsets_.begin();
+ p != this->cie_offsets_.end();
+ ++p)
+ ret += p->first->fde_count();
+ return ret;
+}
+
+// Set the final data size.
+
+void
+Eh_frame::do_set_address(uint64_t, off_t start_file_offset)
+{
+ off_t output_offset = 0;
+
+ for (Unmergeable_cie_offsets::iterator p =
+ this->unmergeable_cie_offsets_.begin();
+ p != this->unmergeable_cie_offsets_.end();
+ ++p)
+ {
+ p->second = start_file_offset + output_offset;
+ output_offset = p->first->set_output_offset(output_offset,
+ this->addralign(),
+ &this->merge_map_);
+ }
+
+ for (Cie_offsets::iterator p = this->cie_offsets_.begin();
+ p != this->cie_offsets_.end();
+ ++p)
+ {
+ p->second = start_file_offset + output_offset;
+ output_offset = p->first->set_output_offset(output_offset,
+ this->addralign(),
+ &this->merge_map_);
+ }
+
+ gold_assert((output_offset & (this->addralign() - 1)) == 0);
+ this->set_data_size(output_offset);
+}
+
+// Return an output offset for an input offset.
+
+bool
+Eh_frame::do_output_offset(const Relobj* object, unsigned int shndx,
+ off_t offset, off_t* poutput) const
+{
+ return this->merge_map_.get_output_offset(object, shndx, offset, poutput);
+}
+
+// Write the data to the output file.
+
+void
+Eh_frame::do_write(Output_file* of)
+{
+ const off_t offset = this->offset();
+ const off_t oview_size = this->data_size();
+ unsigned char* const oview = of->get_output_view(offset, oview_size);
+
+ if (parameters->get_size() == 32)
+ {
+ if (!parameters->is_big_endian())
+ {
+#ifdef HAVE_TARGET_32_LITTLE
+ this->do_sized_write<32, false>(oview);
+#else
+ gold_unreachable();
+#endif
+ }
+ else
+ {
+#ifdef HAVE_TARGET_32_BIG
+ this->do_sized_write<32, true>(oview);
+#else
+ gold_unreachable();
+#endif
+ }
+ }
+ else if (parameters->get_size() == 64)
+ {
+ if (!parameters->is_big_endian())
+ {
+#ifdef HAVE_TARGET_64_LITTLE
+ this->do_sized_write<64, false>(oview);
+#else
+ gold_unreachable();
+#endif
+ }
+ else
+ {
+#ifdef HAVE_TARGET_64_BIG
+ this->do_sized_write<64, true>(oview);
+#else
+ gold_unreachable();
+#endif
+ }
+ }
+ else
+ gold_unreachable();
+
+ of->write_output_view(offset, oview_size, oview);
+}
+
+// Write the data to the output file--template version.
+
+template<int size, bool big_endian>
+void
+Eh_frame::do_sized_write(unsigned char* oview)
+{
+ off_t o = 0;
+ for (Unmergeable_cie_offsets::iterator p =
+ this->unmergeable_cie_offsets_.begin();
+ p != this->unmergeable_cie_offsets_.end();
+ ++p)
+ o = p->first->write<size, big_endian>(oview, o, this->eh_frame_hdr_);
+ for (Cie_offsets::iterator p = this->cie_offsets_.begin();
+ p != this->cie_offsets_.end();
+ ++p)
+ o = p->first->write<size, big_endian>(oview, o, this->eh_frame_hdr_);
+}
+
+#ifdef HAVE_TARGET_32_LITTLE
+template
+bool
+Eh_frame::add_ehframe_input_section<32, false>(
+ Sized_relobj<32, false>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type);
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+bool
+Eh_frame::add_ehframe_input_section<32, true>(
+ Sized_relobj<32, true>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type);
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+bool
+Eh_frame::add_ehframe_input_section<64, false>(
+ Sized_relobj<64, false>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type);
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+bool
+Eh_frame::add_ehframe_input_section<64, true>(
+ Sized_relobj<64, true>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type);
+#endif
+
} // End namespace gold.
diff --git a/gold/ehframe.h b/gold/ehframe.h
index 0641ac8..6a584f9 100644
--- a/gold/ehframe.h
+++ b/gold/ehframe.h
@@ -24,10 +24,16 @@
#define GOLD_EHFRAME_H
#include "output.h"
+#include "merge.h"
namespace gold
{
+template<int size, bool big_endian>
+class Track_relocs;
+
+class Eh_frame;
+
// This class manages the .eh_frame_hdr section, which holds the data
// for the PT_GNU_EH_FRAME segment. gcc's unwind support code uses
// the PT_GNU_EH_FRAME segment to find the list of FDEs. This saves
@@ -42,7 +48,20 @@ namespace gold
class Eh_frame_hdr : public Output_section_data
{
public:
- Eh_frame_hdr(Output_section* eh_frame_section);
+ Eh_frame_hdr(Output_section* eh_frame_section, const Eh_frame*);
+
+ // Record that we found an unrecognized .eh_frame section.
+ void
+ found_unrecognized_eh_frame_section()
+ { this->any_unrecognized_eh_frame_sections_ = true; }
+
+ // Record an FDE.
+ void
+ record_fde(off_t fde_offset, unsigned char fde_encoding)
+ {
+ if (!this->any_unrecognized_eh_frame_sections_)
+ this->fde_offsets_.push_back(std::make_pair(fde_offset, fde_encoding));
+ }
// Set the final data size.
void
@@ -53,8 +72,346 @@ class Eh_frame_hdr : public Output_section_data
do_write(Output_file*);
private:
+ // Write the data to the file with the right endianness.
+ template<int size, bool big_endian>
+ void
+ do_sized_write(Output_file*);
+
+ // The data we record for one FDE: the offset of the FDE within the
+ // .eh_frame section, and the FDE encoding.
+ typedef std::pair<off_t, unsigned char> Fde_offset;
+
+ // The list of information we record for an FDE.
+ typedef std::vector<Fde_offset> Fde_offsets;
+
+ // When writing out the header, we convert the FDE offsets into FDE
+ // addresses. This is a list of pairs of the offset from the header
+ // to the FDE PC and to the FDE itself.
+ template<int size>
+ class Fde_addresses
+ {
+ public:
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+ typedef typename std::pair<Address, Address> Fde_address;
+ typedef typename std::vector<Fde_address> Fde_address_list;
+ typedef typename Fde_address_list::iterator iterator;
+
+ Fde_addresses(unsigned int reserve)
+ : fde_addresses_()
+ { this->fde_addresses_.reserve(reserve); }
+
+ void
+ push_back(Address pc_address, Address fde_address)
+ {
+ this->fde_addresses_.push_back(std::make_pair(pc_address, fde_address));
+ }
+
+ iterator
+ begin()
+ { return this->fde_addresses_.begin(); }
+
+ iterator
+ end()
+ { return this->fde_addresses_.end(); }
+
+ private:
+ Fde_address_list fde_addresses_;
+ };
+
+ // Compare Fde_address objects.
+ template<int size>
+ struct Fde_address_compare
+ {
+ bool
+ operator()(const typename Fde_addresses<size>::Fde_address& f1,
+ const typename Fde_addresses<size>::Fde_address& f2) const
+ { return f1.first < f2.first; }
+ };
+
+ // Return the PC to which an FDE refers.
+ template<int size, bool big_endian>
+ typename elfcpp::Elf_types<size>::Elf_Addr
+ get_fde_pc(const unsigned char* eh_frame_contents,
+ off_t fde_offset, unsigned char fde_encoding);
+
+ // Convert Fde_offsets to Fde_addresses.
+ template<int size, bool big_endian>
+ void
+ get_fde_addresses(Output_file* of,
+ const Fde_offsets* fde_offsets,
+ Fde_addresses<size>* fde_addresses);
+
// The .eh_frame section.
Output_section* eh_frame_section_;
+ // The .eh_frame section data.
+ const Eh_frame* eh_frame_data_;
+ // Data from the FDEs in the .eh_frame sections.
+ Fde_offsets fde_offsets_;
+ // Whether we found any .eh_frame sections which we could not
+ // process.
+ bool any_unrecognized_eh_frame_sections_;
+};
+
+// This class holds an FDE.
+
+class Fde
+{
+ public:
+ Fde(Relobj* object, unsigned int shndx, off_t input_offset,
+ const unsigned char* contents, size_t length)
+ : object_(object), shndx_(shndx), input_offset_(input_offset),
+ contents_(reinterpret_cast<const char*>(contents), length)
+ { }
+
+ // Return the length of this FDE. Add 4 for the length and 4 for
+ // the offset to the CIE.
+ size_t
+ length() const
+ { return this->contents_.length() + 8; }
+
+ // Add a mapping for this FDE to MERGE_MAP.
+ void
+ add_mapping(off_t output_offset, Merge_map* merge_map) const
+ {
+ merge_map->add_mapping(this->object_, this->shndx_,
+ this->input_offset_, this->length(),
+ output_offset);
+ }
+
+ // Write the FDE to OVIEW starting at OFFSET. FDE_ENCODING is the
+ // encoding, from the CIE. Record the FDE in EH_FRAME_HDR. Return
+ // the new offset.
+ template<int size, bool big_endian>
+ off_t
+ write(unsigned char* oview, off_t offset, off_t cie_offset,
+ unsigned char fde_encoding, Eh_frame_hdr* eh_frame_hdr);
+
+ private:
+ // The object in which this FDE was seen.
+ Relobj* object_;
+ // Input section index for this FDE.
+ unsigned int shndx_;
+ // Offset within the input section for this FDE.
+ off_t input_offset_;
+ // FDE data.
+ std::string contents_;
+};
+
+// This class holds a CIE.
+
+class Cie
+{
+ public:
+ Cie(Relobj* object, unsigned int shndx, off_t input_offset,
+ unsigned char fde_encoding, const char* personality_name,
+ const unsigned char* contents, size_t length)
+ : object_(object),
+ shndx_(shndx),
+ input_offset_(input_offset),
+ fde_encoding_(fde_encoding),
+ personality_name_(personality_name),
+ fdes_(),
+ contents_(reinterpret_cast<const char*>(contents), length)
+ { }
+
+ ~Cie();
+
+ // We permit copying a CIE when there are no FDEs. This is
+ // convenient in the code which creates them.
+ Cie(const Cie& cie)
+ : object_(cie.object_),
+ shndx_(cie.shndx_),
+ input_offset_(cie.input_offset_),
+ fde_encoding_(cie.fde_encoding_),
+ personality_name_(cie.personality_name_),
+ fdes_(),
+ contents_(cie.contents_)
+ { gold_assert(cie.fdes_.empty()); }
+
+ // Add an FDE associated with this CIE.
+ void
+ add_fde(Fde* fde)
+ { this->fdes_.push_back(fde); }
+
+ // Return the number of FDEs.
+ unsigned int
+ fde_count() const
+ { return this->fdes_.size(); }
+
+ // Set the output offset of this CIE to OUTPUT_OFFSET. It will be
+ // followed by all its FDEs. ADDRALIGN is the required address
+ // alignment, typically 4 or 8. This updates MERGE_MAP with the
+ // mapping. It returns the new output offset.
+ off_t
+ set_output_offset(off_t 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. Return the new offset.
+ template<int size, bool big_endian>
+ off_t
+ write(unsigned char* oview, off_t offset, Eh_frame_hdr* eh_frame_hdr);
+
+ friend bool operator<(const Cie&, const Cie&);
+ friend bool operator==(const Cie&, const Cie&);
+
+ private:
+ // The class is not assignable.
+ Cie& operator=(const Cie&);
+
+ // The object in which this CIE was first seen.
+ Relobj* object_;
+ // Input section index for this CIE.
+ unsigned int shndx_;
+ // Offset within the input section for this CIE.
+ off_t input_offset_;
+ // The encoding of the FDE. This is a DW_EH_PE code.
+ unsigned char fde_encoding_;
+ // The name of the personality routine. This will be the name of a
+ // global symbol, or will be the empty string.
+ std::string personality_name_;
+ // List of FDEs.
+ std::vector<Fde*> fdes_;
+ // CIE data.
+ std::string contents_;
+};
+
+extern bool operator<(const Cie&, const Cie&);
+extern bool operator==(const Cie&, const Cie&);
+
+// This class manages .eh_frame sections. It discards duplicate
+// exception information.
+
+class Eh_frame : public Output_section_data
+{
+ public:
+ Eh_frame();
+
+ // Record the associated Eh_frame_hdr, if any.
+ void
+ set_eh_frame_hdr(Eh_frame_hdr* hdr)
+ { this->eh_frame_hdr_ = hdr; }
+
+ // Add the input section SHNDX in OBJECT. SYMBOLS is the contents
+ // of the symbol table section (size SYMBOLS_SIZE), SYMBOL_NAMES is
+ // the symbol names section (size SYMBOL_NAMES_SIZE). RELOC_SHNDX
+ // is the relocation section if any (0 for none, -1U for multiple).
+ // RELOC_TYPE is the type of the relocation section if any. This
+ // returns whether the section was incorporated into the .eh_frame
+ // data.
+ template<int size, bool big_endian>
+ bool
+ add_ehframe_input_section(Sized_relobj<size, big_endian>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ unsigned int shndx, unsigned int reloc_shndx,
+ unsigned int reloc_type);
+
+ // Return the number of FDEs.
+ unsigned int
+ fde_count() const;
+
+ // Set the final data size.
+ void
+ do_set_address(uint64_t, off_t);
+
+ // Return the output address for an input address.
+ bool
+ do_output_offset(const Relobj*, unsigned int shndx, off_t offset,
+ off_t* poutput) const;
+
+ // Write the data to the file.
+ void
+ do_write(Output_file*);
+
+ private:
+ // The comparison routine for the CIE map.
+ struct Cie_less
+ {
+ bool
+ operator()(const Cie* cie1, const Cie* cie2) const
+ { return *cie1 < *cie2; }
+ };
+
+ // A mapping from unique CIEs to their offset in the output file.
+ typedef std::map<Cie*, uint64_t, Cie_less> Cie_offsets;
+
+ // A list of unmergeable CIEs with their offsets.
+ typedef std::vector<std::pair<Cie*, uint64_t> > Unmergeable_cie_offsets;
+
+ // A mapping from offsets to CIEs. This is used while reading an
+ // input section.
+ typedef std::map<uint64_t, Cie*> Offsets_to_cie;
+
+ // A list of CIEs, and a bool indicating whether the CIE is
+ // mergeable.
+ typedef std::vector<std::pair<Cie*, bool> > New_cies;
+
+ // Skip an LEB128.
+ static bool
+ skip_leb128(const unsigned char**, const unsigned char*);
+
+ // The implementation of add_ehframe_input_section.
+ template<int size, bool big_endian>
+ bool
+ do_add_ehframe_input_section(Sized_relobj<size, big_endian>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type,
+ const unsigned char* pcontents,
+ off_t contents_len,
+ New_cies*);
+
+ // Read a CIE.
+ template<int size, bool big_endian>
+ bool
+ read_cie(Sized_relobj<size, big_endian>* object,
+ unsigned int shndx,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ const unsigned char* pcontents,
+ const unsigned char* pcie,
+ const unsigned char *pcieend,
+ Track_relocs<size, big_endian>* relocs,
+ Offsets_to_cie* cies,
+ New_cies* new_cies);
+
+ // Read an FDE.
+ template<int size, bool big_endian>
+ bool
+ read_fde(Sized_relobj<size, big_endian>* object,
+ unsigned int shndx,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* pcontents,
+ unsigned int offset,
+ const unsigned char* pfde,
+ const unsigned char *pfdeend,
+ Track_relocs<size, big_endian>* relocs,
+ Offsets_to_cie* cies);
+
+ // Template version of write function.
+ template<int size, bool big_endian>
+ void
+ do_sized_write(unsigned char* oview);
+
+ // The exception frame header, if any.
+ Eh_frame_hdr* eh_frame_hdr_;
+ // A mapping from all unique CIEs to their offset in the output
+ // file.
+ Cie_offsets cie_offsets_;
+ // A mapping from unmergeable CIEs to their offset in the output
+ // file.
+ Unmergeable_cie_offsets unmergeable_cie_offsets_;
+ // A mapping from input sections to the output section.
+ Merge_map merge_map_;
};
} // End namespace gold.
diff --git a/gold/gold.cc b/gold/gold.cc
index 9a3a1e6..83bb3f6 100644
--- a/gold/gold.cc
+++ b/gold/gold.cc
@@ -250,6 +250,14 @@ queue_final_tasks(const General_options& options,
thread_count = input_objects->number_of_input_objects();
workqueue->set_thread_count(thread_count);
+ // Use a blocker to wait until all the input sections have been
+ // written out.
+ Task_token* input_sections_blocker = new Task_token();
+
+ // Use a blocker to block any objects which have to wait for the
+ // output sections to complete before they can apply relocations.
+ Task_token* output_sections_blocker = new Task_token();
+
// Use a blocker to block the final cleanup task.
Task_token* final_blocker = new Task_token();
@@ -259,8 +267,11 @@ queue_final_tasks(const General_options& options,
p != input_objects->relobj_end();
++p)
{
+ input_sections_blocker->add_blocker();
final_blocker->add_blocker();
workqueue->queue(new Relocate_task(options, symtab, layout, *p, of,
+ input_sections_blocker,
+ output_sections_blocker,
final_blocker));
}
@@ -273,10 +284,23 @@ queue_final_tasks(const General_options& options,
of,
final_blocker));
+ // Queue a task to write out the output sections.
+ output_sections_blocker->add_blocker();
+ final_blocker->add_blocker();
+ workqueue->queue(new Write_sections_task(layout, of, output_sections_blocker,
+ final_blocker));
+
// Queue a task to write out everything else.
final_blocker->add_blocker();
workqueue->queue(new Write_data_task(layout, symtab, of, final_blocker));
+ // Queue a task to write out the output sections which depend on
+ // input sections.
+ final_blocker->add_blocker();
+ workqueue->queue(new Write_after_input_sections_task(layout, of,
+ input_sections_blocker,
+ final_blocker));
+
// Queue a task to close the output file. This will be blocked by
// FINAL_BLOCKER.
workqueue->queue(new Task_function(new Close_task_runner(of),
diff --git a/gold/i386.cc b/gold/i386.cc
index 699aa68..d0b0b51 100644
--- a/gold/i386.cc
+++ b/gold/i386.cc
@@ -70,9 +70,10 @@ class Target_i386 : public Sized_target<32, false>
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
size_t local_symbol_count,
- const unsigned char* plocal_symbols,
- Symbol** global_symbols);
+ const unsigned char* plocal_symbols);
// Finalize the sections.
void
@@ -89,6 +90,8 @@ class Target_i386 : public Sized_target<32, false>
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
unsigned char* view,
elfcpp::Elf_types<32>::Elf_Addr view_address,
off_t view_size);
@@ -1157,9 +1160,10 @@ Target_i386::scan_relocs(const General_options& options,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
size_t local_symbol_count,
- const unsigned char* plocal_symbols,
- Symbol** global_symbols)
+ const unsigned char* plocal_symbols)
{
if (sh_type == elfcpp::SHT_RELA)
{
@@ -1178,9 +1182,10 @@ Target_i386::scan_relocs(const General_options& options,
data_shndx,
prelocs,
reloc_count,
+ output_section,
+ needs_special_offset_handling,
local_symbol_count,
- plocal_symbols,
- global_symbols);
+ plocal_symbols);
}
// Finalize the sections.
@@ -1770,6 +1775,8 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
unsigned char* view,
elfcpp::Elf_types<32>::Elf_Addr address,
off_t view_size)
@@ -1782,6 +1789,8 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo,
this,
prelocs,
reloc_count,
+ output_section,
+ needs_special_offset_handling,
view,
address,
view_size);
diff --git a/gold/layout.cc b/gold/layout.cc
index 84e8722..6894264 100644
--- a/gold/layout.cc
+++ b/gold/layout.cc
@@ -101,7 +101,7 @@ is_prefix_of(const char* prefix, const char* str)
template<int size, bool big_endian>
bool
-Layout::include_section(Object*, const char* name,
+Layout::include_section(Sized_relobj<size, big_endian>*, const char* name,
const elfcpp::Shdr<size, big_endian>& shdr)
{
// Some section types are never linked. Some are only linked when
@@ -202,13 +202,20 @@ Layout::get_output_section(const char* name, Stringpool::Key name_key,
}
// Return the output section to use for input section SHNDX, with name
-// NAME, with header HEADER, from object OBJECT. Set *OFF to the
-// offset of this input section without the output section.
+// NAME, with header HEADER, from object OBJECT. RELOC_SHNDX is the
+// index of a relocation section which applies to this section, or 0
+// if none, or -1U if more than one. RELOC_TYPE is the type of the
+// relocation section if there is one. Set *OFF to the offset of this
+// input section without the output section. Return NULL if the
+// section should be discarded. Set *OFF to -1 if the section
+// contents should not be written directly to the output file, but
+// will instead receive special handling.
template<int size, bool big_endian>
Output_section*
-Layout::layout(Relobj* object, unsigned int shndx, const char* name,
- const elfcpp::Shdr<size, big_endian>& shdr, off_t* off)
+Layout::layout(Sized_relobj<size, big_endian>* object, unsigned int shndx,
+ const char* name, const elfcpp::Shdr<size, big_endian>& shdr,
+ unsigned int reloc_shndx, unsigned int, off_t* off)
{
if (!this->include_section(object, name, shdr))
return NULL;
@@ -231,38 +238,44 @@ Layout::layout(Relobj* object, unsigned int shndx, const char* name,
shdr.get_sh_type(),
shdr.get_sh_flags());
- // Special GNU handling of sections named .eh_frame.
- if (!parameters->output_is_object()
- && strcmp(name, ".eh_frame") == 0
- && shdr.get_sh_size() > 0
- && shdr.get_sh_type() == elfcpp::SHT_PROGBITS
- && shdr.get_sh_flags() == elfcpp::SHF_ALLOC)
- {
- this->layout_eh_frame(object, shndx, name, shdr, os, off);
- return os;
- }
-
// FIXME: Handle SHF_LINK_ORDER somewhere.
- *off = os->add_input_section(object, shndx, name, shdr);
+ *off = os->add_input_section(object, shndx, name, shdr, reloc_shndx);
return os;
}
-// Special GNU handling of sections named .eh_frame. They will
-// normally hold exception frame data.
+// Special GNU handling of sections name .eh_frame. They will
+// normally hold exception frame data as defined by the C++ ABI
+// (http://codesourcery.com/cxx-abi/).
template<int size, bool big_endian>
-void
-Layout::layout_eh_frame(Relobj* object,
+Output_section*
+Layout::layout_eh_frame(Sized_relobj<size, big_endian>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
unsigned int shndx,
- const char* name,
const elfcpp::Shdr<size, big_endian>& shdr,
- Output_section* os, off_t* off)
+ unsigned int reloc_shndx, unsigned int reloc_type,
+ off_t* off)
{
+ gold_assert(shdr.get_sh_type() == elfcpp::SHT_PROGBITS);
+ gold_assert(shdr.get_sh_flags() == elfcpp::SHF_ALLOC);
+
+ Stringpool::Key name_key;
+ const char* name = this->namepool_.add(".eh_frame", false, &name_key);
+
+ Output_section* os = this->get_output_section(name, name_key,
+ elfcpp::SHT_PROGBITS,
+ elfcpp::SHF_ALLOC);
+
if (this->eh_frame_section_ == NULL)
{
this->eh_frame_section_ = os;
+ this->eh_frame_data_ = new Eh_frame();
+ os->add_output_section_data(this->eh_frame_data_);
if (this->options_.create_eh_frame_hdr())
{
@@ -275,19 +288,39 @@ Layout::layout_eh_frame(Relobj* object,
elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC);
- Eh_frame_hdr* hdr_posd = new Eh_frame_hdr(os);
+ 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();
+
Output_segment* hdr_oseg =
new Output_segment(elfcpp::PT_GNU_EH_FRAME, elfcpp::PF_R);
this->segment_list_.push_back(hdr_oseg);
hdr_oseg->add_output_section(hdr_os, elfcpp::PF_R);
+
+ this->eh_frame_data_->set_eh_frame_hdr(hdr_posd);
}
}
gold_assert(this->eh_frame_section_ == os);
- *off = os->add_input_section(object, shndx, name, shdr);
+ if (this->eh_frame_data_->add_ehframe_input_section(object,
+ symbols,
+ symbols_size,
+ symbol_names,
+ symbol_names_size,
+ shndx,
+ reloc_shndx,
+ reloc_type))
+ *off = -1;
+ else
+ {
+ // We couldn't handle this .eh_frame section for some reason.
+ // Add it as a normal section.
+ *off = os->add_input_section(object, shndx, name, shdr, reloc_shndx);
+ }
+
+ return os;
}
// Add POSD to an output section using NAME, TYPE, and FLAGS.
@@ -1724,6 +1757,22 @@ Layout::add_comdat(const char* signature, bool group)
}
}
+// Write out the Output_sections. Most won't have anything to write,
+// since most of the data will come from input sections which are
+// handled elsewhere. But some Output_sections do have Output_data.
+
+void
+Layout::write_output_sections(Output_file* of) const
+{
+ for (Section_list::const_iterator p = this->section_list_.begin();
+ p != this->section_list_.end();
+ ++p)
+ {
+ if (!(*p)->after_input_sections())
+ (*p)->write(of);
+ }
+}
+
// Write out data not associated with a section or the symbol table.
void
@@ -1764,15 +1813,6 @@ Layout::write_data(const Symbol_table* symtab, Output_file* of) const
}
}
- // Write out the Output_sections. Most won't have anything to
- // write, since most of the data will come from input sections which
- // are handled elsewhere. But some Output_sections do have
- // Output_data.
- for (Section_list::const_iterator p = this->section_list_.begin();
- p != this->section_list_.end();
- ++p)
- (*p)->write(of);
-
// Write out the Output_data which are not in an Output_section.
for (Data_list::const_iterator p = this->special_output_list_.begin();
p != this->special_output_list_.end();
@@ -1780,6 +1820,65 @@ Layout::write_data(const Symbol_table* symtab, Output_file* of) const
(*p)->write(of);
}
+// Write out the Output_sections which can only be written after the
+// input sections are complete.
+
+void
+Layout::write_sections_after_input_sections(Output_file* of) const
+{
+ for (Section_list::const_iterator p = this->section_list_.begin();
+ p != this->section_list_.end();
+ ++p)
+ {
+ if ((*p)->after_input_sections())
+ (*p)->write(of);
+ }
+}
+
+// Write_sections_task methods.
+
+// We can always run this task.
+
+Task::Is_runnable_type
+Write_sections_task::is_runnable(Workqueue*)
+{
+ return IS_RUNNABLE;
+}
+
+// We need to unlock both OUTPUT_SECTIONS_BLOCKER and FINAL_BLOCKER
+// when finished.
+
+class Write_sections_task::Write_sections_locker : public Task_locker
+{
+ public:
+ Write_sections_locker(Task_token& output_sections_blocker,
+ Task_token& final_blocker,
+ Workqueue* workqueue)
+ : output_sections_block_(output_sections_blocker, workqueue),
+ final_block_(final_blocker, workqueue)
+ { }
+
+ private:
+ Task_block_token output_sections_block_;
+ Task_block_token final_block_;
+};
+
+Task_locker*
+Write_sections_task::locks(Workqueue* workqueue)
+{
+ return new Write_sections_locker(*this->output_sections_blocker_,
+ *this->final_blocker_,
+ workqueue);
+}
+
+// Run the task--write out the data.
+
+void
+Write_sections_task::run(Workqueue*)
+{
+ this->layout_->write_output_sections(this->of_);
+}
+
// Write_data_task methods.
// We can always run this task.
@@ -1833,6 +1932,34 @@ Write_symbols_task::run(Workqueue*)
this->of_);
}
+// Write_after_input_sections_task methods.
+
+// We can only run this task after the input sections have completed.
+
+Task::Is_runnable_type
+Write_after_input_sections_task::is_runnable(Workqueue*)
+{
+ if (this->input_sections_blocker_->is_blocked())
+ return IS_BLOCKED;
+ return IS_RUNNABLE;
+}
+
+// We need to unlock FINAL_BLOCKER when finished.
+
+Task_locker*
+Write_after_input_sections_task::locks(Workqueue* workqueue)
+{
+ return new Task_locker_block(*this->final_blocker_, workqueue);
+}
+
+// Run the task.
+
+void
+Write_after_input_sections_task::run(Workqueue*)
+{
+ this->layout_->write_sections_after_input_sections(this->of_);
+}
+
// Close_task_runner methods.
// Run the task--close the file.
@@ -1849,30 +1976,97 @@ Close_task_runner::run(Workqueue*)
#ifdef HAVE_TARGET_32_LITTLE
template
Output_section*
-Layout::layout<32, false>(Relobj* object, unsigned int shndx, const char* name,
- const elfcpp::Shdr<32, false>& shdr, off_t*);
+Layout::layout<32, false>(Sized_relobj<32, false>* object, unsigned int shndx,
+ const char* name,
+ const elfcpp::Shdr<32, false>& shdr,
+ unsigned int, unsigned int, off_t*);
#endif
#ifdef HAVE_TARGET_32_BIG
template
Output_section*
-Layout::layout<32, true>(Relobj* object, unsigned int shndx, const char* name,
- const elfcpp::Shdr<32, true>& shdr, off_t*);
+Layout::layout<32, true>(Sized_relobj<32, true>* object, unsigned int shndx,
+ const char* name,
+ const elfcpp::Shdr<32, true>& shdr,
+ unsigned int, unsigned int, off_t*);
#endif
#ifdef HAVE_TARGET_64_LITTLE
template
Output_section*
-Layout::layout<64, false>(Relobj* object, unsigned int shndx, const char* name,
- const elfcpp::Shdr<64, false>& shdr, off_t*);
+Layout::layout<64, false>(Sized_relobj<64, false>* object, unsigned int shndx,
+ const char* name,
+ const elfcpp::Shdr<64, false>& shdr,
+ unsigned int, unsigned int, off_t*);
#endif
#ifdef HAVE_TARGET_64_BIG
template
Output_section*
-Layout::layout<64, true>(Relobj* object, unsigned int shndx, const char* name,
- const elfcpp::Shdr<64, true>& shdr, off_t*);
+Layout::layout<64, true>(Sized_relobj<64, true>* object, unsigned int shndx,
+ const char* name,
+ const elfcpp::Shdr<64, true>& shdr,
+ unsigned int, unsigned int, off_t*);
#endif
+#ifdef HAVE_TARGET_32_LITTLE
+template
+Output_section*
+Layout::layout_eh_frame<32, false>(Sized_relobj<32, false>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ unsigned int shndx,
+ const elfcpp::Shdr<32, false>& shdr,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type,
+ off_t* off);
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+Output_section*
+Layout::layout_eh_frame<32, true>(Sized_relobj<32, true>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ unsigned int shndx,
+ const elfcpp::Shdr<32, true>& shdr,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type,
+ off_t* off);
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+Output_section*
+Layout::layout_eh_frame<64, false>(Sized_relobj<64, false>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ unsigned int shndx,
+ const elfcpp::Shdr<64, false>& shdr,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type,
+ off_t* off);
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+Output_section*
+Layout::layout_eh_frame<64, true>(Sized_relobj<64, true>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ unsigned int shndx,
+ const elfcpp::Shdr<64, true>& shdr,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type,
+ off_t* off);
+#endif
} // End namespace gold.
diff --git a/gold/layout.h b/gold/layout.h
index 5e9871e..fa804ad 100644
--- a/gold/layout.h
+++ b/gold/layout.h
@@ -45,6 +45,7 @@ class Output_section_headers;
class Output_segment;
class Output_data;
class Output_data_dynamic;
+class Eh_frame;
class Target;
// This task function handles mapping the input sections to output
@@ -87,12 +88,37 @@ class Layout
// Given an input section SHNDX, named NAME, with data in SHDR, from
// the object file OBJECT, return the output section where this
- // input section should go. Set *OFFSET to the offset within the
- // output section.
+ // input section should go. RELOC_SHNDX is the index of a
+ // relocation section which applies to this section, or 0 if none,
+ // or -1U if more than one. RELOC_TYPE is the type of the
+ // relocation section if there is one. Set *OFFSET to the offset
+ // within the output section.
template<int size, bool big_endian>
Output_section*
- layout(Relobj *object, unsigned int shndx, const char* name,
- const elfcpp::Shdr<size, big_endian>& shdr, off_t* offset);
+ layout(Sized_relobj<size, big_endian> *object, unsigned int shndx,
+ const char* name, const elfcpp::Shdr<size, big_endian>& shdr,
+ unsigned int reloc_shndx, unsigned int reloc_type, off_t* offset);
+
+ // Like layout, only for exception frame sections. OBJECT is an
+ // object file. SYMBOLS is the contents of the symbol table
+ // section, with size SYMBOLS_SIZE. SYMBOL_NAMES is the contents of
+ // the symbol name section, with size SYMBOL_NAMES_SIZE. SHNDX is a
+ // .eh_frame section in OBJECT. SHDR is the section header.
+ // RELOC_SHNDX is the index of a relocation section which applies to
+ // this section, or 0 if none, or -1U if more than one. RELOC_TYPE
+ // is the type of the relocation section if there is one. This
+ // returns the output section, and sets *OFFSET to the offset.
+ template<int size, bool big_endian>
+ Output_section*
+ layout_eh_frame(Sized_relobj<size, big_endian>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ const unsigned char* symbol_names,
+ off_t symbol_names_size,
+ unsigned int shndx,
+ const elfcpp::Shdr<size, big_endian>& shdr,
+ unsigned int reloc_shndx, unsigned int reloc_type,
+ off_t* offset);
// Handle a GNU stack note. This is called once per input object
// file. SEEN_GNU_STACK is true if the object file has a
@@ -176,11 +202,20 @@ class Layout
dynamic_data() const
{ return this->dynamic_data_; }
+ // Write out the output sections.
+ void
+ write_output_sections(Output_file* of) const;
+
// Write out data not associated with an input file or the symbol
// table.
void
write_data(const Symbol_table*, Output_file*) const;
+ // Write out output sections which can not be written until all the
+ // input sections are complete.
+ void
+ write_sections_after_input_sections(Output_file* of) const;
+
// Return an output section named NAME, or NULL if there is none.
Output_section*
find_output_section(const char* name) const;
@@ -218,13 +253,6 @@ class Layout
static const Linkonce_mapping linkonce_mapping[];
static const int linkonce_mapping_count;
- // Handle an exception frame section.
- template<int size, bool big_endian>
- void
- layout_eh_frame(Relobj*, unsigned int, const char*,
- const elfcpp::Shdr<size, big_endian>&,
- Output_section*, off_t*);
-
// Create a .note section for gold.
void
create_gold_note();
@@ -285,7 +313,7 @@ class Layout
// Return whether to include this section in the link.
template<int size, bool big_endian>
bool
- include_section(Object* object, const char* name,
+ include_section(Sized_relobj<size, big_endian>* object, const char* name,
const elfcpp::Shdr<size, big_endian>&);
// Return the output section name to use given an input section
@@ -389,8 +417,12 @@ class Layout
Output_section* dynamic_section_;
// The dynamic data which goes into dynamic_section_.
Output_data_dynamic* dynamic_data_;
- // The exception frame section.
+ // The exception frame output section if there is one.
Output_section* eh_frame_section_;
+ // The exception frame data for eh_frame_section_.
+ Eh_frame* eh_frame_data_;
+ // The exception frame header output section if there is one.
+ Output_section* eh_frame_hdr_section_;
// The size of the output file.
off_t output_file_size_;
// Whether we have seen an object file marked to require an
@@ -404,6 +436,42 @@ class Layout
bool input_without_gnu_stack_note_;
};
+// This task handles writing out data in output sections which is not
+// part of an input section, or which requires special handling. When
+// this is done, it unblocks both output_sections_blocker and
+// final_blocker.
+
+class Write_sections_task : public Task
+{
+ public:
+ Write_sections_task(const Layout* layout, Output_file* of,
+ Task_token* output_sections_blocker,
+ Task_token* final_blocker)
+ : layout_(layout), of_(of),
+ output_sections_blocker_(output_sections_blocker),
+ final_blocker_(final_blocker)
+ { }
+
+ // The standard Task methods.
+
+ Is_runnable_type
+ is_runnable(Workqueue*);
+
+ Task_locker*
+ locks(Workqueue*);
+
+ void
+ run(Workqueue*);
+
+ private:
+ class Write_sections_locker;
+
+ const Layout* layout_;
+ Output_file* of_;
+ Task_token* output_sections_blocker_;
+ Task_token* final_blocker_;
+};
+
// This task handles writing out data which is not part of a section
// or segment.
@@ -465,6 +533,42 @@ class Write_symbols_task : public Task
Task_token* final_blocker_;
};
+// This task handles writing out data in output sections which can't
+// be written out until all the input sections have been handled.
+// This is for sections whose contents is based on the contents of
+// other output sections.
+
+class Write_after_input_sections_task : public Task
+{
+ public:
+ Write_after_input_sections_task(const Layout* layout, Output_file* of,
+ Task_token* input_sections_blocker,
+ Task_token* final_blocker)
+ : layout_(layout), of_(of),
+ input_sections_blocker_(input_sections_blocker),
+ final_blocker_(final_blocker)
+ { }
+
+ // The standard Task methods.
+
+ Is_runnable_type
+ is_runnable(Workqueue*);
+
+ Task_locker*
+ locks(Workqueue*);
+
+ void
+ run(Workqueue*);
+
+ private:
+ class Write_sections_locker;
+
+ const Layout* layout_;
+ Output_file* of_;
+ Task_token* input_sections_blocker_;
+ Task_token* final_blocker_;
+};
+
// This task function handles closing the file.
class Close_task_runner : public Task_function_runner
diff --git a/gold/merge.cc b/gold/merge.cc
index 13bfc17..15d3b7c 100644
--- a/gold/merge.cc
+++ b/gold/merge.cc
@@ -30,12 +30,14 @@
namespace gold
{
+// Class Merge_map::Merge_key_less.
+
// Sort the entries in a merge mapping. The key is an input object, a
// section index in that object, and an offset in that section.
bool
-Output_merge_base::Merge_key_less::operator()(const Merge_key& mk1,
- const Merge_key& mk2) const
+Merge_map::Merge_key_less::operator()(const Merge_key& mk1,
+ const Merge_key& mk2) const
{
// The order of different objects and different sections doesn't
// matter. We want to get consistent results across links so we
@@ -55,43 +57,44 @@ Output_merge_base::Merge_key_less::operator()(const Merge_key& mk1,
return mk1.offset < mk2.offset;
}
-// Add a mapping from an OFFSET in input section SHNDX in object
-// OBJECT to an OUTPUT_OFFSET in a merged output section. This
-// manages the mapping used to resolve relocations against merged
-// sections.
+// Class Merge_map.
+
+// Add a mapping for the bytes from OFFSET to OFFSET + LENGTH in input
+// section SHNDX in object OBJECT to an OUTPUT_OFFSET in a merged
+// output section.
void
-Output_merge_base::add_mapping(Relobj* object, unsigned int shndx,
- off_t offset, off_t output_offset)
+Merge_map::add_mapping(Relobj* object, unsigned int shndx,
+ off_t offset, off_t length, off_t output_offset)
{
Merge_key mk;
mk.object = object;
mk.shndx = shndx;
mk.offset = offset;
- std::pair<Merge_map::iterator, bool> ins =
- this->merge_map_.insert(std::make_pair(mk, output_offset));
+
+ Merge_value mv;
+ mv.length = length;
+ mv.output_offset = output_offset;
+
+ std::pair<Merge_mapping::iterator, bool> ins =
+ this->merge_map_.insert(std::make_pair(mk, mv));
gold_assert(ins.second);
}
-// Return the output address for an input address. The input address
-// is at offset OFFSET in section SHNDX in OBJECT.
-// OUTPUT_SECTION_ADDRESS is the address of the output section. If we
-// know the address, set *POUTPUT and return true. Otherwise return
-// false.
+// Return the output offset for an input address. The input address
+// is at offset OFFSET in section SHNDX in OBJECT. This sets
+// *OUTPUT_OFFSET to the offset in the output section. This returns
+// true if the mapping is known, false otherwise.
bool
-Output_merge_base::do_output_address(const Relobj* object, unsigned int shndx,
- off_t offset,
- uint64_t output_section_address,
- uint64_t* poutput) const
+Merge_map::get_output_offset(const Relobj* object, unsigned int shndx,
+ off_t offset, off_t* output_offset) const
{
- gold_assert(output_section_address == this->address());
-
Merge_key mk;
mk.object = object;
mk.shndx = shndx;
mk.offset = offset;
- Merge_map::const_iterator p = this->merge_map_.lower_bound(mk);
+ Merge_mapping::const_iterator p = this->merge_map_.lower_bound(mk);
// If MK is not in the map, lower_bound returns the next iterator
// larger than it.
@@ -108,12 +111,32 @@ Output_merge_base::do_output_address(const Relobj* object, unsigned int shndx,
if (p->first.object != object || p->first.shndx != shndx)
return false;
- // Any input section is fully mapped: we don't need to know the size
- // of the range starting at P->FIRST.OFFSET.
- *poutput = output_section_address + p->second + (offset - p->first.offset);
+ if (offset - p->first.offset >= p->second.length)
+ return false;
+
+ *output_offset = p->second.output_offset;
+ if (*output_offset != -1)
+ *output_offset += (offset - p->first.offset);
return true;
}
+// Class Output_merge_base.
+
+// Return the output offset for an input offset. The input address is
+// at offset OFFSET in section SHNDX in OBJECT. If we know the
+// offset, set *POUTPUT and return true. Otherwise return false.
+
+bool
+Output_merge_base::do_output_offset(const Relobj* object,
+ unsigned int shndx,
+ off_t offset,
+ off_t* poutput) const
+{
+ return this->merge_map_.get_output_offset(object, shndx, offset, poutput);
+}
+
+// Class Output_merge_data.
+
// Compute the hash code for a fixed-size constant.
size_t
@@ -214,7 +237,7 @@ Output_merge_data::do_add_input_section(Relobj* object, unsigned int shndx)
}
// Record the offset of this constant in the output section.
- this->add_mapping(object, shndx, i, k);
+ this->add_mapping(object, shndx, i, entsize, k);
}
return true;
@@ -241,6 +264,8 @@ Output_merge_data::do_write(Output_file* of)
of->write(this->offset(), this->p_, this->len_);
}
+// Class Output_merge_string.
+
// Add an input section to a merged string section.
template<typename Char_type>
@@ -279,10 +304,12 @@ Output_merge_string<Char_type>::do_add_input_section(Relobj* object,
const Char_type* str = this->stringpool_.add(p, true, NULL);
- this->merged_strings_.push_back(Merged_string(object, shndx, i, str));
+ off_t bytelen_with_null = (plen + 1) * sizeof(Char_type);
+ this->merged_strings_.push_back(Merged_string(object, shndx, i, str,
+ bytelen_with_null));
p += plen + 1;
- i += (plen + 1) * sizeof(Char_type);
+ i += bytelen_with_null;
}
return true;
@@ -302,7 +329,7 @@ Output_merge_string<Char_type>::do_set_address(uint64_t, off_t)
this->merged_strings_.begin();
p != this->merged_strings_.end();
++p)
- this->add_mapping(p->object, p->shndx, p->offset,
+ this->add_mapping(p->object, p->shndx, p->offset, p->length,
this->stringpool_.get_offset(p->string));
this->set_data_size(this->stringpool_.get_strtab_size());
diff --git a/gold/merge.h b/gold/merge.h
index cc554f2..630b593 100644
--- a/gold/merge.h
+++ b/gold/merge.h
@@ -31,6 +31,62 @@
namespace gold
{
+// This class manages mappings from input sections to offsets in an
+// output section. This is used where input sections are merged.
+
+class Merge_map
+{
+ public:
+ Merge_map()
+ : merge_map_()
+ { }
+
+ // Add a mapping for the bytes from OFFSET to OFFSET + LENGTH in the
+ // input section SHNDX in object OBJECT to OUTPUT_OFFSET in the
+ // output section. An OUTPUT_OFFSET of -1 means that the bytes are
+ // discarded.
+ void
+ add_mapping(Relobj* object, unsigned int shndx, off_t offset, off_t length,
+ off_t output_offset);
+
+ // Return the output offset for an input address. The input address
+ // is at offset OFFSET in section SHNDX in OBJECT. This sets
+ // *OUTPUT_OFFSET to the offset in the output section; this will be
+ // -1 if the bytes are not being copied to the output. This returns
+ // true if the mapping is known, false otherwise.
+ bool
+ get_output_offset(const Relobj* object, unsigned int shndx, off_t offset,
+ off_t *output_offset) const;
+
+ private:
+ // We build a mapping from OBJECT/SHNDX/OFFSET to an offset and
+ // length in the output section.
+ struct Merge_key
+ {
+ const Relobj* object;
+ unsigned int shndx;
+ off_t offset;
+ };
+
+ struct Merge_key_less
+ {
+ bool
+ operator()(const Merge_key&, const Merge_key&) const;
+ };
+
+ struct Merge_value
+ {
+ off_t length;
+ off_t output_offset;
+ };
+
+ typedef std::map<Merge_key, Merge_value, Merge_key_less> Merge_mapping;
+
+ // A mapping from input object/section/offset to offset in output
+ // section.
+ Merge_mapping merge_map_;
+};
+
// A general class for SHF_MERGE data, to hold functions shared by
// fixed-size constant data and string data.
@@ -41,10 +97,10 @@ class Output_merge_base : public Output_section_data
: Output_section_data(addralign), merge_map_(), entsize_(entsize)
{ }
- // Return the output address for an input address.
+ // Return the output offset for an input offset.
bool
- do_output_address(const Relobj* object, unsigned int shndx, off_t offset,
- uint64_t output_section_address, uint64_t* poutput) const;
+ do_output_offset(const Relobj* object, unsigned int shndx, off_t offset,
+ off_t* poutput) const;
protected:
// Return the entry size.
@@ -56,30 +112,15 @@ class Output_merge_base : public Output_section_data
// OBJECT to an OUTPUT_OFFSET in the output section.
void
add_mapping(Relobj* object, unsigned int shndx, off_t offset,
- off_t output_offset);
-
- private:
- // We build a mapping from OBJECT/SHNDX/OFFSET to an offset in the
- // output section.
- struct Merge_key
+ off_t length, off_t output_offset)
{
- const Relobj* object;
- unsigned int shndx;
- off_t offset;
- };
-
- struct Merge_key_less
- {
- bool
- operator()(const Merge_key&, const Merge_key&) const;
- };
-
- typedef std::map<Merge_key, off_t, Merge_key_less> Merge_map;
+ this->merge_map_.add_mapping(object, shndx, offset, length, output_offset);
+ }
+ private:
// A mapping from input object/section/offset to offset in output
// section.
Merge_map merge_map_;
-
// The entry size. For fixed-size constants, this is the size of
// the constants. For strings, this is the size of a character.
uint64_t entsize_;
@@ -221,10 +262,13 @@ class Output_merge_string : public Output_merge_base
off_t offset;
// The string itself, a pointer into a Stringpool.
const Char_type* string;
+ // The length of the string in bytes, including the null terminator.
+ size_t length;
Merged_string(Relobj *objecta, unsigned int shndxa, off_t offseta,
- const Char_type* stringa)
- : object(objecta), shndx(shndxa), offset(offseta), string(stringa)
+ const Char_type* stringa, size_t lengtha)
+ : object(objecta), shndx(shndxa), offset(offseta), string(stringa),
+ length(lengtha)
{ }
};
diff --git a/gold/object.cc b/gold/object.cc
index 0b71be1..d334b17 100644
--- a/gold/object.cc
+++ b/gold/object.cc
@@ -139,10 +139,11 @@ Sized_relobj<size, big_endian>::Sized_relobj(
symtab_shndx_(-1U),
local_symbol_count_(0),
output_local_symbol_count_(0),
- symbols_(NULL),
+ symbols_(),
local_symbol_offset_(0),
local_values_(),
- local_got_offsets_()
+ local_got_offsets_(),
+ has_eh_frame_(false)
{
}
@@ -198,6 +199,50 @@ Sized_relobj<size, big_endian>::find_symtab(const unsigned char* pshdrs)
}
}
+// Return whether SHDR has the right type and flags to be a GNU
+// .eh_frame section.
+
+template<int size, bool big_endian>
+bool
+Sized_relobj<size, big_endian>::check_eh_frame_flags(
+ const elfcpp::Shdr<size, big_endian>* shdr) const
+{
+ return (shdr->get_sh_size() > 0
+ && shdr->get_sh_type() == elfcpp::SHT_PROGBITS
+ && shdr->get_sh_flags() == elfcpp::SHF_ALLOC);
+}
+
+// Return whether there is a GNU .eh_frame section, given the section
+// headers and the section names.
+
+template<int size, bool big_endian>
+bool
+Sized_relobj<size, big_endian>::find_eh_frame(const unsigned char* pshdrs,
+ const char* names,
+ off_t names_size) const
+{
+ const unsigned int shnum = this->shnum();
+ const unsigned char* p = pshdrs + This::shdr_size;
+ for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size)
+ {
+ typename This::Shdr shdr(p);
+ if (this->check_eh_frame_flags(&shdr))
+ {
+ if (shdr.get_sh_name() >= names_size)
+ {
+ this->error(_("bad section name offset for section %u: %lu"),
+ i, static_cast<unsigned long>(shdr.get_sh_name()));
+ continue;
+ }
+
+ const char* name = names + shdr.get_sh_name();
+ if (strcmp(name, ".eh_frame") == 0)
+ return true;
+ }
+ }
+ return false;
+}
+
// Read the sections and symbols from an object file.
template<int size, bool big_endian>
@@ -210,8 +255,14 @@ Sized_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
this->find_symtab(pshdrs);
+ const unsigned char* namesu = sd->section_names->data();
+ const char* names = reinterpret_cast<const char*>(namesu);
+ if (this->find_eh_frame(pshdrs, names, sd->section_names_size))
+ this->has_eh_frame_ = true;
+
sd->symbols = NULL;
sd->symbols_size = 0;
+ sd->external_symbols_offset = 0;
sd->symbol_names = NULL;
sd->symbol_names_size = 0;
@@ -226,16 +277,26 @@ Sized_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
+ this->symtab_shndx_ * This::shdr_size);
gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
- // We only need the external symbols.
+ // If this object has a .eh_frame section, we need all the symbols.
+ // Otherwise we only need the external symbols. While it would be
+ // simpler to just always read all the symbols, I've seen object
+ // files with well over 2000 local symbols, which for a 64-bit
+ // object file format is over 5 pages that we don't need to read
+ // now.
+
const int sym_size = This::sym_size;
const unsigned int loccount = symtabshdr.get_sh_info();
this->local_symbol_count_ = loccount;
off_t locsize = loccount * sym_size;
- off_t extoff = symtabshdr.get_sh_offset() + locsize;
- off_t extsize = symtabshdr.get_sh_size() - locsize;
+ off_t dataoff = symtabshdr.get_sh_offset();
+ off_t datasize = symtabshdr.get_sh_size();
+ off_t extoff = dataoff + locsize;
+ off_t extsize = datasize - locsize;
+
+ off_t readoff = this->has_eh_frame_ ? dataoff : extoff;
+ off_t readsize = this->has_eh_frame_ ? datasize : extsize;
- // Read the symbol table.
- File_view* fvsymtab = this->get_lasting_view(extoff, extsize, false);
+ File_view* fvsymtab = this->get_lasting_view(readoff, readsize, false);
// Read the section header for the symbol names.
unsigned int strtab_shndx = symtabshdr.get_sh_link();
@@ -257,11 +318,36 @@ Sized_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
strtabshdr.get_sh_size(), true);
sd->symbols = fvsymtab;
- sd->symbols_size = extsize;
+ sd->symbols_size = readsize;
+ sd->external_symbols_offset = this->has_eh_frame_ ? locsize : 0;
sd->symbol_names = fvstrtab;
sd->symbol_names_size = strtabshdr.get_sh_size();
}
+// Return the section index of symbol SYM. Set *VALUE to its value in
+// the object file. Note that for a symbol which is not defined in
+// this object file, this will set *VALUE to 0 and return SHN_UNDEF;
+// it will not return the final value of the symbol in the link.
+
+template<int size, bool big_endian>
+unsigned int
+Sized_relobj<size, big_endian>::symbol_section_and_value(unsigned int sym,
+ Address* value)
+{
+ off_t symbols_size;
+ const unsigned char* symbols = this->section_contents(this->symtab_shndx_,
+ &symbols_size,
+ false);
+
+ const size_t count = symbols_size / This::sym_size;
+ gold_assert(sym < count);
+
+ elfcpp::Sym<size, big_endian> elfsym(symbols + sym * This::sym_size);
+ *value = elfsym.get_st_value();
+ // FIXME: Handle SHN_XINDEX.
+ return elfsym.get_st_shndx();
+}
+
// Return whether to include a section group in the link. LAYOUT is
// used to keep track of which section groups we have already seen.
// INDEX is the index of the section group and SHDR is the section
@@ -425,6 +511,38 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab,
const unsigned char* pnamesu = sd->section_names->data();
const char* pnames = reinterpret_cast<const char*>(pnamesu);
+ // For each section, record the index of the reloc section if any.
+ // Use 0 to mean that there is no reloc section, -1U to mean that
+ // there is more than one.
+ std::vector<unsigned int> reloc_shndx(shnum, 0);
+ std::vector<unsigned int> reloc_type(shnum, elfcpp::SHT_NULL);
+ // Skip the first, dummy, section.
+ pshdrs += This::shdr_size;
+ for (unsigned int i = 1; i < shnum; ++i, pshdrs += This::shdr_size)
+ {
+ typename This::Shdr shdr(pshdrs);
+
+ unsigned int sh_type = shdr.get_sh_type();
+ if (sh_type == elfcpp::SHT_REL || sh_type == elfcpp::SHT_RELA)
+ {
+ unsigned int target_shndx = shdr.get_sh_info();
+ if (target_shndx == 0 || target_shndx >= shnum)
+ {
+ this->error(_("relocation section %u has bad info %u"),
+ i, target_shndx);
+ continue;
+ }
+
+ if (reloc_shndx[target_shndx] != 0)
+ reloc_shndx[target_shndx] = -1U;
+ else
+ {
+ reloc_shndx[target_shndx] = i;
+ reloc_type[target_shndx] = sh_type;
+ }
+ }
+ }
+
std::vector<Map_to_output>& map_sections(this->map_to_output());
map_sections.resize(shnum);
@@ -436,8 +554,11 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab,
// Keep track of which sections to omit.
std::vector<bool> omit(shnum, false);
+ // Keep track of .eh_frame sections.
+ std::vector<unsigned int> eh_frame_sections;
+
// Skip the first, dummy, section.
- pshdrs += This::shdr_size;
+ pshdrs = sd->section_headers->data() + This::shdr_size;
for (unsigned int i = 1; i < shnum; ++i, pshdrs += This::shdr_size)
{
typename This::Shdr shdr(pshdrs);
@@ -490,15 +611,70 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab,
continue;
}
+ // The .eh_frame section is special. It holds exception frame
+ // information that we need to read in order to generate the
+ // exception frame header. We process these after all the other
+ // sections so that the exception frame reader can reliably
+ // determine which sections are being discarded, and discard the
+ // corresponding information.
+ if (!parameters->output_is_object()
+ && strcmp(name, ".eh_frame") == 0
+ && this->check_eh_frame_flags(&shdr))
+ {
+ eh_frame_sections.push_back(i);
+ continue;
+ }
+
off_t offset;
- Output_section* os = layout->layout(this, i, name, shdr, &offset);
+ Output_section* os = layout->layout(this, i, name, shdr,
+ reloc_shndx[i], reloc_type[i],
+ &offset);
map_sections[i].output_section = os;
map_sections[i].offset = offset;
+
+ // If this section requires special handling, and if there are
+ // relocs that apply to it, then we must do the special handling
+ // before we apply the relocs.
+ if (offset == -1 && reloc_shndx[i] != 0)
+ this->set_relocs_must_follow_section_writes();
}
layout->layout_gnu_stack(seen_gnu_stack, gnu_stack_flags);
+ // Handle the .eh_frame sections at the end.
+ for (std::vector<unsigned int>::const_iterator p = eh_frame_sections.begin();
+ p != eh_frame_sections.end();
+ ++p)
+ {
+ gold_assert(this->has_eh_frame_);
+ gold_assert(sd->external_symbols_offset != 0);
+
+ unsigned int i = *p;
+ const unsigned char *pshdr;
+ pshdr = sd->section_headers->data() + i * This::shdr_size;
+ typename This::Shdr shdr(pshdr);
+
+ off_t offset;
+ Output_section* os = layout->layout_eh_frame(this,
+ sd->symbols->data(),
+ sd->symbols_size,
+ sd->symbol_names->data(),
+ sd->symbol_names_size,
+ i, shdr,
+ reloc_shndx[i],
+ reloc_type[i],
+ &offset);
+ map_sections[i].output_section = os;
+ map_sections[i].offset = offset;
+
+ // If this section requires special handling, and if there are
+ // relocs that apply to it, then we must do the special handling
+ // before we apply the relocs.
+ if (offset == -1 && reloc_shndx[i] != 0)
+ this->set_relocs_must_follow_section_writes();
+ }
+
delete sd->section_headers;
sd->section_headers = NULL;
delete sd->section_names;
@@ -519,19 +695,23 @@ Sized_relobj<size, big_endian>::do_add_symbols(Symbol_table* symtab,
}
const int sym_size = This::sym_size;
- size_t symcount = sd->symbols_size / sym_size;
- if (static_cast<off_t>(symcount * sym_size) != sd->symbols_size)
+ size_t symcount = ((sd->symbols_size - sd->external_symbols_offset)
+ / sym_size);
+ if (static_cast<off_t>(symcount * sym_size)
+ != sd->symbols_size - sd->external_symbols_offset)
{
this->error(_("size of symbols is not multiple of symbol size"));
return;
}
- this->symbols_ = new Symbol*[symcount];
+ this->symbols_.resize(symcount);
const char* sym_names =
reinterpret_cast<const char*>(sd->symbol_names->data());
- symtab->add_from_relobj(this, sd->symbols->data(), symcount, sym_names,
- sd->symbol_names_size, this->symbols_);
+ symtab->add_from_relobj(this,
+ sd->symbols->data() + sd->external_symbols_offset,
+ symcount, sym_names, sd->symbol_names_size,
+ &this->symbols_);
delete sd->symbols;
sd->symbols = NULL;
diff --git a/gold/object.h b/gold/object.h
index 9fdb9a3..8aa40a2 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -57,6 +57,10 @@ struct Read_symbols_data
File_view* symbols;
// Size of symbol data in bytes.
off_t symbols_size;
+ // Offset of external symbols within symbol data. This structure
+ // sometimes contains only external symbols, in which case this will
+ // be zero. Sometimes it contains all symbols.
+ off_t external_symbols_offset;
// Symbol names.
File_view* symbol_names;
// Size of symbol name data in bytes.
@@ -100,6 +104,10 @@ struct Section_relocs
unsigned int sh_type;
// Number of reloc entries.
size_t reloc_count;
+ // Output section.
+ Output_section* output_section;
+ // Whether this section has special handling for offsets.
+ bool needs_special_offset_handling;
};
// Relocations in an object file. This is read in read_relocs and
@@ -197,6 +205,11 @@ class Object
section_flags(unsigned int shndx)
{ return this->do_section_flags(shndx); }
+ // Return the section type given a section index.
+ unsigned int
+ section_type(unsigned int shndx)
+ { return this->do_section_type(shndx); }
+
// Return the section link field given a section index.
unsigned int
section_link(unsigned int shndx)
@@ -291,6 +304,10 @@ class Object
virtual uint64_t
do_section_flags(unsigned int shndx) = 0;
+ // Get section type--implemented by child class.
+ virtual unsigned int
+ do_section_type(unsigned int shndx) = 0;
+
// Get section link field--implemented by child class.
virtual unsigned int
do_section_link(unsigned int shndx) = 0;
@@ -421,9 +438,21 @@ class Relobj : public Object
return this->map_to_output_[shndx].output_section != NULL;
}
+ // Return whether an input section requires special
+ // handling--whether it is not simply mapped from the input file to
+ // the output file.
+ bool
+ is_section_specially_mapped(unsigned int shndx) const
+ {
+ gold_assert(shndx < this->map_to_output_.size());
+ return (this->map_to_output_[shndx].output_section != NULL
+ && this->map_to_output_[shndx].offset == -1);
+ }
+
// Given a section index, return the corresponding Output_section
// (which will be NULL if the section is not included in the link)
- // and set *POFF to the offset within that section.
+ // and set *POFF to the offset within that section. *POFF will be
+ // set to -1 if the section requires special handling.
inline Output_section*
output_section(unsigned int shndx, off_t* poff) const;
@@ -435,6 +464,14 @@ class Relobj : public Object
this->map_to_output_[shndx].offset = off;
}
+ // Return true if we need to wait for output sections to be written
+ // before we can apply relocations. This is true if the object has
+ // any relocations for sections which require special handling, such
+ // as the exception frame section.
+ bool
+ relocs_must_follow_section_writes()
+ { return this->relocs_must_follow_section_writes_; }
+
protected:
// What we need to know to map an input section to an output
// section. We keep an array of these, one for each input section,
@@ -478,9 +515,18 @@ class Relobj : public Object
map_to_output() const
{ return this->map_to_output_; }
+ // Record that we must wait for the output sections to be written
+ // before applying relocations.
+ void
+ set_relocs_must_follow_section_writes()
+ { this->relocs_must_follow_section_writes_ = true; }
+
private:
// Mapping from input sections to output section.
std::vector<Map_to_output> map_to_output_;
+ // Whether we need to wait for output sections to be written before
+ // we can apply relocations.
+ bool relocs_must_follow_section_writes_;
};
// Implement Object::output_section inline for efficiency.
@@ -495,8 +541,8 @@ Relobj::output_section(unsigned int shndx, off_t* poff) const
// This POD class is holds the value of a symbol. This is used for
// local symbols, and for all symbols during relocation processing.
-// In order to process relocs we need to be able to handle SHF_MERGE
-// sections correctly.
+// For special sections, such as SHF_MERGE sections, this calls a
+// function to get the final symbol value.
template<int size>
class Symbol_value
@@ -577,7 +623,10 @@ class Symbol_value
// Set the index of the input section in the input file.
void
set_input_shndx(unsigned int i)
- { this->input_shndx_ = i; }
+ {
+ this->input_shndx_ = i;
+ gold_assert(this->input_shndx_ == i);
+ }
// Record that this is a section symbol.
void
@@ -610,6 +659,7 @@ class Sized_relobj : public Relobj
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+ typedef std::vector<Symbol*> Symbols;
typedef std::vector<Symbol_value<size> > Local_values;
Sized_relobj(const std::string& name, Input_file* input_file, off_t offset,
@@ -621,6 +671,41 @@ class Sized_relobj : public Relobj
void
setup(const typename elfcpp::Ehdr<size, big_endian>&);
+ // Return the number of local symbols.
+ unsigned int
+ local_symbol_count() const
+ { return this->local_symbol_count_; }
+
+ // If SYM is the index of a global symbol in the object file's
+ // symbol table, return the Symbol object. Otherwise, return NULL.
+ Symbol*
+ global_symbol(unsigned int sym) const
+ {
+ if (sym >= this->local_symbol_count_)
+ {
+ gold_assert(sym - this->local_symbol_count_ < this->symbols_.size());
+ return this->symbols_[sym - this->local_symbol_count_];
+ }
+ return NULL;
+ }
+
+ // Return the section index of symbol SYM. Set *VALUE to its value
+ // in the object file. Note that for a symbol which is not defined
+ // in this object file, this will set *VALUE to 0 and return
+ // SHN_UNDEF; it will not return the final value of the symbol in
+ // the link.
+ unsigned int
+ symbol_section_and_value(unsigned int sym, Address* value);
+
+ // Return a pointer to the Symbol_value structure which holds the
+ // value of a local symbol.
+ const Symbol_value<size>*
+ local_symbol(unsigned int sym) const
+ {
+ gold_assert(sym < this->local_values_.size());
+ return &this->local_values_[sym];
+ }
+
// Return the index of local symbol SYM in the ordinary symbol
// table. A value of -1U means that the symbol is not being output.
unsigned int
@@ -731,6 +816,11 @@ class Sized_relobj : public Relobj
do_section_flags(unsigned int shndx)
{ return this->elf_file_.section_flags(shndx); }
+ // Return section type.
+ unsigned int
+ do_section_type(unsigned int shndx)
+ { return this->elf_file_.section_type(shndx); }
+
// Return the section link field.
unsigned int
do_section_link(unsigned int shndx)
@@ -748,6 +838,17 @@ class Sized_relobj : public Relobj
void
find_symtab(const unsigned char* pshdrs);
+ // Return whether SHDR has the right flags for a GNU style exception
+ // frame section.
+ bool
+ check_eh_frame_flags(const elfcpp::Shdr<size, big_endian>* shdr) const;
+
+ // Return whether there is a section named .eh_frame which might be
+ // a GNU style exception frame section.
+ bool
+ find_eh_frame(const unsigned char* pshdrs, const char* names,
+ off_t names_size) const;
+
// Whether to include a section group in the link.
bool
include_section_group(Layout*, unsigned int,
@@ -766,6 +867,7 @@ class Sized_relobj : public Relobj
typename elfcpp::Elf_types<size>::Elf_Addr address;
off_t offset;
off_t view_size;
+ bool is_input_output_view;
};
typedef std::vector<View_size> Views;
@@ -797,13 +899,15 @@ class Sized_relobj : public Relobj
// The number of local symbols which go into the output file.
unsigned int output_local_symbol_count_;
// The entries in the symbol table for the external symbols.
- Symbol** symbols_;
+ Symbols symbols_;
// File offset for local symbols.
off_t local_symbol_offset_;
// Values of local symbols.
Local_values local_values_;
// GOT offsets for local symbols, indexed by symbol number.
Local_got_offsets local_got_offsets_;
+ // Whether this object has a GNU style .eh_frame section.
+ bool has_eh_frame_;
};
// A class to manage the list of all objects.
@@ -891,12 +995,6 @@ struct Relocate_info
const Layout* layout;
// Object being relocated.
Sized_relobj<size, big_endian>* object;
- // Number of local symbols.
- unsigned int local_symbol_count;
- // Values of local symbols.
- const typename Sized_relobj<size, big_endian>::Local_values* local_values;
- // Global symbols.
- const Symbol* const * symbols;
// Section index of relocation section.
unsigned int reloc_shndx;
// Section index of section being relocated.
diff --git a/gold/output.cc b/gold/output.cc
index 64aa4dc..ca99097 100644
--- a/gold/output.cc
+++ b/gold/output.cc
@@ -63,10 +63,18 @@ Output_data::set_address(uint64_t addr, off_t off)
this->do_set_address(addr, off);
}
+// Return the default alignment for the target size.
+
+uint64_t
+Output_data::default_alignment()
+{
+ return Output_data::default_alignment_for_size(parameters->get_size());
+}
+
// Return the default alignment for a size--32 or 64.
uint64_t
-Output_data::default_alignment(int size)
+Output_data::default_alignment_for_size(int size)
{
if (size == 32)
return 4;
@@ -569,7 +577,14 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::write_rel(
Output_section* os = this->u2_.relobj->output_section(this->shndx_,
&off);
gold_assert(os != NULL);
- address += os->address() + off;
+ if (off != -1)
+ address += os->address() + off;
+ else
+ {
+ address = os->output_address(this->u2_.relobj, this->shndx_,
+ address);
+ gold_assert(address != -1U);
+ }
}
else if (this->u2_.od != NULL)
address += this->u2_.od->address();
@@ -941,27 +956,25 @@ Output_section::Input_section::set_address(uint64_t addr, off_t off,
this->u2_.posd->set_address(addr, off);
}
-// Try to turn an input address into an output address.
+// Try to turn an input offset into an output offset.
bool
-Output_section::Input_section::output_address(const Relobj* object,
- unsigned int shndx,
- off_t offset,
- uint64_t output_section_address,
- uint64_t *poutput) const
+Output_section::Input_section::output_offset(const Relobj* object,
+ unsigned int shndx,
+ off_t offset,
+ off_t *poutput) const
{
if (!this->is_input_section())
- return this->u2_.posd->output_address(object, shndx, offset,
- output_section_address, poutput);
+ return this->u2_.posd->output_offset(object, shndx, offset, poutput);
else
{
- if (this->shndx_ != shndx
- || this->u2_.object != object)
+ if (this->shndx_ != shndx || this->u2_.object != object)
return false;
off_t output_offset;
Output_section* os = object->output_section(shndx, &output_offset);
gold_assert(os != NULL);
- *poutput = output_section_address + output_offset + offset;
+ gold_assert(output_offset != -1);
+ *poutput = output_offset + offset;
return true;
}
}
@@ -1001,7 +1014,8 @@ Output_section::Output_section(const char* name, elfcpp::Elf_Word type,
needs_symtab_index_(false),
needs_dynsym_index_(false),
should_link_to_symtab_(false),
- should_link_to_dynsym_(false)
+ should_link_to_dynsym_(false),
+ after_input_sections_(false)
{
}
@@ -1021,16 +1035,22 @@ Output_section::set_entsize(uint64_t v)
}
// Add the input section SHNDX, with header SHDR, named SECNAME, in
-// OBJECT, to the Output_section. Return the offset of the input
-// section within the output section. We don't always keep track of
-// input sections for an Output_section. Instead, each Object keeps
-// track of the Output_section for each of its input sections.
+// OBJECT, to the Output_section. RELOC_SHNDX is the index of a
+// relocation section which applies to this section, or 0 if none, or
+// -1U if more than one. Return the offset of the input section
+// within the output section. Return -1 if the input section will
+// receive special handling. In the normal case we don't always keep
+// track of input sections for an Output_section. Instead, each
+// Object keeps track of the Output_section for each of its input
+// sections.
template<int size, bool big_endian>
off_t
-Output_section::add_input_section(Relobj* object, unsigned int shndx,
+Output_section::add_input_section(Sized_relobj<size, big_endian>* object,
+ unsigned int shndx,
const char* secname,
- const elfcpp::Shdr<size, big_endian>& shdr)
+ const elfcpp::Shdr<size, big_endian>& shdr,
+ unsigned int reloc_shndx)
{
elfcpp::Elf_Xword addralign = shdr.get_sh_addralign();
if ((addralign & (addralign - 1)) != 0)
@@ -1044,15 +1064,17 @@ Output_section::add_input_section(Relobj* object, unsigned int shndx,
this->addralign_ = addralign;
// If this is a SHF_MERGE section, we pass all the input sections to
- // a Output_data_merge.
- if ((shdr.get_sh_flags() & elfcpp::SHF_MERGE) != 0)
+ // a Output_data_merge. We don't try to handle relocations for such
+ // a section.
+ if ((shdr.get_sh_flags() & elfcpp::SHF_MERGE) != 0
+ && reloc_shndx == 0)
{
if (this->add_merge_input_section(object, shndx, shdr.get_sh_flags(),
shdr.get_sh_entsize(),
addralign))
{
// Tell the relocation routines that they need to call the
- // output_address method to determine the final address.
+ // output_offset method to determine the final address.
return -1;
}
}
@@ -1176,6 +1198,57 @@ Output_section::add_merge_input_section(Relobj* object, unsigned int shndx,
return true;
}
+// Given an address OFFSET relative to the start of input section
+// SHNDX in OBJECT, return whether this address is being included in
+// the final link. This should only be called if SHNDX in OBJECT has
+// a special mapping.
+
+bool
+Output_section::is_input_address_mapped(const Relobj* object,
+ unsigned int shndx,
+ off_t offset) const
+{
+ gold_assert(object->is_section_specially_mapped(shndx));
+
+ for (Input_section_list::const_iterator p = this->input_sections_.begin();
+ p != this->input_sections_.end();
+ ++p)
+ {
+ off_t output_offset;
+ if (p->output_offset(object, shndx, offset, &output_offset))
+ return output_offset != -1;
+ }
+
+ // By default we assume that the address is mapped. This should
+ // only be called after we have passed all sections to Layout. At
+ // that point we should know what we are discarding.
+ return true;
+}
+
+// Given an address OFFSET relative to the start of input section
+// SHNDX in object OBJECT, return the output offset relative to the
+// start of the section. This should only be called if SHNDX in
+// OBJECT has a special mapping.
+
+off_t
+Output_section::output_offset(const Relobj* object, unsigned int shndx,
+ off_t offset) const
+{
+ gold_assert(object->is_section_specially_mapped(shndx));
+ // This can only be called meaningfully when layout is complete.
+ gold_assert(Output_data::is_layout_complete());
+
+ for (Input_section_list::const_iterator p = this->input_sections_.begin();
+ p != this->input_sections_.end();
+ ++p)
+ {
+ off_t output_offset;
+ if (p->output_offset(object, shndx, offset, &output_offset))
+ return output_offset;
+ }
+ gold_unreachable();
+}
+
// Return the output virtual address of OFFSET relative to the start
// of input section SHNDX in object OBJECT.
@@ -1183,15 +1256,23 @@ uint64_t
Output_section::output_address(const Relobj* object, unsigned int shndx,
off_t offset) const
{
+ gold_assert(object->is_section_specially_mapped(shndx));
+ // This can only be called meaningfully when layout is complete.
+ gold_assert(Output_data::is_layout_complete());
+
uint64_t addr = this->address() + this->first_input_offset_;
for (Input_section_list::const_iterator p = this->input_sections_.begin();
p != this->input_sections_.end();
++p)
{
addr = align_address(addr, p->addralign());
- uint64_t output;
- if (p->output_address(object, shndx, offset, addr, &output))
- return output;
+ off_t output_offset;
+ if (p->output_offset(object, shndx, offset, &output_offset))
+ {
+ if (output_offset == -1)
+ return -1U;
+ return addr + output_offset;
+ }
addr += p->data_size();
}
@@ -1739,40 +1820,44 @@ Output_file::close()
template
off_t
Output_section::add_input_section<32, false>(
- Relobj* object,
+ Sized_relobj<32, false>* object,
unsigned int shndx,
const char* secname,
- const elfcpp::Shdr<32, false>& shdr);
+ const elfcpp::Shdr<32, false>& shdr,
+ unsigned int reloc_shndx);
#endif
#ifdef HAVE_TARGET_32_BIG
template
off_t
Output_section::add_input_section<32, true>(
- Relobj* object,
+ Sized_relobj<32, true>* object,
unsigned int shndx,
const char* secname,
- const elfcpp::Shdr<32, true>& shdr);
+ const elfcpp::Shdr<32, true>& shdr,
+ unsigned int reloc_shndx);
#endif
#ifdef HAVE_TARGET_64_LITTLE
template
off_t
Output_section::add_input_section<64, false>(
- Relobj* object,
+ Sized_relobj<64, false>* object,
unsigned int shndx,
const char* secname,
- const elfcpp::Shdr<64, false>& shdr);
+ const elfcpp::Shdr<64, false>& shdr,
+ unsigned int reloc_shndx);
#endif
#ifdef HAVE_TARGET_64_BIG
template
off_t
Output_section::add_input_section<64, true>(
- Relobj* object,
+ Sized_relobj<64, true>* object,
unsigned int shndx,
const char* secname,
- const elfcpp::Shdr<64, true>& shdr);
+ const elfcpp::Shdr<64, true>& shdr,
+ unsigned int reloc_shndx);
#endif
#ifdef HAVE_TARGET_32_LITTLE
diff --git a/gold/output.h b/gold/output.h
index b6c1245..053579f 100644
--- a/gold/output.h
+++ b/gold/output.h
@@ -29,7 +29,6 @@
#include "elfcpp.h"
#include "layout.h"
#include "reloc-types.h"
-#include "parameters.h"
namespace gold
{
@@ -124,6 +123,11 @@ class Output_data
layout_complete()
{ Output_data::sizes_are_fixed = true; }
+ // Used to check that layout has been done.
+ static bool
+ is_layout_complete()
+ { return Output_data::sizes_are_fixed; }
+
protected:
// Functions that child classes may or in some cases must implement.
@@ -179,9 +183,13 @@ class Output_data
this->data_size_ = data_size;
}
- // Return default alignment for a size--32 or 64.
+ // Return default alignment for the target size.
+ static uint64_t
+ default_alignment();
+
+ // Return default alignment for a specified size--32 or 64.
static uint64_t
- default_alignment(int size);
+ default_alignment_for_size(int size);
private:
Output_data(const Output_data&);
@@ -216,7 +224,7 @@ class Output_section_headers : public Output_data
// Return the required alignment.
uint64_t
do_addralign() const
- { return Output_data::default_alignment(parameters->get_size()); }
+ { return Output_data::default_alignment(); }
private:
// Write the data to the file with the right size and endianness.
@@ -244,7 +252,7 @@ class Output_segment_headers : public Output_data
// Return the required alignment.
uint64_t
do_addralign() const
- { return Output_data::default_alignment(parameters->get_size()); }
+ { return Output_data::default_alignment(); }
private:
// Write the data to the file with the right size and endianness.
@@ -276,7 +284,7 @@ class Output_file_header : public Output_data
// Return the required alignment.
uint64_t
do_addralign() const
- { return Output_data::default_alignment(parameters->get_size()); }
+ { return Output_data::default_alignment(); }
// Set the address and offset--we only implement this for error
// checking.
@@ -330,17 +338,14 @@ class Output_section_data : public Output_data
// 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 output address is known.
- // OUTPUT_SECTION_ADDRESS is the address of the output section which
- // this is a part of. If this function returns true, it sets
- // *POUTPUT to the output address.
+ // 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.
virtual bool
- output_address(const Relobj* object, unsigned int shndx, off_t offset,
- uint64_t output_section_address, uint64_t *poutput) const
- {
- return this->do_output_address(object, shndx, offset,
- output_section_address, poutput);
- }
+ output_offset(const Relobj* object, unsigned int shndx, off_t offset,
+ off_t *poutput) const
+ { return this->do_output_offset(object, shndx, offset, poutput); }
protected:
// The child class must implement do_write.
@@ -357,10 +362,9 @@ class Output_section_data : public Output_data
do_add_input_section(Relobj*, unsigned int)
{ gold_unreachable(); }
- // The child class may implement output_address.
+ // The child class may implement output_offset.
virtual bool
- do_output_address(const Relobj*, unsigned int, off_t, uint64_t,
- uint64_t*) const
+ do_output_offset(const Relobj*, unsigned int, off_t, off_t*) const
{ return false; }
// Return the required alignment.
@@ -736,7 +740,7 @@ class Output_data_reloc_base : public Output_section_data
// Construct the section.
Output_data_reloc_base()
- : Output_section_data(Output_data::default_alignment(size))
+ : Output_section_data(Output_data::default_alignment_for_size(size))
{ }
// Write out the data.
@@ -901,7 +905,8 @@ class Output_data_got : public Output_section_data
typedef typename elfcpp::Elf_types<size>::Elf_Addr Valtype;
Output_data_got()
- : Output_section_data(Output_data::default_alignment(size)), entries_()
+ : Output_section_data(Output_data::default_alignment_for_size(size)),
+ entries_()
{ }
// Add an entry for a global symbol to the GOT. Return true if this
@@ -1013,8 +1018,7 @@ class Output_data_dynamic : public Output_section_data
{
public:
Output_data_dynamic(Stringpool* pool)
- : Output_section_data(Output_data::default_alignment(
- parameters->get_size())),
+ : Output_section_data(Output_data::default_alignment()),
entries_(), pool_(pool)
{ }
@@ -1155,11 +1159,15 @@ class Output_section : public Output_data
virtual ~Output_section();
// Add a new input section SHNDX, named NAME, with header SHDR, from
- // object OBJECT. Return the offset within the output section.
+ // object OBJECT. RELOC_SHNDX is the index of a relocation section
+ // which applies to this section, or 0 if none, or -1U if more than
+ // one. Return the offset within the output section.
template<int size, bool big_endian>
off_t
- add_input_section(Relobj* object, unsigned int shndx, const char *name,
- const elfcpp::Shdr<size, big_endian>& shdr);
+ add_input_section(Sized_relobj<size, big_endian>* object, unsigned int shndx,
+ const char *name,
+ const elfcpp::Shdr<size, big_endian>& shdr,
+ unsigned int reloc_shndx);
// Add generated data POSD to this output section.
void
@@ -1326,6 +1334,29 @@ class Output_section : public Output_data
this->dynsym_index_ = index;
}
+ // Return whether this section should be written after all the input
+ // sections are complete.
+ bool
+ after_input_sections() const
+ { return this->after_input_sections_; }
+
+ // Record that this section should be written after all the input
+ // sections are complete.
+ void
+ set_after_input_sections()
+ { this->after_input_sections_ = true; }
+
+ // Return whether the offset OFFSET in the input section SHNDX in
+ // object OBJECT is being included in the link.
+ bool
+ is_input_address_mapped(const Relobj* object, unsigned int shndx,
+ off_t offset) const;
+
+ // Return the offset within the output section of OFFSET relative to
+ // the start of input section SHNDX in object OBJECT.
+ off_t
+ output_offset(const Relobj* object, unsigned int shndx, off_t offset) const;
+
// Return the output virtual address of OFFSET relative to the start
// of input section SHNDX in object OBJECT.
uint64_t
@@ -1477,13 +1508,12 @@ class Output_section : public Output_data
// 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 output address is known.
- // OUTPUT_SECTION_ADDRESS is the address of the output section
- // which this is a part of. If this function returns true, it
- // sets *POUTPUT to the output address.
+ // section, return whether or not the output offset is known. If
+ // this function returns true, it sets *POUTPUT to the output
+ // offset.
bool
- output_address(const Relobj* object, unsigned int shndx, off_t offset,
- uint64_t output_section_address, uint64_t *poutput) const;
+ output_offset(const Relobj* object, unsigned int shndx, off_t offset,
+ off_t *poutput) const;
// Write out the data. This does nothing for an input section.
void
@@ -1648,6 +1678,9 @@ class Output_section : public Output_data
// Whether the link field of this output section should point to the
// dynamic symbol table.
bool should_link_to_dynsym_ : 1;
+ // Whether this section should be written after all the input
+ // sections are complete.
+ bool after_input_sections_ : 1;
};
// An output segment. PT_LOAD segments are built from collections of
@@ -1847,6 +1880,28 @@ class Output_file
write_output_view(off_t, off_t, unsigned char*)
{ }
+ // Get a read/write buffer. This is used when we want to write part
+ // of the file, read it in, and write it again.
+ unsigned char*
+ get_input_output_view(off_t start, off_t size)
+ { return this->get_output_view(start, size); }
+
+ // Write a read/write buffer back to the file.
+ void
+ write_input_output_view(off_t, off_t, unsigned char*)
+ { }
+
+ // Get a read buffer. This is used when we just want to read part
+ // of the file back it in.
+ const unsigned char*
+ get_input_view(off_t start, off_t size)
+ { return this->get_output_view(start, size); }
+
+ // Release a read bfufer.
+ void
+ free_input_view(off_t, off_t, const unsigned char*)
+ { }
+
private:
// General options.
const General_options& options_;
diff --git a/gold/po/gold.pot b/gold/po/gold.pot
index d79701c..140ee86 100644
--- a/gold/po/gold.pot
+++ b/gold/po/gold.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-02 16:01-0700\n"
+"POT-Creation-Date: 2007-11-08 22:56-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -66,110 +66,110 @@ msgstr ""
msgid "%s: can not read directory: %s"
msgstr ""
-#: dynobj.cc:128
+#: dynobj.cc:151
#, c-format
msgid "unexpected duplicate type %u section: %u, %u"
msgstr ""
-#: dynobj.cc:164
+#: dynobj.cc:187
#, c-format
msgid "unexpected link in section %u header: %u != %u"
msgstr ""
-#: dynobj.cc:199
+#: dynobj.cc:222
#, c-format
msgid "DYNAMIC section %u link out of range: %u"
msgstr ""
-#: dynobj.cc:207
+#: dynobj.cc:230
#, c-format
msgid "DYNAMIC section %u link %u is not a strtab"
msgstr ""
-#: dynobj.cc:227
+#: dynobj.cc:250
#, c-format
msgid "DT_SONAME value out of range: %lld >= %lld"
msgstr ""
-#: dynobj.cc:242
+#: dynobj.cc:265
msgid "missing DT_NULL in dynamic segment"
msgstr ""
-#: dynobj.cc:285
+#: dynobj.cc:309
#, c-format
msgid "invalid dynamic symbol table name index: %u"
msgstr ""
-#: dynobj.cc:292
+#: dynobj.cc:316
#, c-format
msgid "dynamic symbol table name section has wrong type: %u"
msgstr ""
-#: dynobj.cc:365 object.cc:447
+#: dynobj.cc:389 object.cc:233 object.cc:568
#, c-format
msgid "bad section name offset for section %u: %lu"
msgstr ""
-#: dynobj.cc:394
+#: dynobj.cc:418
#, c-format
msgid "duplicate definition for version %u"
msgstr ""
-#: dynobj.cc:423
+#: dynobj.cc:447
#, c-format
msgid "unexpected verdef version %u"
msgstr ""
-#: dynobj.cc:439
+#: dynobj.cc:463
#, c-format
msgid "verdef vd_cnt field too small: %u"
msgstr ""
-#: dynobj.cc:446
+#: dynobj.cc:470
#, c-format
msgid "verdef vd_aux field out of range: %u"
msgstr ""
-#: dynobj.cc:456
+#: dynobj.cc:480
#, c-format
msgid "verdaux vda_name field out of range: %u"
msgstr ""
-#: dynobj.cc:465
+#: dynobj.cc:489
#, c-format
msgid "verdef vd_next field out of range: %u"
msgstr ""
-#: dynobj.cc:498
+#: dynobj.cc:522
#, c-format
msgid "unexpected verneed version %u"
msgstr ""
-#: dynobj.cc:507
+#: dynobj.cc:531
#, c-format
msgid "verneed vn_aux field out of range: %u"
msgstr ""
-#: dynobj.cc:520
+#: dynobj.cc:544
#, c-format
msgid "vernaux vna_name field out of range: %u"
msgstr ""
-#: dynobj.cc:531
+#: dynobj.cc:555
#, c-format
msgid "verneed vna_next field out of range: %u"
msgstr ""
-#: dynobj.cc:542
+#: dynobj.cc:566
#, c-format
msgid "verneed vn_next field out of range: %u"
msgstr ""
-#: dynobj.cc:588
+#: dynobj.cc:613
msgid "size of dynamic symbols is not multiple of symbol size"
msgstr ""
-#: dynobj.cc:1265
+#: dynobj.cc:1290
#, c-format
msgid "symbol %s has undefined version %s"
msgstr ""
@@ -317,54 +317,54 @@ msgid "pthread_cond_signal failed: %s"
msgstr ""
#. FIXME: This needs to specify the location somehow.
-#: i386.cc:150 i386.cc:1296 x86_64.cc:162 x86_64.cc:1228
+#: i386.cc:153 i386.cc:1301 x86_64.cc:165 x86_64.cc:1233
msgid "missing expected TLS relocation"
msgstr ""
-#: i386.cc:746 x86_64.cc:709 x86_64.cc:876
+#: i386.cc:749 x86_64.cc:712 x86_64.cc:879
#, c-format
msgid "%s: unsupported reloc %u against local symbol"
msgstr ""
-#: i386.cc:840 i386.cc:1070 x86_64.cc:817 x86_64.cc:1055
+#: i386.cc:843 i386.cc:1073 x86_64.cc:820 x86_64.cc:1058
#, c-format
msgid "%s: unexpected reloc %u in object file"
msgstr ""
-#: i386.cc:926 x86_64.cc:890 x86_64.cc:1114
+#: i386.cc:929 x86_64.cc:893 x86_64.cc:1117
#, c-format
msgid "%s: unsupported reloc %u against global symbol %s"
msgstr ""
-#: i386.cc:1166
+#: i386.cc:1170
#, c-format
msgid "%s: unsupported RELA reloc section"
msgstr ""
-#: i386.cc:1423 x86_64.cc:1426
+#: i386.cc:1428 x86_64.cc:1431
#, c-format
msgid "unexpected reloc %u in object file"
msgstr ""
-#: i386.cc:1455 i386.cc:1502 i386.cc:1509 i386.cc:1529 i386.cc:1558
-#: x86_64.cc:1447 x86_64.cc:1496 x86_64.cc:1507
+#: i386.cc:1460 i386.cc:1507 i386.cc:1514 i386.cc:1534 i386.cc:1563
+#: x86_64.cc:1452 x86_64.cc:1501 x86_64.cc:1512
#, c-format
msgid "unsupported reloc %u"
msgstr ""
-#: i386.cc:1480 x86_64.cc:1472
+#: i386.cc:1485 x86_64.cc:1477
msgid "TLS reloc but no TLS segment"
msgstr ""
-#: i386.cc:1517
+#: i386.cc:1522
msgid "both SUN and GNU model TLS relocations"
msgstr ""
-#: merge.cc:258
+#: merge.cc:283
msgid "mergeable string section length not multiple of character size"
msgstr ""
-#: merge.cc:274
+#: merge.cc:299
msgid "entry in mergeable string section not null terminated"
msgstr ""
@@ -383,112 +383,117 @@ msgstr ""
msgid "section name section has wrong type: %u"
msgstr ""
-#: object.cc:244
+#: object.cc:305
#, c-format
msgid "invalid symbol table name index: %u"
msgstr ""
-#: object.cc:250
+#: object.cc:311
#, c-format
msgid "symbol table name section has wrong type: %u"
msgstr ""
-#: object.cc:305
+#: object.cc:391
#, c-format
msgid "section group %u info %u out of range"
msgstr ""
-#: object.cc:323
+#: object.cc:409
#, c-format
msgid "symbol %u name offset %u out of range"
msgstr ""
-#: object.cc:355
+#: object.cc:441
#, c-format
msgid "section %u in section group %u out of range"
msgstr ""
-#: object.cc:525
+#: object.cc:531 reloc.cc:202 reloc.cc:469
+#, c-format
+msgid "relocation section %u has bad info %u"
+msgstr ""
+
+#: object.cc:703
msgid "size of symbols is not multiple of symbol size"
msgstr ""
#. FIXME: Handle SHN_XINDEX.
-#: object.cc:615
+#: object.cc:795
#, c-format
msgid "unknown section index %u for local symbol %u"
msgstr ""
-#: object.cc:624
+#: object.cc:804
#, c-format
msgid "local symbol %u section index %u out of range"
msgstr ""
-#: object.cc:656
+#: object.cc:836
#, c-format
msgid "local symbol %u section name out of range: %u >= %u"
msgstr ""
-#: object.cc:892
+#: object.cc:1054
#, c-format
msgid "%s: incompatible target"
msgstr ""
-#: object.cc:994
+#: object.cc:1175
#, c-format
msgid "%s: unsupported ELF file type %d"
msgstr ""
-#: object.cc:1013 object.cc:1059 object.cc:1093
+#: object.cc:1194 object.cc:1240 object.cc:1274
#, c-format
msgid "%s: ELF file too short"
msgstr ""
-#: object.cc:1021
+#: object.cc:1202
#, c-format
msgid "%s: invalid ELF version 0"
msgstr ""
-#: object.cc:1023
+#: object.cc:1204
#, c-format
msgid "%s: unsupported ELF version %d"
msgstr ""
-#: object.cc:1030
+#: object.cc:1211
#, c-format
msgid "%s: invalid ELF class 0"
msgstr ""
-#: object.cc:1036
+#: object.cc:1217
#, c-format
msgid "%s: unsupported ELF class %d"
msgstr ""
-#: object.cc:1043
+#: object.cc:1224
#, c-format
msgid "%s: invalid ELF data encoding"
msgstr ""
-#: object.cc:1049
+#: object.cc:1230
#, c-format
msgid "%s: unsupported ELF data encoding %d"
msgstr ""
-#: object.cc:1069
+#: object.cc:1250
#, c-format
msgid "%s: not configured to support 32-bit big-endian object"
msgstr ""
-#: object.cc:1082
+#: object.cc:1263
#, c-format
msgid "%s: not configured to support 32-bit little-endian object"
msgstr ""
-#: object.cc:1103
+#: object.cc:1284
#, c-format
msgid "%s: not configured to support 64-bit big-endian object"
msgstr ""
-#: object.cc:1116
+#: object.cc:1297
#, c-format
msgid "%s: not configured to support 64-bit little-endian object"
msgstr ""
@@ -772,37 +777,37 @@ msgstr ""
msgid "%s: invalid thread count: %s\n"
msgstr ""
-#: output.cc:1038
+#: output.cc:1058
#, c-format
msgid "invalid alignment %lu for section \"%s\""
msgstr ""
-#: output.cc:1703
+#: output.cc:1784
#, c-format
msgid "%s: open: %s"
msgstr ""
-#: output.cc:1708
+#: output.cc:1789
#, c-format
msgid "%s: lseek: %s"
msgstr ""
-#: output.cc:1711
+#: output.cc:1792
#, c-format
msgid "%s: write: %s"
msgstr ""
-#: output.cc:1717
+#: output.cc:1798
#, c-format
msgid "%s: mmap: %s"
msgstr ""
-#: output.cc:1727
+#: output.cc:1808
#, c-format
msgid "%s: munmap: %s"
msgstr ""
-#: output.cc:1731
+#: output.cc:1812
#, c-format
msgid "%s: close: %s"
msgstr ""
@@ -823,26 +828,26 @@ msgstr ""
msgid "%s: not an object or archive"
msgstr ""
-#: reloc.cc:190 reloc.cc:431
-#, c-format
-msgid "relocation section %u has bad info %u"
-msgstr ""
-
-#: reloc.cc:208 reloc.cc:447
+#: reloc.cc:221 reloc.cc:487
#, c-format
msgid "relocation section %u uses unexpected symbol table %u"
msgstr ""
-#: reloc.cc:223 reloc.cc:465
+#: reloc.cc:236 reloc.cc:505
#, c-format
msgid "unexpected entsize for reloc section %u: %lu != %u"
msgstr ""
-#: reloc.cc:232 reloc.cc:474
+#: reloc.cc:245 reloc.cc:514
#, c-format
msgid "reloc section %u size %lu uneven"
msgstr ""
+#: reloc.cc:702
+#, c-format
+msgid "reloc section size %zu is not a multiple of reloc size %d\n"
+msgstr ""
+
#: resolve.cc:165
#, c-format
msgid "%s: invalid STB_LOCAL symbol %s in external symbols"
@@ -907,7 +912,7 @@ msgstr ""
msgid "%s: unsupported symbol section 0x%x"
msgstr ""
-#: target-reloc.h:191
+#: target-reloc.h:211
#, c-format
msgid "reloc has bad offset %zu"
msgstr ""
@@ -935,12 +940,12 @@ msgid ""
"This program has absolutely no warranty.\n"
msgstr ""
-#: x86_64.cc:1137
+#: x86_64.cc:1141
#, c-format
msgid "%s: unsupported REL reloc section"
msgstr ""
-#: x86_64.cc:1535
+#: x86_64.cc:1540
#, c-format
msgid "unsupported reloc type %u"
msgstr ""
diff --git a/gold/reloc.cc b/gold/reloc.cc
index 7647edf..e72c134 100644
--- a/gold/reloc.cc
+++ b/gold/reloc.cc
@@ -116,34 +116,44 @@ Scan_relocs::run(Workqueue*)
// Relocate_task methods.
-// These tasks are always runnable.
+// We may have to wait for the output sections to be written.
Task::Is_runnable_type
Relocate_task::is_runnable(Workqueue*)
{
+ if (this->object_->relocs_must_follow_section_writes()
+ && this->output_sections_blocker_->is_blocked())
+ return IS_BLOCKED;
+
return IS_RUNNABLE;
}
// We want to lock the file while we run. We want to unblock
-// FINAL_BLOCKER when we are done.
+// INPUT_SECTIONS_BLOCKER and FINAL_BLOCKER when we are done.
class Relocate_task::Relocate_locker : public Task_locker
{
public:
- Relocate_locker(Task_token& token, Workqueue* workqueue,
+ Relocate_locker(Task_token& input_sections_blocker,
+ Task_token& final_blocker, Workqueue* workqueue,
Object* object)
- : blocker_(token, workqueue), objlock_(*object)
+ : input_sections_blocker_(input_sections_blocker, workqueue),
+ final_blocker_(final_blocker, workqueue),
+ objlock_(*object)
{ }
private:
- Task_locker_block blocker_;
+ Task_block_token input_sections_blocker_;
+ Task_block_token final_blocker_;
Task_locker_obj<Object> objlock_;
};
Task_locker*
Relocate_task::locks(Workqueue* workqueue)
{
- return new Relocate_locker(*this->final_blocker_, workqueue,
+ return new Relocate_locker(*this->input_sections_blocker_,
+ *this->final_blocker_,
+ workqueue,
this->object_);
}
@@ -171,6 +181,8 @@ Sized_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
rd->relocs.reserve(shnum / 2);
+ std::vector<Map_to_output>& map_sections(this->map_to_output());
+
const unsigned char *pshdrs = this->get_view(this->elf_file_.shoff(),
shnum * This::shdr_size,
true);
@@ -192,7 +204,8 @@ Sized_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
continue;
}
- if (!this->is_section_included(shndx))
+ Output_section* os = map_sections[shndx].output_section;
+ if (os == NULL)
continue;
// We are scanning relocations in order to fill out the GOT and
@@ -242,6 +255,8 @@ Sized_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
true);
sr.sh_type = sh_type;
sr.reloc_count = reloc_count;
+ sr.output_section = os;
+ sr.needs_special_offset_handling = map_sections[shndx].offset == -1;
}
// Read the local symbols.
@@ -286,9 +301,9 @@ Sized_relobj<size, big_endian>::do_scan_relocs(const General_options& options,
{
target->scan_relocs(options, symtab, layout, this, p->data_shndx,
p->sh_type, p->contents->data(), p->reloc_count,
+ p->output_section, p->needs_special_offset_handling,
this->local_symbol_count_,
- local_symbols,
- this->symbols_);
+ local_symbols);
delete p->contents;
p->contents = NULL;
}
@@ -333,8 +348,14 @@ Sized_relobj<size, big_endian>::do_relocate(const General_options& options,
for (unsigned int i = 1; i < shnum; ++i)
{
if (views[i].view != NULL)
- of->write_output_view(views[i].offset, views[i].view_size,
- views[i].view);
+ {
+ if (views[i].is_input_output_view)
+ of->write_input_output_view(views[i].offset, views[i].view_size,
+ views[i].view);
+ else
+ of->write_output_view(views[i].offset, views[i].view_size,
+ views[i].view);
+ }
}
// Write out the local symbols.
@@ -361,34 +382,52 @@ Sized_relobj<size, big_endian>::write_sections(const unsigned char* pshdrs,
pvs->view = NULL;
- if (map_sections[i].offset == -1)
- continue;
-
const Output_section* os = map_sections[i].output_section;
if (os == NULL)
continue;
+ off_t output_offset = map_sections[i].offset;
typename This::Shdr shdr(p);
if (shdr.get_sh_type() == elfcpp::SHT_NOBITS)
continue;
- off_t start = os->offset() + map_sections[i].offset;
- off_t sh_size = shdr.get_sh_size();
+ off_t view_start;
+ off_t view_size;
+ if (output_offset != -1)
+ {
+ view_start = os->offset() + output_offset;
+ view_size = shdr.get_sh_size();
+ }
+ else
+ {
+ view_start = os->offset();
+ view_size = os->data_size();
+ }
- if (sh_size == 0)
+ if (view_size == 0)
continue;
- gold_assert(map_sections[i].offset >= 0
- && map_sections[i].offset + sh_size <= os->data_size());
+ gold_assert(output_offset == -1
+ || (output_offset >= 0
+ && output_offset + view_size <= os->data_size()));
- unsigned char* view = of->get_output_view(start, sh_size);
- this->read(shdr.get_sh_offset(), sh_size, view);
+ unsigned char* view;
+ if (output_offset == -1)
+ view = of->get_input_output_view(view_start, view_size);
+ else
+ {
+ view = of->get_output_view(view_start, view_size);
+ this->read(shdr.get_sh_offset(), view_size, view);
+ }
pvs->view = view;
- pvs->address = os->address() + map_sections[i].offset;
- pvs->offset = start;
- pvs->view_size = sh_size;
+ pvs->address = os->address();
+ if (output_offset != -1)
+ pvs->address += output_offset;
+ pvs->offset = view_start;
+ pvs->view_size = view_size;
+ pvs->is_input_output_view = output_offset == -1;
}
}
@@ -407,14 +446,13 @@ Sized_relobj<size, big_endian>::relocate_sections(
unsigned int shnum = this->shnum();
Sized_target<size, big_endian>* target = this->sized_target();
+ std::vector<Map_to_output>& map_sections(this->map_to_output());
+
Relocate_info<size, big_endian> relinfo;
relinfo.options = &options;
relinfo.symtab = symtab;
relinfo.layout = layout;
relinfo.object = this;
- relinfo.local_symbol_count = this->local_symbol_count_;
- relinfo.local_values = &this->local_values_;
- relinfo.symbols = this->symbols_;
const unsigned char* p = pshdrs + This::shdr_size;
for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size)
@@ -433,12 +471,14 @@ Sized_relobj<size, big_endian>::relocate_sections(
continue;
}
- if (!this->is_section_included(index))
+ Output_section* os = map_sections[index].output_section;
+ if (os == NULL)
{
// This relocation section is against a section which we
// discarded.
continue;
}
+ off_t output_offset = map_sections[index].offset;
gold_assert((*pviews)[index].view != NULL);
@@ -464,7 +504,7 @@ Sized_relobj<size, big_endian>::relocate_sections(
{
gold_error(_("unexpected entsize for reloc section %u: %lu != %u"),
i, static_cast<unsigned long>(shdr.get_sh_entsize()),
- reloc_size);
+ reloc_size);
continue;
}
@@ -482,6 +522,8 @@ Sized_relobj<size, big_endian>::relocate_sections(
sh_type,
prelocs,
reloc_count,
+ os,
+ output_offset == -1,
(*pviews)[index].view,
(*pviews)[index].address,
(*pviews)[index].view_size);
@@ -621,6 +663,104 @@ Copy_relocs<size, big_endian>::emit(
}
}
+// Track_relocs methods.
+
+// Initialize the class to track the relocs. This gets the object,
+// the reloc section index, and the type of the relocs. This returns
+// false if something goes wrong.
+
+template<int size, bool big_endian>
+bool
+Track_relocs<size, big_endian>::initialize(
+ Sized_relobj<size, big_endian>* object,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type)
+{
+ this->object_ = object;
+
+ // If RELOC_SHNDX is -1U, it means there is more than one reloc
+ // section for the .eh_frame section. We can't handle that case.
+ if (reloc_shndx == -1U)
+ return false;
+
+ // If RELOC_SHNDX is 0, there is no reloc section.
+ if (reloc_shndx == 0)
+ return true;
+
+ // Get the contents of the reloc section.
+ this->prelocs_ = object->section_contents(reloc_shndx, &this->len_, false);
+
+ if (reloc_type == elfcpp::SHT_REL)
+ this->reloc_size_ = elfcpp::Elf_sizes<size>::rel_size;
+ else if (reloc_type == elfcpp::SHT_RELA)
+ this->reloc_size_ = elfcpp::Elf_sizes<size>::rela_size;
+ else
+ gold_unreachable();
+
+ if (this->len_ % this->reloc_size_ != 0)
+ {
+ object->error(_("reloc section size %zu is not a multiple of "
+ "reloc size %d\n"),
+ static_cast<size_t>(this->len_),
+ this->reloc_size_);
+ return false;
+ }
+
+ return true;
+}
+
+// Return the offset of the next reloc, or -1 if there isn't one.
+
+template<int size, bool big_endian>
+off_t
+Track_relocs<size, big_endian>::next_offset() const
+{
+ if (this->pos_ >= this->len_)
+ return -1;
+
+ // Rel and Rela start out the same, so we can always use Rel to find
+ // the r_offset value.
+ elfcpp::Rel<size, big_endian> rel(this->prelocs_ + this->pos_);
+ return rel.get_r_offset();
+}
+
+// Return the index of the symbol referenced by the next reloc, or -1U
+// if there aren't any more relocs.
+
+template<int size, bool big_endian>
+unsigned int
+Track_relocs<size, big_endian>::next_symndx() const
+{
+ if (this->pos_ >= this->len_)
+ return -1U;
+
+ // Rel and Rela start out the same, so we can use Rel to find the
+ // symbol index.
+ elfcpp::Rel<size, big_endian> rel(this->prelocs_ + this->pos_);
+ return elfcpp::elf_r_sym<size>(rel.get_r_info());
+}
+
+// Advance to the next reloc whose r_offset is greater than or equal
+// to OFFSET. Return the number of relocs we skip.
+
+template<int size, bool big_endian>
+int
+Track_relocs<size, big_endian>::advance(off_t offset)
+{
+ int ret = 0;
+ while (this->pos_ < this->len_)
+ {
+ // Rel and Rela start out the same, so we can always use Rel to
+ // find the r_offset value.
+ elfcpp::Rel<size, big_endian> rel(this->prelocs_ + this->pos_);
+ if (static_cast<off_t>(rel.get_r_offset()) >= offset)
+ break;
+ ++ret;
+ this->pos_ += this->reloc_size_;
+ }
+ return ret;
+}
+
// Instantiate the templates we need. We could use the configure
// script to restrict this to only the ones for implemented targets.
@@ -796,4 +936,24 @@ Copy_relocs<64, true>::emit<elfcpp::SHT_RELA>(
Output_data_reloc<elfcpp::SHT_RELA, true, 64, true>*);
#endif
+#ifdef HAVE_TARGET_32_LITTLE
+template
+class Track_relocs<32, false>;
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+class Track_relocs<32, true>;
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+class Track_relocs<64, false>;
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+class Track_relocs<64, true>;
+#endif
+
} // End namespace gold.
diff --git a/gold/reloc.h b/gold/reloc.h
index 1bf0457..5d2a160 100644
--- a/gold/reloc.h
+++ b/gold/reloc.h
@@ -129,9 +129,12 @@ class Relocate_task : public Task
public:
Relocate_task(const General_options& options, const Symbol_table* symtab,
const Layout* layout, Relobj* object, Output_file* of,
- Task_token* final_blocker)
+ Task_token* input_sections_blocker,
+ Task_token* output_sections_blocker, Task_token* final_blocker)
: options_(options), symtab_(symtab), layout_(layout), object_(object),
- of_(of), final_blocker_(final_blocker)
+ of_(of), input_sections_blocker_(input_sections_blocker),
+ output_sections_blocker_(output_sections_blocker),
+ final_blocker_(final_blocker)
{ }
// The standard Task methods.
@@ -153,6 +156,8 @@ class Relocate_task : public Task
const Layout* layout_;
Relobj* object_;
Output_file* of_;
+ Task_token* input_sections_blocker_;
+ Task_token* output_sections_blocker_;
Task_token* final_blocker_;
};
@@ -594,6 +599,55 @@ class Copy_relocs
Copy_reloc_entries entries_;
};
+// Track relocations while reading a section. This lets you ask for
+// the relocation at a certain offset, and see how relocs occur
+// between points of interest.
+
+template<int size, bool big_endian>
+class Track_relocs
+{
+ public:
+ Track_relocs()
+ : object_(NULL), prelocs_(NULL), len_(0), pos_(0), reloc_size_(0)
+ { }
+
+ // Initialize the Track_relocs object. OBJECT is the object holding
+ // the reloc section, RELOC_SHNDX is the section index of the reloc
+ // section, and RELOC_TYPE is the type of the reloc section
+ // (elfcpp::SHT_REL or elfcpp::SHT_RELA). This returns false if
+ // something went wrong.
+ bool
+ initialize(Sized_relobj<size, big_endian>* object, unsigned int reloc_shndx,
+ unsigned int reloc_type);
+
+ // Return the offset in the data section to which the next reloc
+ // applies. THis returns -1 if there is no next reloc.
+ off_t
+ next_offset() const;
+
+ // Return the symbol index of the next reloc. This returns -1U if
+ // there is no next reloc.
+ unsigned int
+ next_symndx() const;
+
+ // Advance to OFFSET within the data section, and return the number
+ // of relocs which would be skipped.
+ int
+ advance(off_t offset);
+
+ private:
+ // The object file.
+ Sized_relobj<size, big_endian>* object_;
+ // The contents of the reloc section.
+ const unsigned char* prelocs_;
+ // The length of the reloc section.
+ off_t len_;
+ // Our current position in the reloc section.
+ off_t pos_;
+ // The size of the relocs in the section.
+ int reloc_size_;
+};
+
} // End namespace gold.
#endif // !defined(GOLD_RELOC_H)
diff --git a/gold/stringpool.cc b/gold/stringpool.cc
index bb571f0f..db04b0c 100644
--- a/gold/stringpool.cc
+++ b/gold/stringpool.cc
@@ -103,7 +103,7 @@ Stringpool_template<Stringpool_char>::Stringpool_hash::operator()(
const Stringpool_char* s) const
{
// Fowler/Noll/Vo (FNV) hash (type FNV-1a).
- if (sizeof(size_t) == 8)
+ if (sizeof(size_t) > 4)
{
size_t result = static_cast<size_t>(14695981039346656037ULL);
while (*s != 0)
diff --git a/gold/symtab.cc b/gold/symtab.cc
index 3108433..3c2a976 100644
--- a/gold/symtab.cc
+++ b/gold/symtab.cc
@@ -521,7 +521,7 @@ Symbol_table::add_from_relobj(
size_t count,
const char* sym_names,
size_t sym_name_size,
- Symbol** sympointers)
+ typename Sized_relobj<size, big_endian>::Symbols* sympointers)
{
gold_assert(size == relobj->target()->get_size());
gold_assert(size == parameters->get_size());
@@ -592,7 +592,7 @@ Symbol_table::add_from_relobj(
def, *psym);
}
- *sympointers++ = res;
+ (*sympointers)[i] = res;
}
}
@@ -1870,7 +1870,7 @@ Symbol_table::add_from_relobj<32, false>(
size_t count,
const char* sym_names,
size_t sym_name_size,
- Symbol** sympointers);
+ Sized_relobj<32, true>::Symbols* sympointers);
#endif
#ifdef HAVE_TARGET_32_BIG
@@ -1882,7 +1882,7 @@ Symbol_table::add_from_relobj<32, true>(
size_t count,
const char* sym_names,
size_t sym_name_size,
- Symbol** sympointers);
+ Sized_relobj<32, false>::Symbols* sympointers);
#endif
#ifdef HAVE_TARGET_64_LITTLE
@@ -1894,7 +1894,7 @@ Symbol_table::add_from_relobj<64, false>(
size_t count,
const char* sym_names,
size_t sym_name_size,
- Symbol** sympointers);
+ Sized_relobj<64, true>::Symbols* sympointers);
#endif
#ifdef HAVE_TARGET_64_BIG
@@ -1906,7 +1906,7 @@ Symbol_table::add_from_relobj<64, true>(
size_t count,
const char* sym_names,
size_t sym_name_size,
- Symbol** sympointers);
+ Sized_relobj<64, false>::Symbols* sympointers);
#endif
#ifdef HAVE_TARGET_32_LITTLE
diff --git a/gold/symtab.h b/gold/symtab.h
index 05c8d7a..38320f9 100644
--- a/gold/symtab.h
+++ b/gold/symtab.h
@@ -833,7 +833,7 @@ class Symbol_table
add_from_relobj(Sized_relobj<size, big_endian>* relobj,
const unsigned char* syms, size_t count,
const char* sym_names, size_t sym_name_size,
- Symbol** sympointers);
+ typename Sized_relobj<size, big_endian>::Symbols*);
// Add COUNT dynamic symbols from the dynamic object DYNOBJ to the
// symbol table. SYMS is the symbols. SYM_NAMES is their names.
diff --git a/gold/target-reloc.h b/gold/target-reloc.h
index c38d5f6..0498e9a 100644
--- a/gold/target-reloc.h
+++ b/gold/target-reloc.h
@@ -24,7 +24,6 @@
#define GOLD_TARGET_RELOC_H
#include "elfcpp.h"
-#include "object.h"
#include "symtab.h"
#include "reloc-types.h"
@@ -49,9 +48,10 @@ scan_relocs(
unsigned int data_shndx,
const unsigned char* prelocs,
size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
size_t local_count,
- const unsigned char* plocal_syms,
- Symbol** global_syms)
+ const unsigned char* plocal_syms)
{
typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype;
const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size;
@@ -62,6 +62,11 @@ scan_relocs(
{
Reltype reloc(prelocs);
+ if (needs_special_offset_handling
+ && !output_section->is_input_address_mapped(object, data_shndx,
+ reloc.get_r_offset()))
+ continue;
+
typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info();
unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
@@ -99,7 +104,7 @@ scan_relocs(
}
else
{
- Symbol* gsym = global_syms[r_sym - local_count];
+ Symbol* gsym = object->global_symbol(r_sym);
gold_assert(gsym != NULL);
if (gsym->is_forwarder())
gsym = symtab->resolve_forwards(gsym);
@@ -122,8 +127,14 @@ scan_relocs(
// RELOCATE implements operator() to do a relocation.
// PRELOCS points to the relocation data. RELOC_COUNT is the number
-// of relocs. VIEW is the section data, VIEW_ADDRESS is its memory
-// address, and VIEW_SIZE is the size.
+// of relocs. OUTPUT_SECTION is the output section.
+// NEEDS_SPECIAL_OFFSET_HANDLING is true if input offsets need to be
+// mapped to output offsets.
+
+// VIEW is the section data, VIEW_ADDRESS is its memory address, and
+// VIEW_SIZE is the size. These refer to the input section, unless
+// NEEDS_SPECIAL_OFFSET_HANDLING is true, in which case they refer to
+// the output section.
template<int size, bool big_endian, typename Target_type, int sh_type,
typename Relocate>
@@ -133,6 +144,8 @@ relocate_section(
Target_type* target,
const unsigned char* prelocs,
size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr view_address,
off_t view_size)
@@ -141,10 +154,8 @@ relocate_section(
const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size;
Relocate relocate;
- unsigned int local_count = relinfo->local_symbol_count;
- const typename Sized_relobj<size, big_endian>::Local_values* local_values =
- relinfo->local_values;
- const Symbol* const * global_syms = relinfo->symbols;
+ Sized_relobj<size, big_endian>* object = relinfo->object;
+ unsigned int local_count = object->local_symbol_count();
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
{
@@ -152,6 +163,15 @@ relocate_section(
off_t offset = reloc.get_r_offset();
+ if (needs_special_offset_handling)
+ {
+ offset = output_section->output_offset(relinfo->object,
+ relinfo->data_shndx,
+ offset);
+ if (offset == -1)
+ continue;
+ }
+
typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info();
unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
@@ -163,11 +183,11 @@ relocate_section(
if (r_sym < local_count)
{
sym = NULL;
- psymval = &(*local_values)[r_sym];
+ psymval = object->local_symbol(r_sym);
}
else
{
- const Symbol* gsym = global_syms[r_sym - local_count];
+ const Symbol* gsym = object->global_symbol(r_sym);
gold_assert(gsym != NULL);
if (gsym->is_forwarder())
gsym = relinfo->symtab->resolve_forwards(gsym);
diff --git a/gold/target.h b/gold/target.h
index 32166d1..8ecc078 100644
--- a/gold/target.h
+++ b/gold/target.h
@@ -43,11 +43,12 @@ class Object;
template<int size, bool big_endian>
class Sized_relobj;
template<int size, bool big_endian>
-struct Relocate_info;
+class Relocate_info;
class Symbol;
template<int size>
class Sized_symbol;
class Symbol_table;
+class Output_section;
// The abstract class for target specific handling.
@@ -228,9 +229,11 @@ class Sized_target : public Target
// relocs apply to. SH_TYPE is the type of the relocation section,
// SHT_REL or SHT_RELA. PRELOCS points to the relocation data.
// RELOC_COUNT is the number of relocs. LOCAL_SYMBOL_COUNT is the
- // number of local symbols. PLOCAL_SYMBOLS points to the local
- // symbol data from OBJECT. GLOBAL_SYMBOLS is the array of pointers
- // to the global symbol table from OBJECT.
+ // number of local symbols. OUTPUT_SECTION is the output section.
+ // NEEDS_SPECIAL_OFFSET_HANDLING is true if offsets to the output
+ // sections are not mapped as usual. PLOCAL_SYMBOLS points to the
+ // local symbol data from OBJECT. GLOBAL_SYMBOLS is the array of
+ // pointers to the global symbol table from OBJECT.
virtual void
scan_relocs(const General_options& options,
Symbol_table* symtab,
@@ -240,21 +243,29 @@ class Sized_target : public Target
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
size_t local_symbol_count,
- const unsigned char* plocal_symbols,
- Symbol** global_symbols) = 0;
+ const unsigned char* plocal_symbols) = 0;
// Relocate section data. SH_TYPE is the type of the relocation
// section, SHT_REL or SHT_RELA. PRELOCS points to the relocation
- // information. RELOC_COUNT is the number of relocs. VIEW is a
- // view into the output file holding the section contents,
- // VIEW_ADDRESS is the virtual address of the view, and VIEW_SIZE is
- // the size of the view.
+ // information. RELOC_COUNT is the number of relocs.
+ // OUTPUT_SECTION is the output section.
+ // NEEDS_SPECIAL_OFFSET_HANDLING is true if offsets must be mapped
+ // to correspond to the output section. VIEW is a view into the
+ // output file holding the section contents, VIEW_ADDRESS is the
+ // virtual address of the view, and VIEW_SIZE is the size of the
+ // view. If NEEDS_SPECIAL_OFFSET_HANDLING is true, the VIEW_xx
+ // parameters refer to the complete output section data, not just
+ // the input section data.
virtual void
relocate_section(const Relocate_info<size, big_endian>*,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr view_address,
off_t view_size) = 0;
diff --git a/gold/testsuite/testfile.cc b/gold/testsuite/testfile.cc
index 8e6a48d..89bad1d 100644
--- a/gold/testsuite/testfile.cc
+++ b/gold/testsuite/testfile.cc
@@ -46,14 +46,15 @@ class Target_test : public Sized_target<size, big_endian>
void
scan_relocs(const General_options&, Symbol_table*, Layout*,
Sized_relobj<size, big_endian>*, unsigned int,
- unsigned int, const unsigned char*, size_t, size_t,
- const unsigned char*, Symbol**)
+ unsigned int, const unsigned char*, size_t, Output_section*,
+ bool, size_t, const unsigned char*)
{ ERROR("call to Target_test::scan_relocs"); }
void
relocate_section(const Relocate_info<size, big_endian>*, unsigned int,
- const unsigned char*, size_t, unsigned char*,
- typename elfcpp::Elf_types<size>::Elf_Addr, off_t)
+ const unsigned char*, size_t, Output_section*, bool,
+ unsigned char*, typename elfcpp::Elf_types<size>::Elf_Addr,
+ off_t)
{ ERROR("call to Target_test::relocate_section"); }
static const Target::Target_info test_target_info;
diff --git a/gold/x86_64.cc b/gold/x86_64.cc
index 223523e..3be64cf 100644
--- a/gold/x86_64.cc
+++ b/gold/x86_64.cc
@@ -83,9 +83,10 @@ class Target_x86_64 : public Sized_target<64, false>
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
size_t local_symbol_count,
- const unsigned char* plocal_symbols,
- Symbol** global_symbols);
+ const unsigned char* plocal_symbols);
// Finalize the sections.
void
@@ -102,6 +103,8 @@ class Target_x86_64 : public Sized_target<64, false>
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
unsigned char* view,
elfcpp::Elf_types<64>::Elf_Addr view_address,
off_t view_size);
@@ -1128,9 +1131,10 @@ Target_x86_64::scan_relocs(const General_options& options,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
size_t local_symbol_count,
- const unsigned char* plocal_symbols,
- Symbol** global_symbols)
+ const unsigned char* plocal_symbols)
{
if (sh_type == elfcpp::SHT_REL)
{
@@ -1149,9 +1153,10 @@ Target_x86_64::scan_relocs(const General_options& options,
data_shndx,
prelocs,
reloc_count,
+ output_section,
+ needs_special_offset_handling,
local_symbol_count,
- plocal_symbols,
- global_symbols);
+ plocal_symbols);
}
// Finalize the sections.
@@ -1670,6 +1675,8 @@ Target_x86_64::relocate_section(const Relocate_info<64, false>* relinfo,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
unsigned char* view,
elfcpp::Elf_types<64>::Elf_Addr address,
off_t view_size)
@@ -1682,6 +1689,8 @@ Target_x86_64::relocate_section(const Relocate_info<64, false>* relinfo,
this,
prelocs,
reloc_count,
+ output_section,
+ needs_special_offset_handling,
view,
address,
view_size);