blob: f2ee1b06d4943b4b06667da9f763a5b9bb634ce7 [file] [log] [blame]
//===-- RuntimeDyldMachOAArch64.h -- MachO/AArch64 specific code. -*- C++ -*-=//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDMACHOAARCH64_H
#define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDMACHOAARCH64_H
#include "../RuntimeDyldMachO.h"
#include "llvm/Support/Endian.h"
#define DEBUG_TYPE "dyld"
namespace llvm {
class RuntimeDyldMachOAArch64
: public RuntimeDyldMachOCRTPBase<RuntimeDyldMachOAArch64> {
public:
typedef uint64_t TargetPtrT;
RuntimeDyldMachOAArch64(RuntimeDyld::MemoryManager &MM,
JITSymbolResolver &Resolver)
: RuntimeDyldMachOCRTPBase(MM, Resolver) {}
unsigned getMaxStubSize() const override { return 8; }
unsigned getStubAlignment() override { return 8; }
/// Extract the addend encoded in the instruction / memory location.
Expected<int64_t> decodeAddend(const RelocationEntry &RE) const {
const SectionEntry &Section = Sections[RE.SectionID];
uint8_t *LocalAddress = Section.getAddressWithOffset(RE.Offset);
unsigned NumBytes = 1 << RE.Size;
int64_t Addend = 0;
// Verify that the relocation has the correct size and alignment.
switch (RE.RelType) {
default: {
std::string ErrMsg;
{
raw_string_ostream ErrStream(ErrMsg);
ErrStream << "Unsupported relocation type: "
<< getRelocName(RE.RelType);
}
return make_error<StringError>(std::move(ErrMsg),
inconvertibleErrorCode());
}
case MachO::ARM64_RELOC_POINTER_TO_GOT:
case MachO::ARM64_RELOC_UNSIGNED: {
if (NumBytes != 4 && NumBytes != 8) {
std::string ErrMsg;
{
raw_string_ostream ErrStream(ErrMsg);
ErrStream << "Invalid relocation size for relocation "
<< getRelocName(RE.RelType);
}
return make_error<StringError>(std::move(ErrMsg),
inconvertibleErrorCode());
}
break;
}
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(NumBytes == 4 && "Invalid relocation size.");
assert((((uintptr_t)LocalAddress & 0x3) == 0) &&
"Instruction address is not aligned to 4 bytes.");
break;
}
switch (RE.RelType) {
default:
llvm_unreachable("Unsupported relocation type!");
case MachO::ARM64_RELOC_POINTER_TO_GOT:
case MachO::ARM64_RELOC_UNSIGNED:
// This could be an unaligned memory location.
if (NumBytes == 4)
Addend = *reinterpret_cast<support::ulittle32_t *>(LocalAddress);
else
Addend = *reinterpret_cast<support::ulittle64_t *>(LocalAddress);
break;
case MachO::ARM64_RELOC_BRANCH26: {
// Verify that the relocation points to a B/BL instruction.
auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);
assert(((*p & 0xFC000000) == 0x14000000 ||
(*p & 0xFC000000) == 0x94000000) &&
"Expected branch instruction.");
// Get the 26 bit addend encoded in the branch instruction and sign-extend
// to 64 bit. The lower 2 bits are always zeros and are therefore implicit
// (<< 2).
Addend = (*p & 0x03FFFFFF) << 2;
Addend = SignExtend64(Addend, 28);
break;
}
case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:
case MachO::ARM64_RELOC_PAGE21: {
// Verify that the relocation points to the expected adrp instruction.
auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);
assert((*p & 0x9F000000) == 0x90000000 && "Expected adrp instruction.");
// Get the 21 bit addend encoded in the adrp instruction and sign-extend
// to 64 bit. The lower 12 bits (4096 byte page) are always zeros and are
// therefore implicit (<< 12).
Addend = ((*p & 0x60000000) >> 29) | ((*p & 0x01FFFFE0) >> 3) << 12;
Addend = SignExtend64(Addend, 33);
break;
}
case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12: {
// Verify that the relocation points to one of the expected load / store
// instructions.
auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);
(void)p;
assert((*p & 0x3B000000) == 0x39000000 &&
"Only expected load / store instructions.");
LLVM_FALLTHROUGH;
}
case MachO::ARM64_RELOC_PAGEOFF12: {
// Verify that the relocation points to one of the expected load / store
// or add / sub instructions.
auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);
assert((((*p & 0x3B000000) == 0x39000000) ||
((*p & 0x11C00000) == 0x11000000) ) &&
"Expected load / store or add/sub instruction.");
// Get the 12 bit addend encoded in the instruction.
Addend = (*p & 0x003FFC00) >> 10;
// Check which instruction we are decoding to obtain the implicit shift
// factor of the instruction.
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);
if (ImplicitShift == 0) {
// Check if this a vector op to get the correct shift value.
if ((*p & 0x04800000) == 0x04800000)
ImplicitShift = 4;
}
}
// Compensate for implicit shift.
Addend <<= ImplicitShift;
break;
}
}
return Addend;
}
/// Extract the addend encoded in the instruction.
void encodeAddend(uint8_t *LocalAddress, unsigned NumBytes,
MachO::RelocationInfoType 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_POINTER_TO_GOT:
case MachO::ARM64_RELOC_UNSIGNED:
assert((NumBytes == 4 || NumBytes == 8) && "Invalid relocation size.");
break;
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(NumBytes == 4 && "Invalid relocation size.");
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_POINTER_TO_GOT:
case MachO::ARM64_RELOC_UNSIGNED:
// This could be an unaligned memory location.
if (NumBytes == 4)
*reinterpret_cast<support::ulittle32_t *>(LocalAddress) = Addend;
else
*reinterpret_cast<support::ulittle64_t *>(LocalAddress) = Addend;
break;
case MachO::ARM64_RELOC_BRANCH26: {
auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);
// Verify that the relocation points to the expected branch instruction.
assert(((*p & 0xFC000000) == 0x14000000 ||
(*p & 0xFC000000) == 0x94000000) &&
"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.
auto *p = reinterpret_cast<support::aligned_ulittle32_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 = ((uint64_t)Addend << 17) & 0x60000000;
uint32_t ImmHiValue = ((uint64_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.
auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);
assert((*p & 0x3B000000) == 0x39000000 &&
"Only expected load / store instructions.");
(void)p;
LLVM_FALLTHROUGH;
}
case MachO::ARM64_RELOC_PAGEOFF12: {
// Verify that the relocation points to one of the expected load / store
// or add / sub instructions.
auto *p = reinterpret_cast<support::aligned_ulittle32_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;
}
}
}
Expected<relocation_iterator>
processRelocationRef(unsigned SectionID, relocation_iterator RelI,
const ObjectFile &BaseObjT,
ObjSectionToIDMap &ObjSectionToID,
StubMap &Stubs) override {
const MachOObjectFile &Obj =
static_cast<const MachOObjectFile &>(BaseObjT);
MachO::any_relocation_info RelInfo =
Obj.getRelocation(RelI->getRawDataRefImpl());
if (Obj.isRelocationScattered(RelInfo))
return make_error<RuntimeDyldError>("Scattered relocations not supported "
"for MachO AArch64");
// ARM64 has an ARM64_RELOC_ADDEND relocation type that carries an explicit
// addend for the following relocation. If found: (1) store the associated
// addend, (2) consume the next relocation, and (3) use the stored addend to
// override the addend.
int64_t ExplicitAddend = 0;
if (Obj.getAnyRelocationType(RelInfo) == MachO::ARM64_RELOC_ADDEND) {
assert(!Obj.getPlainRelocationExternal(RelInfo));
assert(!Obj.getAnyRelocationPCRel(RelInfo));
assert(Obj.getAnyRelocationLength(RelInfo) == 2);
int64_t RawAddend = Obj.getPlainRelocationSymbolNum(RelInfo);
// Sign-extend the 24-bit to 64-bit.
ExplicitAddend = SignExtend64(RawAddend, 24);
++RelI;
RelInfo = Obj.getRelocation(RelI->getRawDataRefImpl());
}
if (Obj.getAnyRelocationType(RelInfo) == MachO::ARM64_RELOC_SUBTRACTOR)
return processSubtractRelocation(SectionID, RelI, Obj, ObjSectionToID);
RelocationEntry RE(getRelocationEntry(SectionID, Obj, RelI));
if (RE.RelType == MachO::ARM64_RELOC_POINTER_TO_GOT) {
bool Valid =
(RE.Size == 2 && RE.IsPCRel) || (RE.Size == 3 && !RE.IsPCRel);
if (!Valid)
return make_error<StringError>("ARM64_RELOC_POINTER_TO_GOT supports "
"32-bit pc-rel or 64-bit absolute only",
inconvertibleErrorCode());
}
if (auto Addend = decodeAddend(RE))
RE.Addend = *Addend;
else
return Addend.takeError();
assert((ExplicitAddend == 0 || RE.Addend == 0) && "Relocation has "\
"ARM64_RELOC_ADDEND and embedded addend in the instruction.");
if (ExplicitAddend)
RE.Addend = ExplicitAddend;
RelocationValueRef Value;
if (auto ValueOrErr = getRelocationValueRef(Obj, RelI, RE, ObjSectionToID))
Value = *ValueOrErr;
else
return ValueOrErr.takeError();
bool IsExtern = Obj.getPlainRelocationExternal(RelInfo);
if (RE.RelType == MachO::ARM64_RELOC_POINTER_TO_GOT) {
// We'll take care of the offset in processGOTRelocation.
Value.Offset = 0;
} else if (!IsExtern && RE.IsPCRel)
makeValueAddendPCRel(Value, RelI, 1 << RE.Size);
RE.Addend = Value.Offset;
if (RE.RelType == MachO::ARM64_RELOC_GOT_LOAD_PAGE21 ||
RE.RelType == MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12 ||
RE.RelType == MachO::ARM64_RELOC_POINTER_TO_GOT)
processGOTRelocation(RE, Value, Stubs);
else {
if (Value.SymbolName)
addRelocationForSymbol(RE, Value.SymbolName);
else
addRelocationForSection(RE, Value.SectionID);
}
return ++RelI;
}
void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override {
LLVM_DEBUG(dumpRelocationToResolve(RE, Value));
const SectionEntry &Section = Sections[RE.SectionID];
uint8_t *LocalAddress = Section.getAddressWithOffset(RE.Offset);
MachO::RelocationInfoType RelType =
static_cast<MachO::RelocationInfoType>(RE.RelType);
switch (RelType) {
default:
llvm_unreachable("Invalid relocation type!");
case MachO::ARM64_RELOC_UNSIGNED: {
assert(!RE.IsPCRel && "PCRel and ARM64_RELOC_UNSIGNED not supported");
// Mask in the target value a byte at a time (we don't have an alignment
// guarantee for the target address, so this is safest).
if (RE.Size < 2)
llvm_unreachable("Invalid size for ARM64_RELOC_UNSIGNED");
encodeAddend(LocalAddress, 1 << RE.Size, RelType, Value + RE.Addend);
break;
}
case MachO::ARM64_RELOC_POINTER_TO_GOT: {
assert(((RE.Size == 2 && RE.IsPCRel) || (RE.Size == 3 && !RE.IsPCRel)) &&
"ARM64_RELOC_POINTER_TO_GOT only supports 32-bit pc-rel or 64-bit "
"absolute");
// Addend is the GOT entry address and RE.Offset the target of the
// relocation.
uint64_t Result =
RE.IsPCRel ? (RE.Addend - RE.Offset) : (Value + RE.Addend);
encodeAddend(LocalAddress, 1 << RE.Size, RelType, Result);
break;
}
case MachO::ARM64_RELOC_BRANCH26: {
assert(RE.IsPCRel && "not PCRel and ARM64_RELOC_BRANCH26 not supported");
// Check if branch is in range.
uint64_t FinalAddress = Section.getLoadAddressWithOffset(RE.Offset);
int64_t PCRelVal = Value - FinalAddress + RE.Addend;
encodeAddend(LocalAddress, /*Size=*/4, 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");
// Adjust for PC-relative relocation and offset.
uint64_t FinalAddress = Section.getLoadAddressWithOffset(RE.Offset);
int64_t PCRelVal =
((Value + RE.Addend) & (-4096)) - (FinalAddress & (-4096));
encodeAddend(LocalAddress, /*Size=*/4, 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");
// Add the offset from the symbol.
Value += RE.Addend;
// Mask out the page address and only use the lower 12 bits.
Value &= 0xFFF;
encodeAddend(LocalAddress, /*Size=*/4, RelType, Value);
break;
}
case MachO::ARM64_RELOC_SUBTRACTOR: {
uint64_t SectionABase = Sections[RE.Sections.SectionA].getLoadAddress();
uint64_t SectionBBase = Sections[RE.Sections.SectionB].getLoadAddress();
assert((Value == SectionABase || Value == SectionBBase) &&
"Unexpected SUBTRACTOR relocation value.");
Value = SectionABase - SectionBBase + RE.Addend;
writeBytesUnaligned(Value, LocalAddress, 1 << RE.Size);
break;
}
case MachO::ARM64_RELOC_TLVP_LOAD_PAGE21:
case MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
llvm_unreachable("Relocation type not yet implemented!");
case MachO::ARM64_RELOC_ADDEND:
llvm_unreachable("ARM64_RELOC_ADDEND should have been handeled by "
"processRelocationRef!");
}
}
Error finalizeSection(const ObjectFile &Obj, unsigned SectionID,
const SectionRef &Section) {
return Error::success();
}
private:
void processGOTRelocation(const RelocationEntry &RE,
RelocationValueRef &Value, StubMap &Stubs) {
assert((RE.RelType == MachO::ARM64_RELOC_POINTER_TO_GOT &&
(RE.Size == 2 || RE.Size == 3)) ||
RE.Size == 2);
SectionEntry &Section = Sections[RE.SectionID];
StubMap::const_iterator i = Stubs.find(Value);
int64_t Offset;
if (i != Stubs.end())
Offset = static_cast<int64_t>(i->second);
else {
// FIXME: There must be a better way to do this then to check and fix the
// alignment every time!!!
uintptr_t BaseAddress = uintptr_t(Section.getAddress());
uintptr_t StubAlignment = getStubAlignment();
uintptr_t StubAddress =
(BaseAddress + Section.getStubOffset() + StubAlignment - 1) &
-StubAlignment;
unsigned StubOffset = StubAddress - BaseAddress;
Stubs[Value] = StubOffset;
assert(((StubAddress % getStubAlignment()) == 0) &&
"GOT entry not aligned");
RelocationEntry GOTRE(RE.SectionID, StubOffset,
MachO::ARM64_RELOC_UNSIGNED, Value.Offset,
/*IsPCRel=*/false, /*Size=*/3);
if (Value.SymbolName)
addRelocationForSymbol(GOTRE, Value.SymbolName);
else
addRelocationForSection(GOTRE, Value.SectionID);
Section.advanceStubOffset(getMaxStubSize());
Offset = static_cast<int64_t>(StubOffset);
}
RelocationEntry TargetRE(RE.SectionID, RE.Offset, RE.RelType, Offset,
RE.IsPCRel, RE.Size);
addRelocationForSection(TargetRE, RE.SectionID);
}
Expected<relocation_iterator>
processSubtractRelocation(unsigned SectionID, relocation_iterator RelI,
const ObjectFile &BaseObjT,
ObjSectionToIDMap &ObjSectionToID) {
const MachOObjectFile &Obj =
static_cast<const MachOObjectFile&>(BaseObjT);
MachO::any_relocation_info RE =
Obj.getRelocation(RelI->getRawDataRefImpl());
unsigned Size = Obj.getAnyRelocationLength(RE);
uint64_t Offset = RelI->getOffset();
uint8_t *LocalAddress = Sections[SectionID].getAddressWithOffset(Offset);
unsigned NumBytes = 1 << Size;
Expected<StringRef> SubtrahendNameOrErr = RelI->getSymbol()->getName();
if (!SubtrahendNameOrErr)
return SubtrahendNameOrErr.takeError();
auto SubtrahendI = GlobalSymbolTable.find(*SubtrahendNameOrErr);
unsigned SectionBID = SubtrahendI->second.getSectionID();
uint64_t SectionBOffset = SubtrahendI->second.getOffset();
int64_t Addend =
SignExtend64(readBytesUnaligned(LocalAddress, NumBytes), NumBytes * 8);
++RelI;
Expected<StringRef> MinuendNameOrErr = RelI->getSymbol()->getName();
if (!MinuendNameOrErr)
return MinuendNameOrErr.takeError();
auto MinuendI = GlobalSymbolTable.find(*MinuendNameOrErr);
unsigned SectionAID = MinuendI->second.getSectionID();
uint64_t SectionAOffset = MinuendI->second.getOffset();
RelocationEntry R(SectionID, Offset, MachO::ARM64_RELOC_SUBTRACTOR, (uint64_t)Addend,
SectionAID, SectionAOffset, SectionBID, SectionBOffset,
false, Size);
addRelocationForSection(R, SectionAID);
return ++RelI;
}
static const char *getRelocName(uint32_t RelocType) {
switch (RelocType) {
case MachO::ARM64_RELOC_UNSIGNED: return "ARM64_RELOC_UNSIGNED";
case MachO::ARM64_RELOC_SUBTRACTOR: return "ARM64_RELOC_SUBTRACTOR";
case MachO::ARM64_RELOC_BRANCH26: return "ARM64_RELOC_BRANCH26";
case MachO::ARM64_RELOC_PAGE21: return "ARM64_RELOC_PAGE21";
case MachO::ARM64_RELOC_PAGEOFF12: return "ARM64_RELOC_PAGEOFF12";
case MachO::ARM64_RELOC_GOT_LOAD_PAGE21: return "ARM64_RELOC_GOT_LOAD_PAGE21";
case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12: return "ARM64_RELOC_GOT_LOAD_PAGEOFF12";
case MachO::ARM64_RELOC_POINTER_TO_GOT: return "ARM64_RELOC_POINTER_TO_GOT";
case MachO::ARM64_RELOC_TLVP_LOAD_PAGE21: return "ARM64_RELOC_TLVP_LOAD_PAGE21";
case MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12: return "ARM64_RELOC_TLVP_LOAD_PAGEOFF12";
case MachO::ARM64_RELOC_ADDEND: return "ARM64_RELOC_ADDEND";
}
return "Unrecognized arm64 addend";
}
};
}
#undef DEBUG_TYPE
#endif