diff options
| author | Denis Protivensky <dprotivensky@accesssoftek.com> | 2015-02-03 10:48:37 +0000 |
|---|---|---|
| committer | Denis Protivensky <dprotivensky@accesssoftek.com> | 2015-02-03 10:48:37 +0000 |
| commit | b635ae8388f45a36878d681fd50be071899c14c4 (patch) | |
| tree | 75d72ecea2c27787dec34e5e0cdc103095ff96c8 /lld/lib/ReaderWriter | |
| parent | 1702eb184092968d6ad7bb5add9e73b3406c2a11 (diff) | |
| download | bcm5719-llvm-b635ae8388f45a36878d681fd50be071899c14c4.tar.gz bcm5719-llvm-b635ae8388f45a36878d681fd50be071899c14c4.zip | |
[lld][ARM] Add relocations to perform function calls
Added relocations to perform function calls with and without passing arguments.
ARM-only, Thumb-only and mixed mode code generations are supported.
Only simple veneers (direct instruction modification) are supported as ARM-Thumb interwork.
Differential Revision: http://reviews.llvm.org/D7223
llvm-svn: 227961
Diffstat (limited to 'lld/lib/ReaderWriter')
| -rw-r--r-- | lld/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp | 306 |
1 files changed, 294 insertions, 12 deletions
diff --git a/lld/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp b/lld/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp index 16be34e7837..1d13ea2c769 100644 --- a/lld/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp +++ b/lld/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp @@ -16,14 +16,61 @@ using namespace lld; using namespace elf; +static Reference::Addend readAddend_THM_MOV(const uint8_t *location) { + const auto halfHi = uint16_t( + *reinterpret_cast<const llvm::support::ulittle16_t *>(location)); + const auto halfLo = uint16_t( + *reinterpret_cast<const llvm::support::ulittle16_t *>(location + 2)); + + const uint16_t imm8 = halfLo & 0xFF; + const uint16_t imm3 = (halfLo >> 12) & 0x7; + + const uint16_t imm4 = halfHi & 0xF; + const uint16_t bitI = (halfHi >> 10) & 0x1; + + const auto result = int16_t((imm4 << 12) | (bitI << 11) | (imm3 << 8) | imm8); + return result; +} + +static Reference::Addend readAddend_ARM_MOV(const uint8_t *location) { + const auto value = uint32_t( + *reinterpret_cast<const llvm::support::ulittle32_t *>(location)); + + const uint32_t imm12 = value & 0xFFF; + const uint32_t imm4 = (value >> 16) & 0xF; + + const auto result = int32_t((imm4 << 12) | imm12); + return result; +} + +static Reference::Addend readAddend_THM_CALL(const uint8_t *location) { + const auto halfHi = uint16_t( + *reinterpret_cast<const llvm::support::ulittle16_t *>(location)); + const auto halfLo = uint16_t( + *reinterpret_cast<const llvm::support::ulittle16_t *>(location + 2)); + + const uint16_t imm10 = halfHi & 0x3FF; + const uint16_t bitS = (halfHi >> 10) & 0x1; + + const uint16_t imm11 = halfLo & 0x7FF; + const uint16_t bitJ2 = (halfLo >> 11) & 0x1; + const uint16_t bitI2 = (~(bitJ2 ^ bitS)) & 0x1; + const uint16_t bitJ1 = (halfLo >> 13) & 0x1; + const uint16_t bitI1 = (~(bitJ1 ^ bitS)) & 0x1; + + const auto result = int32_t((bitS << 24) | (bitI1 << 23) | (bitI2 << 22) | + (imm10 << 12) | (imm11 << 1)); + return llvm::SignExtend64<25>(result); +} + static Reference::Addend readAddend_ARM_CALL(const uint8_t *location) { - const auto value = int32_t( - *reinterpret_cast<const llvm::support::little32_t *>(location)); + const auto value = uint32_t( + *reinterpret_cast<const llvm::support::ulittle32_t *>(location)); const bool isBLX = (value & 0xF0000000) == 0xF0000000; - const int32_t bitH = isBLX ? ((value & 0x1000000) >> 24) : 0; + const uint32_t bitH = isBLX ? ((value & 0x1000000) >> 24) : 0; - const int32_t result = ((value & 0xFFFFFF) << 2) | (bitH << 1); + const auto result = int32_t(((value & 0xFFFFFF) << 2) | (bitH << 1)); return llvm::SignExtend64<26>(result); } @@ -33,8 +80,18 @@ static Reference::Addend readAddend(const uint8_t *location, case R_ARM_ABS32: return int32_t( *reinterpret_cast<const llvm::support::little32_t *>(location)); + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + return readAddend_THM_CALL(location); case R_ARM_CALL: + case R_ARM_JUMP24: return readAddend_ARM_CALL(location); + case R_ARM_MOVW_ABS_NC: + case R_ARM_MOVT_ABS: + return readAddend_ARM_MOV(location); + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + return readAddend_THM_MOV(location); default: return 0; } @@ -48,23 +105,102 @@ static inline void applyArmReloc(uint8_t *location, uint32_t result, ~mask) | (result & mask); } -/// \brief R_ARM_ABS32 - (S + A) | T => S + A +static inline void applyThmReloc(uint8_t *location, uint16_t resHi, + uint16_t resLo, uint16_t maskHi, + uint16_t maskLo = 0xFFFF) { + assert(!(resHi & ~maskHi) && !(resLo & ~maskLo)); + *reinterpret_cast<llvm::support::ulittle16_t *>(location) = + (uint16_t(*reinterpret_cast<llvm::support::ulittle16_t *>(location)) & + ~maskHi) | (resHi & maskHi); + location += 2; + *reinterpret_cast<llvm::support::ulittle16_t *>(location) = + (uint16_t(*reinterpret_cast<llvm::support::ulittle16_t *>(location)) & + ~maskLo) | (resLo & maskLo); +} + +/// \brief R_ARM_ABS32 - (S + A) | T static void relocR_ARM_ABS32(uint8_t *location, uint64_t P, uint64_t S, - int64_t A) { - uint32_t result = (uint32_t)(S + A); + int64_t A, bool addressesThumb) { + uint64_t T = addressesThumb; + uint32_t result = (uint32_t)((S + A) | T); + DEBUG_WITH_TYPE( "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); applyArmReloc(location, result); } -/// \brief R_ARM_CALL - ((S + A) | T) - P => S + A - P +/// \brief Relocate B/BL instructions. useJs defines whether J1 & J2 are used +static void relocR_ARM_THM_B_L(uint8_t *location, uint32_t result, bool useJs) { + result = (result & 0x01FFFFFE) >> 1; + + const uint16_t imm10 = (result >> 11) & 0x3FF; + const uint16_t bitS = (result >> 23) & 0x1; + const uint16_t resHi = (bitS << 10) | imm10; + + const uint16_t imm11 = result & 0x7FF; + const uint16_t bitJ2 = useJs ? ((result >> 21) & 0x1) : bitS; + const uint16_t bitI2 = (~(bitJ2 ^ bitS)) & 0x1; + const uint16_t bitJ1 = useJs ? ((result >> 22) & 0x1) : bitS; + const uint16_t bitI1 = (~(bitJ1 ^ bitS)) & 0x1; + const uint16_t resLo = (bitI1 << 13) | (bitI2 << 11) | imm11; + + applyThmReloc(location, resHi, resLo, 0x7FF, 0x2FFF); +} + +/// \brief R_ARM_THM_CALL - ((S + A) | T) - P +static void relocR_ARM_THM_CALL(uint8_t *location, uint64_t P, uint64_t S, + int64_t A, bool useJs, bool addressesThumb) { + uint64_t T = addressesThumb; + const bool switchMode = !addressesThumb; + + if (switchMode) { + P &= ~0x3; // Align(P, 4) by rounding down + } + + uint32_t result = (uint32_t)(((S + A) | T) - P); + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + relocR_ARM_THM_B_L(location, result, useJs); + + if (switchMode) { + applyThmReloc(location, 0, 0, 0, 0x1001); + } +} + +/// \brief R_ARM_THM_JUMP24 - ((S + A) | T) - P +static void relocR_ARM_THM_JUMP24(uint8_t *location, uint64_t P, uint64_t S, + int64_t A, bool addressesThumb) { + uint64_t T = addressesThumb; + uint32_t result = (uint32_t)(((S + A) | T) - P); + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + relocR_ARM_THM_B_L(location, result, true); +} + +/// \brief R_ARM_CALL - ((S + A) | T) - P static void relocR_ARM_CALL(uint8_t *location, uint64_t P, uint64_t S, - int64_t A) { - uint32_t result = (uint32_t)((S + A) - P); + int64_t A, bool addressesThumb) { + uint64_t T = addressesThumb; + const bool switchMode = addressesThumb; + + uint32_t result = (uint32_t)(((S + A) | T) - P); const uint32_t imm24 = (result & 0x03FFFFFC) >> 2; DEBUG_WITH_TYPE( @@ -72,8 +208,117 @@ static void relocR_ARM_CALL(uint8_t *location, uint64_t P, uint64_t S, llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); applyArmReloc(location, imm24, 0xFFFFFF); + + if (switchMode) { + const uint32_t bitH = (result & 0x2) >> 1; + applyArmReloc(location, (0xFA | bitH) << 24, 0xFF000000); + } +} + +/// \brief R_ARM_JUMP24 - ((S + A) | T) - P +static void relocR_ARM_JUMP24(uint8_t *location, uint64_t P, uint64_t S, + int64_t A, bool addressesThumb) { + uint64_t T = addressesThumb; + uint32_t result = (uint32_t)(((S + A) | T) - P); + const uint32_t imm24 = (result & 0x03FFFFFC) >> 2; + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + applyArmReloc(location, imm24, 0xFFFFFF); +} + +/// \brief Relocate ARM MOVW/MOVT instructions +static void relocR_ARM_MOV(uint8_t *location, uint32_t result) { + const uint32_t imm12 = result & 0xFFF; + const uint32_t imm4 = (result >> 12) & 0xF; + + applyArmReloc(location, (imm4 << 16) | imm12, 0xF0FFF); +} + +/// \brief R_ARM_MOVW_ABS_NC - (S + A) | T +static void relocR_ARM_MOVW_ABS_NC(uint8_t *location, uint64_t P, uint64_t S, + int64_t A, bool addressesThumb) { + uint64_t T = addressesThumb; + uint32_t result = (uint32_t)((S + A) | T); + const uint32_t arg = result & 0x0000FFFF; + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return relocR_ARM_MOV(location, arg); +} + +/// \brief R_ARM_MOVT_ABS - S + A +static void relocR_ARM_MOVT_ABS(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + uint32_t result = (uint32_t)(S + A); + const uint32_t arg = (result & 0xFFFF0000) >> 16; + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return relocR_ARM_MOV(location, arg); +} + +/// \brief Relocate Thumb MOVW/MOVT instructions +static void relocR_ARM_THM_MOV(uint8_t *location, uint32_t result) { + const uint16_t imm8 = result & 0xFF; + const uint16_t imm3 = (result >> 8) & 0x7; + const uint16_t resLo = (imm3 << 12) | imm8; + + const uint16_t imm4 = (result >> 12) & 0xF; + const uint16_t bitI = (result >> 11) & 0x1; + const uint16_t resHi = (bitI << 10) | imm4; + + applyThmReloc(location, resHi, resLo, 0x40F, 0x70FF); +} + +/// \brief R_ARM_THM_MOVW_ABS_NC - (S + A) | T +static void relocR_ARM_THM_MOVW_ABS_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, + bool addressesThumb) { + uint64_t T = addressesThumb; + uint32_t result = (uint32_t)((S + A) | T); + const uint32_t arg = result & 0x0000FFFF; + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return relocR_ARM_THM_MOV(location, arg); +} + +/// \brief R_ARM_THM_MOVT_ABS - S + A +static void relocR_ARM_THM_MOVT_ABS(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + uint32_t result = (uint32_t)(S + A); + const uint32_t arg = (result & 0xFFFF0000) >> 16; + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return relocR_ARM_THM_MOV(location, arg); } std::error_code ARMTargetRelocationHandler::applyRelocation( @@ -92,14 +337,51 @@ std::error_code ARMTargetRelocationHandler::applyRelocation( const Reference::Addend addend = readAddend(location, ref.kindValue()); + // Flags that the relocation addresses Thumb instruction + bool addressesThumb = false; + + if (const auto *definedAtom = dyn_cast<DefinedAtom>(ref.target())) { + addressesThumb = (DefinedAtom::codeARMThumb == definedAtom->codeModel()); + } + switch (ref.kindValue()) { case R_ARM_NONE: break; case R_ARM_ABS32: - relocR_ARM_ABS32(location, relocVAddress, targetVAddress, addend); + relocR_ARM_ABS32(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_THM_CALL: + // TODO: consider adding bool variable to disable J1 & J2 for archs + // before ARMv6 + relocR_ARM_THM_CALL(location, relocVAddress, targetVAddress, addend, true, + addressesThumb); break; case R_ARM_CALL: - relocR_ARM_CALL(location, relocVAddress, targetVAddress, addend); + relocR_ARM_CALL(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_JUMP24: + relocR_ARM_JUMP24(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_THM_JUMP24: + relocR_ARM_THM_JUMP24(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_MOVW_ABS_NC: + relocR_ARM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_MOVT_ABS: + relocR_ARM_MOVT_ABS(location, relocVAddress, targetVAddress, addend); + break; + case R_ARM_THM_MOVW_ABS_NC: + relocR_ARM_THM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_THM_MOVT_ABS: + relocR_ARM_THM_MOVT_ABS(location, relocVAddress, targetVAddress, addend); break; default: return make_unhandled_reloc_error(); |

