Index: arm.cc
===================================================================
RCS file: /cvs/src/src/gold/arm.cc,v
retrieving revision 1.57
diff -u -r1.57 arm.cc
--- arm.cc	15 Jan 2010 15:41:42 -0000	1.57
+++ arm.cc	18 Jan 2010 21:16:14 -0000
@@ -122,6 +122,7 @@
 // R_ARM_MOVT_PREL
 // R_ARM_THM_MOVW_PREL_NC
 // R_ARM_THM_MOVT_PREL
+// R_ARM_V4BX
 // R_ARM_THM_JUMP6
 // R_ARM_THM_JUMP8
 // R_ARM_THM_JUMP11
@@ -254,7 +255,8 @@
   DEF_STUB(a8_veneer_b_cond) \
   DEF_STUB(a8_veneer_b) \
   DEF_STUB(a8_veneer_bl) \
-  DEF_STUB(a8_veneer_blx)
+  DEF_STUB(a8_veneer_blx) \
+  DEF_STUB(v4_veneer_bx)
 
 // Stub types.
 
@@ -275,7 +277,7 @@
     arm_stub_cortex_a8_last = arm_stub_a8_veneer_blx,
     
     // Last stub type.
-    arm_stub_type_last = arm_stub_a8_veneer_blx
+    arm_stub_type_last = arm_stub_v4_veneer_bx
   } Stub_type;
 #undef DEF_STUB
 
@@ -748,6 +750,60 @@
   uint32_t original_insn_;
 };
 
+// ARMv4 BX Rx branch relocation stub class.
+class Arm_v4bx_stub : public Stub
+{
+ public:
+  ~Arm_v4bx_stub()
+  { }
+
+  // Return the associated register.
+  uint32_t
+  reg() const
+  { return this->reg_; }
+
+ protected:
+  // Arm V4BX stubs are created via a stub factory.  So these are protected.
+  Arm_v4bx_stub(const Stub_template* stub_template, const uint32_t reg)
+    : Stub(stub_template), reg_(reg)
+  { }
+
+  friend class Stub_factory;
+
+  // Return the relocation target address of the i-th relocation in the
+  // stub.
+  Arm_address
+  do_reloc_target(size_t)
+  { return NULL; }
+
+  // This may be overridden in the child class.
+  virtual void
+  do_write(unsigned char* view, section_size_type view_size, bool big_endian)
+  {
+    if (big_endian)
+      this->do_fixed_endian_v4bx_write<true>(view, view_size);
+    else
+      this->do_fixed_endian_v4bx_write<false>(view, view_size);
+  }
+
+ private:
+  // A template to implement do_write.
+  template<bool big_endian>
+  void inline
+  do_fixed_endian_v4bx_write(unsigned char* view, section_size_type)
+  {
+    const Insn_template* insns = this->stub_template()->insns();
+    elfcpp::Swap<32, big_endian>::writeval(view, insns[0].data() + (this->reg_ << 16));
+    view += insns[0].size();
+    elfcpp::Swap<32, big_endian>::writeval(view, insns[1].data() + this->reg_);
+    view += insns[1].size();
+    elfcpp::Swap<32, big_endian>::writeval(view, insns[2].data() + this->reg_);
+  }
+
+  // A register index (r0-r14), which is associated with the stub.
+  uint32_t reg_;
+};
+
 // Stub factory class.
 
 class Stub_factory
@@ -782,6 +838,15 @@
 			      source, destination, original_insn);
   }
 
+  // Make an ARM V4BX relocation stub.
+  // This method creates a stub from the arm_stub_v4_veneer_bx template only.
+  Arm_v4bx_stub*
+  make_arm_v4bx_stub(uint32_t reg) const
+  {
+    gold_assert(reg < 0xf);
+    return new Arm_v4bx_stub(this->stub_templates_[arm_stub_v4_veneer_bx], reg);
+  }
+
  private:
   // Constructor and destructor are protected since we only return a single
   // instance created in Stub_factory::get_instance().
@@ -808,7 +873,7 @@
   { }
 
   ~Stub_table()
-  { }
+  { this->remove_all_arm_v4bx_stubs(); }
 
   // Owner of this stub table.
   Arm_input_section<big_endian>*
@@ -818,7 +883,9 @@
   // Whether this stub table is empty.
   bool
   empty() const
-  { return this->reloc_stubs_.empty() && this->cortex_a8_stubs_.empty(); }
+  { return this->reloc_stubs_.empty()
+	&& this->cortex_a8_stubs_.empty()
+	&& this->arm_v4bx_stubs_.empty(); }
 
   // Return the current data size.
   off_t
@@ -845,10 +912,25 @@
     this->cortex_a8_stubs_.insert(value);
   }
 
+  // Add an ARM V4BX relocation stub. A register index will be retrieved
+  // from the stub.
+  void
+  add_arm_v4bx_stub(Arm_v4bx_stub* stub)
+  {
+    gold_assert(stub);
+    if (this->find_arm_v4bx_stub(stub->reg()) == NULL)
+      this->arm_v4bx_stubs_.insert(
+	  std::pair<uint32_t, Arm_v4bx_stub*>(stub->reg(), stub));
+  }
+
   // Remove all Cortex-A8 stubs.
   void
   remove_all_cortex_a8_stubs();
 
+  // Remove all ARM V4BX stubs.
+  void
+  remove_all_arm_v4bx_stubs();
+
   // Look up a relocation stub using KEY.  Return NULL if there is none.
   Reloc_stub*
   find_reloc_stub(const Reloc_stub::Key& key) const
@@ -857,6 +939,15 @@
     return (p != this->reloc_stubs_.end()) ? p->second : NULL;
   }
 
+  // Look up an arm v4bx relocation stub using the register index.
+  // Return NULL if there is none.
+  Arm_v4bx_stub*
+  find_arm_v4bx_stub(const uint32_t reg) const
+  {
+    typename Arm_v4bx_stub_list::const_iterator p = this->arm_v4bx_stubs_.find(reg);
+    return (p != this->arm_v4bx_stubs_.end()) ? p->second : NULL;
+  }
+
   // Relocate stubs in this stub table.
   void
   relocate_stubs(const Relocate_info<32, big_endian>*,
@@ -916,6 +1007,8 @@
   // List of Cortex-A8 stubs ordered by addresses of branches being
   // fixed up in output.
   typedef std::map<Arm_address, Cortex_a8_stub*> Cortex_a8_stub_list;
+  // List of Arm V4BX relocation stubs ordered by associated registers.
+  typedef std::map<uint32_t, Arm_v4bx_stub*> Arm_v4bx_stub_list;
 
   // Owner of this stub table.
   Arm_input_section<big_endian>* owner_;
@@ -923,6 +1016,8 @@
   Reloc_stub_map reloc_stubs_;
   // The cortex_a8_stubs.
   Cortex_a8_stub_list cortex_a8_stubs_;
+  // The Arm V4BX relocation stubs.
+  Arm_v4bx_stub_list arm_v4bx_stubs_;
   // data size of this in the previous pass.
   off_t prev_data_size_;
   // address alignment of this in the previous pass.
@@ -1073,7 +1168,8 @@
   Arm_relobj(const std::string& name, Input_file* input_file, off_t offset,
              const typename elfcpp::Ehdr<32, big_endian>& ehdr)
     : Sized_relobj<32, big_endian>(name, input_file, offset, ehdr),
-      stub_tables_(), local_symbol_is_thumb_function_(),
+      stub_tables_(), glue_owner_stub_table_(NULL),
+      local_symbol_is_thumb_function_(),
       attributes_section_data_(NULL), mapping_symbols_info_(),
       section_has_cortex_a8_workaround_(NULL)
   { }
@@ -1097,6 +1193,22 @@
     this->stub_tables_[shndx] = stub_table;
   }
 
+  // Return the stub table to save a common interworking veneer code.
+  Stub_table<big_endian>*
+  glue_owner_stub_table() const
+  {
+    return this->glue_owner_stub_table_;
+  }
+
+  // Set the stub table to save a common interworking veneer code.
+  void
+  set_glue_owner_stub_table(Stub_table<big_endian>* stub_table)
+  {
+    gold_assert(stub_table != NULL);
+    if (this->glue_owner_stub_table_ == NULL)
+      this->glue_owner_stub_table_ = stub_table;
+  }
+
   // Whether a local symbol is a THUMB function.  R_SYM is the symbol table
   // index.  This is only valid after do_count_local_symbol is called.
   bool
@@ -1231,6 +1343,8 @@
   // List of stub tables.
   typedef std::vector<Stub_table<big_endian>*> Stub_table_list;
   Stub_table_list stub_tables_;
+  // Stub table to save a common veneer code.
+  Stub_table<big_endian>* glue_owner_stub_table_;
   // Bit vector to tell if a local symbol is a thumb function or not.
   // This is only valid after do_count_local_symbol is called.
   std::vector<bool> local_symbol_is_thumb_function_;
@@ -1452,7 +1566,7 @@
       stub_factory_(Stub_factory::get_instance()), may_use_blx_(false),
       should_force_pic_veneer_(false), arm_input_section_map_(),
       attributes_section_data_(NULL), fix_cortex_a8_(false),
-      cortex_a8_relocs_info_()
+      cortex_a8_relocs_info_(), fix_v4bx_(0)
   { }
 
   // Whether we can use BLX.
@@ -1685,6 +1799,14 @@
   fix_cortex_a8() const
   { return this->fix_cortex_a8_; }
 
+  // Whether we fix R_ARM_V4BX relocation.
+  // 0 - do not fix
+  // 1 - replace with MOV instruction (armv4 target)
+  // 2 - make interworking veneer (>= armv4t targets only)
+  short
+  fix_v4bx() const
+  { return this->fix_v4bx_; }
+
   // Scan a span of THUMB code section for Cortex-A8 erratum.
   void
   scan_span_for_cortex_a8_erratum(Arm_relobj<big_endian>*, unsigned int,
@@ -2048,6 +2170,8 @@
   bool fix_cortex_a8_;
   // Map addresses to relocs for Cortex-A8 erratum.
   Cortex_a8_relocs_info cortex_a8_relocs_info_;
+  // Whether we need to fix code for V4BX relocations.
+  short fix_v4bx_;
 };
 
 template<bool big_endian>
@@ -2745,6 +2869,48 @@
     elfcpp::Swap<16, big_endian>::writeval(wv + 1, val & 0xffff);
     return This::STATUS_OKAY;
   }
+
+  // R_ARM_V4BX
+  static inline typename This::Status
+  v4bx(unsigned char *view,
+       const Arm_relobj<big_endian>* object,
+       const Arm_address address,
+       const bool is_interworking)
+  {
+
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    // Ensure that we have a BX instruction.
+    gold_assert((val & 0x0ffffff0) == 0x012fff10);
+    const uint32_t reg = (val & 0xf);
+    if (is_interworking && reg != 0xf)
+      {
+	Stub_table<big_endian>* stub_table =
+	  object->glue_owner_stub_table();
+	gold_assert(stub_table != NULL);
+
+	Arm_v4bx_stub* stub = stub_table->find_arm_v4bx_stub(reg);
+	gold_assert(stub != NULL);
+	int32_t veneer_address =
+	    stub_table->address() + stub->offset() - 8 - address;
+	gold_assert((veneer_address <= ARM_MAX_FWD_BRANCH_OFFSET)
+		    && (veneer_address >= ARM_MAX_BWD_BRANCH_OFFSET));
+
+	// Replace with a branch to veneer (B <addr>)
+	val = (val & 0xf0000000) | 0x0a000000
+	      | ((veneer_address >> 2) & 0x00ffffff);
+      }
+    else
+      {
+	// Preserve Rm (lowest four bits) and the condition code
+	// (highest four bits). Other bits encode MOV PC,Rm.
+	val = (val & 0xf000000f) | 0x01a0f000;
+      }
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    return This::STATUS_OKAY;
+  }
 };
 
 // Relocate ARM long branches.  This handles relocation types
@@ -3676,6 +3842,15 @@
       Insn_template::arm_rel_insn(0xea000000, -8)	// b dest
     };
 
+  // Stub used to provide an interworking for R_ARM_V4BX relocation
+  // (bx r[n] instruction).
+  static const Insn_template elf32_arm_stub_v4_veneer_bx[] =
+    {
+      Insn_template::arm_insn(0xe3100001),		// tst   r<n>, #1
+      Insn_template::arm_insn(0x01a0f000),		// moveq pc, r<n>
+      Insn_template::arm_insn(0xe12fff10)		// bx    r<n>
+    };
+
   // Fill in the stub template look-up table.  Stub templates are constructed
   // per instance of Stub_factory for fast look-up without locking
   // in a thread-enabled environment.
@@ -3713,6 +3888,19 @@
   this->cortex_a8_stubs_.clear();
 }
 
+// Remove all ARM V4BX relocation stubs.
+
+template<bool big_endian>
+void
+Stub_table<big_endian>::remove_all_arm_v4bx_stubs()
+{
+  for (Arm_v4bx_stub_list::iterator p = this->arm_v4bx_stubs_.begin();
+       p != this->arm_v4bx_stubs_.end();
+       ++p)
+    delete p->second;
+  this->arm_v4bx_stubs_.clear();
+}
+
 // Relocate one stub.  This is a helper for Stub_table::relocate_stubs().
 
 template<bool big_endian>
@@ -3770,6 +3958,13 @@
        ++p)
     this->relocate_stub(p->second, relinfo, arm_target, output_section, view,
 			address, view_size);
+
+  // Relocate all ARM V4BX stubs.
+  for (Arm_v4bx_stub_list::iterator p = this->arm_v4bx_stubs_.begin();
+       p != this->arm_v4bx_stubs_.end();
+       ++p)
+      this->relocate_stub(p->second, relinfo, arm_target, output_section, view,
+			  address, view_size);
 }
 
 // Write out the stubs to file.
@@ -3811,6 +4006,20 @@
 		  big_endian);
     }
 
+  // Write ARM V4BX relocation stubs.
+  for (Arm_v4bx_stub_list::const_iterator p = this->arm_v4bx_stubs_.begin();
+       p != this->arm_v4bx_stubs_.end();
+       ++p)
+    {
+      Arm_v4bx_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);
 }
 
@@ -3847,6 +4056,16 @@
 	      + stub_template->size());
     }
 
+  for (Arm_v4bx_stub_list::const_iterator p = this->arm_v4bx_stubs_.begin();
+       p != this->arm_v4bx_stubs_.end();
+       ++p)
+    {
+      const Stub_template* stub_template = p->second->stub_template();
+      addralign = std::max(addralign, stub_template->alignment());
+      size = (align_address(size, stub_template->alignment())
+	      + stub_template->size());
+    }
+
   // Check if either data size or alignment changed in this pass.
   // Update prev_data_size_ and prev_addralign_.  These will be used
   // as the current data size and address alignment for the next pass.
@@ -3898,6 +4117,18 @@
       arm_relobj->mark_section_for_cortex_a8_workaround(stub->shndx());
     }
 
+  for (typename Arm_v4bx_stub_list::const_iterator p = this->arm_v4bx_stubs_.begin();
+      p != this->arm_v4bx_stubs_.end();
+      ++p)
+    {
+    Arm_v4bx_stub* stub = p->second;
+      const Stub_template* stub_template = stub->stub_template();
+      uint64_t stub_addralign = stub_template->alignment();
+      off = align_address(off, stub_addralign);
+      stub->set_offset(off);
+      off += stub_template->size();
+    }
+
   gold_assert(off <= this->prev_data_size_);
 }
 
@@ -4080,6 +4311,9 @@
 	  Arm_relobj<big_endian>* arm_relobj =
 	    Arm_relobj<big_endian>::as_arm_relobj(p->relobj());
 	  arm_relobj->set_stub_table(p->shndx(), stub_table);
+	  // Only a stub table for the first passed input code segment will be
+	  // used.
+	  arm_relobj->set_glue_owner_stub_table(stub_table);
 	}
       prev_p = p++;
     }
@@ -4626,7 +4860,8 @@
 
       if (arm_input_section != NULL
 	  && arm_input_section->is_stub_table_owner()
-	  && !arm_input_section->stub_table()->empty())
+	  && (!arm_input_section->stub_table()->empty()
+	      || (this->glue_owner_stub_table_ != NULL && !this->glue_owner_stub_table_->empty())))
 	{
 	  // We cannot discard a section if it owns a stub table.
 	  Output_section* os = this->output_section(i);
@@ -4840,6 +5075,18 @@
 	return RelocFuncs::thumb32_cond_branch_offset(upper_insn, lower_insn);
       }
 
+    // This is specific relocation, which does not have a real addend and
+    // using to replace a whole ARM instruction. We return an instruction
+    // "under" the relocation instead of regular addend to give an ability
+    // extracting the used register index within the instruction and create
+    // an appropriate stub.
+    case elfcpp::R_ARM_V4BX:
+      {
+	typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+	const Valtype* wv = reinterpret_cast<const Valtype*>(view);
+	return elfcpp::Swap<32, big_endian>::readval(wv);
+      }
+
     default:
       gold_unreachable();
     }
@@ -5204,6 +5451,7 @@
     case elfcpp::R_ARM_THM_JUMP6:
     case elfcpp::R_ARM_THM_JUMP8:
     case elfcpp::R_ARM_THM_JUMP11:
+    case elfcpp::R_ARM_V4BX:
       break;
 
     case elfcpp::R_ARM_GOTOFF32:
@@ -5335,6 +5583,7 @@
     case elfcpp::R_ARM_THM_JUMP6:
     case elfcpp::R_ARM_THM_JUMP8:
     case elfcpp::R_ARM_THM_JUMP11:
+    case elfcpp::R_ARM_V4BX:
       break;
 
     case elfcpp::R_ARM_THM_ABS5:
@@ -5603,6 +5852,13 @@
              || cpu_arch_profile_attr->int_value() == 0));
     }
   
+  // Check if we can use V4BX interworking.
+  // The V4BX interworking stub contains BX instruction,
+  // which is not specified for some profiles.
+  if (this->fix_v4bx() == 2 && !this->may_use_blx())
+    gold_error("unable to provide V4BX reloc interworking fix up. "
+	"The target profile does not support BX instruction.");
+
   // Fill in some more dynamic tags.
   const Reloc_section* rel_plt = (this->plt_ == NULL
 				  ? NULL
@@ -6090,6 +6346,15 @@
 						    address, thumb_bit);
       break;
 
+    case elfcpp::R_ARM_V4BX:
+      if (target->fix_v4bx() == 1
+	  || !target->may_use_blx()
+	  || (target->fix_v4bx() == 2 && target->may_use_blx()))
+	reloc_status =
+	  Arm_relocate_functions::v4bx(view, object, address,
+				       (target->fix_v4bx() == 2));
+      break;
+
     case elfcpp::R_ARM_TARGET1:
       // This should have been mapped to another type already.
       // Fall through.
@@ -6238,6 +6503,7 @@
     case elfcpp::R_ARM_MOVT_PREL:
     case elfcpp::R_ARM_THM_MOVW_PREL_NC:
     case elfcpp::R_ARM_THM_MOVT_PREL:
+    case elfcpp::R_ARM_V4BX:
       return 4;
 
     case elfcpp::R_ARM_TARGET1:
@@ -7446,6 +7712,9 @@
       // THUMB branches.
       destination = value + addend + 4;
       break;
+    case elfcpp::R_ARM_V4BX:
+      destination = 0;
+      break;
     default:
       gold_unreachable();
     }
@@ -7493,6 +7762,23 @@
 	new Cortex_a8_reloc(stub, r_type,
 			    destination | (target_is_thumb ? 1 : 0));
     }
+
+  // Process the R_ARM_V4BX relocations.
+  const uint32_t reg = (addend & 0xf);
+  if (r_type == elfcpp::R_ARM_V4BX && this->fix_v4bx() == 2 && reg < 0xf)
+    {
+      Stub_table<big_endian>* stub_table =
+	  arm_relobj->glue_owner_stub_table();
+      // Locate stub by the register index.
+      if (stub_table && stub_table->find_arm_v4bx_stub(reg) == NULL)
+        {
+	  // create a new stub and add it to stub table.
+	  Arm_v4bx_stub* stub =
+	    this->stub_factory().make_arm_v4bx_stub(reg);
+	  gold_assert(stub != NULL);
+	  stub_table->add_arm_v4bx_stub(stub);
+        }
+    }
 }
 
 // This function scans a relocation sections for stub generation.
@@ -7553,7 +7839,8 @@
          && (r_type != elfcpp::R_ARM_THM_CALL)
          && (r_type != elfcpp::R_ARM_THM_XPC22)
          && (r_type != elfcpp::R_ARM_THM_JUMP24)
-         && (r_type != elfcpp::R_ARM_THM_JUMP19))
+         && (r_type != elfcpp::R_ARM_THM_JUMP19)
+         && (r_type != elfcpp::R_ARM_V4BX))
 	continue;
 
       section_offset_type offset =
