2010-01-21 Doug Kwan <dougkwan@google.com>
authorDoug Kwan <dougkwan@google.com>
Fri, 22 Jan 2010 06:06:46 +0000 (06:06 +0000)
committerDoug Kwan <dougkwan@google.com>
Fri, 22 Jan 2010 06:06:46 +0000 (06:06 +0000)
elfcpp/ChangeLog:

* arm.h (EXIDX_CANTUNWIND): New enum.

gold/ChangeLog:

* arm.cc (Arm_exidx_cantunwind, Arm_exidx_merged_section): New
classes.
(Arm_exidx_section_offset_map): New type.

elfcpp/ChangeLog
elfcpp/arm.h
gold/ChangeLog
gold/arm.cc

index ab3d2df..a6c75cd 100644 (file)
@@ -1,3 +1,7 @@
+2010-01-21  Doug Kwan  <dougkwan@google.com>
+
+       * arm.h (EXIDX_CANTUNWIND): New enum.
+
 2010-01-19  Ian Lance Taylor  <iant@google.com>
 
        * elfcpp.h (PN_XNUM): Define.
index 08a17ad..397d0d0 100644 (file)
@@ -321,6 +321,13 @@ enum
   AEABI_enum_forced_wide = 3
 };
 
+// For Exception Index Table. (Exception handling ABI for the ARM
+// architectue, Section 5)
+enum
+{
+  EXIDX_CANTUNWIND = 1,
+};
+
 } // End namespace elfcpp.
 
 #endif // !defined(ELFCPP_ARM_H)
index 3d2b181..6cb3d92 100644 (file)
@@ -1,5 +1,11 @@
 2010-01-21  Doug Kwan  <dougkwan@google.com>
 
+       * arm.cc (Arm_exidx_cantunwind, Arm_exidx_merged_section): New
+       classes.
+       (Arm_exidx_section_offset_map): New type.
+
+2010-01-21  Doug Kwan  <dougkwan@google.com>
+
        * arm.cc (Arm_exidx_input_section): New class.
        (Arm_relobj::exidx_input_section_by_link,
        Arm_relobj::exidx_input_section_by_shndx,
index b7d369c..1e2b8e6 100644 (file)
@@ -64,6 +64,10 @@ class Stub_table;
 template<bool big_endian>
 class Arm_input_section;
 
+class Arm_exidx_cantunwind;
+
+class Arm_exidx_merged_section;
+
 template<bool big_endian>
 class Arm_output_section;
 
@@ -1027,6 +1031,97 @@ class Stub_table : public Output_data
   uint64_t prev_addralign_;
 };
 
+// Arm_exidx_cantunwind class.  This represents an EXIDX_CANTUNWIND entry
+// we add to the end of an EXIDX input section that goes into the output.
+
+class Arm_exidx_cantunwind : public Output_section_data
+{
+ public:
+  Arm_exidx_cantunwind(Relobj* relobj, unsigned int shndx)
+    : Output_section_data(8, 4, true), relobj_(relobj), shndx_(shndx)
+  { }
+
+  // Return the object containing the section pointed by this.
+  Relobj*
+  relobj() const
+  { return this->relobj_; }
+
+  // Return the section index of the section pointed by this.
+  unsigned int
+  shndx() const
+  { return this->shndx_; }
+
+ protected:
+  void
+  do_write(Output_file* of)
+  {
+    if (parameters->target().is_big_endian())
+      this->do_fixed_endian_write<true>(of);
+    else
+      this->do_fixed_endian_write<false>(of);
+  }
+
+ private:
+  // Implement do_write for a given endianity.
+  template<bool big_endian>
+  void inline
+  do_fixed_endian_write(Output_file*);
+  
+  // The object containing the section pointed by this.
+  Relobj* relobj_;
+  // The section index of the section pointed by this.
+  unsigned int shndx_;
+};
+
+// During EXIDX coverage fix-up, we compact an EXIDX section.  The
+// Offset map is used to map input section offset within the EXIDX section
+// to the output offset from the start of this EXIDX section. 
+
+typedef std::map<section_offset_type, section_offset_type>
+       Arm_exidx_section_offset_map;
+
+// Arm_exidx_merged_section class.  This represents an EXIDX input section
+// with some of its entries merged.
+
+class Arm_exidx_merged_section : public Output_relaxed_input_section
+{
+ public:
+  // Constructor for Arm_exidx_merged_section.
+  // EXIDX_INPUT_SECTION points to the unmodified EXIDX input section.
+  // SECTION_OFFSET_MAP points to a section offset map describing how
+  // parts of the input section are mapped to output.  DELETED_BYTES is
+  // the number of bytes deleted from the EXIDX input section.
+  Arm_exidx_merged_section(
+      const Arm_exidx_input_section& exidx_input_section,
+      const Arm_exidx_section_offset_map& section_offset_map,
+      uint32_t deleted_bytes);
+
+  // Return the original EXIDX input section.
+  const Arm_exidx_input_section&
+  exidx_input_section() const
+  { return this->exidx_input_section_; }
+
+  // Return the section offset map.
+  const Arm_exidx_section_offset_map&
+  section_offset_map() const
+  { return this->section_offset_map_; }
+
+ protected:
+  // Write merged section into file OF.
+  void
+  do_write(Output_file* of);
+
+  bool
+  do_output_offset(const Relobj*, unsigned int, section_offset_type,
+                 section_offset_type*) const;
+
+ private:
+  // Original EXIDX input section.
+  const Arm_exidx_input_section& exidx_input_section_;
+  // Section offset map.
+  const Arm_exidx_section_offset_map& section_offset_map_;
+};
+
 // A class to wrap an ordinary input section containing executable code.
 
 template<bool big_endian>
@@ -4329,6 +4424,177 @@ Arm_input_section<big_endian>::do_reset_address_and_file_offset()
   this->set_current_data_size(off);
 }
 
+// Arm_exidx_cantunwind methods.
+
+// Write this to Output file OF for a fixed endianity.
+
+template<bool big_endian>
+void
+Arm_exidx_cantunwind::do_fixed_endian_write(Output_file* of)
+{
+  off_t offset = this->offset();
+  const section_size_type oview_size = 8;
+  unsigned char* const oview = of->get_output_view(offset, oview_size);
+  
+  typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+  Valtype* wv = reinterpret_cast<Valtype*>(oview);
+
+  Output_section* os = this->relobj_->output_section(this->shndx_);
+  gold_assert(os != NULL);
+
+  Arm_relobj<big_endian>* arm_relobj =
+    Arm_relobj<big_endian>::as_arm_relobj(this->relobj_);
+  Arm_address output_offset =
+    arm_relobj->get_output_section_offset(this->shndx_);
+  Arm_address section_start;
+  if(output_offset != Arm_relobj<big_endian>::invalid_address)
+    section_start = os->address() + output_offset;
+  else
+    {
+      // Currently this only happens for a relaxed section.
+      const Output_relaxed_input_section* poris =
+       os->find_relaxed_input_section(this->relobj_, this->shndx_);
+      gold_assert(poris != NULL);
+      section_start = poris->address();
+    }
+
+  // We always append this to the end of an EXIDX section.
+  Arm_address output_address =
+    section_start + this->relobj_->section_size(this->shndx_);
+
+  // Write out the entry.  The first word either points to the beginning
+  // or after the end of a text section.  The second word is the special
+  // EXIDX_CANTUNWIND value.
+  elfcpp::Swap<32, big_endian>::writeval(wv, output_address);
+  elfcpp::Swap<32, big_endian>::writeval(wv + 1, elfcpp::EXIDX_CANTUNWIND);
+
+  of->write_output_view(this->offset(), oview_size, oview);
+}
+
+// Arm_exidx_merged_section methods.
+
+// Constructor for Arm_exidx_merged_section.
+// EXIDX_INPUT_SECTION points to the unmodified EXIDX input section.
+// SECTION_OFFSET_MAP points to a section offset map describing how
+// parts of the input section are mapped to output.  DELETED_BYTES is
+// the number of bytes deleted from the EXIDX input section.
+
+Arm_exidx_merged_section::Arm_exidx_merged_section(
+    const Arm_exidx_input_section& exidx_input_section,
+    const Arm_exidx_section_offset_map& section_offset_map,
+    uint32_t deleted_bytes)
+  : Output_relaxed_input_section(exidx_input_section.relobj(),
+                                exidx_input_section.shndx(),
+                                exidx_input_section.addralign()),
+    exidx_input_section_(exidx_input_section),
+    section_offset_map_(section_offset_map)
+{
+  // Fix size here so that we do not need to implement set_final_data_size.
+  this->set_data_size(exidx_input_section.size() - deleted_bytes);
+  this->fix_data_size();
+}
+
+// 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 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.
+
+bool
+Arm_exidx_merged_section::do_output_offset(
+    const Relobj* relobj,
+    unsigned int shndx,
+    section_offset_type offset,
+    section_offset_type* poutput) const
+{
+  // We only handle offsets for the original EXIDX input section.
+  if (relobj != this->exidx_input_section_.relobj()
+      || shndx != this->exidx_input_section_.shndx())
+    return false;
+
+  if (offset < 0 || offset >= this->exidx_input_section_.size())
+    // Input offset is out of valid range.
+    *poutput = -1;
+  else
+    {
+      // We need to look up the section offset map to determine the output
+      // offset.  Find the reference point in map that is first offset
+      // bigger than or equal to this offset.
+      Arm_exidx_section_offset_map::const_iterator p =
+       this->section_offset_map_.lower_bound(offset);
+
+      // The section offset maps are build such that this should not happen if
+      // input offset is in the valid range.
+      gold_assert(p != this->section_offset_map_.end());
+
+      // We need to check if this is dropped.
+     section_offset_type ref = p->first;
+     section_offset_type mapped_ref = p->second;
+
+      if (mapped_ref != Arm_exidx_input_section::invalid_offset)
+       // Offset is present in output.
+       *poutput = mapped_ref + (offset - ref);
+      else
+       // Offset is discarded owing to EXIDX entry merging.
+       *poutput = -1;
+    }
+  
+  return true;
+}
+
+// Write this to output file OF.
+
+void
+Arm_exidx_merged_section::do_write(Output_file* of)
+{
+  // If we retain or discard the whole EXIDX input section,  we would
+  // not be here.
+  gold_assert(this->data_size() != this->exidx_input_section_.size()
+             && this->data_size() != 0);
+
+  off_t offset = this->offset();
+  const section_size_type oview_size = this->data_size();
+  unsigned char* const oview = of->get_output_view(offset, oview_size);
+  
+  Output_section* os = this->relobj()->output_section(this->shndx());
+  gold_assert(os != NULL);
+
+  // Get contents of EXIDX input section.
+  section_size_type section_size;
+  const unsigned char* section_contents =
+    this->relobj()->section_contents(this->shndx(), &section_size, false); 
+  gold_assert(section_size == this->exidx_input_section_.size());
+
+  // Go over spans of input offsets and write only those that are not
+  // discarded.
+  section_offset_type in_start = 0;
+  section_offset_type out_start = 0;
+  for(Arm_exidx_section_offset_map::const_iterator p =
+        this->section_offset_map_.begin();
+      p != this->section_offset_map_.end();
+      ++p)
+    {
+      section_offset_type in_end = p->first;
+      gold_assert(in_end >= in_start);
+      section_offset_type out_end = p->second;
+      size_t in_chunk_size = convert_types<size_t>(in_end - in_start + 1);
+      if (out_end != -1)
+       {
+         size_t out_chunk_size =
+           convert_types<size_t>(out_end - out_start + 1);
+         gold_assert(out_chunk_size == in_chunk_size);
+         memcpy(oview + out_start, section_contents + in_start,
+                out_chunk_size);
+         out_start += out_chunk_size;
+       }
+      in_start += in_chunk_size;
+    }
+
+  gold_assert(convert_to_section_size_type(out_start) == oview_size);
+  of->write_output_view(this->offset(), oview_size, oview);
+}
+
 // Arm_output_section methods.
 
 // Create a stub group for input sections from BEGIN to END.  OWNER