//===----- MachOLinkGraphBuilder.h - MachO LinkGraph builder ----*- 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 // //===----------------------------------------------------------------------===// // // Generic MachO LinkGraph building code. // //===----------------------------------------------------------------------===// #ifndef LIB_EXECUTIONENGINE_JITLINK_MACHOLINKGRAPHBUILDER_H #define LIB_EXECUTIONENGINE_JITLINK_MACHOLINKGRAPHBUILDER_H #include "llvm/ExecutionEngine/JITLink/JITLink.h" #include "EHFrameSupportImpl.h" #include "JITLinkGeneric.h" #include "llvm/Object/MachO.h" #include namespace llvm { namespace jitlink { class MachOLinkGraphBuilder { public: virtual ~MachOLinkGraphBuilder(); Expected> buildGraph(); protected: class MachOEHFrameBinaryParser : public EHFrameBinaryParser { public: MachOEHFrameBinaryParser(MachOLinkGraphBuilder &Builder, JITTargetAddress EHFrameAddress, StringRef EHFrameContent, Section &EHFrameSection, uint64_t CIEAlignment, uint64_t FDEAlignment, Edge::Kind FDEToCIERelocKind, Edge::Kind FDEToTargetRelocKind) : EHFrameBinaryParser(EHFrameAddress, EHFrameContent, Builder.getGraph().getPointerSize(), Builder.getGraph().getEndianness()), Builder(Builder), EHFrameSection(EHFrameSection), CIEAlignment(CIEAlignment), FDEAlignment(FDEAlignment), FDEToCIERelocKind(FDEToCIERelocKind), FDEToTargetRelocKind(FDEToTargetRelocKind) {} Symbol *getSymbolAtAddress(JITTargetAddress Address) override { if (auto *Sym = Builder.getSymbolByAddress(Address)) if (Sym->getAddress() == Address) return Sym; return nullptr; } Symbol &createCIERecord(JITTargetAddress RecordAddr, StringRef RecordContent) override { auto &G = Builder.getGraph(); auto &B = G.createContentBlock(EHFrameSection, RecordContent, RecordAddr, CIEAlignment, 0); auto &CIESymbol = G.addAnonymousSymbol(B, 0, RecordContent.size(), false, false); Builder.setCanonicalSymbol(CIESymbol); return CIESymbol; } Expected createFDERecord(JITTargetAddress RecordAddr, StringRef RecordContent, Symbol &CIE, size_t CIEOffset, Symbol &Func, size_t FuncOffset, Symbol *LSDA, size_t LSDAOffset) override { auto &G = Builder.getGraph(); auto &B = G.createContentBlock(EHFrameSection, RecordContent, RecordAddr, FDEAlignment, 0); // Add edges to CIE, Func, and (conditionally) LSDA. B.addEdge(FDEToCIERelocKind, CIEOffset, CIE, 0); B.addEdge(FDEToTargetRelocKind, FuncOffset, Func, 0); if (LSDA) B.addEdge(FDEToTargetRelocKind, LSDAOffset, *LSDA, 0); auto &FDESymbol = G.addAnonymousSymbol(B, 0, RecordContent.size(), false, false); // Add a keep-alive relocation from the function to the FDE to ensure it // is not dead stripped. Func.getBlock().addEdge(Edge::KeepAlive, 0, FDESymbol, 0); return FDESymbol; } private: MachOLinkGraphBuilder &Builder; Section &EHFrameSection; uint64_t CIEAlignment; uint64_t FDEAlignment; Edge::Kind FDEToCIERelocKind; Edge::Kind FDEToTargetRelocKind; }; struct NormalizedSymbol { friend class MachOLinkGraphBuilder; private: NormalizedSymbol(Optional Name, uint64_t Value, uint8_t Type, uint8_t Sect, uint16_t Desc, Linkage L, Scope S) : Name(Name), Value(Value), Type(Type), Sect(Sect), Desc(Desc), L(L), S(S) { assert((!Name || !Name->empty()) && "Name must be none or non-empty"); } public: NormalizedSymbol(const NormalizedSymbol &) = delete; NormalizedSymbol &operator=(const NormalizedSymbol &) = delete; NormalizedSymbol(NormalizedSymbol &&) = delete; NormalizedSymbol &operator=(NormalizedSymbol &&) = delete; Optional Name; uint64_t Value = 0; uint8_t Type = 0; uint8_t Sect = 0; uint16_t Desc = 0; Linkage L = Linkage::Strong; Scope S = Scope::Default; Symbol *GraphSymbol = nullptr; }; class NormalizedSection { friend class MachOLinkGraphBuilder; private: NormalizedSection() = default; public: Section *GraphSection = nullptr; uint64_t Address = 0; uint64_t Size = 0; uint64_t Alignment = 0; uint32_t Flags = 0; const char *Data = nullptr; }; using SectionParserFunction = std::function; MachOLinkGraphBuilder(const object::MachOObjectFile &Obj); LinkGraph &getGraph() const { return *G; } const object::MachOObjectFile &getObject() const { return Obj; } void addCustomSectionParser(StringRef SectionName, SectionParserFunction Parse); virtual Error addRelocations() = 0; /// Create a symbol. template NormalizedSymbol &createNormalizedSymbol(ArgTs &&... Args) { NormalizedSymbol *Sym = reinterpret_cast( Allocator.Allocate()); new (Sym) NormalizedSymbol(std::forward(Args)...); return *Sym; } /// Index is zero-based (MachO section indexes are usually one-based) and /// assumed to be in-range. Client is responsible for checking. NormalizedSection &getSectionByIndex(unsigned Index) { auto I = IndexToSection.find(Index); assert(I != IndexToSection.end() && "No section recorded at index"); return I->second; } /// Try to get the section at the given index. Will return an error if the /// given index is out of range, or if no section has been added for the given /// index. Expected findSectionByIndex(unsigned Index) { auto I = IndexToSection.find(Index); if (I == IndexToSection.end()) return make_error("No section recorded for index " + formatv("{0:u}", Index)); return I->second; } /// Try to get the symbol at the given index. Will return an error if the /// given index is out of range, or if no symbol has been added for the given /// index. Expected findSymbolByIndex(uint64_t Index) { if (Index >= IndexToSymbol.size()) return make_error("Symbol index out of range"); auto *Sym = IndexToSymbol[Index]; if (!Sym) return make_error("No symbol at index " + formatv("{0:u}", Index)); return *Sym; } /// Returns the symbol with the highest address not greater than the search /// address, or null if no such symbol exists. Symbol *getSymbolByAddress(JITTargetAddress Address) { auto I = AddrToCanonicalSymbol.upper_bound(Address); if (I == AddrToCanonicalSymbol.begin()) return nullptr; return std::prev(I)->second; } /// Returns the symbol with the highest address not greater than the search /// address, or an error if no such symbol exists. Expected findSymbolByAddress(JITTargetAddress Address) { auto *Sym = getSymbolByAddress(Address); if (Sym) if (Address < Sym->getAddress() + Sym->getSize()) return *Sym; return make_error("No symbol covering address " + formatv("{0:x16}", Address)); } static Linkage getLinkage(uint16_t Desc); static Scope getScope(StringRef Name, uint8_t Type); static bool isAltEntry(const NormalizedSymbol &NSym); private: static unsigned getPointerSize(const object::MachOObjectFile &Obj); static support::endianness getEndianness(const object::MachOObjectFile &Obj); void setCanonicalSymbol(Symbol &Sym) { auto *&CanonicalSymEntry = AddrToCanonicalSymbol[Sym.getAddress()]; // There should be no symbol at this address, or, if there is, // it should be a zero-sized symbol from an empty section (which // we can safely override). assert((!CanonicalSymEntry || CanonicalSymEntry->getSize() == 0) && "Duplicate canonical symbol at address"); CanonicalSymEntry = &Sym; } Section &getCommonSection(); void addSectionStartSymAndBlock(Section &GraphSec, uint64_t Address, const char *Data, uint64_t Size, uint32_t Alignment, bool IsLive); Error createNormalizedSections(); Error createNormalizedSymbols(); /// Create graph blocks and symbols for externals, absolutes, commons and /// all defined symbols in sections without custom parsers. Error graphifyRegularSymbols(); /// Create graph blocks and symbols for all sections. Error graphifySectionsWithCustomParsers(); // Put the BumpPtrAllocator first so that we don't free any of the underlying // memory until the Symbol/Addressable destructors have been run. BumpPtrAllocator Allocator; const object::MachOObjectFile &Obj; std::unique_ptr G; DenseMap IndexToSection; Section *CommonSection = nullptr; DenseMap IndexToSymbol; std::map AddrToCanonicalSymbol; StringMap CustomSectionParserFunctions; }; } // end namespace jitlink } // end namespace llvm #endif // LIB_EXECUTIONENGINE_JITLINK_MACHOLINKGRAPHBUILDER_H