1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-02-01 05:01:59 +01:00
llvm-mirror/lib/ExecutionEngine/Orc/MachOPlatform.cpp
Lang Hames efc9f3486a [ORC] Add support for resource tracking/removal (removable code).
This patch introduces new APIs to support resource tracking and removal in Orc.
It is intended as a thread-safe generalization of the removeModule concept from
OrcV1.

Clients can now create ResourceTracker objects (using
JITDylib::createResourceTracker) to track resources for each MaterializationUnit
(code, data, aliases, absolute symbols, etc.) added to the JIT. Every
MaterializationUnit will be associated with a ResourceTracker, and
ResourceTrackers can be re-used for multiple MaterializationUnits. Each JITDylib
has a default ResourceTracker that will be used for MaterializationUnits added
to that JITDylib if no ResourceTracker is explicitly specified.

Two operations can be performed on ResourceTrackers: transferTo and remove. The
transferTo operation transfers tracking of the resources to a different
ResourceTracker object, allowing ResourceTrackers to be merged to reduce
administrative overhead (the source tracker is invalidated in the process). The
remove operation removes all resources associated with a ResourceTracker,
including any symbols defined by MaterializationUnits associated with the
tracker, and also invalidates the tracker. These operations are thread safe, and
should work regardless of the the state of the MaterializationUnits. In the case
of resource transfer any existing resources associated with the source tracker
will be transferred to the destination tracker, and all future resources for
those units will be automatically associated with the destination tracker. In
the case of resource removal all already-allocated resources will be
deallocated, any if any program representations associated with the tracker have
not been compiled yet they will be destroyed. If any program representations are
currently being compiled then they will be prevented from completing: their
MaterializationResponsibility will return errors on any attempt to update the
JIT state.

Clients (usually Layer writers) wishing to track resources can implement the
ResourceManager API to receive notifications when ResourceTrackers are
transferred or removed. The MaterializationResponsibility::withResourceKeyDo
method can be used to create associations between the key for a ResourceTracker
and an allocated resource in a thread-safe way.

RTDyldObjectLinkingLayer and ObjectLinkingLayer are updated to use the
ResourceManager API to enable tracking and removal of memory allocated by the
JIT linker.

The new JITDylib::clear method can be used to trigger removal of every
ResourceTracker associated with the JITDylib (note that this will only
remove resources for the JITDylib, it does not run static destructors).

This patch includes unit tests showing basic usage. A follow-up patch will
update the Kaleidoscope and BuildingAJIT tutorial series to OrcV2 and will
use this API to release code associated with anonymous expressions.
2020-10-18 21:02:54 -07:00

490 lines
17 KiB
C++

//===------ MachOPlatform.cpp - Utilities for executing MachO in Orc ------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/MachOPlatform.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/ExecutionEngine/Orc/DebugUtils.h"
#include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/Debug.h"
#define DEBUG_TYPE "orc"
namespace {
struct objc_class;
struct objc_image_info;
struct objc_object;
struct objc_selector;
using Class = objc_class *;
using id = objc_object *;
using SEL = objc_selector *;
using ObjCMsgSendTy = id (*)(id, SEL, ...);
using ObjCReadClassPairTy = Class (*)(Class, const objc_image_info *);
using SelRegisterNameTy = SEL (*)(const char *);
enum class ObjCRegistrationAPI { Uninitialized, Unavailable, Initialized };
ObjCRegistrationAPI ObjCRegistrationAPIState =
ObjCRegistrationAPI::Uninitialized;
ObjCMsgSendTy objc_msgSend = nullptr;
ObjCReadClassPairTy objc_readClassPair = nullptr;
SelRegisterNameTy sel_registerName = nullptr;
} // end anonymous namespace
namespace llvm {
namespace orc {
template <typename FnTy>
static Error setUpObjCRegAPIFunc(FnTy &Target, sys::DynamicLibrary &LibObjC,
const char *Name) {
if (void *Addr = LibObjC.getAddressOfSymbol(Name))
Target = reinterpret_cast<FnTy>(Addr);
else
return make_error<StringError>(
(Twine("Could not find address for ") + Name).str(),
inconvertibleErrorCode());
return Error::success();
}
Error enableObjCRegistration(const char *PathToLibObjC) {
// If we've already tried to initialize then just bail out.
if (ObjCRegistrationAPIState != ObjCRegistrationAPI::Uninitialized)
return Error::success();
ObjCRegistrationAPIState = ObjCRegistrationAPI::Unavailable;
std::string ErrMsg;
auto LibObjC =
sys::DynamicLibrary::getPermanentLibrary(PathToLibObjC, &ErrMsg);
if (!LibObjC.isValid())
return make_error<StringError>(std::move(ErrMsg), inconvertibleErrorCode());
if (auto Err = setUpObjCRegAPIFunc(objc_msgSend, LibObjC, "objc_msgSend"))
return Err;
if (auto Err = setUpObjCRegAPIFunc(objc_readClassPair, LibObjC,
"objc_readClassPair"))
return Err;
if (auto Err =
setUpObjCRegAPIFunc(sel_registerName, LibObjC, "sel_registerName"))
return Err;
ObjCRegistrationAPIState = ObjCRegistrationAPI::Initialized;
return Error::success();
}
bool objCRegistrationEnabled() {
return ObjCRegistrationAPIState == ObjCRegistrationAPI::Initialized;
}
void MachOJITDylibInitializers::runModInits() const {
for (const auto &ModInit : ModInitSections) {
for (uint64_t I = 0; I != ModInit.NumPtrs; ++I) {
auto *InitializerAddr = jitTargetAddressToPointer<uintptr_t *>(
ModInit.Address + (I * sizeof(uintptr_t)));
auto *Initializer =
jitTargetAddressToFunction<void (*)()>(*InitializerAddr);
Initializer();
}
}
}
void MachOJITDylibInitializers::registerObjCSelectors() const {
assert(objCRegistrationEnabled() && "ObjC registration not enabled.");
for (const auto &ObjCSelRefs : ObjCSelRefsSections) {
for (uint64_t I = 0; I != ObjCSelRefs.NumPtrs; ++I) {
auto SelEntryAddr = ObjCSelRefs.Address + (I * sizeof(uintptr_t));
const auto *SelName =
*jitTargetAddressToPointer<const char **>(SelEntryAddr);
auto Sel = sel_registerName(SelName);
*jitTargetAddressToPointer<SEL *>(SelEntryAddr) = Sel;
}
}
}
Error MachOJITDylibInitializers::registerObjCClasses() const {
assert(objCRegistrationEnabled() && "ObjC registration not enabled.");
struct ObjCClassCompiled {
void *Metaclass;
void *Parent;
void *Cache1;
void *Cache2;
void *Data;
};
auto *ImageInfo =
jitTargetAddressToPointer<const objc_image_info *>(ObjCImageInfoAddr);
auto ClassSelector = sel_registerName("class");
for (const auto &ObjCClassList : ObjCClassListSections) {
for (uint64_t I = 0; I != ObjCClassList.NumPtrs; ++I) {
auto ClassPtrAddr = ObjCClassList.Address + (I * sizeof(uintptr_t));
auto Cls = *jitTargetAddressToPointer<Class *>(ClassPtrAddr);
auto *ClassCompiled =
*jitTargetAddressToPointer<ObjCClassCompiled **>(ClassPtrAddr);
objc_msgSend(reinterpret_cast<id>(ClassCompiled->Parent), ClassSelector);
auto Registered = objc_readClassPair(Cls, ImageInfo);
// FIXME: Improve diagnostic by reporting the failed class's name.
if (Registered != Cls)
return make_error<StringError>("Unable to register Objective-C class",
inconvertibleErrorCode());
}
}
return Error::success();
}
MachOPlatform::MachOPlatform(
ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
std::unique_ptr<MemoryBuffer> StandardSymbolsObject)
: ES(ES), ObjLinkingLayer(ObjLinkingLayer),
StandardSymbolsObject(std::move(StandardSymbolsObject)) {
ObjLinkingLayer.addPlugin(std::make_unique<InitScraperPlugin>(*this));
}
Error MachOPlatform::setupJITDylib(JITDylib &JD) {
auto ObjBuffer = MemoryBuffer::getMemBuffer(
StandardSymbolsObject->getMemBufferRef(), false);
return ObjLinkingLayer.add(JD, std::move(ObjBuffer));
}
Error MachOPlatform::notifyAdding(ResourceTracker &RT,
const MaterializationUnit &MU) {
auto &JD = RT.getJITDylib();
const auto &InitSym = MU.getInitializerSymbol();
if (!InitSym)
return Error::success();
RegisteredInitSymbols[&JD].add(InitSym,
SymbolLookupFlags::WeaklyReferencedSymbol);
LLVM_DEBUG({
dbgs() << "MachOPlatform: Registered init symbol " << *InitSym << " for MU "
<< MU.getName() << "\n";
});
return Error::success();
}
Error MachOPlatform::notifyRemoving(ResourceTracker &RT) {
llvm_unreachable("Not supported yet");
}
Expected<MachOPlatform::InitializerSequence>
MachOPlatform::getInitializerSequence(JITDylib &JD) {
LLVM_DEBUG({
dbgs() << "MachOPlatform: Building initializer sequence for "
<< JD.getName() << "\n";
});
std::vector<JITDylibSP> DFSLinkOrder;
while (true) {
DenseMap<JITDylib *, SymbolLookupSet> NewInitSymbols;
ES.runSessionLocked([&]() {
DFSLinkOrder = JD.getDFSLinkOrder();
for (auto &InitJD : DFSLinkOrder) {
auto RISItr = RegisteredInitSymbols.find(InitJD.get());
if (RISItr != RegisteredInitSymbols.end()) {
NewInitSymbols[InitJD.get()] = std::move(RISItr->second);
RegisteredInitSymbols.erase(RISItr);
}
}
});
if (NewInitSymbols.empty())
break;
LLVM_DEBUG({
dbgs() << "MachOPlatform: Issuing lookups for new init symbols: "
"(lookup may require multiple rounds)\n";
for (auto &KV : NewInitSymbols)
dbgs() << " \"" << KV.first->getName() << "\": " << KV.second << "\n";
});
// Outside the lock, issue the lookup.
if (auto R = lookupInitSymbols(JD.getExecutionSession(), NewInitSymbols))
; // Nothing to do in the success case.
else
return R.takeError();
}
LLVM_DEBUG({
dbgs() << "MachOPlatform: Init symbol lookup complete, building init "
"sequence\n";
});
// Lock again to collect the initializers.
InitializerSequence FullInitSeq;
{
std::lock_guard<std::mutex> Lock(InitSeqsMutex);
for (auto &InitJD : reverse(DFSLinkOrder)) {
LLVM_DEBUG({
dbgs() << "MachOPlatform: Appending inits for \"" << InitJD->getName()
<< "\" to sequence\n";
});
auto ISItr = InitSeqs.find(InitJD.get());
if (ISItr != InitSeqs.end()) {
FullInitSeq.emplace_back(InitJD.get(), std::move(ISItr->second));
InitSeqs.erase(ISItr);
}
}
}
return FullInitSeq;
}
Expected<MachOPlatform::DeinitializerSequence>
MachOPlatform::getDeinitializerSequence(JITDylib &JD) {
std::vector<JITDylibSP> DFSLinkOrder = JD.getDFSLinkOrder();
DeinitializerSequence FullDeinitSeq;
{
std::lock_guard<std::mutex> Lock(InitSeqsMutex);
for (auto &DeinitJD : DFSLinkOrder) {
FullDeinitSeq.emplace_back(DeinitJD.get(), MachOJITDylibDeinitializers());
}
}
return FullDeinitSeq;
}
void MachOPlatform::registerInitInfo(
JITDylib &JD, JITTargetAddress ObjCImageInfoAddr,
MachOJITDylibInitializers::SectionExtent ModInits,
MachOJITDylibInitializers::SectionExtent ObjCSelRefs,
MachOJITDylibInitializers::SectionExtent ObjCClassList) {
std::lock_guard<std::mutex> Lock(InitSeqsMutex);
auto &InitSeq = InitSeqs[&JD];
InitSeq.setObjCImageInfoAddr(ObjCImageInfoAddr);
if (ModInits.Address)
InitSeq.addModInitsSection(std::move(ModInits));
if (ObjCSelRefs.Address)
InitSeq.addObjCSelRefsSection(std::move(ObjCSelRefs));
if (ObjCClassList.Address)
InitSeq.addObjCClassListSection(std::move(ObjCClassList));
}
static Expected<MachOJITDylibInitializers::SectionExtent>
getSectionExtent(jitlink::LinkGraph &G, StringRef SectionName) {
auto *Sec = G.findSectionByName(SectionName);
if (!Sec)
return MachOJITDylibInitializers::SectionExtent();
jitlink::SectionRange R(*Sec);
if (R.getSize() % G.getPointerSize() != 0)
return make_error<StringError>(SectionName + " section size is not a "
"multiple of the pointer size",
inconvertibleErrorCode());
return MachOJITDylibInitializers::SectionExtent(
R.getStart(), R.getSize() / G.getPointerSize());
}
void MachOPlatform::InitScraperPlugin::modifyPassConfig(
MaterializationResponsibility &MR, const Triple &TT,
jitlink::PassConfiguration &Config) {
if (!MR.getInitializerSymbol())
return;
Config.PrePrunePasses.push_back([this, &MR](jitlink::LinkGraph &G) -> Error {
JITLinkSymbolVector InitSectionSymbols;
preserveInitSectionIfPresent(InitSectionSymbols, G, "__mod_init_func");
preserveInitSectionIfPresent(InitSectionSymbols, G, "__objc_selrefs");
preserveInitSectionIfPresent(InitSectionSymbols, G, "__objc_classlist");
if (!InitSectionSymbols.empty()) {
std::lock_guard<std::mutex> Lock(InitScraperMutex);
InitSymbolDeps[&MR] = std::move(InitSectionSymbols);
}
if (auto Err = processObjCImageInfo(G, MR))
return Err;
return Error::success();
});
Config.PostFixupPasses.push_back([this, &JD = MR.getTargetJITDylib()](
jitlink::LinkGraph &G) -> Error {
MachOJITDylibInitializers::SectionExtent ModInits, ObjCSelRefs,
ObjCClassList;
JITTargetAddress ObjCImageInfoAddr = 0;
if (auto *ObjCImageInfoSec = G.findSectionByName("__objc_image_info")) {
if (auto Addr = jitlink::SectionRange(*ObjCImageInfoSec).getStart())
ObjCImageInfoAddr = Addr;
}
// Record __mod_init_func.
if (auto ModInitsOrErr = getSectionExtent(G, "__mod_init_func"))
ModInits = std::move(*ModInitsOrErr);
else
return ModInitsOrErr.takeError();
// Record __objc_selrefs.
if (auto ObjCSelRefsOrErr = getSectionExtent(G, "__objc_selrefs"))
ObjCSelRefs = std::move(*ObjCSelRefsOrErr);
else
return ObjCSelRefsOrErr.takeError();
// Record __objc_classlist.
if (auto ObjCClassListOrErr = getSectionExtent(G, "__objc_classlist"))
ObjCClassList = std::move(*ObjCClassListOrErr);
else
return ObjCClassListOrErr.takeError();
// Dump the scraped inits.
LLVM_DEBUG({
dbgs() << "MachOPlatform: Scraped " << G.getName() << " init sections:\n";
dbgs() << " __objc_selrefs: ";
if (ObjCSelRefs.NumPtrs)
dbgs() << ObjCSelRefs.NumPtrs << " pointer(s) at "
<< formatv("{0:x16}", ObjCSelRefs.Address) << "\n";
else
dbgs() << "none\n";
dbgs() << " __objc_classlist: ";
if (ObjCClassList.NumPtrs)
dbgs() << ObjCClassList.NumPtrs << " pointer(s) at "
<< formatv("{0:x16}", ObjCClassList.Address) << "\n";
else
dbgs() << "none\n";
dbgs() << " __mod_init_func: ";
if (ModInits.NumPtrs)
dbgs() << ModInits.NumPtrs << " pointer(s) at "
<< formatv("{0:x16}", ModInits.Address) << "\n";
else
dbgs() << "none\n";
});
MP.registerInitInfo(JD, ObjCImageInfoAddr, std::move(ModInits),
std::move(ObjCSelRefs), std::move(ObjCClassList));
return Error::success();
});
}
ObjectLinkingLayer::Plugin::LocalDependenciesMap
MachOPlatform::InitScraperPlugin::getSyntheticSymbolLocalDependencies(
MaterializationResponsibility &MR) {
std::lock_guard<std::mutex> Lock(InitScraperMutex);
auto I = InitSymbolDeps.find(&MR);
if (I != InitSymbolDeps.end()) {
LocalDependenciesMap Result;
Result[MR.getInitializerSymbol()] = std::move(I->second);
InitSymbolDeps.erase(&MR);
return Result;
}
return LocalDependenciesMap();
}
void MachOPlatform::InitScraperPlugin::preserveInitSectionIfPresent(
JITLinkSymbolVector &Symbols, jitlink::LinkGraph &G,
StringRef SectionName) {
if (auto *Sec = G.findSectionByName(SectionName)) {
auto SecBlocks = Sec->blocks();
if (!llvm::empty(SecBlocks))
Symbols.push_back(
&G.addAnonymousSymbol(**SecBlocks.begin(), 0, 0, false, true));
}
}
Error MachOPlatform::InitScraperPlugin::processObjCImageInfo(
jitlink::LinkGraph &G, MaterializationResponsibility &MR) {
// If there's an ObjC imagine info then either
// (1) It's the first __objc_imageinfo we've seen in this JITDylib. In
// this case we name and record it.
// OR
// (2) We already have a recorded __objc_imageinfo for this JITDylib,
// in which case we just verify it.
auto *ObjCImageInfo = G.findSectionByName("__objc_imageinfo");
if (!ObjCImageInfo)
return Error::success();
auto ObjCImageInfoBlocks = ObjCImageInfo->blocks();
// Check that the section is not empty if present.
if (llvm::empty(ObjCImageInfoBlocks))
return make_error<StringError>("Empty __objc_imageinfo section in " +
G.getName(),
inconvertibleErrorCode());
// Check that there's only one block in the section.
if (std::next(ObjCImageInfoBlocks.begin()) != ObjCImageInfoBlocks.end())
return make_error<StringError>("Multiple blocks in __objc_imageinfo "
"section in " +
G.getName(),
inconvertibleErrorCode());
// Check that the __objc_imageinfo section is unreferenced.
// FIXME: We could optimize this check if Symbols had a ref-count.
for (auto &Sec : G.sections()) {
if (&Sec != ObjCImageInfo)
for (auto *B : Sec.blocks())
for (auto &E : B->edges())
if (E.getTarget().isDefined() &&
&E.getTarget().getBlock().getSection() == ObjCImageInfo)
return make_error<StringError>("__objc_imageinfo is referenced "
"within file " +
G.getName(),
inconvertibleErrorCode());
}
auto &ObjCImageInfoBlock = **ObjCImageInfoBlocks.begin();
auto *ObjCImageInfoData = ObjCImageInfoBlock.getContent().data();
auto Version = support::endian::read32(ObjCImageInfoData, G.getEndianness());
auto Flags =
support::endian::read32(ObjCImageInfoData + 4, G.getEndianness());
// Lock the mutex while we verify / update the ObjCImageInfos map.
std::lock_guard<std::mutex> Lock(InitScraperMutex);
auto ObjCImageInfoItr = ObjCImageInfos.find(&MR.getTargetJITDylib());
if (ObjCImageInfoItr != ObjCImageInfos.end()) {
// We've already registered an __objc_imageinfo section. Verify the
// content of this new section matches, then delete it.
if (ObjCImageInfoItr->second.first != Version)
return make_error<StringError>(
"ObjC version in " + G.getName() +
" does not match first registered version",
inconvertibleErrorCode());
if (ObjCImageInfoItr->second.second != Flags)
return make_error<StringError>("ObjC flags in " + G.getName() +
" do not match first registered flags",
inconvertibleErrorCode());
// __objc_imageinfo is valid. Delete the block.
for (auto *S : ObjCImageInfo->symbols())
G.removeDefinedSymbol(*S);
G.removeBlock(ObjCImageInfoBlock);
} else {
// We haven't registered an __objc_imageinfo section yet. Register and
// move on. The section should already be marked no-dead-strip.
ObjCImageInfos[&MR.getTargetJITDylib()] = std::make_pair(Version, Flags);
}
return Error::success();
}
} // End namespace orc.
} // End namespace llvm.