From 51938283769ef9915b3106f48193ed42a3cea44e Mon Sep 17 00:00:00 2001 From: Doug Kwan Date: Wed, 25 Nov 2009 04:11:56 +0000 Subject: [PATCH] 2009-11-25 Doug Kwan * 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 | 16 +++ gold/arm.cc | 289 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 267 insertions(+), 38 deletions(-) diff --git a/gold/ChangeLog b/gold/ChangeLog index daa729eef7f..041c9e6317d 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,19 @@ +2009-11-25 Doug Kwan + + * 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 * Makefile.am: Build incremental-dump diff --git a/gold/arm.cc b/gold/arm.cc index 5b702f22055..3ea98de853c 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -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*, 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*, 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* 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(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* 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* 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::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 +typename Arm_relocate_functions::Status +Arm_relocate_functions::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* 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(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* arm_target = + Target_arm::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* 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(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 @@ -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::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::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::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); -- 2.30.2