aboutsummaryrefslogtreecommitdiff
path: root/gold/arm.cc
diff options
context:
space:
mode:
authorDoug Kwan <dougkwan@google.com>2010-01-07 18:38:43 +0000
committerDoug Kwan <dougkwan@google.com>2010-01-07 18:38:43 +0000
commitbb0d3eb035b0b353e9bb52f250b17774b91be3f0 (patch)
treecc066885b2dfb5fab687e5d7bfc7b8bf8918a624 /gold/arm.cc
parented22650ea572075e7cf012c918fb789a934bd178 (diff)
downloadgdb-bb0d3eb035b0b353e9bb52f250b17774b91be3f0.zip
gdb-bb0d3eb035b0b353e9bb52f250b17774b91be3f0.tar.gz
gdb-bb0d3eb035b0b353e9bb52f250b17774b91be3f0.tar.bz2
2010-01-07 Doug Kwan <dougkwan@google.com>
* arm.cc (Insn_template::Type): New enum value THUMB16_SPECIAL_TYPE. (Insn_template::thumb16_bcond_insn): New method declaration. (Insn_template): Fix spelling. (Stub::thumb16_special): New method declaration. (Stub::do_write): Define virtual method which was previously pure virtual. (Stub::do_thumb16_special): New method declaration. (Stub::do_fixed_endian_write): New template member. (Reloc_stub::do_write): Remove. (Reloc_stub::do_fixed_endian_write): Remove. (Cortex_a8_stub): New class definition. (Stub_factory::make_cortex_a8_stub): New method definition. (Stub_factory::Stub_factory): Add missing static storage class qualifier for elf32_arm_stub_a8_veneer_blx.
Diffstat (limited to 'gold/arm.cc')
-rw-r--r--gold/arm.cc280
1 files changed, 214 insertions, 66 deletions
diff --git a/gold/arm.cc b/gold/arm.cc
index d104033..b77adc7 100644
--- a/gold/arm.cc
+++ b/gold/arm.cc
@@ -137,22 +137,27 @@ class Insn_template
enum Type
{
THUMB16_TYPE = 1,
+ // THUMB16_SPECIAL_TYPE is used by sub-classes of Stub for instruction
+ // templates with class-specific semantics. Currently this is used
+ // only by the Cortex_a8_stub class for handling condition codes in
+ // conditional branches.
+ THUMB16_SPECIAL_TYPE,
THUMB32_TYPE,
ARM_TYPE,
DATA_TYPE
};
- // Factory methods to create instrunction templates in different formats.
+ // Factory methods to create instruction templates in different formats.
static const Insn_template
thumb16_insn(uint32_t data)
{ return Insn_template(data, THUMB16_TYPE, elfcpp::R_ARM_NONE, 0); }
- // A bit of a hack. A Thumb conditional branch, in which the proper
- // condition is inserted when we build the stub.
+ // A Thumb conditional branch, in which the proper condition is inserted
+ // when we build the stub.
static const Insn_template
thumb16_bcond_insn(uint32_t data)
- { return Insn_template(data, THUMB16_TYPE, elfcpp::R_ARM_NONE, 1); }
+ { return Insn_template(data, THUMB16_SPECIAL_TYPE, elfcpp::R_ARM_NONE, 1); }
static const Insn_template
thumb32_insn(uint32_t data)
@@ -198,11 +203,11 @@ class Insn_template
reloc_addend() const
{ return this->reloc_addend_; }
- // Return size of instrunction template in bytes.
+ // Return size of instruction template in bytes.
size_t
size() const;
- // Return byte-alignment of instrunction template.
+ // Return byte-alignment of instruction template.
unsigned
alignment() const;
@@ -410,16 +415,39 @@ class Stub
write(unsigned char* view, section_size_type view_size, bool big_endian)
{ this->do_write(view, view_size, big_endian); }
+ // Return the instruction for THUMB16_SPECIAL_TYPE instruction template
+ // for the i-th instruction.
+ uint16_t
+ thumb16_special(size_t i)
+ { return this->do_thumb16_special(i); }
+
protected:
// This must be defined in the child class.
virtual Arm_address
do_reloc_target(size_t) = 0;
- // This must be defined in the child class.
+ // This may be overridden in the child class.
virtual void
- do_write(unsigned char*, section_size_type, bool) = 0;
+ do_write(unsigned char* view, section_size_type view_size, bool big_endian)
+ {
+ if (big_endian)
+ this->do_fixed_endian_write<true>(view, view_size);
+ else
+ this->do_fixed_endian_write<false>(view, view_size);
+ }
+ // This must be overridden if a child class uses the THUMB16_SPECIAL_TYPE
+ // instruction template.
+ virtual uint16_t
+ do_thumb16_special(size_t)
+ { gold_unreachable(); }
+
private:
+ // A template to implement do_write.
+ template<bool big_endian>
+ void inline
+ do_fixed_endian_write(unsigned char*, section_size_type);
+
// Its template.
const Stub_template* stub_template_;
// Offset within the section of containing this stub.
@@ -594,7 +622,6 @@ class Reloc_stub : public Stub
friend class Stub_factory;
- private:
// Return the relocation target address of the i-th relocation in the
// stub.
Arm_address
@@ -605,17 +632,115 @@ class Reloc_stub : public Stub
return this->destination_address_;
}
- // A template to implement do_write below.
- template<bool big_endian>
- void inline
- do_fixed_endian_write(unsigned char*, section_size_type);
+ private:
+ // Address of destination.
+ Arm_address destination_address_;
+};
- // Write a stub.
- void
- do_write(unsigned char* view, section_size_type view_size, bool big_endian);
+// Cortex-A8 stub class. We need a Cortex-A8 stub to redirect any 32-bit
+// THUMB branch that meets the following conditions:
+//
+// 1. The branch straddles across a page boundary. i.e. lower 12-bit of
+// branch address is 0xffe.
+// 2. The branch target address is in the same page as the first word of the
+// branch.
+// 3. The branch follows a 32-bit instruction which is not a branch.
+//
+// To do the fix up, we need to store the address of the branch instruction
+// and its target at least. We also need to store the original branch
+// instruction bits for the condition code in a conditional branch. The
+// condition code is used in a special instruction template. We also want
+// to identify input sections needing Cortex-A8 workaround quickly. We store
+// extra information about object and section index of the code section
+// containing a branch being fixed up. The information is used to mark
+// the code section when we finalize the Cortex-A8 stubs.
+//
- // Address of destination.
+class Cortex_a8_stub : public Stub
+{
+ public:
+ ~Cortex_a8_stub()
+ { }
+
+ // Return the object of the code section containing the branch being fixed
+ // up.
+ Relobj*
+ relobj() const
+ { return this->relobj_; }
+
+ // Return the section index of the code section containing the branch being
+ // fixed up.
+ unsigned int
+ shndx() const
+ { return this->shndx_; }
+
+ // Return the source address of stub. This is the address of the original
+ // branch instruction. LSB is 1 always set to indicate that it is a THUMB
+ // instruction.
+ Arm_address
+ source_address() const
+ { return this->source_address_; }
+
+ // Return the destination address of the stub. This is the branch taken
+ // address of the original branch instruction. LSB is 1 if it is a THUMB
+ // instruction address.
+ Arm_address
+ destination_address() const
+ { return this->destination_address_; }
+
+ // Return the instruction being fixed up.
+ uint32_t
+ original_insn() const
+ { return this->original_insn_; }
+
+ protected:
+ // Cortex_a8_stubs are created via a stub factory. So these are protected.
+ Cortex_a8_stub(const Stub_template* stub_template, Relobj* relobj,
+ unsigned int shndx, Arm_address source_address,
+ Arm_address destination_address, uint32_t original_insn)
+ : Stub(stub_template), relobj_(relobj), shndx_(shndx),
+ source_address_(source_address | 1U),
+ destination_address_(destination_address),
+ original_insn_(original_insn)
+ { }
+
+ friend class Stub_factory;
+
+ // Return the relocation target address of the i-th relocation in the
+ // stub.
+ Arm_address
+ do_reloc_target(size_t i)
+ {
+ if (this->stub_template()->type() == arm_stub_a8_veneer_b_cond)
+ {
+ // The conditional branch veneer has two relocations.
+ gold_assert(i < 2);
+ return i == 0 ? this->source_address_ + 4 : this->destination_address_;
+ }
+ else
+ {
+ // All other Cortex-A8 stubs have only one relocation.
+ gold_assert(i == 0);
+ return this->destination_address_;
+ }
+ }
+
+ // Return an instruction for the THUMB16_SPECIAL_TYPE instruction template.
+ uint16_t
+ do_thumb16_special(size_t);
+
+ private:
+ // Object of the code section containing the branch being fixed up.
+ Relobj* relobj_;
+ // Section index of the code section containing the branch begin fixed up.
+ unsigned int shndx_;
+ // Source address of original branch.
+ Arm_address source_address_;
+ // Destination address of the original branch.
Arm_address destination_address_;
+ // Original branch instruction. This is needed for copying the condition
+ // code from a condition branch to its stub.
+ uint32_t original_insn_;
};
// Stub factory class.
@@ -640,6 +765,18 @@ class Stub_factory
return new Reloc_stub(this->stub_templates_[stub_type]);
}
+ // Make a Cortex-A8 stub.
+ Cortex_a8_stub*
+ make_cortex_a8_stub(Stub_type stub_type, Relobj* relobj, unsigned int shndx,
+ Arm_address source, Arm_address destination,
+ uint32_t original_insn) const
+ {
+ gold_assert(stub_type >= arm_stub_cortex_a8_first
+ && stub_type <= arm_stub_cortex_a8_last);
+ return new Cortex_a8_stub(this->stub_templates_[stub_type], relobj, shndx,
+ source, destination, original_insn);
+ }
+
private:
// Constructor and destructor are protected since we only return a single
// instance created in Stub_factory::get_instance().
@@ -2728,6 +2865,51 @@ Stub_template::Stub_template(
this->size_ = offset;
}
+// Stub methods.
+
+// Template to implement do_write for a specific target endianity.
+
+template<bool big_endian>
+void inline
+Stub::do_fixed_endian_write(unsigned char* view, section_size_type view_size)
+{
+ const Stub_template* stub_template = this->stub_template();
+ const Insn_template* insns = stub_template->insns();
+
+ // FIXME: We do not handle BE8 encoding yet.
+ unsigned char* pov = view;
+ for (size_t i = 0; i < stub_template->insn_count(); i++)
+ {
+ switch (insns[i].type())
+ {
+ case Insn_template::THUMB16_TYPE:
+ elfcpp::Swap<16, big_endian>::writeval(pov, insns[i].data() & 0xffff);
+ break;
+ case Insn_template::THUMB16_SPECIAL_TYPE:
+ elfcpp::Swap<16, big_endian>::writeval(
+ pov,
+ this->thumb16_special(i));
+ break;
+ case Insn_template::THUMB32_TYPE:
+ {
+ uint32_t hi = (insns[i].data() >> 16) & 0xffff;
+ uint32_t lo = insns[i].data() & 0xffff;
+ elfcpp::Swap<16, big_endian>::writeval(pov, hi);
+ elfcpp::Swap<16, big_endian>::writeval(pov + 2, lo);
+ }
+ break;
+ case Insn_template::ARM_TYPE:
+ case Insn_template::DATA_TYPE:
+ elfcpp::Swap<32, big_endian>::writeval(pov, insns[i].data());
+ break;
+ default:
+ gold_unreachable();
+ }
+ pov += insns[i].size();
+ }
+ gold_assert(static_cast<section_size_type>(pov - view) == view_size);
+}
+
// Reloc_stub::Key methods.
// Dump a Key as a string for debugging.
@@ -2934,57 +3116,23 @@ Reloc_stub::stub_type_for_reloc(
return stub_type;
}
-// Template to implement do_write for a specific target endianity.
-
-template<bool big_endian>
-void inline
-Reloc_stub::do_fixed_endian_write(unsigned char* view,
- section_size_type view_size)
-{
- const Stub_template* stub_template = this->stub_template();
- const Insn_template* insns = stub_template->insns();
-
- // FIXME: We do not handle BE8 encoding yet.
- unsigned char* pov = view;
- for (size_t i = 0; i < stub_template->insn_count(); i++)
- {
- switch (insns[i].type())
- {
- case Insn_template::THUMB16_TYPE:
- // Non-zero reloc addends are only used in Cortex-A8 stubs.
- gold_assert(insns[i].reloc_addend() == 0);
- elfcpp::Swap<16, big_endian>::writeval(pov, insns[i].data() & 0xffff);
- break;
- case Insn_template::THUMB32_TYPE:
- {
- uint32_t hi = (insns[i].data() >> 16) & 0xffff;
- uint32_t lo = insns[i].data() & 0xffff;
- elfcpp::Swap<16, big_endian>::writeval(pov, hi);
- elfcpp::Swap<16, big_endian>::writeval(pov + 2, lo);
- }
- break;
- case Insn_template::ARM_TYPE:
- case Insn_template::DATA_TYPE:
- elfcpp::Swap<32, big_endian>::writeval(pov, insns[i].data());
- break;
- default:
- gold_unreachable();
- }
- pov += insns[i].size();
- }
- gold_assert(static_cast<section_size_type>(pov - view) == view_size);
-}
+// Cortex_a8_stub methods.
-// Write a reloc stub to VIEW with endianity specified by BIG_ENDIAN.
+// Return the instruction for a THUMB16_SPECIAL_TYPE instruction template.
+// I is the position of the instruction template in the stub template.
-void
-Reloc_stub::do_write(unsigned char* view, section_size_type view_size,
- bool big_endian)
+uint16_t
+Cortex_a8_stub::do_thumb16_special(size_t i)
{
- if (big_endian)
- this->do_fixed_endian_write<true>(view, view_size);
- else
- this->do_fixed_endian_write<false>(view, view_size);
+ // The only use of this is to copy condition code from a conditional
+ // branch being worked around to the corresponding conditional branch in
+ // to the stub.
+ gold_assert(this->stub_template()->type() == arm_stub_a8_veneer_b_cond
+ && i == 0);
+ uint16_t data = this->stub_template()->insns()[i].data();
+ gold_assert((data & 0xff00U) == 0xd000U);
+ data |= ((this->original_insn_ >> 22) & 0xf) << 8;
+ return data;
}
// Stub_factory methods.
@@ -3162,7 +3310,7 @@ Stub_factory::Stub_factory()
// Stub used for Thumb-2 blx.w instructions. We modified the original blx.w
// instruction (which switches to ARM mode) to point to this stub. Jump to
// the real destination using an ARM-mode branch.
- const Insn_template elf32_arm_stub_a8_veneer_blx[] =
+ static const Insn_template elf32_arm_stub_a8_veneer_blx[] =
{
Insn_template::arm_rel_insn(0xea000000, -8) // b dest
};