Line 1... |
Line 1... |
// arm.cc -- arm target support for gold.
|
// arm.cc -- arm target support for gold.
|
|
|
// Copyright 2009, 2010, 2011 Free Software Foundation, Inc.
|
// Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
|
// Written by Doug Kwan <dougkwan@google.com> based on the i386 code
|
// Written by Doug Kwan <dougkwan@google.com> based on the i386 code
|
// by Ian Lance Taylor <iant@google.com>.
|
// by Ian Lance Taylor <iant@google.com>.
|
// This file also contains borrowed and adapted code from
|
// This file also contains borrowed and adapted code from
|
// bfd/elf32-arm.c.
|
// bfd/elf32-arm.c.
|
|
|
Line 2101... |
Line 2101... |
}
|
}
|
}
|
}
|
}
|
}
|
};
|
};
|
|
|
// Utilities for manipulating integers of up to 32-bits
|
|
|
|
namespace utils
|
|
{
|
|
// Sign extend an n-bit unsigned integer stored in an uint32_t into
|
|
// an int32_t. NO_BITS must be between 1 to 32.
|
|
template<int no_bits>
|
|
static inline int32_t
|
|
sign_extend(uint32_t bits)
|
|
{
|
|
gold_assert(no_bits >= 0 && no_bits <= 32);
|
|
if (no_bits == 32)
|
|
return static_cast<int32_t>(bits);
|
|
uint32_t mask = (~((uint32_t) 0)) >> (32 - no_bits);
|
|
bits &= mask;
|
|
uint32_t top_bit = 1U << (no_bits - 1);
|
|
int32_t as_signed = static_cast<int32_t>(bits);
|
|
return (bits & top_bit) ? as_signed + (-top_bit * 2) : as_signed;
|
|
}
|
|
|
|
// Detects overflow of an NO_BITS integer stored in a uint32_t.
|
|
template<int no_bits>
|
|
static inline bool
|
|
has_overflow(uint32_t bits)
|
|
{
|
|
gold_assert(no_bits >= 0 && no_bits <= 32);
|
|
if (no_bits == 32)
|
|
return false;
|
|
int32_t max = (1 << (no_bits - 1)) - 1;
|
|
int32_t min = -(1 << (no_bits - 1));
|
|
int32_t as_signed = static_cast<int32_t>(bits);
|
|
return as_signed > max || as_signed < min;
|
|
}
|
|
|
|
// Detects overflow of an NO_BITS integer stored in a uint32_t when it
|
|
// fits in the given number of bits as either a signed or unsigned value.
|
|
// For example, has_signed_unsigned_overflow<8> would check
|
|
// -128 <= bits <= 255
|
|
template<int no_bits>
|
|
static inline bool
|
|
has_signed_unsigned_overflow(uint32_t bits)
|
|
{
|
|
gold_assert(no_bits >= 2 && no_bits <= 32);
|
|
if (no_bits == 32)
|
|
return false;
|
|
int32_t max = static_cast<int32_t>((1U << no_bits) - 1);
|
|
int32_t min = -(1 << (no_bits - 1));
|
|
int32_t as_signed = static_cast<int32_t>(bits);
|
|
return as_signed > max || as_signed < min;
|
|
}
|
|
|
|
// Select bits from A and B using bits in MASK. For each n in [0..31],
|
|
// the n-th bit in the result is chosen from the n-th bits of A and B.
|
|
// A zero selects A and a one selects B.
|
|
static inline uint32_t
|
|
bit_select(uint32_t a, uint32_t b, uint32_t mask)
|
|
{ return (a & ~mask) | (b & mask); }
|
|
};
|
|
|
|
template<bool big_endian>
|
template<bool big_endian>
|
class Target_arm : public Sized_target<32, big_endian>
|
class Target_arm : public Sized_target<32, big_endian>
|
{
|
{
|
public:
|
public:
|
typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian>
|
typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian>
|
Line 3013... |
Line 2954... |
extract_arm_movw_movt_addend(
|
extract_arm_movw_movt_addend(
|
typename elfcpp::Swap<32, big_endian>::Valtype val)
|
typename elfcpp::Swap<32, big_endian>::Valtype val)
|
{
|
{
|
// According to the Elf ABI for ARM Architecture the immediate
|
// According to the Elf ABI for ARM Architecture the immediate
|
// field is sign-extended to form the addend.
|
// field is sign-extended to form the addend.
|
return utils::sign_extend<16>(((val >> 4) & 0xf000) | (val & 0xfff));
|
return Bits<16>::sign_extend32(((val >> 4) & 0xf000) | (val & 0xfff));
|
}
|
}
|
|
|
// Insert X into VAL based on the ARM instruction encoding described
|
// Insert X into VAL based on the ARM instruction encoding described
|
// above.
|
// above.
|
static inline typename elfcpp::Swap<32, big_endian>::Valtype
|
static inline typename elfcpp::Swap<32, big_endian>::Valtype
|
Line 3047... |
Line 2988... |
extract_thumb_movw_movt_addend(
|
extract_thumb_movw_movt_addend(
|
typename elfcpp::Swap<32, big_endian>::Valtype val)
|
typename elfcpp::Swap<32, big_endian>::Valtype val)
|
{
|
{
|
// According to the Elf ABI for ARM Architecture the immediate
|
// According to the Elf ABI for ARM Architecture the immediate
|
// field is sign-extended to form the addend.
|
// field is sign-extended to form the addend.
|
return utils::sign_extend<16>(((val >> 4) & 0xf000)
|
return Bits<16>::sign_extend32(((val >> 4) & 0xf000)
|
| ((val >> 15) & 0x0800)
|
| ((val >> 15) & 0x0800)
|
| ((val >> 4) & 0x0700)
|
| ((val >> 4) & 0x0700)
|
| (val & 0x00ff));
|
| (val & 0x00ff));
|
}
|
}
|
|
|
Line 3158... |
Line 3099... |
uint32_t j1 = (lower_insn & (1U << 13)) >> 13;
|
uint32_t j1 = (lower_insn & (1U << 13)) >> 13;
|
uint32_t j2 = (lower_insn & (1U << 11)) >> 11;
|
uint32_t j2 = (lower_insn & (1U << 11)) >> 11;
|
uint32_t i1 = j1 ^ s ? 0 : 1;
|
uint32_t i1 = j1 ^ s ? 0 : 1;
|
uint32_t i2 = j2 ^ s ? 0 : 1;
|
uint32_t i2 = j2 ^ s ? 0 : 1;
|
|
|
return utils::sign_extend<25>((s << 24) | (i1 << 23) | (i2 << 22)
|
return Bits<25>::sign_extend32((s << 24) | (i1 << 23) | (i2 << 22)
|
| (upper << 12) | (lower << 1));
|
| (upper << 12) | (lower << 1));
|
}
|
}
|
|
|
// Insert OFFSET to a 32-bit THUMB branch and return the upper instruction.
|
// Insert OFFSET to a 32-bit THUMB branch and return the upper instruction.
|
// UPPER_INSN is the original upper instruction of the branch. Caller is
|
// UPPER_INSN is the original upper instruction of the branch. Caller is
|
Line 3197... |
Line 3138... |
uint32_t j1 = (lower_insn & 0x2000U) >> 13;
|
uint32_t j1 = (lower_insn & 0x2000U) >> 13;
|
uint32_t j2 = (lower_insn & 0x0800U) >> 11;
|
uint32_t j2 = (lower_insn & 0x0800U) >> 11;
|
uint32_t lower = (lower_insn & 0x07ffU);
|
uint32_t lower = (lower_insn & 0x07ffU);
|
uint32_t upper = (s << 8) | (j2 << 7) | (j1 << 6) | (upper_insn & 0x003fU);
|
uint32_t upper = (s << 8) | (j2 << 7) | (j1 << 6) | (upper_insn & 0x003fU);
|
|
|
return utils::sign_extend<21>((upper << 12) | (lower << 1));
|
return Bits<21>::sign_extend32((upper << 12) | (lower << 1));
|
}
|
}
|
|
|
// Insert OFFSET to a 32-bit THUMB conditional branch and return the upper
|
// Insert OFFSET to a 32-bit THUMB conditional branch and return the upper
|
// instruction. UPPER_INSN is the original upper instruction of the branch.
|
// instruction. UPPER_INSN is the original upper instruction of the branch.
|
// Caller is responsible for overflow checking.
|
// Caller is responsible for overflow checking.
|
Line 3234... |
Line 3175... |
const Symbol_value<32>* psymval)
|
const Symbol_value<32>* psymval)
|
{
|
{
|
typedef typename elfcpp::Swap<8, big_endian>::Valtype Valtype;
|
typedef typename elfcpp::Swap<8, big_endian>::Valtype Valtype;
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
Valtype val = elfcpp::Swap<8, big_endian>::readval(wv);
|
Valtype val = elfcpp::Swap<8, big_endian>::readval(wv);
|
int32_t addend = utils::sign_extend<8>(val);
|
int32_t addend = Bits<8>::sign_extend32(val);
|
Arm_address x = psymval->value(object, addend);
|
Arm_address x = psymval->value(object, addend);
|
val = utils::bit_select(val, x, 0xffU);
|
val = Bits<32>::bit_select32(val, x, 0xffU);
|
elfcpp::Swap<8, big_endian>::writeval(wv, val);
|
elfcpp::Swap<8, big_endian>::writeval(wv, val);
|
|
|
// R_ARM_ABS8 permits signed or unsigned results.
|
// R_ARM_ABS8 permits signed or unsigned results.
|
int signed_x = static_cast<int32_t>(x);
|
return (Bits<8>::has_signed_unsigned_overflow32(x)
|
return ((signed_x < -128 || signed_x > 255)
|
|
? This::STATUS_OVERFLOW
|
? This::STATUS_OVERFLOW
|
: This::STATUS_OKAY);
|
: This::STATUS_OKAY);
|
}
|
}
|
|
|
// R_ARM_THM_ABS5: S + A
|
// R_ARM_THM_ABS5: S + A
|
Line 3258... |
Line 3198... |
typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
|
typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
|
Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
|
Reltype addend = (val & 0x7e0U) >> 6;
|
Reltype addend = (val & 0x7e0U) >> 6;
|
Reltype x = psymval->value(object, addend);
|
Reltype x = psymval->value(object, addend);
|
val = utils::bit_select(val, x << 6, 0x7e0U);
|
val = Bits<32>::bit_select32(val, x << 6, 0x7e0U);
|
elfcpp::Swap<16, big_endian>::writeval(wv, val);
|
elfcpp::Swap<16, big_endian>::writeval(wv, val);
|
|
return (Bits<5>::has_overflow32(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_OVERFLOW
|
: This::STATUS_OKAY);
|
: This::STATUS_OKAY);
|
}
|
}
|
|
|
// R_ARM_ABS12: S + A
|
// R_ARM_ABS12: S + A
|
Line 3280... |
Line 3217... |
typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
|
typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
|
Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
|
Reltype addend = val & 0x0fffU;
|
Reltype addend = val & 0x0fffU;
|
Reltype x = psymval->value(object, addend);
|
Reltype x = psymval->value(object, addend);
|
val = utils::bit_select(val, x, 0x0fffU);
|
val = Bits<32>::bit_select32(val, x, 0x0fffU);
|
elfcpp::Swap<32, big_endian>::writeval(wv, val);
|
elfcpp::Swap<32, big_endian>::writeval(wv, val);
|
return (utils::has_overflow<12>(x)
|
return (Bits<12>::has_overflow32(x)
|
? This::STATUS_OVERFLOW
|
? This::STATUS_OVERFLOW
|
: This::STATUS_OKAY);
|
: This::STATUS_OKAY);
|
}
|
}
|
|
|
// R_ARM_ABS16: S + A
|
// R_ARM_ABS16: S + A
|
Line 3296... |
Line 3233... |
const Symbol_value<32>* psymval)
|
const Symbol_value<32>* psymval)
|
{
|
{
|
typedef typename elfcpp::Swap_unaligned<16, big_endian>::Valtype Valtype;
|
typedef typename elfcpp::Swap_unaligned<16, big_endian>::Valtype Valtype;
|
typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
|
typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
|
Valtype val = elfcpp::Swap_unaligned<16, big_endian>::readval(view);
|
Valtype val = elfcpp::Swap_unaligned<16, big_endian>::readval(view);
|
int32_t addend = utils::sign_extend<16>(val);
|
int32_t addend = Bits<16>::sign_extend32(val);
|
Arm_address x = psymval->value(object, addend);
|
Arm_address x = psymval->value(object, addend);
|
val = utils::bit_select(val, x, 0xffffU);
|
val = Bits<32>::bit_select32(val, x, 0xffffU);
|
elfcpp::Swap_unaligned<16, big_endian>::writeval(view, val);
|
elfcpp::Swap_unaligned<16, big_endian>::writeval(view, val);
|
|
|
// R_ARM_ABS16 permits signed or unsigned results.
|
// R_ARM_ABS16 permits signed or unsigned results.
|
int signed_x = static_cast<int32_t>(x);
|
return (Bits<16>::has_signed_unsigned_overflow32(x)
|
return ((signed_x < -32768 || signed_x > 65536)
|
|
? This::STATUS_OVERFLOW
|
? This::STATUS_OVERFLOW
|
: This::STATUS_OKAY);
|
: This::STATUS_OKAY);
|
}
|
}
|
|
|
// R_ARM_ABS32: (S + A) | T
|
// R_ARM_ABS32: (S + A) | T
|
Line 3375... |
Line 3311... |
Arm_address address)
|
Arm_address address)
|
{
|
{
|
typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
|
typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
|
Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
|
int32_t addend = utils::sign_extend<8>((val & 0x00ff) << 1);
|
int32_t addend = Bits<8>::sign_extend32((val & 0x00ff) << 1);
|
int32_t x = (psymval->value(object, addend) - address);
|
int32_t x = (psymval->value(object, addend) - address);
|
elfcpp::Swap<16, big_endian>::writeval(wv, ((val & 0xff00)
|
elfcpp::Swap<16, big_endian>::writeval(wv, ((val & 0xff00)
|
| ((x & 0x01fe) >> 1)));
|
| ((x & 0x01fe) >> 1)));
|
// We do a 9-bit overflow check because x is right-shifted by 1 bit.
|
// We do a 9-bit overflow check because x is right-shifted by 1 bit.
|
return (utils::has_overflow<9>(x)
|
return (Bits<9>::has_overflow32(x)
|
? This::STATUS_OVERFLOW
|
? This::STATUS_OVERFLOW
|
: This::STATUS_OKAY);
|
: This::STATUS_OKAY);
|
}
|
}
|
|
|
// R_ARM_THM_JUMP11: S + A – P
|
// R_ARM_THM_JUMP11: S + A – P
|
Line 3395... |
Line 3331... |
Arm_address address)
|
Arm_address address)
|
{
|
{
|
typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
|
typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
|
Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
|
int32_t addend = utils::sign_extend<11>((val & 0x07ff) << 1);
|
int32_t addend = Bits<11>::sign_extend32((val & 0x07ff) << 1);
|
int32_t x = (psymval->value(object, addend) - address);
|
int32_t x = (psymval->value(object, addend) - address);
|
elfcpp::Swap<16, big_endian>::writeval(wv, ((val & 0xf800)
|
elfcpp::Swap<16, big_endian>::writeval(wv, ((val & 0xf800)
|
| ((x & 0x0ffe) >> 1)));
|
| ((x & 0x0ffe) >> 1)));
|
// We do a 12-bit overflow check because x is right-shifted by 1 bit.
|
// We do a 12-bit overflow check because x is right-shifted by 1 bit.
|
return (utils::has_overflow<12>(x)
|
return (Bits<12>::has_overflow32(x)
|
? This::STATUS_OVERFLOW
|
? This::STATUS_OVERFLOW
|
: This::STATUS_OKAY);
|
: This::STATUS_OKAY);
|
}
|
}
|
|
|
// R_ARM_BASE_PREL: B(S) + A - P
|
// R_ARM_BASE_PREL: B(S) + A - P
|
Line 3453... |
Line 3389... |
Arm_address address,
|
Arm_address address,
|
Arm_address thumb_bit)
|
Arm_address thumb_bit)
|
{
|
{
|
typedef typename elfcpp::Swap_unaligned<32, big_endian>::Valtype Valtype;
|
typedef typename elfcpp::Swap_unaligned<32, big_endian>::Valtype Valtype;
|
Valtype val = elfcpp::Swap_unaligned<32, big_endian>::readval(view);
|
Valtype val = elfcpp::Swap_unaligned<32, big_endian>::readval(view);
|
Valtype addend = utils::sign_extend<31>(val);
|
Valtype addend = Bits<31>::sign_extend32(val);
|
Valtype x = (psymval->value(object, addend) | thumb_bit) - address;
|
Valtype x = (psymval->value(object, addend) | thumb_bit) - address;
|
val = utils::bit_select(val, x, 0x7fffffffU);
|
val = Bits<32>::bit_select32(val, x, 0x7fffffffU);
|
elfcpp::Swap_unaligned<32, big_endian>::writeval(view, val);
|
elfcpp::Swap_unaligned<32, big_endian>::writeval(view, val);
|
return (utils::has_overflow<31>(x) ?
|
return (Bits<31>::has_overflow32(x)
|
This::STATUS_OVERFLOW : This::STATUS_OKAY);
|
? This::STATUS_OVERFLOW
|
|
: This::STATUS_OKAY);
|
}
|
}
|
|
|
// R_ARM_MOVW_ABS_NC: (S + A) | T (relative address base is )
|
// R_ARM_MOVW_ABS_NC: (S + A) | T (relative address base is )
|
// R_ARM_MOVW_PREL_NC: (S + A) | T - P
|
// R_ARM_MOVW_PREL_NC: (S + A) | T - P
|
// R_ARM_MOVW_BREL_NC: ((S + A) | T) - B(S)
|
// R_ARM_MOVW_BREL_NC: ((S + A) | T) - B(S)
|
Line 3481... |
Line 3418... |
Valtype addend = This::extract_arm_movw_movt_addend(val);
|
Valtype addend = This::extract_arm_movw_movt_addend(val);
|
Valtype x = ((psymval->value(object, addend) | thumb_bit)
|
Valtype x = ((psymval->value(object, addend) | thumb_bit)
|
- relative_address_base);
|
- relative_address_base);
|
val = This::insert_val_arm_movw_movt(val, x);
|
val = This::insert_val_arm_movw_movt(val, x);
|
elfcpp::Swap<32, big_endian>::writeval(wv, val);
|
elfcpp::Swap<32, big_endian>::writeval(wv, val);
|
return ((check_overflow && utils::has_overflow<16>(x))
|
return ((check_overflow && Bits<16>::has_overflow32(x))
|
? This::STATUS_OVERFLOW
|
? This::STATUS_OVERFLOW
|
: This::STATUS_OKAY);
|
: This::STATUS_OKAY);
|
}
|
}
|
|
|
// R_ARM_MOVT_ABS: S + A (relative address base is 0)
|
// R_ARM_MOVT_ABS: S + A (relative address base is 0)
|
Line 3529... |
Line 3466... |
Reltype x =
|
Reltype x =
|
(psymval->value(object, addend) | thumb_bit) - relative_address_base;
|
(psymval->value(object, addend) | thumb_bit) - relative_address_base;
|
val = This::insert_val_thumb_movw_movt(val, x);
|
val = This::insert_val_thumb_movw_movt(val, x);
|
elfcpp::Swap<16, big_endian>::writeval(wv, val >> 16);
|
elfcpp::Swap<16, big_endian>::writeval(wv, val >> 16);
|
elfcpp::Swap<16, big_endian>::writeval(wv + 1, val & 0xffff);
|
elfcpp::Swap<16, big_endian>::writeval(wv + 1, val & 0xffff);
|
return ((check_overflow && utils::has_overflow<16>(x))
|
return ((check_overflow && Bits<16>::has_overflow32(x))
|
? This::STATUS_OVERFLOW
|
? This::STATUS_OVERFLOW
|
: This::STATUS_OKAY);
|
: This::STATUS_OKAY);
|
}
|
}
|
|
|
// R_ARM_THM_MOVT_ABS: S + A (relative address base is 0)
|
// R_ARM_THM_MOVT_ABS: S + A (relative address base is 0)
|
Line 3961... |
Line 3898... |
val = cond | 0x01a00000; // Using pre-UAL nop: mov r0, r0.
|
val = cond | 0x01a00000; // Using pre-UAL nop: mov r0, r0.
|
elfcpp::Swap<32, big_endian>::writeval(wv, val);
|
elfcpp::Swap<32, big_endian>::writeval(wv, val);
|
return This::STATUS_OKAY;
|
return This::STATUS_OKAY;
|
}
|
}
|
|
|
Valtype addend = utils::sign_extend<26>(val << 2);
|
Valtype addend = Bits<26>::sign_extend32(val << 2);
|
Valtype branch_target = psymval->value(object, addend);
|
Valtype branch_target = psymval->value(object, addend);
|
int32_t branch_offset = branch_target - address;
|
int32_t branch_offset = branch_target - address;
|
|
|
// We need a stub if the branch offset is too large or if we need
|
// We need a stub if the branch offset is too large or if we need
|
// to switch mode.
|
// to switch mode.
|
bool may_use_blx = arm_target->may_use_v5t_interworking();
|
bool may_use_blx = arm_target->may_use_v5t_interworking();
|
Reloc_stub* stub = NULL;
|
Reloc_stub* stub = NULL;
|
|
|
if (!parameters->options().relocatable()
|
if (!parameters->options().relocatable()
|
&& (utils::has_overflow<26>(branch_offset)
|
&& (Bits<26>::has_overflow32(branch_offset)
|
|| ((thumb_bit != 0)
|
|| ((thumb_bit != 0)
|
&& !(may_use_blx && r_type == elfcpp::R_ARM_CALL))))
|
&& !(may_use_blx && r_type == elfcpp::R_ARM_CALL))))
|
{
|
{
|
Valtype unadjusted_branch_target = psymval->value(object, 0);
|
Valtype unadjusted_branch_target = psymval->value(object, 0);
|
|
|
Line 3993... |
Line 3930... |
stub = stub_table->find_reloc_stub(stub_key);
|
stub = stub_table->find_reloc_stub(stub_key);
|
gold_assert(stub != NULL);
|
gold_assert(stub != NULL);
|
thumb_bit = stub->stub_template()->entry_in_thumb_mode() ? 1 : 0;
|
thumb_bit = stub->stub_template()->entry_in_thumb_mode() ? 1 : 0;
|
branch_target = stub_table->address() + stub->offset() + addend;
|
branch_target = stub_table->address() + stub->offset() + addend;
|
branch_offset = branch_target - address;
|
branch_offset = branch_target - address;
|
gold_assert(!utils::has_overflow<26>(branch_offset));
|
gold_assert(!Bits<26>::has_overflow32(branch_offset));
|
}
|
}
|
}
|
}
|
|
|
// At this point, if we still need to switch mode, the instruction
|
// 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.
|
// must either be a BLX or a BL that can be converted to a BLX.
|
Line 4006... |
Line 3943... |
// Turn BL to BLX.
|
// Turn BL to BLX.
|
gold_assert(may_use_blx && r_type == elfcpp::R_ARM_CALL);
|
gold_assert(may_use_blx && r_type == elfcpp::R_ARM_CALL);
|
val = (val & 0xffffff) | 0xfa000000 | ((branch_offset & 2) << 23);
|
val = (val & 0xffffff) | 0xfa000000 | ((branch_offset & 2) << 23);
|
}
|
}
|
|
|
val = utils::bit_select(val, (branch_offset >> 2), 0xffffffUL);
|
val = Bits<32>::bit_select32(val, (branch_offset >> 2), 0xffffffUL);
|
elfcpp::Swap<32, big_endian>::writeval(wv, val);
|
elfcpp::Swap<32, big_endian>::writeval(wv, val);
|
return (utils::has_overflow<26>(branch_offset)
|
return (Bits<26>::has_overflow32(branch_offset)
|
? This::STATUS_OVERFLOW : This::STATUS_OKAY);
|
? This::STATUS_OVERFLOW
|
|
: This::STATUS_OKAY);
|
}
|
}
|
|
|
// Relocate THUMB long branches. This handles relocation types
|
// Relocate THUMB long branches. This handles relocation types
|
// R_ARM_THM_CALL, R_ARM_THM_JUMP24 and R_ARM_THM_XPC22.
|
// 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
|
// If IS_WEAK_UNDEFINED_WITH_PLT is true. The target symbol is weakly
|
Line 4100... |
Line 4038... |
Arm_address branch_target = psymval->value(object, addend);
|
Arm_address branch_target = psymval->value(object, addend);
|
|
|
// For BLX, bit 1 of target address comes from bit 1 of base address.
|
// For BLX, bit 1 of target address comes from bit 1 of base address.
|
bool may_use_blx = arm_target->may_use_v5t_interworking();
|
bool may_use_blx = arm_target->may_use_v5t_interworking();
|
if (thumb_bit == 0 && may_use_blx)
|
if (thumb_bit == 0 && may_use_blx)
|
branch_target = utils::bit_select(branch_target, address, 0x2);
|
branch_target = Bits<32>::bit_select32(branch_target, address, 0x2);
|
|
|
int32_t branch_offset = branch_target - address;
|
int32_t branch_offset = branch_target - address;
|
|
|
// We need a stub if the branch offset is too large or if we need
|
// We need a stub if the branch offset is too large or if we need
|
// to switch mode.
|
// to switch mode.
|
bool thumb2 = arm_target->using_thumb2();
|
bool thumb2 = arm_target->using_thumb2();
|
if (!parameters->options().relocatable()
|
if (!parameters->options().relocatable()
|
&& ((!thumb2 && utils::has_overflow<23>(branch_offset))
|
&& ((!thumb2 && Bits<23>::has_overflow32(branch_offset))
|
|| (thumb2 && utils::has_overflow<25>(branch_offset))
|
|| (thumb2 && Bits<25>::has_overflow32(branch_offset))
|
|| ((thumb_bit == 0)
|
|| ((thumb_bit == 0)
|
&& (((r_type == elfcpp::R_ARM_THM_CALL) && !may_use_blx)
|
&& (((r_type == elfcpp::R_ARM_THM_CALL) && !may_use_blx)
|
|| r_type == elfcpp::R_ARM_THM_JUMP24))))
|
|| r_type == elfcpp::R_ARM_THM_JUMP24))))
|
{
|
{
|
Arm_address unadjusted_branch_target = psymval->value(object, 0);
|
Arm_address unadjusted_branch_target = psymval->value(object, 0);
|
Line 4133... |
Line 4071... |
Reloc_stub* stub = stub_table->find_reloc_stub(stub_key);
|
Reloc_stub* stub = stub_table->find_reloc_stub(stub_key);
|
gold_assert(stub != NULL);
|
gold_assert(stub != NULL);
|
thumb_bit = stub->stub_template()->entry_in_thumb_mode() ? 1 : 0;
|
thumb_bit = stub->stub_template()->entry_in_thumb_mode() ? 1 : 0;
|
branch_target = stub_table->address() + stub->offset() + addend;
|
branch_target = stub_table->address() + stub->offset() + addend;
|
if (thumb_bit == 0 && may_use_blx)
|
if (thumb_bit == 0 && may_use_blx)
|
branch_target = utils::bit_select(branch_target, address, 0x2);
|
branch_target = Bits<32>::bit_select32(branch_target, address, 0x2);
|
branch_offset = branch_target - address;
|
branch_offset = branch_target - address;
|
}
|
}
|
}
|
}
|
|
|
// At this point, if we still need to switch mode, the instruction
|
// At this point, if we still need to switch mode, the instruction
|
Line 4170... |
Line 4108... |
lower_insn = This::thumb32_branch_lower(lower_insn, branch_offset);
|
lower_insn = This::thumb32_branch_lower(lower_insn, branch_offset);
|
|
|
elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn);
|
elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn);
|
elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn);
|
elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn);
|
|
|
gold_assert(!utils::has_overflow<25>(branch_offset));
|
gold_assert(!Bits<25>::has_overflow32(branch_offset));
|
|
|
return ((thumb2
|
return ((thumb2
|
? utils::has_overflow<25>(branch_offset)
|
? Bits<25>::has_overflow32(branch_offset)
|
: utils::has_overflow<23>(branch_offset))
|
: Bits<23>::has_overflow32(branch_offset))
|
? This::STATUS_OVERFLOW
|
? This::STATUS_OVERFLOW
|
: This::STATUS_OKAY);
|
: This::STATUS_OKAY);
|
}
|
}
|
|
|
// Relocate THUMB-2 long conditional branches.
|
// Relocate THUMB-2 long conditional branches.
|
Line 4219... |
Line 4157... |
|
|
// Put the relocated value back in the object file:
|
// Put the relocated value back in the object file:
|
elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn);
|
elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn);
|
elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn);
|
elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn);
|
|
|
return (utils::has_overflow<21>(branch_offset)
|
return (Bits<21>::has_overflow32(branch_offset)
|
? This::STATUS_OVERFLOW
|
? This::STATUS_OVERFLOW
|
: This::STATUS_OKAY);
|
: This::STATUS_OKAY);
|
}
|
}
|
|
|
// Get the GOT section, creating it if necessary.
|
// Get the GOT section, creating it if necessary.
|
Line 4497... |
Line 4435... |
thumb2 = little_endian_target->using_thumb2();
|
thumb2 = little_endian_target->using_thumb2();
|
thumb_only = little_endian_target->using_thumb_only();
|
thumb_only = little_endian_target->using_thumb_only();
|
}
|
}
|
|
|
int64_t branch_offset;
|
int64_t branch_offset;
|
|
bool output_is_position_independent =
|
|
parameters->options().output_is_position_independent();
|
if (r_type == elfcpp::R_ARM_THM_CALL || r_type == elfcpp::R_ARM_THM_JUMP24)
|
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
|
// For THUMB BLX instruction, bit 1 of target comes from bit 1 of the
|
// base address (instruction address + 4).
|
// base address (instruction address + 4).
|
if ((r_type == elfcpp::R_ARM_THM_CALL) && may_use_blx && !target_is_thumb)
|
if ((r_type == elfcpp::R_ARM_THM_CALL) && may_use_blx && !target_is_thumb)
|
destination = utils::bit_select(destination, location, 0x2);
|
destination = Bits<32>::bit_select32(destination, location, 0x2);
|
branch_offset = static_cast<int64_t>(destination) - location;
|
branch_offset = static_cast<int64_t>(destination) - location;
|
|
|
// Handle cases where:
|
// Handle cases where:
|
// - this call goes too far (different Thumb/Thumb2 max
|
// - this call goes too far (different Thumb/Thumb2 max
|
// distance)
|
// distance)
|
Line 4525... |
Line 4465... |
if (target_is_thumb)
|
if (target_is_thumb)
|
{
|
{
|
// Thumb to thumb.
|
// Thumb to thumb.
|
if (!thumb_only)
|
if (!thumb_only)
|
{
|
{
|
stub_type = (parameters->options().shared()
|
stub_type = (output_is_position_independent
|
|| should_force_pic_veneer)
|
|| should_force_pic_veneer)
|
// PIC stubs.
|
// PIC stubs.
|
? ((may_use_blx
|
? ((may_use_blx
|
&& (r_type == elfcpp::R_ARM_THM_CALL))
|
&& (r_type == elfcpp::R_ARM_THM_CALL))
|
// V5T and above. Stub starts with ARM code, so
|
// V5T and above. Stub starts with ARM code, so
|
Line 4546... |
Line 4486... |
? arm_stub_long_branch_any_any // V5T and above.
|
? arm_stub_long_branch_any_any // V5T and above.
|
: arm_stub_long_branch_v4t_thumb_thumb); // V4T.
|
: arm_stub_long_branch_v4t_thumb_thumb); // V4T.
|
}
|
}
|
else
|
else
|
{
|
{
|
stub_type = (parameters->options().shared()
|
stub_type = (output_is_position_independent
|
|| should_force_pic_veneer)
|
|| should_force_pic_veneer)
|
? arm_stub_long_branch_thumb_only_pic // PIC stub.
|
? arm_stub_long_branch_thumb_only_pic // PIC stub.
|
: arm_stub_long_branch_thumb_only; // non-PIC stub.
|
: arm_stub_long_branch_thumb_only; // non-PIC stub.
|
}
|
}
|
}
|
}
|
Line 4559... |
Line 4499... |
// Thumb to arm.
|
// Thumb to arm.
|
|
|
// FIXME: We should check that the input section is from an
|
// FIXME: We should check that the input section is from an
|
// object that has interwork enabled.
|
// object that has interwork enabled.
|
|
|
stub_type = (parameters->options().shared()
|
stub_type = (output_is_position_independent
|
|| should_force_pic_veneer)
|
|| should_force_pic_veneer)
|
// PIC stubs.
|
// PIC stubs.
|
? ((may_use_blx
|
? ((may_use_blx
|
&& (r_type == elfcpp::R_ARM_THM_CALL))
|
&& (r_type == elfcpp::R_ARM_THM_CALL))
|
? arm_stub_long_branch_any_arm_pic // V5T and above.
|
? arm_stub_long_branch_any_arm_pic // V5T and above.
|
Line 4601... |
Line 4541... |
|| (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET)
|
|| (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET)
|
|| ((r_type == elfcpp::R_ARM_CALL) && !may_use_blx)
|
|| ((r_type == elfcpp::R_ARM_CALL) && !may_use_blx)
|
|| (r_type == elfcpp::R_ARM_JUMP24)
|
|| (r_type == elfcpp::R_ARM_JUMP24)
|
|| (r_type == elfcpp::R_ARM_PLT32))
|
|| (r_type == elfcpp::R_ARM_PLT32))
|
{
|
{
|
stub_type = (parameters->options().shared()
|
stub_type = (output_is_position_independent
|
|| should_force_pic_veneer)
|
|| should_force_pic_veneer)
|
// PIC stubs.
|
// PIC stubs.
|
? (may_use_blx
|
? (may_use_blx
|
? arm_stub_long_branch_any_thumb_pic// V5T and above.
|
? arm_stub_long_branch_any_thumb_pic// V5T and above.
|
: arm_stub_long_branch_v4t_arm_thumb_pic) // V4T stub.
|
: arm_stub_long_branch_v4t_arm_thumb_pic) // V4T stub.
|
Line 4620... |
Line 4560... |
{
|
{
|
// Arm to arm.
|
// Arm to arm.
|
if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
|
if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
|
|| (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET))
|
|| (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET))
|
{
|
{
|
stub_type = (parameters->options().shared()
|
stub_type = (output_is_position_independent
|
|| should_force_pic_veneer)
|
|| should_force_pic_veneer)
|
? arm_stub_long_branch_any_arm_pic // PIC stubs.
|
? arm_stub_long_branch_any_arm_pic // PIC stubs.
|
: arm_stub_long_branch_any_any; /// non-PIC.
|
: arm_stub_long_branch_any_any; /// non-PIC.
|
}
|
}
|
}
|
}
|
Line 5275... |
Line 5215... |
|
|
// Write out the entry. The first word either points to the beginning
|
// Write out the entry. The first word either points to the beginning
|
// or after the end of a text section. The second word is the special
|
// or after the end of a text section. The second word is the special
|
// EXIDX_CANTUNWIND value.
|
// EXIDX_CANTUNWIND value.
|
uint32_t prel31_offset = output_address - this->address();
|
uint32_t prel31_offset = output_address - this->address();
|
if (utils::has_overflow<31>(offset))
|
if (Bits<31>::has_overflow32(offset))
|
gold_error(_("PREL31 overflow in EXIDX_CANTUNWIND entry"));
|
gold_error(_("PREL31 overflow in EXIDX_CANTUNWIND entry"));
|
elfcpp::Swap_unaligned<32, big_endian>::writeval(oview,
|
elfcpp::Swap_unaligned<32, big_endian>::writeval(oview,
|
prel31_offset & 0x7fffffffU);
|
prel31_offset & 0x7fffffffU);
|
elfcpp::Swap_unaligned<32, big_endian>::writeval(oview + 4,
|
elfcpp::Swap_unaligned<32, big_endian>::writeval(oview + 4,
|
elfcpp::EXIDX_CANTUNWIND);
|
elfcpp::EXIDX_CANTUNWIND);
|
Line 7048... |
Line 6988... |
case elfcpp::R_ARM_PLT32:
|
case elfcpp::R_ARM_PLT32:
|
{
|
{
|
typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
|
typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
|
const Valtype* wv = reinterpret_cast<const Valtype*>(view);
|
const Valtype* wv = reinterpret_cast<const Valtype*>(view);
|
Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
|
Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
|
return utils::sign_extend<26>(val << 2);
|
return Bits<26>::sign_extend32(val << 2);
|
}
|
}
|
|
|
case elfcpp::R_ARM_THM_CALL:
|
case elfcpp::R_ARM_THM_CALL:
|
case elfcpp::R_ARM_THM_JUMP24:
|
case elfcpp::R_ARM_THM_JUMP24:
|
case elfcpp::R_ARM_THM_XPC22:
|
case elfcpp::R_ARM_THM_XPC22:
|
Line 11899... |
Line 11839... |
// the instruction which specifies that bit 1 of the target
|
// the instruction which specifies that bit 1 of the target
|
// address will come from bit 1 of the base address.
|
// address will come from bit 1 of the base address.
|
branch_offset = (branch_offset + 2) & ~3;
|
branch_offset = (branch_offset + 2) & ~3;
|
|
|
// Put BRANCH_OFFSET back into the insn.
|
// Put BRANCH_OFFSET back into the insn.
|
gold_assert(!utils::has_overflow<25>(branch_offset));
|
gold_assert(!Bits<25>::has_overflow32(branch_offset));
|
upper_insn = RelocFuncs::thumb32_branch_upper(upper_insn, branch_offset);
|
upper_insn = RelocFuncs::thumb32_branch_upper(upper_insn, branch_offset);
|
lower_insn = RelocFuncs::thumb32_branch_lower(lower_insn, branch_offset);
|
lower_insn = RelocFuncs::thumb32_branch_lower(lower_insn, branch_offset);
|
break;
|
break;
|
|
|
default:
|
default:
|