diff options
Diffstat (limited to 'gold/arm.cc')
-rw-r--r-- | gold/arm.cc | 58 |
1 files changed, 42 insertions, 16 deletions
diff --git a/gold/arm.cc b/gold/arm.cc index 016b2cc..890646e 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -2985,7 +2985,10 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian> Reltype x = psymval->value(object, addend); val = utils::bit_select(val, x, 0xffU); elfcpp::Swap<8, big_endian>::writeval(wv, val); - return (utils::has_signed_unsigned_overflow<8>(x) + + // R_ARM_ABS8 permits signed or unsigned results. + int signed_x = static_cast<int32_t>(x); + return ((signed_x < -128 || signed_x > 255) ? This::STATUS_OVERFLOW : This::STATUS_OKAY); } @@ -3004,7 +3007,10 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian> Reltype x = psymval->value(object, addend); val = utils::bit_select(val, x << 6, 0x7e0U); elfcpp::Swap<16, big_endian>::writeval(wv, val); - return (utils::has_overflow<5>(x) + + // R_ARM_ABS16 permits signed or unsigned results. + int signed_x = static_cast<int32_t>(x); + return ((signed_x < -32768 || signed_x > 65535) ? This::STATUS_OVERFLOW : This::STATUS_OKAY); } @@ -3831,11 +3837,16 @@ Arm_relocate_functions<big_endian>::thumb_branch_common( int32_t addend = This::thumb32_branch_offset(upper_insn, lower_insn); Arm_address branch_target = psymval->value(object, addend); + + // For BLX, bit 1 of target address comes from bit 1 of base address. + bool may_use_blx = arm_target->may_use_blx(); + if (thumb_bit == 0 && may_use_blx) + branch_target = utils::bit_select(branch_target, address, 0x2); + 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 && utils::has_overflow<23>(branch_offset)) || (thumb2 && utils::has_overflow<25>(branch_offset)) @@ -3861,6 +3872,8 @@ Arm_relocate_functions<big_endian>::thumb_branch_common( gold_assert(stub != NULL); thumb_bit = stub->stub_template()->entry_in_thumb_mode() ? 1 : 0; branch_target = stub_table->address() + stub->offset() + addend; + if (thumb_bit == 0 && may_use_blx) + branch_target = utils::bit_select(branch_target, address, 0x2); branch_offset = branch_target - address; } } @@ -3881,12 +3894,12 @@ Arm_relocate_functions<big_endian>::thumb_branch_common( lower_insn |= 0x1000U; } + // 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. 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. - branch_offset = (branch_offset + 2) & ~3; + gold_assert((branch_offset & 3) == 0); // Put BRANCH_OFFSET back into the insn. Assumes two's complement. // We use the Thumb-2 encoding, which is safe even if dealing with @@ -3897,6 +3910,8 @@ Arm_relocate_functions<big_endian>::thumb_branch_common( elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn); elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn); + gold_assert(!utils::has_overflow<25>(branch_offset)); + return ((thumb2 ? utils::has_overflow<25>(branch_offset) : utils::has_overflow<23>(branch_offset)) @@ -4225,10 +4240,15 @@ Reloc_stub::stub_type_for_reloc( thumb_only = little_endian_target->using_thumb_only(); } - int64_t branch_offset = (int64_t)destination - location; - + int64_t branch_offset; if (r_type == elfcpp::R_ARM_THM_CALL || r_type == elfcpp::R_ARM_THM_JUMP24) { + // For THUMB BLX instruction, bit 1 of target comes from bit 1 of the + // base address (instruction address + 4). + if ((r_type == elfcpp::R_ARM_THM_CALL) && may_use_blx && !target_is_thumb) + destination = utils::bit_select(destination, location, 0x2); + branch_offset = static_cast<int64_t>(destination) - location; + // Handle cases where: // - this call goes too far (different Thumb/Thumb2 max // distance) @@ -4309,6 +4329,7 @@ Reloc_stub::stub_type_for_reloc( || r_type == elfcpp::R_ARM_JUMP24 || r_type == elfcpp::R_ARM_PLT32) { + branch_offset = static_cast<int64_t>(destination) - location; if (target_is_thumb) { // Arm to thumb. @@ -8562,16 +8583,16 @@ Target_arm<big_endian>::Relocate::relocate( break; case Arm_relocate_functions::STATUS_OVERFLOW: gold_error_at_location(relinfo, relnum, rel.get_r_offset(), - _("relocation overflow in relocation %u"), - r_type); + _("relocation overflow in %s"), + reloc_property->name().c_str()); break; case Arm_relocate_functions::STATUS_BAD_RELOC: gold_error_at_location( relinfo, relnum, rel.get_r_offset(), - _("unexpected opcode while processing relocation %u"), - r_type); + _("unexpected opcode while processing relocation %s"), + reloc_property->name().c_str()); break; default: gold_unreachable(); @@ -10297,13 +10318,18 @@ Target_arm<big_endian>::do_relax( // Default value. // Thumb branch range is +-4MB has to be used as the default // maximum size (a given section can contain both ARM and Thumb - // code, so the worst case has to be taken into account). + // code, so the worst case has to be taken into account). If we are + // fixing cortex-a8 errata, the branch range has to be even smaller, + // since wide conditional branch has a range of +-1MB only. // // This value is 24K less than that, which allows for 2025 // 12-byte stubs. If we exceed that, then we will fail to link. // The user will have to relink with an explicit group size // option. - stub_group_size = 4170000; + if (this->fix_cortex_a8_) + stub_group_size = 1024276; + else + stub_group_size = 4170000; } group_sections(layout, stub_group_size, stubs_always_after_branch); |