Skip to content

Commit

Permalink
[lld][ARM] Add relocations to perform function calls
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Denis Protivensky committed Feb 3, 2015
1 parent 1702eb1 commit b635ae8
Show file tree
Hide file tree
Showing 9 changed files with 732 additions and 15 deletions.
306 changes: 294 additions & 12 deletions lld/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp
Expand Up @@ -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);
}

Expand All @@ -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;
}
Expand All @@ -48,32 +105,220 @@ 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(
"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);

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(
Expand All @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion lld/test/elf/ARM/rel-abs32.test
Expand Up @@ -10,7 +10,7 @@
# data main addr content
# 0x400084 = 0x400074 + 0x10
# CHECK: SYMBOL TABLE:
# CHECK: 00400074 g F .text 0000001c main
# CHECK: 00400074 g F .text {{[0-9a-f]+}} main
# CHECK: 00401000 g .data 00000004 data

---
Expand Down
4 changes: 2 additions & 2 deletions lld/test/elf/ARM/rel-arm-call.test
Expand Up @@ -10,8 +10,8 @@
# call site offset PC(arm) _Z1fv addr
# 0x400090 + (-0x24) + 0x8 = 0x400074
# CHECK: SYMBOL TABLE:
# CHECK: 00400074 g F .text 00000014 _Z1fv
# CHECK: 00400088 g F .text 00000018 main
# CHECK: 00400074 g F .text {{[0-9a-f]+}} _Z1fv
# CHECK: 00400088 g F .text {{[0-9a-f]+}} main

---
FileHeader:
Expand Down

0 comments on commit b635ae8

Please sign in to comment.