[RuntimeDyld][MachO][AArch64] Add a helper function for encoding addends in instructions.

Factor out the addend encoding into a helper function and simplify the
processRelocationRef.

Also add a few simple rtdyld tests. More tests to come once GOTs can be tested too.

Related to <rdar://problem/17768539>

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@213689 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Juergen Ributzka
2014-07-22 21:42:55 +00:00
parent 6f2f090b06
commit 04e8cc79cd
3 changed files with 167 additions and 75 deletions

View File

@ -118,6 +118,109 @@ public:
return Addend;
}
/// Extract the addend encoded in the instruction.
void encodeAddend(uint8_t *LocalAddress, uint32_t RelType,
int64_t Addend) const {
// Verify that the relocation has the correct alignment.
switch (RelType) {
default:
llvm_unreachable("Unsupported relocation type!");
case MachO::ARM64_RELOC_UNSIGNED:
llvm_unreachable("Invalid relocation type for instruction.");
case MachO::ARM64_RELOC_BRANCH26:
case MachO::ARM64_RELOC_PAGE21:
case MachO::ARM64_RELOC_PAGEOFF12:
case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:
case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12:
assert((((uintptr_t)LocalAddress & 0x3) == 0) &&
"Instruction address is not aligned to 4 bytes.");
break;
}
switch (RelType) {
default:
llvm_unreachable("Unsupported relocation type!");
case MachO::ARM64_RELOC_BRANCH26: {
// Verify that the relocation points to the expected branch instruction.
uint32_t *p = (uint32_t *)LocalAddress;
assert((*p & 0xFC000000) == 0x14000000 && "Expected branch instruction.");
// Verify addend value.
assert((Addend & 0x3) == 0 && "Branch target is not aligned");
assert(isInt<28>(Addend) && "Branch target is out of range.");
// Encode the addend as 26 bit immediate in the branch instruction.
*p = (*p & 0xFC000000) | ((uint32_t)(Addend >> 2) & 0x03FFFFFF);
break;
}
case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:
case MachO::ARM64_RELOC_PAGE21: {
// Verify that the relocation points to the expected adrp instruction.
uint32_t *p = (uint32_t *)LocalAddress;
assert((*p & 0x9F000000) == 0x90000000 && "Expected adrp instruction.");
// Check that the addend fits into 21 bits (+ 12 lower bits).
assert((Addend & 0xFFF) == 0 && "ADRP target is not page aligned.");
assert(isInt<33>(Addend) && "Invalid page reloc value.");
// Encode the addend into the instruction.
uint32_t ImmLoValue = (uint32_t)(Addend << 17) & 0x60000000;
uint32_t ImmHiValue = (uint32_t)(Addend >> 9) & 0x00FFFFE0;
*p = (*p & 0x9F00001F) | ImmHiValue | ImmLoValue;
break;
}
case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12: {
// Verify that the relocation points to one of the expected load / store
// instructions.
uint32_t *p = (uint32_t *)LocalAddress;
assert((*p & 0x3B000000) == 0x39000000 &&
"Only expected load / store instructions.");
} // fall-through
case MachO::ARM64_RELOC_PAGEOFF12: {
// Verify that the relocation points to one of the expected load / store
// or add / sub instructions.
uint32_t *p = (uint32_t *)LocalAddress;
assert((((*p & 0x3B000000) == 0x39000000) ||
((*p & 0x11C00000) == 0x11000000) ) &&
"Expected load / store or add/sub instruction.");
// Check which instruction we are decoding to obtain the implicit shift
// factor of the instruction and verify alignment.
int ImplicitShift = 0;
if ((*p & 0x3B000000) == 0x39000000) { // << load / store
// For load / store instructions the size is encoded in bits 31:30.
ImplicitShift = ((*p >> 30) & 0x3);
switch (ImplicitShift) {
case 0:
// Check if this a vector op to get the correct shift value.
if ((*p & 0x04800000) == 0x04800000) {
ImplicitShift = 4;
assert(((Addend & 0xF) == 0) &&
"128-bit LDR/STR not 16-byte aligned.");
}
break;
case 1:
assert(((Addend & 0x1) == 0) && "16-bit LDR/STR not 2-byte aligned.");
break;
case 2:
assert(((Addend & 0x3) == 0) && "32-bit LDR/STR not 4-byte aligned.");
break;
case 3:
assert(((Addend & 0x7) == 0) && "64-bit LDR/STR not 8-byte aligned.");
break;
}
}
// Compensate for implicit shift.
Addend >>= ImplicitShift;
assert(isUInt<12>(Addend) && "Addend cannot be encoded.");
// Encode the addend into the instruction.
*p = (*p & 0xFFC003FF) | ((uint32_t)(Addend << 10) & 0x003FFC00);
break;
}
}
}
relocation_iterator
processRelocationRef(unsigned SectionID, relocation_iterator RelI,
ObjectImage &ObjImg, ObjSectionToIDMap &ObjSectionToID,
@ -196,105 +299,37 @@ public:
}
case MachO::ARM64_RELOC_BRANCH26: {
assert(RE.IsPCRel && "not PCRel and ARM64_RELOC_BRANCH26 not supported");
// Mask the value into the target address. We know instructions are
// 32-bit aligned, so we can do it all at once.
uint32_t *p = (uint32_t *)LocalAddress;
// Check if the addend is encoded in the instruction.
uint32_t EncodedAddend = *p & 0x03FFFFFF;
if (EncodedAddend != 0) {
if (RE.Addend == 0)
llvm_unreachable("branch26 instruction has embedded addend.");
else
llvm_unreachable("branch26 instruction has embedded addend and"
"ARM64_RELOC_ADDEND.");
}
// Check if branch is in range.
uint64_t FinalAddress = Section.LoadAddress + RE.Offset;
uint64_t PCRelVal = Value - FinalAddress + RE.Addend;
assert(isInt<26>(PCRelVal) && "Branch target out of range!");
// Insert the value into the instruction.
*p = (*p & 0xFC000000) | ((uint32_t)(PCRelVal >> 2) & 0x03FFFFFF);
int64_t PCRelVal = Value - FinalAddress + RE.Addend;
encodeAddend(LocalAddress, RE.RelType, PCRelVal);
break;
}
case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:
case MachO::ARM64_RELOC_PAGE21: {
assert(RE.IsPCRel && "not PCRel and ARM64_RELOC_PAGE21 not supported");
// Mask the value into the target address. We know instructions are
// 32-bit aligned, so we can do it all at once.
uint32_t *p = (uint32_t *)LocalAddress;
// Check if the addend is encoded in the instruction.
uint32_t EncodedAddend =
((*p & 0x60000000) >> 29) | ((*p & 0x01FFFFE0) >> 3);
if (EncodedAddend != 0) {
if (RE.Addend == 0)
llvm_unreachable("adrp instruction has embedded addend.");
else
llvm_unreachable("adrp instruction has embedded addend and"
"ARM64_RELOC_ADDEND.");
}
// Adjust for PC-relative relocation and offset.
uint64_t FinalAddress = Section.LoadAddress + RE.Offset;
uint64_t PCRelVal =
((Value + RE.Addend) & (-4096)) - (FinalAddress & (-4096));
// Check that the value fits into 21 bits (+ 12 lower bits).
assert(isInt<33>(PCRelVal) && "Invalid page reloc value!");
// Insert the value into the instruction.
uint32_t ImmLoValue = (uint32_t)(PCRelVal << 17) & 0x60000000;
uint32_t ImmHiValue = (uint32_t)(PCRelVal >> 9) & 0x00FFFFE0;
*p = (*p & 0x9F00001F) | ImmHiValue | ImmLoValue;
int64_t PCRelVal =
((Value + RE.Addend) & (-4096)) - (FinalAddress & (-4096));
encodeAddend(LocalAddress, RE.RelType, PCRelVal);
break;
}
case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case MachO::ARM64_RELOC_PAGEOFF12: {
assert(!RE.IsPCRel && "PCRel and ARM64_RELOC_PAGEOFF21 not supported");
// Mask the value into the target address. We know instructions are
// 32-bit aligned, so we can do it all at once.
uint32_t *p = (uint32_t *)LocalAddress;
// Check if the addend is encoded in the instruction.
uint32_t EncodedAddend = *p & 0x003FFC00;
if (EncodedAddend != 0) {
if (RE.Addend == 0)
llvm_unreachable("adrp instruction has embedded addend.");
else
llvm_unreachable("adrp instruction has embedded addend and"
"ARM64_RELOC_ADDEND.");
}
// Add the offset from the symbol.
Value += RE.Addend;
// Mask out the page address and only use the lower 12 bits.
Value &= 0xFFF;
// Check which instruction we are updating to obtain the implicit shift
// factor from LDR/STR instructions.
if (*p & 0x08000000) {
uint32_t ImplicitShift = ((*p >> 30) & 0x3);
switch (ImplicitShift) {
case 0:
// Check if this a vector op.
if ((*p & 0x04800000) == 0x04800000) {
ImplicitShift = 4;
assert(((Value & 0xF) == 0) &&
"128-bit LDR/STR not 16-byte aligned.");
}
break;
case 1:
assert(((Value & 0x1) == 0) && "16-bit LDR/STR not 2-byte aligned.");
case 2:
assert(((Value & 0x3) == 0) && "32-bit LDR/STR not 4-byte aligned.");
case 3:
assert(((Value & 0x7) == 0) && "64-bit LDR/STR not 8-byte aligned.");
}
// Compensate for implicit shift.
Value >>= ImplicitShift;
}
// Insert the value into the instruction.
*p = (*p & 0xFFC003FF) | ((uint32_t)(Value << 10) & 0x003FFC00);
encodeAddend(LocalAddress, RE.RelType, Value);
break;
}
case MachO::ARM64_RELOC_SUBTRACTOR:
case MachO::ARM64_RELOC_POINTER_TO_GOT:
case MachO::ARM64_RELOC_TLVP_LOAD_PAGE21:
case MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
llvm_unreachable("Relocation type not implemented yet!");
llvm_unreachable("Relocation type not yet implemented!");
case MachO::ARM64_RELOC_ADDEND:
llvm_unreachable("ARM64_RELOC_ADDEND should have been handeled by "
"processRelocationRef!");