From 56ee5e00a8dd7d6821958b9e0c8e84e405e362ff Mon Sep 17 00:00:00 2001
From: Doug Kwan <dougkwan@google.com>
Date: Wed, 21 Oct 2009 18:33:18 +0000
Subject: 2009-10-21  Doug Kwan  <dougkwan@google.com>

	* arm.cc (Stub_table, Arm_input_section): New forward class
	declarations.
	(Stub_table): New class defintion.
	(Stub_table::add_reloc_stub, Stub_table::relocate_stubs
	Stub_table::do_reset_address_and_file_offset, Stub_table::do_write):
	New method definition.
---
 gold/arm.cc | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 209 insertions(+)

(limited to 'gold/arm.cc')

diff --git a/gold/arm.cc b/gold/arm.cc
index 4b225bf..718c210 100644
--- a/gold/arm.cc
+++ b/gold/arm.cc
@@ -29,6 +29,7 @@
 #include <limits>
 #include <cstdio>
 #include <string>
+#include <algorithm>
 
 #include "elfcpp.h"
 #include "parameters.h"
@@ -55,6 +56,12 @@ template<bool big_endian>
 class Output_data_plt_arm;
 
 template<bool big_endian>
+class Stub_table;
+
+template<bool big_endian>
+class Arm_input_section;
+
+template<bool big_endian>
 class Target_arm;
 
 // For convenience.
@@ -643,6 +650,101 @@ class Stub_factory
   const Stub_template* stub_templates_[arm_stub_type_last+1];
 };
 
+// A class to hold stubs for the ARM target.
+
+template<bool big_endian>
+class Stub_table : public Output_data
+{
+ public:
+  Stub_table(Arm_input_section<big_endian>* owner)
+    : Output_data(), addralign_(1), owner_(owner), has_been_changed_(false),
+      reloc_stubs_()
+  { }
+
+  ~Stub_table()
+  { }
+
+  // Owner of this stub table.
+  Arm_input_section<big_endian>*
+  owner() const
+  { return this->owner_; }
+
+  // Whether this stub table is empty.
+  bool
+  empty() const
+  { return this->reloc_stubs_.empty(); }
+
+  // Whether this has been changed.
+  bool
+  has_been_changed() const
+  { return this->has_been_changed_; }
+
+  // Set the has-been-changed flag.
+  void
+  set_has_been_changed(bool value)
+  { this->has_been_changed_ = value; }
+
+  // Return the current data size.
+  off_t
+  current_data_size() const
+  { return this->current_data_size_for_child(); }
+
+  // Add a STUB with using KEY.  Caller is reponsible for avoid adding
+  // if already a STUB with the same key has been added. 
+  void
+  add_reloc_stub(Reloc_stub* stub, const Reloc_stub::Key& key);
+
+  // Look up a relocation stub using KEY.  Return NULL if there is none.
+  Reloc_stub*
+  find_reloc_stub(const Reloc_stub::Key& key) const
+  {
+    typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.find(key);
+    return (p != this->reloc_stubs_.end()) ? p->second : NULL;
+  }
+
+  // Relocate stubs in this stub table.
+  void
+  relocate_stubs(const Relocate_info<32, big_endian>*,
+		 Target_arm<big_endian>*, Output_section*,
+		 unsigned char*, Arm_address, section_size_type);
+
+ protected:
+  // Write out section contents.
+  void
+  do_write(Output_file*);
+ 
+  // Return the required alignment.
+  uint64_t
+  do_addralign() const
+  { return this->addralign_; }
+
+  // Finalize data size.
+  void
+  set_final_data_size()
+  { this->set_data_size(this->current_data_size_for_child()); }
+
+  // Reset address and file offset.
+  void
+  do_reset_address_and_file_offset();
+
+ private:
+  // Unordered map of stubs.
+  typedef
+    Unordered_map<Reloc_stub::Key, Reloc_stub*, Reloc_stub::Key::hash,
+		  Reloc_stub::Key::equal_to>
+    Reloc_stub_map;
+
+  // Address alignment
+  uint64_t addralign_;
+  // Owner of this stub table.
+  Arm_input_section<big_endian>* owner_;
+  // This is set to true during relaxiong if the size of the stub table
+  // has been changed.
+  bool has_been_changed_;
+  // The relocation stubs.
+  Reloc_stub_map reloc_stubs_;
+};
+
 // Utilities for manipulating integers of up to 32-bits
 
 namespace utils
@@ -2261,6 +2363,113 @@ Stub_factory::Stub_factory()
 #undef DEF_STUB
 }
 
+// Stub_table methods.
+
+// Add a STUB with using KEY.  Caller is reponsible for avoid adding
+// if already a STUB with the same key has been added. 
+
+template<bool big_endian>
+void
+Stub_table<big_endian>::add_reloc_stub(
+    Reloc_stub* stub,
+    const Reloc_stub::Key& key)
+{
+  const Stub_template* stub_template = stub->stub_template();
+  gold_assert(stub_template->type() == key.stub_type());
+  this->reloc_stubs_[key] = stub;
+  if (this->addralign_ < stub_template->alignment())
+    this->addralign_ = stub_template->alignment();
+  this->has_been_changed_ = true;
+}
+
+template<bool big_endian>
+void
+Stub_table<big_endian>::relocate_stubs(
+    const Relocate_info<32, big_endian>* relinfo,
+    Target_arm<big_endian>* arm_target,
+    Output_section* output_section,
+    unsigned char* view,
+    Arm_address address,
+    section_size_type view_size)
+{
+  // If we are passed a view bigger than the stub table's.  we need to
+  // adjust the view.
+  gold_assert(address == this->address()
+	      && (view_size
+		  == static_cast<section_size_type>(this->data_size())));
+
+  for (typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.begin();
+      p != this->reloc_stubs_.end();
+      ++p)
+    {
+      Reloc_stub* stub = p->second;
+      const Stub_template* stub_template = stub->stub_template();
+      if (stub_template->reloc_count() != 0)
+	{
+	  // Adjust view to cover the stub only.
+	  section_size_type offset = stub->offset();
+	  section_size_type stub_size = stub_template->size();
+	  gold_assert(offset + stub_size <= view_size);
+
+	  arm_target->relocate_stub(stub, relinfo, output_section,
+				    view + offset, address + offset,
+				    stub_size);
+	}
+    }
+}
+
+// Reset address and file offset.
+
+template<bool big_endian>
+void
+Stub_table<big_endian>::do_reset_address_and_file_offset()
+{
+  off_t off = 0;
+  uint64_t max_addralign = 1;
+  for (typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.begin();
+      p != this->reloc_stubs_.end();
+      ++p)
+    {
+      Reloc_stub* stub = p->second;
+      const Stub_template* stub_template = stub->stub_template();
+      uint64_t stub_addralign = stub_template->alignment();
+      max_addralign = std::max(max_addralign, stub_addralign);
+      off = align_address(off, stub_addralign);
+      stub->set_offset(off);
+      stub->reset_destination_address();
+      off += stub_template->size();
+    }
+
+  this->addralign_ = max_addralign;
+  this->set_current_data_size_for_child(off);
+}
+
+// Write out the stubs to file.
+
+template<bool big_endian>
+void
+Stub_table<big_endian>::do_write(Output_file* of)
+{
+  off_t offset = this->offset();
+  const section_size_type oview_size =
+    convert_to_section_size_type(this->data_size());
+  unsigned char* const oview = of->get_output_view(offset, oview_size);
+
+  for (typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.begin();
+      p != this->reloc_stubs_.end();
+      ++p)
+    {
+      Reloc_stub* stub = p->second;
+      Arm_address address = this->address() + stub->offset();
+      gold_assert(address
+		  == align_address(address,
+				   stub->stub_template()->alignment()));
+      stub->write(oview + stub->offset(), stub->stub_template()->size(),
+		  big_endian);
+    } 
+  of->write_output_view(this->offset(), oview_size, oview);
+}
+
 // A class to handle the PLT data.
 
 template<bool big_endian>
-- 
cgit v1.1