Generate a complete exception frame header. Discard duplicate
authorIan Lance Taylor <iant@google.com>
Fri, 9 Nov 2007 07:00:15 +0000 (07:00 +0000)
committerIan Lance Taylor <iant@google.com>
Fri, 9 Nov 2007 07:00:15 +0000 (07:00 +0000)
exception frame information.

24 files changed:
gold/dynobj.cc
gold/dynobj.h
gold/ehframe.cc
gold/ehframe.h
gold/gold.cc
gold/i386.cc
gold/layout.cc
gold/layout.h
gold/merge.cc
gold/merge.h
gold/object.cc
gold/object.h
gold/output.cc
gold/output.h
gold/po/gold.pot
gold/reloc.cc
gold/reloc.h
gold/stringpool.cc
gold/symtab.cc
gold/symtab.h
gold/target-reloc.h
gold/target.h
gold/testsuite/testfile.cc
gold/x86_64.cc

index 95ffe087337a6346080afc2460c023331d240ee6..9845194b3b6a87a4cf92c4e66b30dba83a01e983 100644 (file)
@@ -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"));
index d9c3e107530e576e96cd134d7beb7fb4a48e4f21..7895ddac72f0d335cb2d8a2e30684e3aa3f2aeb8 100644 (file)
@@ -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)
index 607654b2a2a61f9dd07fe3d95e807fa5f3b78eb1..1c7b7141aba7d3884064047436fcb741804fb659 100644 (file)
 
 #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,13 +93,69 @@ 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.
 
 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();
@@ -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.
index 0641ac866f5b4aa2ef96e7eccda7ea8e9f97e8ca..6a584f97133b88352f441c0f03a29f7d8d4c109f 100644 (file)
 #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.
index 9a3a1e6818d6eb857352c2a2b794534b1988b6be..83bb3f6065b3c09b03ed0d200eceb29d693bbc53 100644 (file)
@@ -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),
index 699aa682a09244dd3a24d79ef404957b34b97e99..d0b0b5128828635ae4184c21075d90d8fd0815e1 100644 (file)
@@ -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);
index 84e8722714c607a2d7e7af00b219cee4b12be985..6894264272caddb15a2124fbcac630c807633649 100644 (file)
@@ -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.
index 5e9871e38c4d20e3b449f42410a2b52de98678ff..fa804ad45f19c0cbb448d04a446412651292625b 100644 (file)
@@ -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
index 13bfc177848b9cfede7f98aae6b08aaf596d2fad..15d3b7cd1bd9e510d2390963019881bc5627b4e8 100644 (file)
 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());
index cc554f26f4dc3cc964a22dbfed692f826cab93b1..630b5936ec0c3eb9012c583ca08db37d475fd4d0 100644 (file)
 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)
     { }
   };
 
index 0b71be1256919b71b0803230ede9f002c113fd42..d334b172d7d841f26df44358a5bb9bfca05134e4 100644 (file)
@@ -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;
index 9fdb9a372083000437ce33b2e19016d42741e5d5..8aa40a2e182850a1ad8d5565045f9bae2744222f 100644 (file)
@@ -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.
index 64aa4dc872bd2d5b99ebb4a69cbf5ff879c31a0d..ca990976e3476df9142463bf3bf8c247abf8adf7 100644 (file)
@@ -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
index b6c12456d01584154063a97ed9fe4d9c025e701d..053579f9364f1f73da3ba7a503384ad0a24a9fc4 100644 (file)
@@ -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_;
index d79701c3363f1bfd765e62a7265d6b90cf306edf..140ee8671f82031c0834eae95742b2c2d413f8dd 100644 (file)
@@ -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 ""
index 7647edf511038ebd842830ebcbcb45f0c3cacf16..e72c134e1d5840fbe2697c3c90bbb509d0ff3f26 100644 (file)
@@ -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.
index 1bf0457ecc3fb197cb777506d1ad1339736edd78..5d2a160e41c1d2410b6fd0815837d95d8a9ab240 100644 (file)
@@ -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)
index bb571f0fba2e90d80dd5b1ae76333d8018ab282b..db04b0c704faa1abfddbd47d199761d1a4ed903b 100644 (file)
@@ -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)
index 3108433799ba5750f95fd513b77a2be664a9eb0a..3c2a9761a09a42fd266745a49cf2972468c4c4f3 100644 (file)
@@ -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
index 05c8d7a85fea191c39ce0f0fa87ac30b9444deab..38320f9d8f2b4774ee76c3fa214950321914af5b 100644 (file)
@@ -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.
index c38d5f6a74ba3dcb243a15914f01f9893a771e48..0498e9a3385fca4c9fd56d07fe15f715779a7f9f 100644 (file)
@@ -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);
index 32166d12a7091b6efdd675454fc14c89b01f519b..8ecc078330d76efeace35f8dfd10e1bd57459586 100644 (file)
@@ -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;
index 8e6a48d0b4b8f5b38d5db6881683aa94c12d3b74..89bad1d17f91953ea8c8d6888f5336b81ec52a2f 100644 (file)
@@ -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;
index 223523eb5f7470cbe20c11460ab57436379b02b0..3be64cfed5b5850874e717ee5cfcac3e86987bfb 100644 (file)
@@ -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);