* arm.cc: Added support for ARM group relocations.
authorIan Lance Taylor <ian@airs.com>
Wed, 27 Jan 2010 15:17:46 +0000 (15:17 +0000)
committerIan Lance Taylor <ian@airs.com>
Wed, 27 Jan 2010 15:17:46 +0000 (15:17 +0000)
(Target_arm::reloc_needs_sym_origin): New method.
(Arm_relocate_functions::calc_grp_kn): New method.
(Arm_relocate_functions::calc_grp_residual): New method.
(Arm_relocate_functions::calc_grp_gn): New method.
(Arm_relocate_functions::arm_grp_alu): New Method.
(Arm_relocate_functions::arm_grp_ldr): New Method.
(Arm_relocate_functions::arm_grp_ldrs): New Method.
(Arm_relocate_functions::arm_grp_ldc): New Method.
(Target_arm::Scan::local): Handle the ARM group relocations.
(Target_arm::Scan::global): Likewise.
(Target_arm::Relocate::relocate): Likewise.
(Target_arm::Relocatable_size_for_reloc::get_size_for_reloc):
Likewise.

gold/ChangeLog
gold/arm.cc

index 320eb3e96dfe22478c55f2ec8f492d760516ed6d..633e9e4a91cb8962292ea15310f0dd5fc1b04e1f 100644 (file)
@@ -1,3 +1,20 @@
+2010-01-27  Viktor Kutuzov  <vkutuzov@accesssoftek.com>
+
+       * arm.cc: Added support for ARM group relocations.
+       (Target_arm::reloc_needs_sym_origin): New method.
+       (Arm_relocate_functions::calc_grp_kn): New method.
+       (Arm_relocate_functions::calc_grp_residual): New method.
+       (Arm_relocate_functions::calc_grp_gn): New method.
+       (Arm_relocate_functions::arm_grp_alu): New Method.
+       (Arm_relocate_functions::arm_grp_ldr): New Method.
+       (Arm_relocate_functions::arm_grp_ldrs): New Method.
+       (Arm_relocate_functions::arm_grp_ldc): New Method.
+       (Target_arm::Scan::local): Handle the ARM group relocations.
+       (Target_arm::Scan::global): Likewise.
+       (Target_arm::Relocate::relocate): Likewise.
+       (Target_arm::Relocatable_size_for_reloc::get_size_for_reloc):
+       Likewise.
+
 2010-01-26  Doug Kwan  <dougkwan@google.com>
 
        * arm.cc (set): Include.
index 92d8a7abe32185d8c52161323fc06d4c3f9a7245..3fe897e0024e1fd51757f62320c28c74fb640195 100644 (file)
@@ -1,6 +1,6 @@
 // arm.cc -- arm target support for gold.
 
-// Copyright 2009 Free Software Foundation, Inc.
+// Copyright 2009, 2010 Free Software Foundation, Inc.
 // Written by Doug Kwan <dougkwan@google.com> based on the i386 code
 // by Ian Lance Taylor <iant@google.com>.
 // This file also contains borrowed and adapted code from
@@ -96,48 +96,38 @@ const int32_t THM2_MAX_BWD_BRANCH_OFFSET = (-(1 << 24) + 4);
 // The arm target class.
 //
 // This is a very simple port of gold for ARM-EABI.  It is intended for
-// supporting Android only for the time being.  Only these relocation types
-// are supported.
-//
-// R_ARM_NONE
-// R_ARM_ABS32
-// R_ARM_ABS32_NOI
-// R_ARM_ABS16
-// R_ARM_ABS12
-// R_ARM_ABS8
-// R_ARM_THM_ABS5
-// R_ARM_BASE_ABS
-// R_ARM_REL32
-// R_ARM_THM_CALL
-// R_ARM_COPY
-// R_ARM_GLOB_DAT
-// R_ARM_BASE_PREL
-// R_ARM_JUMP_SLOT
-// R_ARM_RELATIVE
-// R_ARM_GOTOFF32
-// R_ARM_GOT_BREL
-// R_ARM_GOT_PREL
-// R_ARM_PLT32
-// R_ARM_CALL
-// R_ARM_JUMP24
-// R_ARM_TARGET1
-// R_ARM_PREL31
-// R_ARM_ABS8
-// R_ARM_MOVW_ABS_NC
-// R_ARM_MOVT_ABS
-// R_ARM_THM_MOVW_ABS_NC
-// R_ARM_THM_MOVT_ABS
-// R_ARM_MOVW_PREL_NC
-// 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
+// supporting Android only for the time being.
 // 
 // TODOs:
-// - Support more relocation types as needed. 
+// - Support the following relocation types as needed:
+//     R_ARM_SBREL32
+//     R_ARM_THM_PC8
+//     R_ARM_LDR_SBREL_11_0_NC
+//     R_ARM_ALU_SBREL_19_12_NC
+//     R_ARM_ALU_SBREL_27_20_CK
+//     R_ARM_SBREL31
+//     R_ARM_THM_ALU_PREL_11_0
+//     R_ARM_THM_PC12
+//     R_ARM_REL32_NOI
+//     R_ARM_MOVW_BREL_NC
+//     R_ARM_MOVT_BREL
+//     R_ARM_MOVW_BREL
+//     R_ARM_THM_MOVW_BREL_NC
+//     R_ARM_THM_MOVT_BREL
+//     R_ARM_THM_MOVW_BREL
+//     R_ARM_PLT32_ABS
+//     R_ARM_GOT_ABS
+//     R_ARM_GOT_BREL12
+//     R_ARM_GOTOFF12
+//     R_ARM_TLS_GD32
+//     R_ARM_TLS_LDM32
+//     R_ARM_TLS_LDO32
+//     R_ARM_TLS_IE32
+//     R_ARM_TLS_LE32
+//     R_ARM_TLS_LDO12
+//     R_ARM_TLS_LE12
+//     R_ARM_TLS_IE12GP
+//
 // - Make PLTs more flexible for different architecture features like
 //   Thumb-2 and BE8.
 // There are probably a lot more.
@@ -2245,6 +2235,47 @@ class Target_arm : public Sized_target<32, big_endian>
          return true;
        }
     }
+
+    // Return whether we need to calculate the addressing origin of
+    // the output segment defining the symbol - B(S).
+    static bool
+    reloc_needs_sym_origin(unsigned int r_type)
+    {
+      switch (r_type)
+       {
+       case elfcpp::R_ARM_SBREL32:
+       case elfcpp::R_ARM_BASE_PREL:
+       case elfcpp::R_ARM_BASE_ABS:
+       case elfcpp::R_ARM_LDR_SBREL_11_0_NC:
+       case elfcpp::R_ARM_ALU_SBREL_19_12_NC:
+       case elfcpp::R_ARM_ALU_SBREL_27_20_CK:
+       case elfcpp::R_ARM_SBREL31:
+       case elfcpp::R_ARM_ALU_SB_G0_NC:
+       case elfcpp::R_ARM_ALU_SB_G0:
+       case elfcpp::R_ARM_ALU_SB_G1_NC:
+       case elfcpp::R_ARM_ALU_SB_G1:
+       case elfcpp::R_ARM_ALU_SB_G2:
+       case elfcpp::R_ARM_LDR_SB_G0:
+       case elfcpp::R_ARM_LDR_SB_G1:
+       case elfcpp::R_ARM_LDR_SB_G2:
+       case elfcpp::R_ARM_LDRS_SB_G0:
+       case elfcpp::R_ARM_LDRS_SB_G1:
+       case elfcpp::R_ARM_LDRS_SB_G2:
+       case elfcpp::R_ARM_LDC_SB_G0:
+       case elfcpp::R_ARM_LDC_SB_G1:
+       case elfcpp::R_ARM_LDC_SB_G2:
+       case elfcpp::R_ARM_MOVW_BREL_NC:
+       case elfcpp::R_ARM_MOVT_BREL:
+       case elfcpp::R_ARM_MOVW_BREL:
+       case elfcpp::R_ARM_THM_MOVW_BREL_NC:
+       case elfcpp::R_ARM_THM_MOVT_BREL:
+       case elfcpp::R_ARM_THM_MOVW_BREL:
+         return true;
+
+       default:
+         return false;
+       }
+    }
   };
 
   // A class which returns the size required for a relocation type,
@@ -2557,6 +2588,66 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian>
     return val;
   }
 
+  // Calculate the smallest constant Kn for the specified residual.
+  // (see (AAELF 4.6.1.4 Static ARM relocations, Group Relocations, p.32)
+  static uint32_t
+  calc_grp_kn(typename elfcpp::Swap<32, big_endian>::Valtype residual)
+  {
+    int32_t msb;
+
+    if (residual == 0)
+      return 0;
+    // Determine the most significant bit in the residual and
+    // align the resulting value to a 2-bit boundary.
+    for (msb = 30; (msb >= 0) && !(residual & (3 << msb)); msb -= 2)
+      ;
+    // The desired shift is now (msb - 6), or zero, whichever
+    // is the greater.
+    return (((msb - 6) < 0) ? 0 : (msb - 6));
+  }
+
+  // Calculate the final residual for the specified group index.
+  // If the passed group index is less than zero, the method will return
+  // the value of the specified residual without any change.
+  // (see (AAELF 4.6.1.4 Static ARM relocations, Group Relocations, p.32)
+  static typename elfcpp::Swap<32, big_endian>::Valtype
+  calc_grp_residual(typename elfcpp::Swap<32, big_endian>::Valtype residual,
+                   const int group)
+  {
+    for (int n = 0; n <= group; n++)
+      {
+       // Calculate which part of the value to mask.
+       uint32_t shift = calc_grp_kn(residual);
+       // Calculate the residual for the next time around.
+       residual &= ~(residual & (0xff << shift));
+      }
+
+    return residual;
+  }
+
+  // Calculate the value of Gn for the specified group index.
+  // We return it in the form of an encoded constant-and-rotation.
+  // (see (AAELF 4.6.1.4 Static ARM relocations, Group Relocations, p.32)
+  static typename elfcpp::Swap<32, big_endian>::Valtype
+  calc_grp_gn(typename elfcpp::Swap<32, big_endian>::Valtype residual,
+             const int group)
+  {
+    typename elfcpp::Swap<32, big_endian>::Valtype gn = 0;
+    uint32_t shift = 0;
+
+    for (int n = 0; n <= group; n++)
+      {
+       // Calculate which part of the value to mask.
+       shift = calc_grp_kn(residual);
+       // Calculate Gn in 32-bit as well as encoded constant-and-rotation form.
+       gn = residual & (0xff << shift);
+       // Calculate the residual for the next time around.
+       residual &= ~gn;
+      }
+    // Return Gn in the form of an encoded constant-and-rotation.
+    return ((gn >> shift) | ((gn <= 0xff ? 0 : (32 - shift) / 2) << 8));
+  }
+
   // Handle ARM long branches.
   static typename This::Status
   arm_branch_common(unsigned int, const Relocate_info<32, big_endian>*,
@@ -3181,6 +3272,177 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian>
     elfcpp::Swap<32, big_endian>::writeval(wv, val);
     return This::STATUS_OKAY;
   }
+
+  // R_ARM_ALU_PC_G0_NC: ((S + A) | T) - P
+  // R_ARM_ALU_PC_G0:    ((S + A) | T) - P
+  // R_ARM_ALU_PC_G1_NC: ((S + A) | T) - P
+  // R_ARM_ALU_PC_G1:    ((S + A) | T) - P
+  // R_ARM_ALU_PC_G2:    ((S + A) | T) - P
+  // R_ARM_ALU_SB_G0_NC: ((S + A) | T) - B(S)
+  // R_ARM_ALU_SB_G0:    ((S + A) | T) - B(S)
+  // R_ARM_ALU_SB_G1_NC: ((S + A) | T) - B(S)
+  // R_ARM_ALU_SB_G1:    ((S + A) | T) - B(S)
+  // R_ARM_ALU_SB_G2:    ((S + A) | T) - B(S)
+  static inline typename This::Status
+  arm_grp_alu(unsigned char* view,
+       const Sized_relobj<32, big_endian>* object,
+       const Symbol_value<32>* psymval,
+       const int group,
+       Arm_address address,
+       Arm_address thumb_bit,
+       bool check_overflow)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    // ALU group relocations are allowed only for the ADD/SUB instructions.
+    // (0x00800000 - ADD, 0x00400000 - SUB)
+    const Valtype opcode = insn & 0x01e00000;
+    if (opcode != 0x00800000 && opcode != 0x00400000)
+      return This::STATUS_BAD_RELOC;
+
+    // Determine a sign for the addend.
+    const int sign = (opcode == 0x00800000) ? 1 : -1;
+    // shifter = rotate_imm * 2
+    const uint32_t shifter = (insn & 0xf00) >> 7;
+    // Initial addend value.
+    int32_t addend = insn & 0xff;
+    // Rotate addend right by shifter.
+    addend = (addend >> shifter) | (addend << (32 - shifter));
+    // Apply a sign to the added.
+    addend *= sign;
+
+    int32_t x = ((psymval->value(object, addend) | thumb_bit) - address);
+    Valtype gn = Arm_relocate_functions::calc_grp_gn(abs(x), group);
+    // Check for overflow if required
+    if (check_overflow
+       && (Arm_relocate_functions::calc_grp_residual(abs(x), group) != 0))
+      return This::STATUS_OVERFLOW;
+
+    // Mask out the value and the ADD/SUB part of the opcode; take care
+    // not to destroy the S bit.
+    insn &= 0xff1ff000;
+    // Set the opcode according to whether the value to go in the
+    // place is negative.
+    insn |= ((x < 0) ? 0x00400000 : 0x00800000);
+    // Encode the offset (encoded Gn).
+    insn |= gn;
+
+    elfcpp::Swap<32, big_endian>::writeval(wv, insn);
+    return This::STATUS_OKAY;
+  }
+
+  // R_ARM_LDR_PC_G0: S + A - P
+  // R_ARM_LDR_PC_G1: S + A - P
+  // R_ARM_LDR_PC_G2: S + A - P
+  // R_ARM_LDR_SB_G0: S + A - B(S)
+  // R_ARM_LDR_SB_G1: S + A - B(S)
+  // R_ARM_LDR_SB_G2: S + A - B(S)
+  static inline typename This::Status
+  arm_grp_ldr(unsigned char* view,
+       const Sized_relobj<32, big_endian>* object,
+       const Symbol_value<32>* psymval,
+       const int group,
+       Arm_address address)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    const int sign = (insn & 0x00800000) ? 1 : -1;
+    int32_t addend = (insn & 0xfff) * sign;
+    int32_t x = (psymval->value(object, addend) - address);
+    // Calculate the relevant G(n-1) value to obtain this stage residual.
+    Valtype residual =
+       Arm_relocate_functions::calc_grp_residual(abs(x), group - 1);
+    if (residual >= 0x1000)
+      return This::STATUS_OVERFLOW;
+
+    // Mask out the value and U bit.
+    insn &= 0xff7ff000;
+    // Set the U bit for non-negative values.
+    if (x >= 0)
+      insn |= 0x00800000;
+    insn |= residual;
+
+    elfcpp::Swap<32, big_endian>::writeval(wv, insn);
+    return This::STATUS_OKAY;
+  }
+
+  // R_ARM_LDRS_PC_G0: S + A - P
+  // R_ARM_LDRS_PC_G1: S + A - P
+  // R_ARM_LDRS_PC_G2: S + A - P
+  // R_ARM_LDRS_SB_G0: S + A - B(S)
+  // R_ARM_LDRS_SB_G1: S + A - B(S)
+  // R_ARM_LDRS_SB_G2: S + A - B(S)
+  static inline typename This::Status
+  arm_grp_ldrs(unsigned char* view,
+       const Sized_relobj<32, big_endian>* object,
+       const Symbol_value<32>* psymval,
+       const int group,
+       Arm_address address)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    const int sign = (insn & 0x00800000) ? 1 : -1;
+    int32_t addend = (((insn & 0xf00) >> 4) + (insn & 0xf)) * sign;
+    int32_t x = (psymval->value(object, addend) - address);
+    // Calculate the relevant G(n-1) value to obtain this stage residual.
+    Valtype residual =
+       Arm_relocate_functions::calc_grp_residual(abs(x), group - 1);
+   if (residual >= 0x100)
+      return This::STATUS_OVERFLOW;
+
+    // Mask out the value and U bit.
+    insn &= 0xff7ff0f0;
+    // Set the U bit for non-negative values.
+    if (x >= 0)
+      insn |= 0x00800000;
+    insn |= ((residual & 0xf0) << 4) | (residual & 0xf);
+
+    elfcpp::Swap<32, big_endian>::writeval(wv, insn);
+    return This::STATUS_OKAY;
+  }
+
+  // R_ARM_LDC_PC_G0: S + A - P
+  // R_ARM_LDC_PC_G1: S + A - P
+  // R_ARM_LDC_PC_G2: S + A - P
+  // R_ARM_LDC_SB_G0: S + A - B(S)
+  // R_ARM_LDC_SB_G1: S + A - B(S)
+  // R_ARM_LDC_SB_G2: S + A - B(S)
+  static inline typename This::Status
+  arm_grp_ldc(unsigned char* view,
+      const Sized_relobj<32, big_endian>* object,
+      const Symbol_value<32>* psymval,
+      const int group,
+      Arm_address address)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    const int sign = (insn & 0x00800000) ? 1 : -1;
+    int32_t addend = ((insn & 0xff) << 2) * sign;
+    int32_t x = (psymval->value(object, addend) - address);
+    // Calculate the relevant G(n-1) value to obtain this stage residual.
+    Valtype residual =
+      Arm_relocate_functions::calc_grp_residual(abs(x), group - 1);
+    if ((residual & 0x3) != 0 || residual >= 0x400)
+      return This::STATUS_OVERFLOW;
+
+    // Mask out the value and U bit.
+    insn &= 0xff7fff00;
+    // Set the U bit for non-negative values.
+    if (x >= 0)
+      insn |= 0x00800000;
+    insn |= (residual >> 2);
+
+    elfcpp::Swap<32, big_endian>::writeval(wv, insn);
+    return This::STATUS_OKAY;
+  }
 };
 
 // Relocate ARM long branches.  This handles relocation types
@@ -6256,6 +6518,34 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
     case elfcpp::R_ARM_THM_JUMP8:
     case elfcpp::R_ARM_THM_JUMP11:
     case elfcpp::R_ARM_V4BX:
+    case elfcpp::R_ARM_ALU_PC_G0_NC:
+    case elfcpp::R_ARM_ALU_PC_G0:
+    case elfcpp::R_ARM_ALU_PC_G1_NC:
+    case elfcpp::R_ARM_ALU_PC_G1:
+    case elfcpp::R_ARM_ALU_PC_G2:
+    case elfcpp::R_ARM_ALU_SB_G0_NC:
+    case elfcpp::R_ARM_ALU_SB_G0:
+    case elfcpp::R_ARM_ALU_SB_G1_NC:
+    case elfcpp::R_ARM_ALU_SB_G1:
+    case elfcpp::R_ARM_ALU_SB_G2:
+    case elfcpp::R_ARM_LDR_PC_G0:
+    case elfcpp::R_ARM_LDR_PC_G1:
+    case elfcpp::R_ARM_LDR_PC_G2:
+    case elfcpp::R_ARM_LDR_SB_G0:
+    case elfcpp::R_ARM_LDR_SB_G1:
+    case elfcpp::R_ARM_LDR_SB_G2:
+    case elfcpp::R_ARM_LDRS_PC_G0:
+    case elfcpp::R_ARM_LDRS_PC_G1:
+    case elfcpp::R_ARM_LDRS_PC_G2:
+    case elfcpp::R_ARM_LDRS_SB_G0:
+    case elfcpp::R_ARM_LDRS_SB_G1:
+    case elfcpp::R_ARM_LDRS_SB_G2:
+    case elfcpp::R_ARM_LDC_PC_G0:
+    case elfcpp::R_ARM_LDC_PC_G1:
+    case elfcpp::R_ARM_LDC_PC_G2:
+    case elfcpp::R_ARM_LDC_SB_G0:
+    case elfcpp::R_ARM_LDC_SB_G1:
+    case elfcpp::R_ARM_LDC_SB_G2:
       break;
 
     case elfcpp::R_ARM_GOTOFF32:
@@ -6388,6 +6678,34 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
     case elfcpp::R_ARM_THM_JUMP8:
     case elfcpp::R_ARM_THM_JUMP11:
     case elfcpp::R_ARM_V4BX:
+    case elfcpp::R_ARM_ALU_PC_G0_NC:
+    case elfcpp::R_ARM_ALU_PC_G0:
+    case elfcpp::R_ARM_ALU_PC_G1_NC:
+    case elfcpp::R_ARM_ALU_PC_G1:
+    case elfcpp::R_ARM_ALU_PC_G2:
+    case elfcpp::R_ARM_ALU_SB_G0_NC:
+    case elfcpp::R_ARM_ALU_SB_G0:
+    case elfcpp::R_ARM_ALU_SB_G1_NC:
+    case elfcpp::R_ARM_ALU_SB_G1:
+    case elfcpp::R_ARM_ALU_SB_G2:
+    case elfcpp::R_ARM_LDR_PC_G0:
+    case elfcpp::R_ARM_LDR_PC_G1:
+    case elfcpp::R_ARM_LDR_PC_G2:
+    case elfcpp::R_ARM_LDR_SB_G0:
+    case elfcpp::R_ARM_LDR_SB_G1:
+    case elfcpp::R_ARM_LDR_SB_G2:
+    case elfcpp::R_ARM_LDRS_PC_G0:
+    case elfcpp::R_ARM_LDRS_PC_G1:
+    case elfcpp::R_ARM_LDRS_PC_G2:
+    case elfcpp::R_ARM_LDRS_SB_G0:
+    case elfcpp::R_ARM_LDRS_SB_G1:
+    case elfcpp::R_ARM_LDRS_SB_G2:
+    case elfcpp::R_ARM_LDC_PC_G0:
+    case elfcpp::R_ARM_LDC_PC_G1:
+    case elfcpp::R_ARM_LDC_PC_G2:
+    case elfcpp::R_ARM_LDC_SB_G0:
+    case elfcpp::R_ARM_LDC_SB_G1:
+    case elfcpp::R_ARM_LDC_SB_G2:
       break;
 
     case elfcpp::R_ARM_THM_ABS5:
@@ -6881,6 +7199,28 @@ Target_arm<big_endian>::Relocate::relocate(
   // a local symbol.
   unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
 
+  // Get the addressing origin of the output segment defining the
+  // symbol gsym if needed (AAELF 4.6.1.2 Relocation types).
+  Arm_address sym_origin = 0;
+  if (Relocate::reloc_needs_sym_origin(r_type))
+    {
+      if (r_type == elfcpp::R_ARM_BASE_ABS && gsym == NULL)
+       // R_ARM_BASE_ABS with the NULL symbol will give the
+       // absolute address of the GOT origin (GOT_ORG) (see ARM IHI
+       // 0044C (AAELF): 4.6.1.8 Proxy generating relocations).
+       sym_origin = target->got_plt_section()->address();
+      else if (gsym == NULL)
+       sym_origin = 0;
+      else if (gsym->source() == Symbol::IN_OUTPUT_SEGMENT)
+       sym_origin = gsym->output_segment()->vaddr();
+      else if (gsym->source() == Symbol::IN_OUTPUT_DATA)
+       sym_origin = gsym->output_data()->address();
+
+      // TODO: Assumes the segment base to be zero for the global symbols
+      // till the proper support for the segment-base-relative addressing
+      // will be implemented.  This is consistent with GNU ld.
+    }
+
   typename Arm_relocate_functions::Status reloc_status =
        Arm_relocate_functions::STATUS_OKAY;
   switch (r_type)
@@ -7026,23 +7366,9 @@ Target_arm<big_endian>::Relocate::relocate(
       break;
 
     case elfcpp::R_ARM_BASE_PREL:
-      {
-       uint32_t origin;
-       // Get the addressing origin of the output segment defining the 
-       // symbol gsym (AAELF 4.6.1.2 Relocation types)
-       gold_assert(gsym != NULL); 
-       if (gsym->source() == Symbol::IN_OUTPUT_SEGMENT)
-         origin = gsym->output_segment()->vaddr();
-       else if (gsym->source () == Symbol::IN_OUTPUT_DATA)
-         origin = gsym->output_data()->address();
-       else
-         {
-            gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
-                                  _("cannot find origin of R_ARM_BASE_PREL"));
-           return true;
-         }
-       reloc_status = Arm_relocate_functions::base_prel(view, origin, address);
-      }
+      gold_assert(gsym != NULL);
+      reloc_status =
+         Arm_relocate_functions::base_prel(view, sym_origin, address);
       break;
 
     case elfcpp::R_ARM_BASE_ABS:
@@ -7051,26 +7377,7 @@ Target_arm<big_endian>::Relocate::relocate(
                                      output_section))
          break;
 
-       uint32_t origin;
-       // Get the addressing origin of the output segment defining
-       // the symbol gsym (AAELF 4.6.1.2 Relocation types).
-       if (gsym == NULL)
-         // R_ARM_BASE_ABS with the NULL symbol will give the
-         // absolute address of the GOT origin (GOT_ORG) (see ARM IHI
-         // 0044C (AAELF): 4.6.1.8 Proxy generating relocations).
-         origin = target->got_plt_section()->address();
-       else if (gsym->source() == Symbol::IN_OUTPUT_SEGMENT)
-         origin = gsym->output_segment()->vaddr();
-       else if (gsym->source () == Symbol::IN_OUTPUT_DATA)
-         origin = gsym->output_data()->address();
-       else
-         {
-            gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
-                                  _("cannot find origin of R_ARM_BASE_ABS"));
-           return true;
-         }
-
-       reloc_status = Arm_relocate_functions::base_abs(view, origin);
+       reloc_status = Arm_relocate_functions::base_abs(view, sym_origin);
       }
       break;
 
@@ -7162,6 +7469,174 @@ Target_arm<big_endian>::Relocate::relocate(
        }
       break;
 
+    case elfcpp::R_ARM_ALU_PC_G0_NC:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_alu(view, object, psymval, 0,
+                                             address, thumb_bit, false);
+      break;
+
+    case elfcpp::R_ARM_ALU_PC_G0:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_alu(view, object, psymval, 0,
+                                             address, thumb_bit, true);
+      break;
+
+    case elfcpp::R_ARM_ALU_PC_G1_NC:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_alu(view, object, psymval, 1,
+                                             address, thumb_bit, false);
+      break;
+
+    case elfcpp::R_ARM_ALU_PC_G1:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_alu(view, object, psymval, 1,
+                                             address, thumb_bit, true);
+      break;
+
+    case elfcpp::R_ARM_ALU_PC_G2:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_alu(view, object, psymval, 2,
+                                             address, thumb_bit, true);
+      break;
+
+    case elfcpp::R_ARM_ALU_SB_G0_NC:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_alu(view, object, psymval, 0,
+                                             sym_origin, thumb_bit, false);
+      break;
+
+    case elfcpp::R_ARM_ALU_SB_G0:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_alu(view, object, psymval, 0,
+                                             sym_origin, thumb_bit, true);
+      break;
+
+    case elfcpp::R_ARM_ALU_SB_G1_NC:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_alu(view, object, psymval, 1,
+                                             sym_origin, thumb_bit, false);
+      break;
+
+    case elfcpp::R_ARM_ALU_SB_G1:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_alu(view, object, psymval, 1,
+                                             sym_origin, thumb_bit, true);
+      break;
+
+    case elfcpp::R_ARM_ALU_SB_G2:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_alu(view, object, psymval, 2,
+                                             sym_origin, thumb_bit, true);
+      break;
+
+    case elfcpp::R_ARM_LDR_PC_G0:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldr(view, object, psymval, 0,
+                                             address);
+      break;
+
+    case elfcpp::R_ARM_LDR_PC_G1:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldr(view, object, psymval, 1,
+                                             address);
+      break;
+
+    case elfcpp::R_ARM_LDR_PC_G2:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldr(view, object, psymval, 2,
+                                             address);
+      break;
+
+    case elfcpp::R_ARM_LDR_SB_G0:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldr(view, object, psymval, 0,
+                                             sym_origin);
+      break;
+
+    case elfcpp::R_ARM_LDR_SB_G1:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldr(view, object, psymval, 1,
+                                             sym_origin);
+      break;
+
+    case elfcpp::R_ARM_LDR_SB_G2:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldr(view, object, psymval, 2,
+                                             sym_origin);
+      break;
+
+    case elfcpp::R_ARM_LDRS_PC_G0:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldrs(view, object, psymval, 0,
+                                              address);
+      break;
+
+    case elfcpp::R_ARM_LDRS_PC_G1:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldrs(view, object, psymval, 1,
+                                              address);
+      break;
+
+    case elfcpp::R_ARM_LDRS_PC_G2:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldrs(view, object, psymval, 2,
+                                              address);
+      break;
+
+    case elfcpp::R_ARM_LDRS_SB_G0:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldrs(view, object, psymval, 0,
+                                              sym_origin);
+      break;
+
+    case elfcpp::R_ARM_LDRS_SB_G1:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldrs(view, object, psymval, 1,
+                                              sym_origin);
+      break;
+
+    case elfcpp::R_ARM_LDRS_SB_G2:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldrs(view, object, psymval, 2,
+                                              sym_origin);
+      break;
+
+    case elfcpp::R_ARM_LDC_PC_G0:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldc(view, object, psymval, 0,
+                                             address);
+      break;
+
+    case elfcpp::R_ARM_LDC_PC_G1:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldc(view, object, psymval, 1,
+                                             address);
+      break;
+
+    case elfcpp::R_ARM_LDC_PC_G2:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldc(view, object, psymval, 2,
+                                             address);
+      break;
+
+    case elfcpp::R_ARM_LDC_SB_G0:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldc(view, object, psymval, 0,
+                                             sym_origin);
+      break;
+
+    case elfcpp::R_ARM_LDC_SB_G1:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldc(view, object, psymval, 1,
+                                             sym_origin);
+      break;
+
+    case elfcpp::R_ARM_LDC_SB_G2:
+      reloc_status =
+         Arm_relocate_functions::arm_grp_ldc(view, object, psymval, 2,
+                                             sym_origin);
+      break;
+
     case elfcpp::R_ARM_TARGET1:
       // This should have been mapped to another type already.
       // Fall through.
@@ -7311,6 +7786,34 @@ Target_arm<big_endian>::Relocatable_size_for_reloc::get_size_for_reloc(
     case elfcpp::R_ARM_THM_MOVW_PREL_NC:
     case elfcpp::R_ARM_THM_MOVT_PREL:
     case elfcpp::R_ARM_V4BX:
+    case elfcpp::R_ARM_ALU_PC_G0_NC:
+    case elfcpp::R_ARM_ALU_PC_G0:
+    case elfcpp::R_ARM_ALU_PC_G1_NC:
+    case elfcpp::R_ARM_ALU_PC_G1:
+    case elfcpp::R_ARM_ALU_PC_G2:
+    case elfcpp::R_ARM_ALU_SB_G0_NC:
+    case elfcpp::R_ARM_ALU_SB_G0:
+    case elfcpp::R_ARM_ALU_SB_G1_NC:
+    case elfcpp::R_ARM_ALU_SB_G1:
+    case elfcpp::R_ARM_ALU_SB_G2:
+    case elfcpp::R_ARM_LDR_PC_G0:
+    case elfcpp::R_ARM_LDR_PC_G1:
+    case elfcpp::R_ARM_LDR_PC_G2:
+    case elfcpp::R_ARM_LDR_SB_G0:
+    case elfcpp::R_ARM_LDR_SB_G1:
+    case elfcpp::R_ARM_LDR_SB_G2:
+    case elfcpp::R_ARM_LDRS_PC_G0:
+    case elfcpp::R_ARM_LDRS_PC_G1:
+    case elfcpp::R_ARM_LDRS_PC_G2:
+    case elfcpp::R_ARM_LDRS_SB_G0:
+    case elfcpp::R_ARM_LDRS_SB_G1:
+    case elfcpp::R_ARM_LDRS_SB_G2:
+    case elfcpp::R_ARM_LDC_PC_G0:
+    case elfcpp::R_ARM_LDC_PC_G1:
+    case elfcpp::R_ARM_LDC_PC_G2:
+    case elfcpp::R_ARM_LDC_SB_G0:
+    case elfcpp::R_ARM_LDC_SB_G1:
+    case elfcpp::R_ARM_LDC_SB_G2:
       return 4;
 
     case elfcpp::R_ARM_TARGET1: