From cdcc35476833eca4f4996256e3ca0b21ecc26ad8 Mon Sep 17 00:00:00 2001 From: Lang Hames Date: Mon, 26 Jul 2021 17:49:05 +1000 Subject: [PATCH] [ORC][ORC-RT] Add initial Objective-C and Swift support to MachOPlatform. This allows ORC to execute code containing Objective-C and Swift classes and methods (provided that the language runtime is loaded into the executor). --- compiler-rt/lib/orc/macho_platform.cpp | 143 ++++++++++++++++++- compiler-rt/lib/orc/macho_platform.h | 12 +- .../TestCases/Darwin/x86-64/trivial-objc-methods.S | 157 +++++++++++++++++++++ .../llvm/ExecutionEngine/Orc/MachOPlatform.h | 21 ++- llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp | 116 ++++++++++++++- llvm/lib/ExecutionEngine/Orc/Mangling.cpp | 37 +++-- 6 files changed, 456 insertions(+), 30 deletions(-) create mode 100644 compiler-rt/test/orc/TestCases/Darwin/x86-64/trivial-objc-methods.S diff --git a/compiler-rt/lib/orc/macho_platform.cpp b/compiler-rt/lib/orc/macho_platform.cpp index c84ff78..2a960fb 100644 --- a/compiler-rt/lib/orc/macho_platform.cpp +++ b/compiler-rt/lib/orc/macho_platform.cpp @@ -34,6 +34,36 @@ ORC_RT_JIT_DISPATCH_TAG(__orc_rt_macho_symbol_lookup_tag) extern "C" void __register_frame(const void *); extern "C" void __deregister_frame(const void *); +// Objective-C types. +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 *; + +// Objective-C registration functions. +// These are weakly imported. If the Objective-C runtime has not been loaded +// then code containing Objective-C sections will generate an error. +extern "C" id objc_msgSend(id, SEL, ...) ORC_RT_WEAK_IMPORT; +extern "C" Class objc_readClassPair(Class, + const objc_image_info *) ORC_RT_WEAK_IMPORT; +extern "C" SEL sel_registerName(const char *) ORC_RT_WEAK_IMPORT; + +// Swift types. +class ProtocolRecord; +class ProtocolConformanceRecord; + +extern "C" void +swift_registerProtocols(const ProtocolRecord *begin, + const ProtocolRecord *end) ORC_RT_WEAK_IMPORT; + +extern "C" void swift_registerProtocolConformances( + const ProtocolConformanceRecord *begin, + const ProtocolConformanceRecord *end) ORC_RT_WEAK_IMPORT; + namespace { template @@ -70,6 +100,108 @@ Error validatePointerSectionExtent(const char *SectionName, return Error::success(); } +Error registerObjCSelectors( + const std::vector &ObjCSelRefsSections, + const MachOJITDylibInitializers &MOJDIs) { + + if (ORC_RT_UNLIKELY(!sel_registerName)) + return make_error("sel_registerName is not available"); + + for (const auto &ObjCSelRefs : ObjCSelRefsSections) { + + if (auto Err = validatePointerSectionExtent("__objc_selrefs", ObjCSelRefs)) + return Err; + + fprintf(stderr, "Processing selrefs section at 0x%llx\n", + ObjCSelRefs.StartAddress.getValue()); + for (uintptr_t SelEntry : ObjCSelRefs.toSpan()) { + const char *SelName = reinterpret_cast(SelEntry); + fprintf(stderr, "Registering selector \"%s\"\n", SelName); + auto Sel = sel_registerName(SelName); + *reinterpret_cast(SelEntry) = Sel; + } + } + + return Error::success(); +} + +Error registerObjCClasses( + const std::vector &ObjCClassListSections, + const MachOJITDylibInitializers &MOJDIs) { + + if (ObjCClassListSections.empty()) + return Error::success(); + + if (ORC_RT_UNLIKELY(!objc_msgSend)) + return make_error("objc_msgSend is not available"); + if (ORC_RT_UNLIKELY(!objc_readClassPair)) + return make_error("objc_readClassPair is not available"); + + struct ObjCClassCompiled { + void *Metaclass; + void *Parent; + void *Cache1; + void *Cache2; + void *Data; + }; + + auto *ImageInfo = + MOJDIs.ObjCImageInfoAddress.toPtr(); + auto ClassSelector = sel_registerName("class"); + + for (const auto &ObjCClassList : ObjCClassListSections) { + + if (auto Err = + validatePointerSectionExtent("__objc_classlist", ObjCClassList)) + return Err; + + for (uintptr_t ClassPtr : ObjCClassList.toSpan()) { + auto *Cls = reinterpret_cast(ClassPtr); + auto *ClassCompiled = reinterpret_cast(ClassPtr); + objc_msgSend(reinterpret_cast(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("Unable to register Objective-C class"); + } + } + return Error::success(); +} + +Error registerSwift5Protocols( + const std::vector &Swift5ProtocolSections, + const MachOJITDylibInitializers &MOJDIs) { + + if (ORC_RT_UNLIKELY(!Swift5ProtocolSections.empty() && + !swift_registerProtocols)) + return make_error("swift_registerProtocols is not available"); + + for (const auto &Swift5Protocols : Swift5ProtocolSections) + swift_registerProtocols( + Swift5Protocols.StartAddress.toPtr(), + Swift5Protocols.EndAddress.toPtr()); + + return Error::success(); +} + +Error registerSwift5ProtocolConformances( + const std::vector &Swift5ProtocolConformanceSections, + const MachOJITDylibInitializers &MOJDIs) { + + if (ORC_RT_UNLIKELY(!Swift5ProtocolConformanceSections.empty() && + !swift_registerProtocolConformances)) + return make_error( + "swift_registerProtocolConformances is not available"); + + for (const auto &ProtoConfSec : Swift5ProtocolConformanceSections) + swift_registerProtocolConformances( + ProtoConfSec.StartAddress.toPtr(), + ProtoConfSec.EndAddress.toPtr()); + + return Error::success(); +} + Error runModInits(const std::vector &ModInitsSections, const MachOJITDylibInitializers &MOJDIs) { @@ -156,8 +288,12 @@ private: using InitSectionHandler = Error (*)(const std::vector &Sections, const MachOJITDylibInitializers &MOJDIs); - const std::vector> InitSections = { - {"__DATA,__mod_init_func", runModInits}}; + const std::vector> InitSections = + {{"__DATA,__objc_selrefs", registerObjCSelectors}, + {"__DATA,__objc_classlist", registerObjCClasses}, + {"__TEXT,__swift5_protos", registerSwift5Protocols}, + {"__TEXT,__swift5_proto", registerSwift5ProtocolConformances}, + {"__DATA,__mod_init_func", runModInits}}; // FIXME: Move to thread-state. std::string DLFcnError; @@ -404,8 +540,7 @@ Error MachOPlatformRuntimeState::initializeJITDylib( for (auto &KV : InitSections) { const auto &Name = KV.first; const auto &Handler = KV.second; - // FIXME: Remove copy once we have C++17. - auto I = MOJDIs.InitSections.find(to_string(Name)); + auto I = MOJDIs.InitSections.find(Name); if (I != MOJDIs.InitSections.end()) { if (auto Err = Handler(I->second, MOJDIs)) return Err; diff --git a/compiler-rt/lib/orc/macho_platform.h b/compiler-rt/lib/orc/macho_platform.h index e097c15..6c05e84 100644 --- a/compiler-rt/lib/orc/macho_platform.h +++ b/compiler-rt/lib/orc/macho_platform.h @@ -47,6 +47,7 @@ struct MachOJITDylibInitializers { std::string Name; ExecutorAddress MachOHeaderAddress; + ExecutorAddress ObjCImageInfoAddress; std::unordered_map InitSections; }; @@ -97,7 +98,7 @@ using SPSNamedExecutorAddressRangeSequenceMap = SPSSequence>; using SPSMachOJITDylibInitializers = - SPSTuple; using SPSMachOJITDylibInitializerSequence = @@ -110,19 +111,22 @@ class SPSSerializationTraits InitSections; }; @@ -121,6 +122,9 @@ public: static ArrayRef> standardRuntimeUtilityAliases(); + /// Returns true if the given section name is an initializer section. + static bool isInitializerSection(StringRef SegName, StringRef SectName); + private: // The MachOPlatformPlugin scans/modifies LinkGraphs to support MachO // platform features including initializers, exceptions, TLV, and language @@ -165,12 +169,16 @@ private: Error preserveInitSections(jitlink::LinkGraph &G, MaterializationResponsibility &MR); + Error processObjCImageInfo(jitlink::LinkGraph &G, + MaterializationResponsibility &MR); + Error registerInitSections(jitlink::LinkGraph &G, JITDylib &JD); Error fixTLVSectionsAndEdges(jitlink::LinkGraph &G, JITDylib &JD); std::mutex PluginMutex; MachOPlatform &MP; + DenseMap> ObjCImageInfos; InitSymbolDepMap InitSymbolDeps; }; @@ -211,7 +219,7 @@ private: // Records the addresses of runtime symbols used by the platform. Error bootstrapMachORuntime(JITDylib &PlatformJD); - Error registerInitInfo(JITDylib &JD, + Error registerInitInfo(JITDylib &JD, ExecutorAddress ObjCImageInfoAddr, ArrayRef InitSections); Error registerPerObjectSections(const MachOPerObjectSectionsToRegister &POSR); @@ -274,7 +282,7 @@ using SPSNamedExecutorAddressRangeSequenceMap = SPSSequence>; using SPSMachOJITDylibInitializers = - SPSTuple; using SPSMachOJITDylibInitializerSequence = @@ -287,19 +295,22 @@ class SPSSerializationTraits InitSections) { + JITDylib &JD, ExecutorAddress ObjCImageInfoAddr, + ArrayRef InitSections) { std::unique_lock Lock(PlatformMutex); @@ -550,6 +567,8 @@ Error MachOPlatform::registerInitInfo( InitSeq = &I->second; } + InitSeq->ObjCImageInfoAddress = ObjCImageInfoAddr; + for (auto *Sec : InitSections) { // FIXME: Avoid copy here. jitlink::SectionRange R(*Sec); @@ -631,7 +650,9 @@ void MachOPlatform::MachOPlatformPlugin::addInitializerSupportPasses( /// Preserve init sections. Config.PrePrunePasses.push_back([this, &MR](jitlink::LinkGraph &G) { - return preserveInitSections(G, MR); + if (auto Err = preserveInitSections(G, MR)) + return Err; + return processObjCImageInfo(G, MR); }); Config.PostFixupPasses.push_back( @@ -768,11 +789,95 @@ Error MachOPlatform::MachOPlatformPlugin::preserveInitSections( return Error::success(); } +Error MachOPlatform::MachOPlatformPlugin::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(ObjCImageInfoSectionName); + 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("Empty " + ObjCImageInfoSectionName + + " 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("Multiple blocks in " + + ObjCImageInfoSectionName + + " 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(ObjCImageInfoSectionName + + " 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 Lock(PluginMutex); + + 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( + "ObjC version in " + G.getName() + + " does not match first registered version", + inconvertibleErrorCode()); + if (ObjCImageInfoItr->second.second != Flags) + return make_error("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(); +} + Error MachOPlatform::MachOPlatformPlugin::registerInitSections( jitlink::LinkGraph &G, JITDylib &JD) { + ExecutorAddress ObjCImageInfoAddr; SmallVector InitSections; + if (auto *ObjCImageInfoSec = G.findSectionByName(ObjCImageInfoSectionName)) { + if (auto Addr = jitlink::SectionRange(*ObjCImageInfoSec).getStart()) + ObjCImageInfoAddr.setValue(Addr); + } + for (auto InitSectionName : InitSectionNames) if (auto *Sec = G.findSectionByName(InitSectionName)) InitSections.push_back(Sec); @@ -780,6 +885,9 @@ Error MachOPlatform::MachOPlatformPlugin::registerInitSections( // Dump the scraped inits. LLVM_DEBUG({ dbgs() << "MachOPlatform: Scraped " << G.getName() << " init sections:\n"; + if (ObjCImageInfoAddr) + dbgs() << " " << ObjCImageInfoSectionName << ": " + << formatv("{0:x}", ObjCImageInfoAddr.getValue()) << "\n"; for (auto *Sec : InitSections) { jitlink::SectionRange R(*Sec); dbgs() << " " << Sec->getName() << ": " @@ -787,7 +895,7 @@ Error MachOPlatform::MachOPlatformPlugin::registerInitSections( } }); - return MP.registerInitInfo(JD, InitSections); + return MP.registerInitInfo(JD, ObjCImageInfoAddr, InitSections); } Error MachOPlatform::MachOPlatformPlugin::fixTLVSectionsAndEdges( diff --git a/llvm/lib/ExecutionEngine/Orc/Mangling.cpp b/llvm/lib/ExecutionEngine/Orc/Mangling.cpp index 6063047..14b2288 100644 --- a/llvm/lib/ExecutionEngine/Orc/Mangling.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Mangling.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ExecutionEngine/Orc/Mangling.h" +#include "llvm/ExecutionEngine/Orc/MachOPlatform.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Mangler.h" #include "llvm/Object/MachO.h" @@ -130,24 +131,34 @@ getObjectSymbolInfo(ExecutionSession &ES, MemoryBufferRef ObjBuffer) { SymbolStringPtr InitSymbol; + size_t Counter = 0; + auto AddInitSymbol = [&]() { + while (true) { + std::string InitSymString; + raw_string_ostream(InitSymString) + << "$." << ObjBuffer.getBufferIdentifier() << ".__inits." + << Counter++; + InitSymbol = ES.intern(InitSymString); + if (SymbolFlags.count(InitSymbol)) + continue; + SymbolFlags[InitSymbol] = JITSymbolFlags::MaterializationSideEffectsOnly; + return; + } + }; + if (IsMachO) { auto &MachOObj = cast(*Obj->get()); for (auto &Sec : MachOObj.sections()) { auto SecType = MachOObj.getSectionType(Sec); if ((SecType & MachO::SECTION_TYPE) == MachO::S_MOD_INIT_FUNC_POINTERS) { - size_t Counter = 0; - while (true) { - std::string InitSymString; - raw_string_ostream(InitSymString) - << "$." << ObjBuffer.getBufferIdentifier() << ".__inits." - << Counter++; - InitSymbol = ES.intern(InitSymString); - if (SymbolFlags.count(InitSymbol)) - continue; - SymbolFlags[InitSymbol] = - JITSymbolFlags::MaterializationSideEffectsOnly; - break; - } + AddInitSymbol(); + break; + } + auto SegName = + MachOObj.getSectionFinalSegmentName(Sec.getRawDataRefImpl()); + auto SecName = cantFail(MachOObj.getSectionName(Sec.getRawDataRefImpl())); + if (MachOPlatform::isInitializerSection(SegName, SecName)) { + AddInitSymbol(); break; } } -- 2.7.4