Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/RuntimeDyld/Targets/RuntimeDyldMachOAArch64.h
35294 views
//===-- RuntimeDyldMachOAArch64.h -- MachO/AArch64 specific code. -*- C++ -*-=//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDMACHOAARCH64_H9#define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDMACHOAARCH64_H1011#include "../RuntimeDyldMachO.h"12#include "llvm/Support/Endian.h"1314#define DEBUG_TYPE "dyld"1516namespace llvm {1718class RuntimeDyldMachOAArch6419: public RuntimeDyldMachOCRTPBase<RuntimeDyldMachOAArch64> {20public:2122typedef uint64_t TargetPtrT;2324RuntimeDyldMachOAArch64(RuntimeDyld::MemoryManager &MM,25JITSymbolResolver &Resolver)26: RuntimeDyldMachOCRTPBase(MM, Resolver) {}2728unsigned getMaxStubSize() const override { return 8; }2930Align getStubAlignment() override { return Align(8); }3132/// Extract the addend encoded in the instruction / memory location.33Expected<int64_t> decodeAddend(const RelocationEntry &RE) const {34const SectionEntry &Section = Sections[RE.SectionID];35uint8_t *LocalAddress = Section.getAddressWithOffset(RE.Offset);36unsigned NumBytes = 1 << RE.Size;37int64_t Addend = 0;38// Verify that the relocation has the correct size and alignment.39switch (RE.RelType) {40default: {41std::string ErrMsg;42{43raw_string_ostream ErrStream(ErrMsg);44ErrStream << "Unsupported relocation type: "45<< getRelocName(RE.RelType);46}47return make_error<StringError>(std::move(ErrMsg),48inconvertibleErrorCode());49}50case MachO::ARM64_RELOC_POINTER_TO_GOT:51case MachO::ARM64_RELOC_UNSIGNED: {52if (NumBytes != 4 && NumBytes != 8) {53std::string ErrMsg;54{55raw_string_ostream ErrStream(ErrMsg);56ErrStream << "Invalid relocation size for relocation "57<< getRelocName(RE.RelType);58}59return make_error<StringError>(std::move(ErrMsg),60inconvertibleErrorCode());61}62break;63}64case MachO::ARM64_RELOC_BRANCH26:65case MachO::ARM64_RELOC_PAGE21:66case MachO::ARM64_RELOC_PAGEOFF12:67case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:68case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12:69assert(NumBytes == 4 && "Invalid relocation size.");70assert((((uintptr_t)LocalAddress & 0x3) == 0) &&71"Instruction address is not aligned to 4 bytes.");72break;73}7475switch (RE.RelType) {76default:77llvm_unreachable("Unsupported relocation type!");78case MachO::ARM64_RELOC_POINTER_TO_GOT:79case MachO::ARM64_RELOC_UNSIGNED:80// This could be an unaligned memory location.81if (NumBytes == 4)82Addend = *reinterpret_cast<support::ulittle32_t *>(LocalAddress);83else84Addend = *reinterpret_cast<support::ulittle64_t *>(LocalAddress);85break;86case MachO::ARM64_RELOC_BRANCH26: {87// Verify that the relocation points to a B/BL instruction.88auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);89assert(((*p & 0xFC000000) == 0x14000000 ||90(*p & 0xFC000000) == 0x94000000) &&91"Expected branch instruction.");9293// Get the 26 bit addend encoded in the branch instruction and sign-extend94// to 64 bit. The lower 2 bits are always zeros and are therefore implicit95// (<< 2).96Addend = (*p & 0x03FFFFFF) << 2;97Addend = SignExtend64(Addend, 28);98break;99}100case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:101case MachO::ARM64_RELOC_PAGE21: {102// Verify that the relocation points to the expected adrp instruction.103auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);104assert((*p & 0x9F000000) == 0x90000000 && "Expected adrp instruction.");105106// Get the 21 bit addend encoded in the adrp instruction and sign-extend107// to 64 bit. The lower 12 bits (4096 byte page) are always zeros and are108// therefore implicit (<< 12).109Addend = ((*p & 0x60000000) >> 29) | ((*p & 0x01FFFFE0) >> 3) << 12;110Addend = SignExtend64(Addend, 33);111break;112}113case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12: {114// Verify that the relocation points to one of the expected load / store115// instructions.116auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);117(void)p;118assert((*p & 0x3B000000) == 0x39000000 &&119"Only expected load / store instructions.");120[[fallthrough]];121}122case MachO::ARM64_RELOC_PAGEOFF12: {123// Verify that the relocation points to one of the expected load / store124// or add / sub instructions.125auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);126assert((((*p & 0x3B000000) == 0x39000000) ||127((*p & 0x11C00000) == 0x11000000) ) &&128"Expected load / store or add/sub instruction.");129130// Get the 12 bit addend encoded in the instruction.131Addend = (*p & 0x003FFC00) >> 10;132133// Check which instruction we are decoding to obtain the implicit shift134// factor of the instruction.135int ImplicitShift = 0;136if ((*p & 0x3B000000) == 0x39000000) { // << load / store137// For load / store instructions the size is encoded in bits 31:30.138ImplicitShift = ((*p >> 30) & 0x3);139if (ImplicitShift == 0) {140// Check if this a vector op to get the correct shift value.141if ((*p & 0x04800000) == 0x04800000)142ImplicitShift = 4;143}144}145// Compensate for implicit shift.146Addend <<= ImplicitShift;147break;148}149}150return Addend;151}152153/// Extract the addend encoded in the instruction.154void encodeAddend(uint8_t *LocalAddress, unsigned NumBytes,155MachO::RelocationInfoType RelType, int64_t Addend) const {156// Verify that the relocation has the correct alignment.157switch (RelType) {158default:159llvm_unreachable("Unsupported relocation type!");160case MachO::ARM64_RELOC_POINTER_TO_GOT:161case MachO::ARM64_RELOC_UNSIGNED:162assert((NumBytes == 4 || NumBytes == 8) && "Invalid relocation size.");163break;164case MachO::ARM64_RELOC_BRANCH26:165case MachO::ARM64_RELOC_PAGE21:166case MachO::ARM64_RELOC_PAGEOFF12:167case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:168case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12:169assert(NumBytes == 4 && "Invalid relocation size.");170assert((((uintptr_t)LocalAddress & 0x3) == 0) &&171"Instruction address is not aligned to 4 bytes.");172break;173}174175switch (RelType) {176default:177llvm_unreachable("Unsupported relocation type!");178case MachO::ARM64_RELOC_POINTER_TO_GOT:179case MachO::ARM64_RELOC_UNSIGNED:180// This could be an unaligned memory location.181if (NumBytes == 4)182*reinterpret_cast<support::ulittle32_t *>(LocalAddress) = Addend;183else184*reinterpret_cast<support::ulittle64_t *>(LocalAddress) = Addend;185break;186case MachO::ARM64_RELOC_BRANCH26: {187auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);188// Verify that the relocation points to the expected branch instruction.189assert(((*p & 0xFC000000) == 0x14000000 ||190(*p & 0xFC000000) == 0x94000000) &&191"Expected branch instruction.");192193// Verify addend value.194assert((Addend & 0x3) == 0 && "Branch target is not aligned");195assert(isInt<28>(Addend) && "Branch target is out of range.");196197// Encode the addend as 26 bit immediate in the branch instruction.198*p = (*p & 0xFC000000) | ((uint32_t)(Addend >> 2) & 0x03FFFFFF);199break;200}201case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:202case MachO::ARM64_RELOC_PAGE21: {203// Verify that the relocation points to the expected adrp instruction.204auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);205assert((*p & 0x9F000000) == 0x90000000 && "Expected adrp instruction.");206207// Check that the addend fits into 21 bits (+ 12 lower bits).208assert((Addend & 0xFFF) == 0 && "ADRP target is not page aligned.");209assert(isInt<33>(Addend) && "Invalid page reloc value.");210211// Encode the addend into the instruction.212uint32_t ImmLoValue = ((uint64_t)Addend << 17) & 0x60000000;213uint32_t ImmHiValue = ((uint64_t)Addend >> 9) & 0x00FFFFE0;214*p = (*p & 0x9F00001F) | ImmHiValue | ImmLoValue;215break;216}217case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12: {218// Verify that the relocation points to one of the expected load / store219// instructions.220auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);221assert((*p & 0x3B000000) == 0x39000000 &&222"Only expected load / store instructions.");223(void)p;224[[fallthrough]];225}226case MachO::ARM64_RELOC_PAGEOFF12: {227// Verify that the relocation points to one of the expected load / store228// or add / sub instructions.229auto *p = reinterpret_cast<support::aligned_ulittle32_t *>(LocalAddress);230assert((((*p & 0x3B000000) == 0x39000000) ||231((*p & 0x11C00000) == 0x11000000) ) &&232"Expected load / store or add/sub instruction.");233234// Check which instruction we are decoding to obtain the implicit shift235// factor of the instruction and verify alignment.236int ImplicitShift = 0;237if ((*p & 0x3B000000) == 0x39000000) { // << load / store238// For load / store instructions the size is encoded in bits 31:30.239ImplicitShift = ((*p >> 30) & 0x3);240switch (ImplicitShift) {241case 0:242// Check if this a vector op to get the correct shift value.243if ((*p & 0x04800000) == 0x04800000) {244ImplicitShift = 4;245assert(((Addend & 0xF) == 0) &&246"128-bit LDR/STR not 16-byte aligned.");247}248break;249case 1:250assert(((Addend & 0x1) == 0) && "16-bit LDR/STR not 2-byte aligned.");251break;252case 2:253assert(((Addend & 0x3) == 0) && "32-bit LDR/STR not 4-byte aligned.");254break;255case 3:256assert(((Addend & 0x7) == 0) && "64-bit LDR/STR not 8-byte aligned.");257break;258}259}260// Compensate for implicit shift.261Addend >>= ImplicitShift;262assert(isUInt<12>(Addend) && "Addend cannot be encoded.");263264// Encode the addend into the instruction.265*p = (*p & 0xFFC003FF) | ((uint32_t)(Addend << 10) & 0x003FFC00);266break;267}268}269}270271Expected<relocation_iterator>272processRelocationRef(unsigned SectionID, relocation_iterator RelI,273const ObjectFile &BaseObjT,274ObjSectionToIDMap &ObjSectionToID,275StubMap &Stubs) override {276const MachOObjectFile &Obj =277static_cast<const MachOObjectFile &>(BaseObjT);278MachO::any_relocation_info RelInfo =279Obj.getRelocation(RelI->getRawDataRefImpl());280281if (Obj.isRelocationScattered(RelInfo))282return make_error<RuntimeDyldError>("Scattered relocations not supported "283"for MachO AArch64");284285// ARM64 has an ARM64_RELOC_ADDEND relocation type that carries an explicit286// addend for the following relocation. If found: (1) store the associated287// addend, (2) consume the next relocation, and (3) use the stored addend to288// override the addend.289int64_t ExplicitAddend = 0;290if (Obj.getAnyRelocationType(RelInfo) == MachO::ARM64_RELOC_ADDEND) {291assert(!Obj.getPlainRelocationExternal(RelInfo));292assert(!Obj.getAnyRelocationPCRel(RelInfo));293assert(Obj.getAnyRelocationLength(RelInfo) == 2);294int64_t RawAddend = Obj.getPlainRelocationSymbolNum(RelInfo);295// Sign-extend the 24-bit to 64-bit.296ExplicitAddend = SignExtend64(RawAddend, 24);297++RelI;298RelInfo = Obj.getRelocation(RelI->getRawDataRefImpl());299}300301if (Obj.getAnyRelocationType(RelInfo) == MachO::ARM64_RELOC_SUBTRACTOR)302return processSubtractRelocation(SectionID, RelI, Obj, ObjSectionToID);303304RelocationEntry RE(getRelocationEntry(SectionID, Obj, RelI));305306if (RE.RelType == MachO::ARM64_RELOC_POINTER_TO_GOT) {307bool Valid =308(RE.Size == 2 && RE.IsPCRel) || (RE.Size == 3 && !RE.IsPCRel);309if (!Valid)310return make_error<StringError>("ARM64_RELOC_POINTER_TO_GOT supports "311"32-bit pc-rel or 64-bit absolute only",312inconvertibleErrorCode());313}314315if (auto Addend = decodeAddend(RE))316RE.Addend = *Addend;317else318return Addend.takeError();319320assert((ExplicitAddend == 0 || RE.Addend == 0) && "Relocation has "\321"ARM64_RELOC_ADDEND and embedded addend in the instruction.");322if (ExplicitAddend)323RE.Addend = ExplicitAddend;324325RelocationValueRef Value;326if (auto ValueOrErr = getRelocationValueRef(Obj, RelI, RE, ObjSectionToID))327Value = *ValueOrErr;328else329return ValueOrErr.takeError();330331bool IsExtern = Obj.getPlainRelocationExternal(RelInfo);332if (RE.RelType == MachO::ARM64_RELOC_POINTER_TO_GOT) {333// We'll take care of the offset in processGOTRelocation.334Value.Offset = 0;335} else if (!IsExtern && RE.IsPCRel)336makeValueAddendPCRel(Value, RelI, 1 << RE.Size);337338RE.Addend = Value.Offset;339340if (RE.RelType == MachO::ARM64_RELOC_GOT_LOAD_PAGE21 ||341RE.RelType == MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12 ||342RE.RelType == MachO::ARM64_RELOC_POINTER_TO_GOT)343processGOTRelocation(RE, Value, Stubs);344else {345if (Value.SymbolName)346addRelocationForSymbol(RE, Value.SymbolName);347else348addRelocationForSection(RE, Value.SectionID);349}350351return ++RelI;352}353354void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override {355LLVM_DEBUG(dumpRelocationToResolve(RE, Value));356357const SectionEntry &Section = Sections[RE.SectionID];358uint8_t *LocalAddress = Section.getAddressWithOffset(RE.Offset);359MachO::RelocationInfoType RelType =360static_cast<MachO::RelocationInfoType>(RE.RelType);361362switch (RelType) {363default:364llvm_unreachable("Invalid relocation type!");365case MachO::ARM64_RELOC_UNSIGNED: {366assert(!RE.IsPCRel && "PCRel and ARM64_RELOC_UNSIGNED not supported");367// Mask in the target value a byte at a time (we don't have an alignment368// guarantee for the target address, so this is safest).369if (RE.Size < 2)370llvm_unreachable("Invalid size for ARM64_RELOC_UNSIGNED");371372encodeAddend(LocalAddress, 1 << RE.Size, RelType, Value + RE.Addend);373break;374}375376case MachO::ARM64_RELOC_POINTER_TO_GOT: {377assert(((RE.Size == 2 && RE.IsPCRel) || (RE.Size == 3 && !RE.IsPCRel)) &&378"ARM64_RELOC_POINTER_TO_GOT only supports 32-bit pc-rel or 64-bit "379"absolute");380// Addend is the GOT entry address and RE.Offset the target of the381// relocation.382uint64_t Result =383RE.IsPCRel ? (RE.Addend - RE.Offset) : (Value + RE.Addend);384encodeAddend(LocalAddress, 1 << RE.Size, RelType, Result);385break;386}387388case MachO::ARM64_RELOC_BRANCH26: {389assert(RE.IsPCRel && "not PCRel and ARM64_RELOC_BRANCH26 not supported");390// Check if branch is in range.391uint64_t FinalAddress = Section.getLoadAddressWithOffset(RE.Offset);392int64_t PCRelVal = Value - FinalAddress + RE.Addend;393encodeAddend(LocalAddress, /*Size=*/4, RelType, PCRelVal);394break;395}396case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:397case MachO::ARM64_RELOC_PAGE21: {398assert(RE.IsPCRel && "not PCRel and ARM64_RELOC_PAGE21 not supported");399// Adjust for PC-relative relocation and offset.400uint64_t FinalAddress = Section.getLoadAddressWithOffset(RE.Offset);401int64_t PCRelVal =402((Value + RE.Addend) & (-4096)) - (FinalAddress & (-4096));403encodeAddend(LocalAddress, /*Size=*/4, RelType, PCRelVal);404break;405}406case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12:407case MachO::ARM64_RELOC_PAGEOFF12: {408assert(!RE.IsPCRel && "PCRel and ARM64_RELOC_PAGEOFF21 not supported");409// Add the offset from the symbol.410Value += RE.Addend;411// Mask out the page address and only use the lower 12 bits.412Value &= 0xFFF;413encodeAddend(LocalAddress, /*Size=*/4, RelType, Value);414break;415}416case MachO::ARM64_RELOC_SUBTRACTOR: {417uint64_t SectionABase = Sections[RE.Sections.SectionA].getLoadAddress();418uint64_t SectionBBase = Sections[RE.Sections.SectionB].getLoadAddress();419assert((Value == SectionABase || Value == SectionBBase) &&420"Unexpected SUBTRACTOR relocation value.");421Value = SectionABase - SectionBBase + RE.Addend;422writeBytesUnaligned(Value, LocalAddress, 1 << RE.Size);423break;424}425426case MachO::ARM64_RELOC_TLVP_LOAD_PAGE21:427case MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12:428llvm_unreachable("Relocation type not yet implemented!");429case MachO::ARM64_RELOC_ADDEND:430llvm_unreachable("ARM64_RELOC_ADDEND should have been handeled by "431"processRelocationRef!");432}433}434435Error finalizeSection(const ObjectFile &Obj, unsigned SectionID,436const SectionRef &Section) {437return Error::success();438}439440private:441void processGOTRelocation(const RelocationEntry &RE,442RelocationValueRef &Value, StubMap &Stubs) {443assert((RE.RelType == MachO::ARM64_RELOC_POINTER_TO_GOT &&444(RE.Size == 2 || RE.Size == 3)) ||445RE.Size == 2);446SectionEntry &Section = Sections[RE.SectionID];447StubMap::const_iterator i = Stubs.find(Value);448int64_t Offset;449if (i != Stubs.end())450Offset = static_cast<int64_t>(i->second);451else {452// FIXME: There must be a better way to do this then to check and fix the453// alignment every time!!!454uintptr_t BaseAddress = uintptr_t(Section.getAddress());455uintptr_t StubAlignment = getStubAlignment().value();456uintptr_t StubAddress =457(BaseAddress + Section.getStubOffset() + StubAlignment - 1) &458-StubAlignment;459unsigned StubOffset = StubAddress - BaseAddress;460Stubs[Value] = StubOffset;461assert(isAligned(getStubAlignment(), StubAddress) &&462"GOT entry not aligned");463RelocationEntry GOTRE(RE.SectionID, StubOffset,464MachO::ARM64_RELOC_UNSIGNED, Value.Offset,465/*IsPCRel=*/false, /*Size=*/3);466if (Value.SymbolName)467addRelocationForSymbol(GOTRE, Value.SymbolName);468else469addRelocationForSection(GOTRE, Value.SectionID);470Section.advanceStubOffset(getMaxStubSize());471Offset = static_cast<int64_t>(StubOffset);472}473RelocationEntry TargetRE(RE.SectionID, RE.Offset, RE.RelType, Offset,474RE.IsPCRel, RE.Size);475addRelocationForSection(TargetRE, RE.SectionID);476}477478Expected<relocation_iterator>479processSubtractRelocation(unsigned SectionID, relocation_iterator RelI,480const ObjectFile &BaseObjT,481ObjSectionToIDMap &ObjSectionToID) {482const MachOObjectFile &Obj =483static_cast<const MachOObjectFile&>(BaseObjT);484MachO::any_relocation_info RE =485Obj.getRelocation(RelI->getRawDataRefImpl());486487unsigned Size = Obj.getAnyRelocationLength(RE);488uint64_t Offset = RelI->getOffset();489uint8_t *LocalAddress = Sections[SectionID].getAddressWithOffset(Offset);490unsigned NumBytes = 1 << Size;491492Expected<StringRef> SubtrahendNameOrErr = RelI->getSymbol()->getName();493if (!SubtrahendNameOrErr)494return SubtrahendNameOrErr.takeError();495auto SubtrahendI = GlobalSymbolTable.find(*SubtrahendNameOrErr);496unsigned SectionBID = SubtrahendI->second.getSectionID();497uint64_t SectionBOffset = SubtrahendI->second.getOffset();498int64_t Addend =499SignExtend64(readBytesUnaligned(LocalAddress, NumBytes), NumBytes * 8);500501++RelI;502Expected<StringRef> MinuendNameOrErr = RelI->getSymbol()->getName();503if (!MinuendNameOrErr)504return MinuendNameOrErr.takeError();505auto MinuendI = GlobalSymbolTable.find(*MinuendNameOrErr);506unsigned SectionAID = MinuendI->second.getSectionID();507uint64_t SectionAOffset = MinuendI->second.getOffset();508509RelocationEntry R(SectionID, Offset, MachO::ARM64_RELOC_SUBTRACTOR, (uint64_t)Addend,510SectionAID, SectionAOffset, SectionBID, SectionBOffset,511false, Size);512513addRelocationForSection(R, SectionAID);514515return ++RelI;516}517518static const char *getRelocName(uint32_t RelocType) {519switch (RelocType) {520case MachO::ARM64_RELOC_UNSIGNED: return "ARM64_RELOC_UNSIGNED";521case MachO::ARM64_RELOC_SUBTRACTOR: return "ARM64_RELOC_SUBTRACTOR";522case MachO::ARM64_RELOC_BRANCH26: return "ARM64_RELOC_BRANCH26";523case MachO::ARM64_RELOC_PAGE21: return "ARM64_RELOC_PAGE21";524case MachO::ARM64_RELOC_PAGEOFF12: return "ARM64_RELOC_PAGEOFF12";525case MachO::ARM64_RELOC_GOT_LOAD_PAGE21: return "ARM64_RELOC_GOT_LOAD_PAGE21";526case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12: return "ARM64_RELOC_GOT_LOAD_PAGEOFF12";527case MachO::ARM64_RELOC_POINTER_TO_GOT: return "ARM64_RELOC_POINTER_TO_GOT";528case MachO::ARM64_RELOC_TLVP_LOAD_PAGE21: return "ARM64_RELOC_TLVP_LOAD_PAGE21";529case MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12: return "ARM64_RELOC_TLVP_LOAD_PAGEOFF12";530case MachO::ARM64_RELOC_ADDEND: return "ARM64_RELOC_ADDEND";531}532return "Unrecognized arm64 addend";533}534535};536}537538#undef DEBUG_TYPE539540#endif541542543