2009-11-25 Doug Kwan <dougkwan@google.com>
authorDoug Kwan <dougkwan@google.com>
Wed, 25 Nov 2009 04:11:56 +0000 (04:11 +0000)
committerDoug Kwan <dougkwan@google.com>
Wed, 25 Nov 2009 04:11:56 +0000 (04:11 +0000)
* arm.cc (Target_arm::Target_arm): Move method definition outside of
class definition.  Add code to handle --target1-rel, --target1-abs
and --target2= options.
(Target_arm::get_reloc_reloc_type): Change method to be non-static
and const.
(Target_arm::target1_is_rel_, Target_arm::target2_reloc_): New data
member declaration.
(Target_arm::Scan::local, Target_arm::Scan::global,
Target_arm::Relocate::relocate,
Target_arm::Relocatable_size_for_reloc::get_size_for_reloc): Adjust
call to Target_arm::get_real_reloc_type.
(Target_arm::get_real_reloc_type): Use command line options to
determine real types of R_ARM_TARGET1 and R_ARM_TARGET2.
* options.h (--target1-rel, --target1-abs, --target2): New ARM-only
options.

gold/ChangeLog
gold/arm.cc

index daa729eef7fcde69f9bb6dd6fb42aac9c34f11d5..041c9e6317d4ad10ba4b93f1b3fe2b2559f5e08d 100644 (file)
@@ -1,3 +1,19 @@
+2009-11-25  Doug Kwan  <dougkwan@google.com>
+
+       * arm.cc (Target_arm::may_use_thumb2_nop): New method definition.
+       (Arm_relocate_functions::thumb_branch_common): New metod declaration.
+       (Arm_relocate_functions::abs12, Arm_relocate_functions::abs16): Fix
+       formatting.
+       (Arm_relocate_functions::thm_call): Replace body with a call to
+       Arm_relocate_functions::thumb_branch_common.
+       (Arm_relocate_functions::thm_jump24,
+       Arm_relocate_functions::thm_xpc22): New method definitions.
+       (Arm_relocate_functions::thumb_branch_common): New method definition.
+       (Reloc_stub::stbu_type_for_reloc): Fix incorrect uses of bit-wise-or
+       operator.
+       (Target_arm::Relocate::relocate): Adjust call to thm_call.
+       Add code to handle R_ARM_THM_XPC22 and R_ARM_THM_JUMP24.
+
 2009-11-24  Rafael Avila de Espindola  <espindola@google.com>
 
        * Makefile.am: Build incremental-dump
index 5b702f220554de572f9ff7f4dd36d93a69f146d4..3ea98de853c35a5eee1ba9b9987bd03a5bfd157d 100644 (file)
@@ -1195,6 +1195,14 @@ class Target_arm : public Sized_target<32, big_endian>
     return false;
   }
 
+  // Whether we have THUMB-2 NOP.W instruction.
+  bool
+  may_use_thumb2_nop() const
+  {
+    // FIXME:  This should not hard-coded.
+    return false;
+  }
+  
   // Process the relocations to determine unreferenced sections for 
   // garbage collection.
   void
@@ -1750,6 +1758,13 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian>
                    const Arm_relobj<big_endian>*, unsigned int,
                    const Symbol_value<32>*, Arm_address, Arm_address, bool);
 
+  // Handle THUMB long branches.
+  static typename This::Status
+  thumb_branch_common(unsigned int, const Relocate_info<32, big_endian>*,
+                     unsigned char *, const Sized_symbol<32>*,
+                     const Arm_relobj<big_endian>*, unsigned int,
+                     const Symbol_value<32>*, Arm_address, Arm_address, bool);
+
  public:
 
   // R_ARM_ABS8: S + A
@@ -1793,8 +1808,8 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian>
   // R_ARM_ABS12: S + A
   static inline typename This::Status
   abs12(unsigned char *view,
-       const Sized_relobj<32, big_endian>* object,
-       const Symbol_value<32>* psymval)
+       const Sized_relobj<32, big_endian>* object,
+       const Symbol_value<32>* psymval)
   {
     typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
     typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
@@ -1812,8 +1827,8 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian>
   // R_ARM_ABS16: S + A
   static inline typename This::Status
   abs16(unsigned char *view,
-       const Sized_relobj<32, big_endian>* object,
-       const Symbol_value<32>* psymval)
+       const Sized_relobj<32, big_endian>* object,
+       const Symbol_value<32>* psymval)
   {
     typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
     typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
@@ -1861,38 +1876,41 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian>
 
   // R_ARM_THM_CALL: (S + A) | T - P
   static inline typename This::Status
-  thm_call(unsigned char *view,
-          const Sized_relobj<32, big_endian>* object,
-          const Symbol_value<32>* psymval,
-          Arm_address address,
-          Arm_address thumb_bit)
+  thm_call(const Relocate_info<32, big_endian>* relinfo, unsigned char *view,
+          const Sized_symbol<32>* gsym, const Arm_relobj<big_endian>* object,
+          unsigned int r_sym, const Symbol_value<32>* psymval,
+          Arm_address address, Arm_address thumb_bit,
+          bool is_weakly_undefined_without_plt)
   {
-    // A thumb call consists of two instructions.
-    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
-    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
-    Valtype* wv = reinterpret_cast<Valtype*>(view);
-    Valtype hi = elfcpp::Swap<16, big_endian>::readval(wv);
-    Valtype lo = elfcpp::Swap<16, big_endian>::readval(wv + 1);
-    // Must be a BL instruction. lo == 11111xxxxxxxxxxx.
-    gold_assert((lo & 0xf800) == 0xf800);
-    Reltype addend = utils::sign_extend<23>(((hi & 0x7ff) << 12)
-                                          | ((lo & 0x7ff) << 1));
-    Reltype x = (psymval->value(object, addend) | thumb_bit) - address;
+    return thumb_branch_common(elfcpp::R_ARM_THM_CALL, relinfo, view, gsym,
+                              object, r_sym, psymval, address, thumb_bit,
+                              is_weakly_undefined_without_plt);
+  }
 
-    // If target has no thumb bit set, we need to either turn the BL
-    // into a BLX (for ARMv5 or above) or generate a stub.
-    if ((x & 1) == 0)
-      {
-       // This only works for ARMv5 and above with interworking enabled.
-       lo &= 0xefff;
-      }
-    hi = utils::bit_select(hi, (x >> 12), 0x7ffU);
-    lo = utils::bit_select(lo, (x >> 1), 0x7ffU);
-    elfcpp::Swap<16, big_endian>::writeval(wv, hi);
-    elfcpp::Swap<16, big_endian>::writeval(wv + 1, lo);
-    return (utils::has_overflow<23>(x)
-           ? This::STATUS_OVERFLOW
-           : This::STATUS_OKAY);
+  // R_ARM_THM_JUMP24: (S + A) | T - P
+  static inline typename This::Status
+  thm_jump24(const Relocate_info<32, big_endian>* relinfo, unsigned char *view,
+            const Sized_symbol<32>* gsym, const Arm_relobj<big_endian>* object,
+            unsigned int r_sym, const Symbol_value<32>* psymval,
+            Arm_address address, Arm_address thumb_bit,
+            bool is_weakly_undefined_without_plt)
+  {
+    return thumb_branch_common(elfcpp::R_ARM_THM_JUMP24, relinfo, view, gsym,
+                              object, r_sym, psymval, address, thumb_bit,
+                              is_weakly_undefined_without_plt);
+  }
+
+  // R_ARM_THM_XPC22: (S + A) | T - P
+  static inline typename This::Status
+  thm_xpc22(const Relocate_info<32, big_endian>* relinfo, unsigned char *view,
+           const Sized_symbol<32>* gsym, const Arm_relobj<big_endian>* object,
+           unsigned int r_sym, const Symbol_value<32>* psymval,
+           Arm_address address, Arm_address thumb_bit,
+           bool is_weakly_undefined_without_plt)
+  {
+    return thumb_branch_common(elfcpp::R_ARM_THM_XPC22, relinfo, view, gsym,
+                              object, r_sym, psymval, address, thumb_bit,
+                              is_weakly_undefined_without_plt);
   }
 
   // R_ARM_BASE_PREL: B(S) + A - P
@@ -2292,6 +2310,183 @@ Arm_relocate_functions<big_endian>::arm_branch_common(
          ? This::STATUS_OVERFLOW : This::STATUS_OKAY);
 }
 
+// Relocate THUMB long branches.  This handles relocation types
+// R_ARM_THM_CALL, R_ARM_THM_JUMP24 and R_ARM_THM_XPC22.
+// If IS_WEAK_UNDEFINED_WITH_PLT is true.  The target symbol is weakly
+// undefined and we do not use PLT in this relocation.  In such a case,
+// the branch is converted into an NOP.
+
+template<bool big_endian>
+typename Arm_relocate_functions<big_endian>::Status
+Arm_relocate_functions<big_endian>::thumb_branch_common(
+    unsigned int r_type,
+    const Relocate_info<32, big_endian>* relinfo,
+    unsigned char *view,
+    const Sized_symbol<32>* gsym,
+    const Arm_relobj<big_endian>* object,
+    unsigned int r_sym,
+    const Symbol_value<32>* psymval,
+    Arm_address address,
+    Arm_address thumb_bit,
+    bool is_weakly_undefined_without_plt)
+{
+  typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
+  Valtype* wv = reinterpret_cast<Valtype*>(view);
+  uint32_t upper_insn = elfcpp::Swap<16, big_endian>::readval(wv);
+  uint32_t lower_insn = elfcpp::Swap<16, big_endian>::readval(wv + 1);
+
+  // FIXME: These tests are too loose and do not take THUMB/THUMB-2 difference
+  // into account.
+  bool is_bl_insn = (lower_insn & 0x1000U) == 0x1000U;
+  bool is_blx_insn = (lower_insn & 0x1000U) == 0x0000U;
+     
+  // Check that the instruction is valid.
+  if (r_type == elfcpp::R_ARM_THM_CALL)
+    {
+      if (!is_bl_insn && !is_blx_insn)
+       return This::STATUS_BAD_RELOC;
+    }
+  else if (r_type == elfcpp::R_ARM_THM_JUMP24)
+    {
+      // This cannot be a BLX.
+      if (!is_bl_insn)
+       return This::STATUS_BAD_RELOC;
+    }
+  else if (r_type == elfcpp::R_ARM_THM_XPC22)
+    {
+      // Check for Thumb to Thumb call.
+      if (!is_blx_insn)
+       return This::STATUS_BAD_RELOC;
+      if (thumb_bit != 0)
+       {
+         gold_warning(_("%s: Thumb BLX instruction targets "
+                        "thumb function '%s'."),
+                        object->name().c_str(),
+                        (gsym ? gsym->name() : "(local)")); 
+         // Convert BLX to BL.
+         lower_insn |= 0x1000U;
+       }
+    }
+  else
+    gold_unreachable();
+
+  // A branch to an undefined weak symbol is turned into a jump to
+  // the next instruction unless a PLT entry will be created.
+  // The jump to the next instruction is optimized as a NOP.W for
+  // Thumb-2 enabled architectures.
+  const Target_arm<big_endian>* arm_target =
+    Target_arm<big_endian>::default_target();
+  if (is_weakly_undefined_without_plt)
+    {
+      if (arm_target->may_use_thumb2_nop())
+       {
+         elfcpp::Swap<16, big_endian>::writeval(wv, 0xf3af);
+         elfcpp::Swap<16, big_endian>::writeval(wv + 1, 0x8000);
+       }
+      else
+       {
+         elfcpp::Swap<16, big_endian>::writeval(wv, 0xe000);
+         elfcpp::Swap<16, big_endian>::writeval(wv + 1, 0xbf00);
+       }
+      return This::STATUS_OKAY;
+    }
+  // Fetch the addend.  We use the Thumb-2 encoding (backwards compatible
+  // with Thumb-1) involving the J1 and J2 bits.
+  uint32_t s = (upper_insn & (1 << 10)) >> 10;
+  uint32_t upper = upper_insn & 0x3ff;
+  uint32_t lower = lower_insn & 0x7ff;
+  uint32_t j1 = (lower_insn & (1 << 13)) >> 13;
+  uint32_t j2 = (lower_insn & (1 << 11)) >> 11;
+  uint32_t i1 = j1 ^ s ? 0 : 1;
+  uint32_t i2 = j2 ^ s ? 0 : 1;
+  int32_t addend = (i1 << 23) | (i2 << 22) | (upper << 12) | (lower << 1);
+  // Sign extend.
+  addend = (addend | ((s ? 0 : 1) << 24)) - (1 << 24);
+
+  Arm_address branch_target = psymval->value(object, addend);
+  int32_t branch_offset = branch_target - address;
+
+  // We need a stub if the branch offset is too large or if we need
+  // to switch mode.
+  bool may_use_blx = arm_target->may_use_blx();
+  bool thumb2 = arm_target->using_thumb2();
+  if ((!thumb2
+       && (branch_offset > THM_MAX_FWD_BRANCH_OFFSET
+          || (branch_offset < THM_MAX_BWD_BRANCH_OFFSET)))
+      || (thumb2
+         && (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
+             || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
+      || ((thumb_bit == 0)
+          && (((r_type == elfcpp::R_ARM_THM_CALL) && !may_use_blx)
+             || r_type == elfcpp::R_ARM_THM_JUMP24)))
+    {
+      Stub_type stub_type =
+       Reloc_stub::stub_type_for_reloc(r_type, address, branch_target,
+                                       (thumb_bit != 0));
+      if (stub_type != arm_stub_none)
+       {
+         Stub_table<big_endian>* stub_table =
+           object->stub_table(relinfo->data_shndx);
+         gold_assert(stub_table != NULL);
+
+         Reloc_stub::Key stub_key(stub_type, gsym, object, r_sym, addend);
+         Reloc_stub* stub = stub_table->find_reloc_stub(stub_key);
+         gold_assert(stub != NULL);
+         thumb_bit = stub->stub_template()->entry_in_thumb_mode() ? 1 : 0;
+         branch_target = stub_table->address() + stub->offset() + addend;
+         branch_offset = branch_target - address;
+       }
+    }
+
+  // At this point, if we still need to switch mode, the instruction
+  // must either be a BLX or a BL that can be converted to a BLX.
+  if (thumb_bit == 0)
+    {
+      gold_assert(may_use_blx
+                 && (r_type == elfcpp::R_ARM_THM_CALL
+                     || r_type == elfcpp::R_ARM_THM_XPC22));
+      // Make sure this is a BLX.
+      lower_insn &= ~0x1000U;
+    }
+  else
+    {
+      // Make sure this is a BL.
+      lower_insn |= 0x1000U;
+    }
+
+  uint32_t reloc_sign = (branch_offset < 0) ? 1 : 0;
+  uint32_t relocation = static_cast<uint32_t>(branch_offset);
+
+  if ((lower_insn & 0x5000U) == 0x4000U)
+    // For a BLX instruction, make sure that the relocation is rounded up
+    // to a word boundary.  This follows the semantics of the instruction
+    // which specifies that bit 1 of the target address will come from bit
+    // 1 of the base address.
+    relocation = (relocation + 2U) & ~3U;
+
+  // Put BRANCH_OFFSET back into the insn.  Assumes two's complement.
+  // We use the Thumb-2 encoding, which is safe even if dealing with
+  // a Thumb-1 instruction by virtue of our overflow check above.  */
+  upper_insn = (upper_insn & ~0x7ffU)
+                | ((relocation >> 12) & 0x3ffU)
+                | (reloc_sign << 10);
+  lower_insn = (lower_insn & ~0x2fffU)
+                | (((!((relocation >> 23) & 1U)) ^ reloc_sign) << 13)
+                | (((!((relocation >> 22) & 1U)) ^ reloc_sign) << 11)
+                | ((relocation >> 1) & 0x7ffU);
+
+  elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn);
+  elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn);
+
+  return ((thumb2
+          ? utils::has_overflow<25>(relocation)
+          : utils::has_overflow<23>(relocation))
+         ? This::STATUS_OVERFLOW
+         : This::STATUS_OKAY);
+}
+
 // Get the GOT section, creating it if necessary.
 
 template<bool big_endian>
@@ -2543,7 +2738,8 @@ Reloc_stub::stub_type_for_reloc(
              // Thumb to thumb.
              if (!thumb_only)
                {
-                 stub_type = (parameters->options().shared() | should_force_pic_veneer)
+                 stub_type = (parameters->options().shared()
+                              || should_force_pic_veneer)
                    // PIC stubs.
                    ? ((may_use_blx
                        && (r_type == elfcpp::R_ARM_THM_CALL))
@@ -2563,7 +2759,8 @@ Reloc_stub::stub_type_for_reloc(
                }
              else
                {
-                 stub_type = (parameters->options().shared() | should_force_pic_veneer)
+                 stub_type = (parameters->options().shared()
+                              || should_force_pic_veneer)
                    ? arm_stub_long_branch_thumb_only_pic       // PIC stub.
                    : arm_stub_long_branch_thumb_only;  // non-PIC stub.
                }
@@ -4729,8 +4926,10 @@ Target_arm<big_endian>::Relocate::relocate(
       break;
 
     case elfcpp::R_ARM_THM_CALL:
-      reloc_status = Arm_relocate_functions::thm_call(view, object, psymval,
-                                                     address, thumb_bit);
+      reloc_status =
+       Arm_relocate_functions::thm_call(relinfo, view, gsym, object, r_sym,
+                                        psymval, address, thumb_bit,
+                                        is_weakly_undefined_without_plt);
       break;
 
     case elfcpp::R_ARM_XPC25:
@@ -4740,6 +4939,13 @@ Target_arm<big_endian>::Relocate::relocate(
                                      is_weakly_undefined_without_plt);
       break;
 
+    case elfcpp::R_ARM_THM_XPC22:
+      reloc_status =
+       Arm_relocate_functions::thm_xpc22(relinfo, view, gsym, object, r_sym,
+                                         psymval, address, thumb_bit,
+                                         is_weakly_undefined_without_plt);
+      break;
+
     case elfcpp::R_ARM_GOTOFF32:
       {
        Arm_address got_origin;
@@ -4842,6 +5048,13 @@ Target_arm<big_endian>::Relocate::relocate(
                                       is_weakly_undefined_without_plt);
       break;
 
+    case elfcpp::R_ARM_THM_JUMP24:
+      reloc_status =
+       Arm_relocate_functions::thm_jump24(relinfo, view, gsym, object, r_sym,
+                                          psymval, address, thumb_bit,
+                                          is_weakly_undefined_without_plt);
+      break;
+
     case elfcpp::R_ARM_PREL31:
       reloc_status = Arm_relocate_functions::prel31(view, object, psymval,
                                                    address, thumb_bit);