* target.h (Target::plt_fde_location, do_plt_fde_location): Declare.
authorAlan Modra <amodra@gmail.com>
Wed, 27 Feb 2013 23:11:56 +0000 (23:11 +0000)
committerAlan Modra <amodra@gmail.com>
Wed, 27 Feb 2013 23:11:56 +0000 (23:11 +0000)
* target.cc (Target::do_plt_fde_location): New function.
* ehframe.h (class FDE): Add post_map field to u_.from_linker,
accessor function, and constructor param.
(struct Post_fde, Post_fdes): Declare.
(Cie::write): Add post_fdes param.
* ehframe.cc (Fde::write): Use plt_fde_location.
(struct Post_fde): Define.
(Cie::write): Stash FDEs added post merge mapping.
(Eh_frame::add_ehframe_for_plt): Assert no new CIEs after mapping.
Adjust Fde constructor call.  Bump final_data_size_ for post map FDEs.
(Eh_frame::do_sized_write): Arrange to write post map FDES after
other FDEs.
* powerpc.cc (Target_powerpc::do_plt_fde_location): New function.
(Target_powerpc::has_glink): New function.
(Target_powerpc::do_relax): Add eh_frame info for stubs.
(struct Eh_cie, eh_frame_cie, glink_eh_frame_fde_64,
glink_eh_frame_fde_32, default_fde): New data.
(Stub_table::eh_frame_added_): New var.
(Stub_table::find_long_branch_entry, stub_address, stub_offset):
Make const.
(Stub_table::add_eh_frame): New function.
(Output_data_glink::add_eh_frame): New function.
(Target_powerpc::make_glink_section): Call add_eh_frame.

gold/ChangeLog
gold/ehframe.cc
gold/ehframe.h
gold/powerpc.cc
gold/target.cc
gold/target.h

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