Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp
35271 views
//===---- MachO_x86_64.cpp -JIT linker implementation for MachO/x86-64 ----===//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/x86-64 jit-link implementation.9//10//===----------------------------------------------------------------------===//1112#include "llvm/ExecutionEngine/JITLink/MachO_x86_64.h"13#include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h"14#include "llvm/ExecutionEngine/JITLink/x86_64.h"1516#include "DefineExternalSectionStartAndEndSymbols.h"17#include "MachOLinkGraphBuilder.h"1819#define DEBUG_TYPE "jitlink"2021using namespace llvm;22using namespace llvm::jitlink;2324namespace {2526class MachOLinkGraphBuilder_x86_64 : public MachOLinkGraphBuilder {27public:28MachOLinkGraphBuilder_x86_64(const object::MachOObjectFile &Obj,29SubtargetFeatures Features)30: MachOLinkGraphBuilder(Obj, Triple("x86_64-apple-darwin"),31std::move(Features), x86_64::getEdgeKindName) {}3233private:34enum MachONormalizedRelocationType : unsigned {35MachOBranch32,36MachOPointer32,37MachOPointer64,38MachOPointer64Anon,39MachOPCRel32,40MachOPCRel32Minus1,41MachOPCRel32Minus2,42MachOPCRel32Minus4,43MachOPCRel32Anon,44MachOPCRel32Minus1Anon,45MachOPCRel32Minus2Anon,46MachOPCRel32Minus4Anon,47MachOPCRel32GOTLoad,48MachOPCRel32GOT,49MachOPCRel32TLV,50MachOSubtractor32,51MachOSubtractor64,52};5354static Expected<MachONormalizedRelocationType>55getRelocKind(const MachO::relocation_info &RI) {56switch (RI.r_type) {57case MachO::X86_64_RELOC_UNSIGNED:58if (!RI.r_pcrel) {59if (RI.r_length == 3)60return RI.r_extern ? MachOPointer64 : MachOPointer64Anon;61else if (RI.r_extern && RI.r_length == 2)62return MachOPointer32;63}64break;65case MachO::X86_64_RELOC_SIGNED:66if (RI.r_pcrel && RI.r_length == 2)67return RI.r_extern ? MachOPCRel32 : MachOPCRel32Anon;68break;69case MachO::X86_64_RELOC_BRANCH:70if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)71return MachOBranch32;72break;73case MachO::X86_64_RELOC_GOT_LOAD:74if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)75return MachOPCRel32GOTLoad;76break;77case MachO::X86_64_RELOC_GOT:78if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)79return MachOPCRel32GOT;80break;81case MachO::X86_64_RELOC_SUBTRACTOR:82if (!RI.r_pcrel && RI.r_extern) {83if (RI.r_length == 2)84return MachOSubtractor32;85else if (RI.r_length == 3)86return MachOSubtractor64;87}88break;89case MachO::X86_64_RELOC_SIGNED_1:90if (RI.r_pcrel && RI.r_length == 2)91return RI.r_extern ? MachOPCRel32Minus1 : MachOPCRel32Minus1Anon;92break;93case MachO::X86_64_RELOC_SIGNED_2:94if (RI.r_pcrel && RI.r_length == 2)95return RI.r_extern ? MachOPCRel32Minus2 : MachOPCRel32Minus2Anon;96break;97case MachO::X86_64_RELOC_SIGNED_4:98if (RI.r_pcrel && RI.r_length == 2)99return RI.r_extern ? MachOPCRel32Minus4 : MachOPCRel32Minus4Anon;100break;101case MachO::X86_64_RELOC_TLV:102if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)103return MachOPCRel32TLV;104break;105}106107return make_error<JITLinkError>(108"Unsupported x86-64 relocation: address=" +109formatv("{0:x8}", RI.r_address) +110", symbolnum=" + formatv("{0:x6}", RI.r_symbolnum) +111", kind=" + formatv("{0:x1}", RI.r_type) +112", pc_rel=" + (RI.r_pcrel ? "true" : "false") +113", extern=" + (RI.r_extern ? "true" : "false") +114", length=" + formatv("{0:d}", RI.r_length));115}116117using PairRelocInfo = std::tuple<Edge::Kind, Symbol *, uint64_t>;118119// Parses paired SUBTRACTOR/UNSIGNED relocations and, on success,120// returns the edge kind and addend to be used.121Expected<PairRelocInfo> parsePairRelocation(122Block &BlockToFix, MachONormalizedRelocationType SubtractorKind,123const MachO::relocation_info &SubRI, orc::ExecutorAddr FixupAddress,124const char *FixupContent, object::relocation_iterator &UnsignedRelItr,125object::relocation_iterator &RelEnd) {126using namespace support;127128assert(((SubtractorKind == MachOSubtractor32 && SubRI.r_length == 2) ||129(SubtractorKind == MachOSubtractor64 && SubRI.r_length == 3)) &&130"Subtractor kind should match length");131assert(SubRI.r_extern && "SUBTRACTOR reloc symbol should be extern");132assert(!SubRI.r_pcrel && "SUBTRACTOR reloc should not be PCRel");133134if (UnsignedRelItr == RelEnd)135return make_error<JITLinkError>("x86_64 SUBTRACTOR without paired "136"UNSIGNED relocation");137138auto UnsignedRI = getRelocationInfo(UnsignedRelItr);139140if (SubRI.r_address != UnsignedRI.r_address)141return make_error<JITLinkError>("x86_64 SUBTRACTOR and paired UNSIGNED "142"point to different addresses");143144if (SubRI.r_length != UnsignedRI.r_length)145return make_error<JITLinkError>("length of x86_64 SUBTRACTOR and paired "146"UNSIGNED reloc must match");147148Symbol *FromSymbol;149if (auto FromSymbolOrErr = findSymbolByIndex(SubRI.r_symbolnum))150FromSymbol = FromSymbolOrErr->GraphSymbol;151else152return FromSymbolOrErr.takeError();153154// Read the current fixup value.155uint64_t FixupValue = 0;156if (SubRI.r_length == 3)157FixupValue = *(const little64_t *)FixupContent;158else159FixupValue = *(const little32_t *)FixupContent;160161// Find 'ToSymbol' using symbol number or address, depending on whether the162// paired UNSIGNED relocation is extern.163Symbol *ToSymbol = nullptr;164if (UnsignedRI.r_extern) {165// Find target symbol by symbol index.166if (auto ToSymbolOrErr = findSymbolByIndex(UnsignedRI.r_symbolnum))167ToSymbol = ToSymbolOrErr->GraphSymbol;168else169return ToSymbolOrErr.takeError();170} else {171auto ToSymbolSec = findSectionByIndex(UnsignedRI.r_symbolnum - 1);172if (!ToSymbolSec)173return ToSymbolSec.takeError();174ToSymbol = getSymbolByAddress(*ToSymbolSec, ToSymbolSec->Address);175assert(ToSymbol && "No symbol for section");176FixupValue -= ToSymbol->getAddress().getValue();177}178179Edge::Kind DeltaKind;180Symbol *TargetSymbol;181uint64_t Addend;182183bool FixingFromSymbol = true;184if (&BlockToFix == &FromSymbol->getAddressable()) {185if (LLVM_UNLIKELY(&BlockToFix == &ToSymbol->getAddressable())) {186// From and To are symbols in the same block. Decide direction by offset187// instead.188if (ToSymbol->getAddress() > FixupAddress)189FixingFromSymbol = true;190else if (FromSymbol->getAddress() > FixupAddress)191FixingFromSymbol = false;192else193FixingFromSymbol = FromSymbol->getAddress() >= ToSymbol->getAddress();194} else195FixingFromSymbol = true;196} else {197if (&BlockToFix == &ToSymbol->getAddressable())198FixingFromSymbol = false;199else {200// BlockToFix was neither FromSymbol nor ToSymbol.201return make_error<JITLinkError>("SUBTRACTOR relocation must fix up "202"either 'A' or 'B' (or a symbol in one "203"of their alt-entry groups)");204}205}206207if (FixingFromSymbol) {208TargetSymbol = ToSymbol;209DeltaKind = (SubRI.r_length == 3) ? x86_64::Delta64 : x86_64::Delta32;210Addend = FixupValue + (FixupAddress - FromSymbol->getAddress());211// FIXME: handle extern 'from'.212} else {213TargetSymbol = FromSymbol;214DeltaKind =215(SubRI.r_length == 3) ? x86_64::NegDelta64 : x86_64::NegDelta32;216Addend = FixupValue - (FixupAddress - ToSymbol->getAddress());217}218219return PairRelocInfo(DeltaKind, TargetSymbol, Addend);220}221222Error addRelocations() override {223using namespace support;224auto &Obj = getObject();225226LLVM_DEBUG(dbgs() << "Processing relocations:\n");227228for (const auto &S : Obj.sections()) {229230orc::ExecutorAddr SectionAddress(S.getAddress());231232// Skip relocations virtual sections.233if (S.isVirtual()) {234if (S.relocation_begin() != S.relocation_end())235return make_error<JITLinkError>("Virtual section contains "236"relocations");237continue;238}239240auto NSec =241findSectionByIndex(Obj.getSectionIndex(S.getRawDataRefImpl()));242if (!NSec)243return NSec.takeError();244245// Skip relocations for MachO sections without corresponding graph246// sections.247{248if (!NSec->GraphSection) {249LLVM_DEBUG({250dbgs() << " Skipping relocations for MachO section "251<< NSec->SegName << "/" << NSec->SectName252<< " which has no associated graph section\n";253});254continue;255}256}257258// Add relocations for section.259for (auto RelItr = S.relocation_begin(), RelEnd = S.relocation_end();260RelItr != RelEnd; ++RelItr) {261262MachO::relocation_info RI = getRelocationInfo(RelItr);263264// Find the address of the value to fix up.265auto FixupAddress = SectionAddress + (uint32_t)RI.r_address;266267LLVM_DEBUG({268dbgs() << " " << NSec->SectName << " + "269<< formatv("{0:x8}", RI.r_address) << ":\n";270});271272// Find the block that the fixup points to.273Block *BlockToFix = nullptr;274{275auto SymbolToFixOrErr = findSymbolByAddress(*NSec, FixupAddress);276if (!SymbolToFixOrErr)277return SymbolToFixOrErr.takeError();278BlockToFix = &SymbolToFixOrErr->getBlock();279}280281if (FixupAddress + orc::ExecutorAddrDiff(1ULL << RI.r_length) >282BlockToFix->getAddress() + BlockToFix->getContent().size())283return make_error<JITLinkError>(284"Relocation extends past end of fixup block");285286// Get a pointer to the fixup content.287const char *FixupContent = BlockToFix->getContent().data() +288(FixupAddress - BlockToFix->getAddress());289290size_t FixupOffset = FixupAddress - BlockToFix->getAddress();291292// The target symbol and addend will be populated by the switch below.293Symbol *TargetSymbol = nullptr;294uint64_t Addend = 0;295296// Validate the relocation kind.297auto MachORelocKind = getRelocKind(RI);298if (!MachORelocKind)299return MachORelocKind.takeError();300301Edge::Kind Kind = Edge::Invalid;302303switch (*MachORelocKind) {304case MachOBranch32:305if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))306TargetSymbol = TargetSymbolOrErr->GraphSymbol;307else308return TargetSymbolOrErr.takeError();309Addend = *(const little32_t *)FixupContent;310Kind = x86_64::BranchPCRel32;311break;312case MachOPCRel32:313if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))314TargetSymbol = TargetSymbolOrErr->GraphSymbol;315else316return TargetSymbolOrErr.takeError();317Addend = *(const little32_t *)FixupContent - 4;318Kind = x86_64::Delta32;319break;320case MachOPCRel32GOTLoad:321if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))322TargetSymbol = TargetSymbolOrErr->GraphSymbol;323else324return TargetSymbolOrErr.takeError();325Addend = *(const little32_t *)FixupContent;326Kind = x86_64::RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable;327if (FixupOffset < 3)328return make_error<JITLinkError>("GOTLD at invalid offset " +329formatv("{0}", FixupOffset));330break;331case MachOPCRel32GOT:332if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))333TargetSymbol = TargetSymbolOrErr->GraphSymbol;334else335return TargetSymbolOrErr.takeError();336Addend = *(const little32_t *)FixupContent - 4;337Kind = x86_64::RequestGOTAndTransformToDelta32;338break;339case MachOPCRel32TLV:340if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))341TargetSymbol = TargetSymbolOrErr->GraphSymbol;342else343return TargetSymbolOrErr.takeError();344Addend = *(const little32_t *)FixupContent;345Kind = x86_64::RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable;346if (FixupOffset < 3)347return make_error<JITLinkError>("TLV at invalid offset " +348formatv("{0}", FixupOffset));349break;350case MachOPointer32:351if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))352TargetSymbol = TargetSymbolOrErr->GraphSymbol;353else354return TargetSymbolOrErr.takeError();355Addend = *(const ulittle32_t *)FixupContent;356Kind = x86_64::Pointer32;357break;358case MachOPointer64:359if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))360TargetSymbol = TargetSymbolOrErr->GraphSymbol;361else362return TargetSymbolOrErr.takeError();363Addend = *(const ulittle64_t *)FixupContent;364Kind = x86_64::Pointer64;365break;366case MachOPointer64Anon: {367orc::ExecutorAddr TargetAddress(*(const ulittle64_t *)FixupContent);368auto TargetNSec = findSectionByIndex(RI.r_symbolnum - 1);369if (!TargetNSec)370return TargetNSec.takeError();371if (auto TargetSymbolOrErr =372findSymbolByAddress(*TargetNSec, TargetAddress))373TargetSymbol = &*TargetSymbolOrErr;374else375return TargetSymbolOrErr.takeError();376Addend = TargetAddress - TargetSymbol->getAddress();377Kind = x86_64::Pointer64;378break;379}380case MachOPCRel32Minus1:381case MachOPCRel32Minus2:382case MachOPCRel32Minus4:383if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))384TargetSymbol = TargetSymbolOrErr->GraphSymbol;385else386return TargetSymbolOrErr.takeError();387Addend = *(const little32_t *)FixupContent - 4;388Kind = x86_64::Delta32;389break;390case MachOPCRel32Anon: {391orc::ExecutorAddr TargetAddress(FixupAddress + 4 +392*(const little32_t *)FixupContent);393auto TargetNSec = findSectionByIndex(RI.r_symbolnum - 1);394if (!TargetNSec)395return TargetNSec.takeError();396if (auto TargetSymbolOrErr =397findSymbolByAddress(*TargetNSec, TargetAddress))398TargetSymbol = &*TargetSymbolOrErr;399else400return TargetSymbolOrErr.takeError();401Addend = TargetAddress - TargetSymbol->getAddress() - 4;402Kind = x86_64::Delta32;403break;404}405case MachOPCRel32Minus1Anon:406case MachOPCRel32Minus2Anon:407case MachOPCRel32Minus4Anon: {408orc::ExecutorAddrDiff Delta =4094 + orc::ExecutorAddrDiff(4101ULL << (*MachORelocKind - MachOPCRel32Minus1Anon));411orc::ExecutorAddr TargetAddress =412FixupAddress + Delta + *(const little32_t *)FixupContent;413auto TargetNSec = findSectionByIndex(RI.r_symbolnum - 1);414if (!TargetNSec)415return TargetNSec.takeError();416if (auto TargetSymbolOrErr =417findSymbolByAddress(*TargetNSec, TargetAddress))418TargetSymbol = &*TargetSymbolOrErr;419else420return TargetSymbolOrErr.takeError();421Addend = TargetAddress - TargetSymbol->getAddress() - Delta;422Kind = x86_64::Delta32;423break;424}425case MachOSubtractor32:426case MachOSubtractor64: {427// We use Delta32/Delta64 to represent SUBTRACTOR relocations.428// parsePairRelocation handles the paired reloc, and returns the429// edge kind to be used (either Delta32/Delta64, or430// NegDelta32/NegDelta64, depending on the direction of the431// subtraction) along with the addend.432auto PairInfo =433parsePairRelocation(*BlockToFix, *MachORelocKind, RI,434FixupAddress, FixupContent, ++RelItr, RelEnd);435if (!PairInfo)436return PairInfo.takeError();437std::tie(Kind, TargetSymbol, Addend) = *PairInfo;438assert(TargetSymbol && "No target symbol from parsePairRelocation?");439break;440}441}442443LLVM_DEBUG({444dbgs() << " ";445Edge GE(Kind, FixupAddress - BlockToFix->getAddress(), *TargetSymbol,446Addend);447printEdge(dbgs(), *BlockToFix, GE, x86_64::getEdgeKindName(Kind));448dbgs() << "\n";449});450BlockToFix->addEdge(Kind, FixupAddress - BlockToFix->getAddress(),451*TargetSymbol, Addend);452}453}454return Error::success();455}456};457458Error buildGOTAndStubs_MachO_x86_64(LinkGraph &G) {459x86_64::GOTTableManager GOT;460x86_64::PLTTableManager PLT(GOT);461visitExistingEdges(G, GOT, PLT);462return Error::success();463}464465} // namespace466467namespace llvm {468namespace jitlink {469470class MachOJITLinker_x86_64 : public JITLinker<MachOJITLinker_x86_64> {471friend class JITLinker<MachOJITLinker_x86_64>;472473public:474MachOJITLinker_x86_64(std::unique_ptr<JITLinkContext> Ctx,475std::unique_ptr<LinkGraph> G,476PassConfiguration PassConfig)477: JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}478479private:480Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {481return x86_64::applyFixup(G, B, E, nullptr);482}483};484485Expected<std::unique_ptr<LinkGraph>>486createLinkGraphFromMachOObject_x86_64(MemoryBufferRef ObjectBuffer) {487auto MachOObj = object::ObjectFile::createMachOObjectFile(ObjectBuffer);488if (!MachOObj)489return MachOObj.takeError();490491auto Features = (*MachOObj)->getFeatures();492if (!Features)493return Features.takeError();494495return MachOLinkGraphBuilder_x86_64(**MachOObj, std::move(*Features))496.buildGraph();497}498499void link_MachO_x86_64(std::unique_ptr<LinkGraph> G,500std::unique_ptr<JITLinkContext> Ctx) {501502PassConfiguration Config;503504if (Ctx->shouldAddDefaultTargetPasses(G->getTargetTriple())) {505// Add eh-frame passes.506Config.PrePrunePasses.push_back(createEHFrameSplitterPass_MachO_x86_64());507Config.PrePrunePasses.push_back(createEHFrameEdgeFixerPass_MachO_x86_64());508509// Add compact unwind splitter pass.510Config.PrePrunePasses.push_back(511CompactUnwindSplitter("__LD,__compact_unwind"));512513// Add a mark-live pass.514if (auto MarkLive = Ctx->getMarkLivePass(G->getTargetTriple()))515Config.PrePrunePasses.push_back(std::move(MarkLive));516else517Config.PrePrunePasses.push_back(markAllSymbolsLive);518519// Resolve any external section start / end symbols.520Config.PostAllocationPasses.push_back(521createDefineExternalSectionStartAndEndSymbolsPass(522identifyMachOSectionStartAndEndSymbols));523524// Add an in-place GOT/Stubs pass.525Config.PostPrunePasses.push_back(buildGOTAndStubs_MachO_x86_64);526527// Add GOT/Stubs optimizer pass.528Config.PreFixupPasses.push_back(x86_64::optimizeGOTAndStubAccesses);529}530531if (auto Err = Ctx->modifyPassConfig(*G, Config))532return Ctx->notifyFailed(std::move(Err));533534// Construct a JITLinker and run the link function.535MachOJITLinker_x86_64::link(std::move(Ctx), std::move(G), std::move(Config));536}537538LinkGraphPassFunction createEHFrameSplitterPass_MachO_x86_64() {539return DWARFRecordSectionSplitter("__TEXT,__eh_frame");540}541542LinkGraphPassFunction createEHFrameEdgeFixerPass_MachO_x86_64() {543return EHFrameEdgeFixer("__TEXT,__eh_frame", x86_64::PointerSize,544x86_64::Pointer32, x86_64::Pointer64, x86_64::Delta32,545x86_64::Delta64, x86_64::NegDelta32);546}547548} // end namespace jitlink549} // end namespace llvm550551552