Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp
35271 views
//===---- MachO_arm64.cpp - JIT linker implementation for MachO/arm64 -----===//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//===----------------------------------------------------------------------===//7//8// MachO/arm64 jit-link implementation.9//10//===----------------------------------------------------------------------===//1112#include "llvm/ExecutionEngine/JITLink/MachO_arm64.h"13#include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h"14#include "llvm/ExecutionEngine/JITLink/aarch64.h"1516#include "DefineExternalSectionStartAndEndSymbols.h"17#include "MachOLinkGraphBuilder.h"1819#define DEBUG_TYPE "jitlink"2021using namespace llvm;22using namespace llvm::jitlink;2324namespace {2526class MachOLinkGraphBuilder_arm64 : public MachOLinkGraphBuilder {27public:28MachOLinkGraphBuilder_arm64(const object::MachOObjectFile &Obj,29SubtargetFeatures Features)30: MachOLinkGraphBuilder(Obj, Triple("arm64-apple-darwin"),31std::move(Features), aarch64::getEdgeKindName),32NumSymbols(Obj.getSymtabLoadCommand().nsyms) {}3334private:35enum MachOARM64RelocationKind : Edge::Kind {36MachOBranch26 = Edge::FirstRelocation,37MachOPointer32,38MachOPointer64,39MachOPointer64Anon,40MachOPage21,41MachOPageOffset12,42MachOGOTPage21,43MachOGOTPageOffset12,44MachOTLVPage21,45MachOTLVPageOffset12,46MachOPointerToGOT,47MachOPairedAddend,48MachOLDRLiteral19,49MachODelta32,50MachODelta64,51MachONegDelta32,52MachONegDelta64,53};5455static Expected<MachOARM64RelocationKind>56getRelocationKind(const MachO::relocation_info &RI) {57switch (RI.r_type) {58case MachO::ARM64_RELOC_UNSIGNED:59if (!RI.r_pcrel) {60if (RI.r_length == 3)61return RI.r_extern ? MachOPointer64 : MachOPointer64Anon;62else if (RI.r_length == 2)63return MachOPointer32;64}65break;66case MachO::ARM64_RELOC_SUBTRACTOR:67// SUBTRACTOR must be non-pc-rel, extern, with length 2 or 3.68// Initially represent SUBTRACTOR relocations with 'Delta<W>'.69// They may be turned into NegDelta<W> by parsePairRelocation.70if (!RI.r_pcrel && RI.r_extern) {71if (RI.r_length == 2)72return MachODelta32;73else if (RI.r_length == 3)74return MachODelta64;75}76break;77case MachO::ARM64_RELOC_BRANCH26:78if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)79return MachOBranch26;80break;81case MachO::ARM64_RELOC_PAGE21:82if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)83return MachOPage21;84break;85case MachO::ARM64_RELOC_PAGEOFF12:86if (!RI.r_pcrel && RI.r_extern && RI.r_length == 2)87return MachOPageOffset12;88break;89case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:90if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)91return MachOGOTPage21;92break;93case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12:94if (!RI.r_pcrel && RI.r_extern && RI.r_length == 2)95return MachOGOTPageOffset12;96break;97case MachO::ARM64_RELOC_POINTER_TO_GOT:98if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)99return MachOPointerToGOT;100break;101case MachO::ARM64_RELOC_ADDEND:102if (!RI.r_pcrel && !RI.r_extern && RI.r_length == 2)103return MachOPairedAddend;104break;105case MachO::ARM64_RELOC_TLVP_LOAD_PAGE21:106if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)107return MachOTLVPage21;108break;109case MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12:110if (!RI.r_pcrel && RI.r_extern && RI.r_length == 2)111return MachOTLVPageOffset12;112break;113}114115return make_error<JITLinkError>(116"Unsupported arm64 relocation: address=" +117formatv("{0:x8}", RI.r_address) +118", symbolnum=" + formatv("{0:x6}", RI.r_symbolnum) +119", kind=" + formatv("{0:x1}", RI.r_type) +120", pc_rel=" + (RI.r_pcrel ? "true" : "false") +121", extern=" + (RI.r_extern ? "true" : "false") +122", length=" + formatv("{0:d}", RI.r_length));123}124125using PairRelocInfo = std::tuple<Edge::Kind, Symbol *, uint64_t>;126127// Parses paired SUBTRACTOR/UNSIGNED relocations and, on success,128// returns the edge kind and addend to be used.129Expected<PairRelocInfo>130parsePairRelocation(Block &BlockToFix, Edge::Kind SubtractorKind,131const MachO::relocation_info &SubRI,132orc::ExecutorAddr FixupAddress, const char *FixupContent,133object::relocation_iterator &UnsignedRelItr,134object::relocation_iterator &RelEnd) {135using namespace support;136137assert(((SubtractorKind == MachODelta32 && SubRI.r_length == 2) ||138(SubtractorKind == MachODelta64 && SubRI.r_length == 3)) &&139"Subtractor kind should match length");140assert(SubRI.r_extern && "SUBTRACTOR reloc symbol should be extern");141assert(!SubRI.r_pcrel && "SUBTRACTOR reloc should not be PCRel");142143if (UnsignedRelItr == RelEnd)144return make_error<JITLinkError>("arm64 SUBTRACTOR without paired "145"UNSIGNED relocation");146147auto UnsignedRI = getRelocationInfo(UnsignedRelItr);148149if (SubRI.r_address != UnsignedRI.r_address)150return make_error<JITLinkError>("arm64 SUBTRACTOR and paired UNSIGNED "151"point to different addresses");152153if (SubRI.r_length != UnsignedRI.r_length)154return make_error<JITLinkError>("length of arm64 SUBTRACTOR and paired "155"UNSIGNED reloc must match");156157Symbol *FromSymbol;158if (auto FromSymbolOrErr = findSymbolByIndex(SubRI.r_symbolnum))159FromSymbol = FromSymbolOrErr->GraphSymbol;160else161return FromSymbolOrErr.takeError();162163// Read the current fixup value.164uint64_t FixupValue = 0;165if (SubRI.r_length == 3)166FixupValue = *(const little64_t *)FixupContent;167else168FixupValue = *(const little32_t *)FixupContent;169170// Find 'ToSymbol' using symbol number or address, depending on whether the171// paired UNSIGNED relocation is extern.172Symbol *ToSymbol = nullptr;173if (UnsignedRI.r_extern) {174// Find target symbol by symbol index.175if (auto ToSymbolOrErr = findSymbolByIndex(UnsignedRI.r_symbolnum))176ToSymbol = ToSymbolOrErr->GraphSymbol;177else178return ToSymbolOrErr.takeError();179} else {180auto ToSymbolSec = findSectionByIndex(UnsignedRI.r_symbolnum - 1);181if (!ToSymbolSec)182return ToSymbolSec.takeError();183ToSymbol = getSymbolByAddress(*ToSymbolSec, ToSymbolSec->Address);184assert(ToSymbol && "No symbol for section");185FixupValue -= ToSymbol->getAddress().getValue();186}187188Edge::Kind DeltaKind;189Symbol *TargetSymbol;190uint64_t Addend;191192bool FixingFromSymbol = true;193if (&BlockToFix == &FromSymbol->getAddressable()) {194if (LLVM_UNLIKELY(&BlockToFix == &ToSymbol->getAddressable())) {195// From and To are symbols in the same block. Decide direction by offset196// instead.197if (ToSymbol->getAddress() > FixupAddress)198FixingFromSymbol = true;199else if (FromSymbol->getAddress() > FixupAddress)200FixingFromSymbol = false;201else202FixingFromSymbol = FromSymbol->getAddress() >= ToSymbol->getAddress();203} else204FixingFromSymbol = true;205} else {206if (&BlockToFix == &ToSymbol->getAddressable())207FixingFromSymbol = false;208else {209// BlockToFix was neither FromSymbol nor ToSymbol.210return make_error<JITLinkError>("SUBTRACTOR relocation must fix up "211"either 'A' or 'B' (or a symbol in one "212"of their alt-entry groups)");213}214}215216if (FixingFromSymbol) {217TargetSymbol = ToSymbol;218DeltaKind = (SubRI.r_length == 3) ? aarch64::Delta64 : aarch64::Delta32;219Addend = FixupValue + (FixupAddress - FromSymbol->getAddress());220// FIXME: handle extern 'from'.221} else {222TargetSymbol = &*FromSymbol;223DeltaKind =224(SubRI.r_length == 3) ? aarch64::NegDelta64 : aarch64::NegDelta32;225Addend = FixupValue - (FixupAddress - ToSymbol->getAddress());226}227228return PairRelocInfo(DeltaKind, TargetSymbol, Addend);229}230231Error addRelocations() override {232using namespace support;233auto &Obj = getObject();234235LLVM_DEBUG(dbgs() << "Processing relocations:\n");236237for (auto &S : Obj.sections()) {238239orc::ExecutorAddr SectionAddress(S.getAddress());240241// Skip relocations virtual sections.242if (S.isVirtual()) {243if (S.relocation_begin() != S.relocation_end())244return make_error<JITLinkError>("Virtual section contains "245"relocations");246continue;247}248249auto NSec =250findSectionByIndex(Obj.getSectionIndex(S.getRawDataRefImpl()));251if (!NSec)252return NSec.takeError();253254// Skip relocations for MachO sections without corresponding graph255// sections.256{257if (!NSec->GraphSection) {258LLVM_DEBUG({259dbgs() << " Skipping relocations for MachO section "260<< NSec->SegName << "/" << NSec->SectName261<< " which has no associated graph section\n";262});263continue;264}265}266267for (auto RelItr = S.relocation_begin(), RelEnd = S.relocation_end();268RelItr != RelEnd; ++RelItr) {269270MachO::relocation_info RI = getRelocationInfo(RelItr);271272// Validate the relocation kind.273auto MachORelocKind = getRelocationKind(RI);274if (!MachORelocKind)275return MachORelocKind.takeError();276277// Find the address of the value to fix up.278orc::ExecutorAddr FixupAddress =279SectionAddress + (uint32_t)RI.r_address;280LLVM_DEBUG({281dbgs() << " " << NSec->SectName << " + "282<< formatv("{0:x8}", RI.r_address) << ":\n";283});284285// Find the block that the fixup points to.286Block *BlockToFix = nullptr;287{288auto SymbolToFixOrErr = findSymbolByAddress(*NSec, FixupAddress);289if (!SymbolToFixOrErr)290return SymbolToFixOrErr.takeError();291BlockToFix = &SymbolToFixOrErr->getBlock();292}293294if (FixupAddress + orc::ExecutorAddrDiff(1ULL << RI.r_length) >295BlockToFix->getAddress() + BlockToFix->getContent().size())296return make_error<JITLinkError>(297"Relocation content extends past end of fixup block");298299Edge::Kind Kind = Edge::Invalid;300301// Get a pointer to the fixup content.302const char *FixupContent = BlockToFix->getContent().data() +303(FixupAddress - BlockToFix->getAddress());304305// The target symbol and addend will be populated by the switch below.306Symbol *TargetSymbol = nullptr;307uint64_t Addend = 0;308309if (*MachORelocKind == MachOPairedAddend) {310// If this is an Addend relocation then process it and move to the311// paired reloc.312313Addend = SignExtend64(RI.r_symbolnum, 24);314315++RelItr;316if (RelItr == RelEnd)317return make_error<JITLinkError>("Unpaired Addend reloc at " +318formatv("{0:x16}", FixupAddress));319RI = getRelocationInfo(RelItr);320321MachORelocKind = getRelocationKind(RI);322if (!MachORelocKind)323return MachORelocKind.takeError();324325if (*MachORelocKind != MachOBranch26 &&326*MachORelocKind != MachOPage21 &&327*MachORelocKind != MachOPageOffset12)328return make_error<JITLinkError>(329"Invalid relocation pair: Addend + " +330StringRef(getMachOARM64RelocationKindName(*MachORelocKind)));331332LLVM_DEBUG({333dbgs() << " Addend: value = " << formatv("{0:x6}", Addend)334<< ", pair is "335<< getMachOARM64RelocationKindName(*MachORelocKind) << "\n";336});337338// Find the address of the value to fix up.339orc::ExecutorAddr PairedFixupAddress =340SectionAddress + (uint32_t)RI.r_address;341if (PairedFixupAddress != FixupAddress)342return make_error<JITLinkError>("Paired relocation points at "343"different target");344}345346switch (*MachORelocKind) {347case MachOBranch26: {348if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))349TargetSymbol = TargetSymbolOrErr->GraphSymbol;350else351return TargetSymbolOrErr.takeError();352uint32_t Instr = *(const ulittle32_t *)FixupContent;353if ((Instr & 0x7fffffff) != 0x14000000)354return make_error<JITLinkError>("BRANCH26 target is not a B or BL "355"instruction with a zero addend");356Kind = aarch64::Branch26PCRel;357break;358}359case MachOPointer32:360if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))361TargetSymbol = TargetSymbolOrErr->GraphSymbol;362else363return TargetSymbolOrErr.takeError();364Addend = *(const ulittle32_t *)FixupContent;365Kind = aarch64::Pointer32;366break;367case MachOPointer64:368if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))369TargetSymbol = TargetSymbolOrErr->GraphSymbol;370else371return TargetSymbolOrErr.takeError();372Addend = *(const ulittle64_t *)FixupContent;373Kind = aarch64::Pointer64;374break;375case MachOPointer64Anon: {376orc::ExecutorAddr TargetAddress(*(const ulittle64_t *)FixupContent);377auto TargetNSec = findSectionByIndex(RI.r_symbolnum - 1);378if (!TargetNSec)379return TargetNSec.takeError();380if (auto TargetSymbolOrErr =381findSymbolByAddress(*TargetNSec, TargetAddress))382TargetSymbol = &*TargetSymbolOrErr;383else384return TargetSymbolOrErr.takeError();385Addend = TargetAddress - TargetSymbol->getAddress();386Kind = aarch64::Pointer64;387break;388}389case MachOPage21:390case MachOGOTPage21:391case MachOTLVPage21: {392if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))393TargetSymbol = TargetSymbolOrErr->GraphSymbol;394else395return TargetSymbolOrErr.takeError();396uint32_t Instr = *(const ulittle32_t *)FixupContent;397if ((Instr & 0xffffffe0) != 0x90000000)398return make_error<JITLinkError>("PAGE21/GOTPAGE21 target is not an "399"ADRP instruction with a zero "400"addend");401402if (*MachORelocKind == MachOPage21) {403Kind = aarch64::Page21;404} else if (*MachORelocKind == MachOGOTPage21) {405Kind = aarch64::RequestGOTAndTransformToPage21;406} else if (*MachORelocKind == MachOTLVPage21) {407Kind = aarch64::RequestTLVPAndTransformToPage21;408}409break;410}411case MachOPageOffset12: {412if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))413TargetSymbol = TargetSymbolOrErr->GraphSymbol;414else415return TargetSymbolOrErr.takeError();416uint32_t Instr = *(const ulittle32_t *)FixupContent;417uint32_t EncodedAddend = (Instr & 0x003FFC00) >> 10;418if (EncodedAddend != 0)419return make_error<JITLinkError>("GOTPAGEOFF12 target has non-zero "420"encoded addend");421Kind = aarch64::PageOffset12;422break;423}424case MachOGOTPageOffset12:425case MachOTLVPageOffset12: {426if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))427TargetSymbol = TargetSymbolOrErr->GraphSymbol;428else429return TargetSymbolOrErr.takeError();430uint32_t Instr = *(const ulittle32_t *)FixupContent;431if ((Instr & 0xfffffc00) != 0xf9400000)432return make_error<JITLinkError>("GOTPAGEOFF12 target is not an LDR "433"immediate instruction with a zero "434"addend");435436if (*MachORelocKind == MachOGOTPageOffset12) {437Kind = aarch64::RequestGOTAndTransformToPageOffset12;438} else if (*MachORelocKind == MachOTLVPageOffset12) {439Kind = aarch64::RequestTLVPAndTransformToPageOffset12;440}441break;442}443case MachOPointerToGOT:444if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))445TargetSymbol = TargetSymbolOrErr->GraphSymbol;446else447return TargetSymbolOrErr.takeError();448449Kind = aarch64::RequestGOTAndTransformToDelta32;450break;451case MachODelta32:452case MachODelta64: {453// We use Delta32/Delta64 to represent SUBTRACTOR relocations.454// parsePairRelocation handles the paired reloc, and returns the455// edge kind to be used (either Delta32/Delta64, or456// NegDelta32/NegDelta64, depending on the direction of the457// subtraction) along with the addend.458auto PairInfo =459parsePairRelocation(*BlockToFix, *MachORelocKind, RI,460FixupAddress, FixupContent, ++RelItr, RelEnd);461if (!PairInfo)462return PairInfo.takeError();463std::tie(Kind, TargetSymbol, Addend) = *PairInfo;464assert(TargetSymbol && "No target symbol from parsePairRelocation?");465break;466}467default:468llvm_unreachable("Special relocation kind should not appear in "469"mach-o file");470}471472LLVM_DEBUG({473dbgs() << " ";474Edge GE(Kind, FixupAddress - BlockToFix->getAddress(), *TargetSymbol,475Addend);476printEdge(dbgs(), *BlockToFix, GE, aarch64::getEdgeKindName(Kind));477dbgs() << "\n";478});479BlockToFix->addEdge(Kind, FixupAddress - BlockToFix->getAddress(),480*TargetSymbol, Addend);481}482}483return Error::success();484}485486/// Return the string name of the given MachO arm64 edge kind.487const char *getMachOARM64RelocationKindName(Edge::Kind R) {488switch (R) {489case MachOBranch26:490return "MachOBranch26";491case MachOPointer64:492return "MachOPointer64";493case MachOPointer64Anon:494return "MachOPointer64Anon";495case MachOPage21:496return "MachOPage21";497case MachOPageOffset12:498return "MachOPageOffset12";499case MachOGOTPage21:500return "MachOGOTPage21";501case MachOGOTPageOffset12:502return "MachOGOTPageOffset12";503case MachOTLVPage21:504return "MachOTLVPage21";505case MachOTLVPageOffset12:506return "MachOTLVPageOffset12";507case MachOPointerToGOT:508return "MachOPointerToGOT";509case MachOPairedAddend:510return "MachOPairedAddend";511case MachOLDRLiteral19:512return "MachOLDRLiteral19";513case MachODelta32:514return "MachODelta32";515case MachODelta64:516return "MachODelta64";517case MachONegDelta32:518return "MachONegDelta32";519case MachONegDelta64:520return "MachONegDelta64";521default:522return getGenericEdgeKindName(static_cast<Edge::Kind>(R));523}524}525526unsigned NumSymbols = 0;527};528529} // namespace530531namespace llvm {532namespace jitlink {533534Error buildTables_MachO_arm64(LinkGraph &G) {535LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n");536537aarch64::GOTTableManager GOT;538aarch64::PLTTableManager PLT(GOT);539visitExistingEdges(G, GOT, PLT);540return Error::success();541}542543class MachOJITLinker_arm64 : public JITLinker<MachOJITLinker_arm64> {544friend class JITLinker<MachOJITLinker_arm64>;545546public:547MachOJITLinker_arm64(std::unique_ptr<JITLinkContext> Ctx,548std::unique_ptr<LinkGraph> G,549PassConfiguration PassConfig)550: JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}551552private:553Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {554return aarch64::applyFixup(G, B, E);555}556557uint64_t NullValue = 0;558};559560Expected<std::unique_ptr<LinkGraph>>561createLinkGraphFromMachOObject_arm64(MemoryBufferRef ObjectBuffer) {562auto MachOObj = object::ObjectFile::createMachOObjectFile(ObjectBuffer);563if (!MachOObj)564return MachOObj.takeError();565566auto Features = (*MachOObj)->getFeatures();567if (!Features)568return Features.takeError();569570return MachOLinkGraphBuilder_arm64(**MachOObj, std::move(*Features))571.buildGraph();572}573574void link_MachO_arm64(std::unique_ptr<LinkGraph> G,575std::unique_ptr<JITLinkContext> Ctx) {576577PassConfiguration Config;578579if (Ctx->shouldAddDefaultTargetPasses(G->getTargetTriple())) {580// Add a mark-live pass.581if (auto MarkLive = Ctx->getMarkLivePass(G->getTargetTriple()))582Config.PrePrunePasses.push_back(std::move(MarkLive));583else584Config.PrePrunePasses.push_back(markAllSymbolsLive);585586// Add compact unwind splitter pass.587Config.PrePrunePasses.push_back(588CompactUnwindSplitter("__LD,__compact_unwind"));589590// Add eh-frame passes.591// FIXME: Prune eh-frames for which compact-unwind is available once592// we support compact-unwind registration with libunwind.593Config.PrePrunePasses.push_back(createEHFrameSplitterPass_MachO_arm64());594Config.PrePrunePasses.push_back(createEHFrameEdgeFixerPass_MachO_arm64());595596// Resolve any external section start / end symbols.597Config.PostAllocationPasses.push_back(598createDefineExternalSectionStartAndEndSymbolsPass(599identifyMachOSectionStartAndEndSymbols));600601// Add an in-place GOT/Stubs pass.602Config.PostPrunePasses.push_back(buildTables_MachO_arm64);603}604605if (auto Err = Ctx->modifyPassConfig(*G, Config))606return Ctx->notifyFailed(std::move(Err));607608// Construct a JITLinker and run the link function.609MachOJITLinker_arm64::link(std::move(Ctx), std::move(G), std::move(Config));610}611612LinkGraphPassFunction createEHFrameSplitterPass_MachO_arm64() {613return DWARFRecordSectionSplitter("__TEXT,__eh_frame");614}615616LinkGraphPassFunction createEHFrameEdgeFixerPass_MachO_arm64() {617return EHFrameEdgeFixer("__TEXT,__eh_frame", aarch64::PointerSize,618aarch64::Pointer32, aarch64::Pointer64,619aarch64::Delta32, aarch64::Delta64,620aarch64::NegDelta32);621}622623} // end namespace jitlink624} // end namespace llvm625626627