Index: compiler-rt/lib/orc/executor_address.h =================================================================== --- compiler-rt/lib/orc/executor_address.h +++ compiler-rt/lib/orc/executor_address.h @@ -20,6 +20,7 @@ #include "simple_packed_serialization.h" #include +#include #include namespace __orc_rt { @@ -152,7 +153,7 @@ return !(LHS == RHS); } bool contains(ExecutorAddr Addr) const { return Start <= Addr && Addr < End; } - bool overlaps(const ExecutorAddrRange &Other) { + bool overlaps(const ExecutorAddrRange &Other) const { return !(Other.End <= Start || End <= Other.Start); } @@ -166,6 +167,49 @@ ExecutorAddr End; }; +/// A map of non-overlapping ExecutorAddrRanges with O(log(n)) lookup by +/// ExecutorAddr. +template class ExecutorAddrRangeMap { +public: + struct RangeLess { + bool operator()(const ExecutorAddrRange &A, + const ExecutorAddrRange &B) const { + return A.Start < B.Start; + } + }; + using MapT = std::map; + using iterator = typename MapT::iterator; + + iterator begin() { return Map.begin(); } + iterator end() { return Map.end(); } + + std::pair insert(std::pair KV) { + auto I = find(KV.first.End); + assert(I == end() || !I->first.overlaps(KV.first) && + "overlapping range in ExecutorAddrRangeMap"); + // FIXME: if I is adjacent to KV and has the same value we could widen + // instead of inserting a new range. + return std::make_pair(Map.insert(I, std::move(KV)), true); + } + + iterator find(ExecutorAddr Addr) { + auto I = Map.lower_bound({Addr, Addr}); + if (I != end() && I->first.contains(Addr)) + return I; + // Since we search based on the start of the range, we need to check the + // previous element as well. + if (I != begin()) { + --I; + if (I->first.contains(Addr)) + return I; + } + return end(); + } + +private: + MapT Map; +}; + /// SPS serializatior for ExecutorAddr. template <> class SPSSerializationTraits { public: Index: compiler-rt/lib/orc/macho_platform.cpp =================================================================== --- compiler-rt/lib/orc/macho_platform.cpp +++ compiler-rt/lib/orc/macho_platform.cpp @@ -12,6 +12,7 @@ #include "macho_platform.h" #include "common.h" +#include "debug.h" #include "error.h" #include "wrapper_function_utils.h" @@ -21,6 +22,8 @@ #include #include +#define DEBUG_TYPE "macho_platform" + using namespace __orc_rt; using namespace __orc_rt::macho; @@ -35,9 +38,18 @@ struct objc_object; struct objc_selector; +#ifndef __OBJC_BOOL_IS_BOOL +#warning "__OBJC_BOOL_IS_BOOL not defined; defaulting to signed char" +#endif +#if __OBJC_BOOL_IS_BOOL +using BOOL = bool; +#else +using BOOL = signed char; +#endif using Class = objc_class *; using id = objc_object *; using SEL = objc_selector *; +using objc_hook_getImageName = BOOL (*)(Class, const char **); // Objective-C registration functions. // These are weakly imported. If the Objective-C runtime has not been loaded @@ -46,6 +58,9 @@ 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; +extern "C" void objc_setHook_getImageName(objc_hook_getImageName newValue, + objc_hook_getImageName *outOldValue) + ORC_RT_WEAK_IMPORT; // Swift types. class ProtocolRecord; @@ -100,6 +115,11 @@ return Error::success(); } +Error registerObjCData(const std::vector &Sections, + const MachOJITDylibInitializers &MOJDIs) { + return Error::success(); +} + Error registerObjCClasses( const std::vector &ObjCClassListSections, const MachOJITDylibInitializers &MOJDIs) { @@ -224,6 +244,7 @@ struct PerJITDylibState { void *Header = nullptr; + const char *Name = nullptr; size_t RefCount = 0; bool AllowReinitialization = false; AtExitsVector AtExits; @@ -251,6 +272,10 @@ int dlclose(void *DSOHandle); void *dlsym(void *DSOHandle, string_view Symbol); + /// Returns the name of the JITDylib containing the given InitSection address, + /// if any. FIXME: replace with a more general dladdr-like lookup. + const char *lookupImageNameContaining(void *Addr); + int registerAtExit(void (*F)(void *), void *Arg, void *DSOHandle); void runAtExits(void *DSOHandle); @@ -272,6 +297,13 @@ Error initializeJITDylib(MachOJITDylibInitializers &MOJDIs); static MachOPlatformRuntimeState *MOPS; + /// Mutex used held during destroy(). Operations that use MOPS without + /// external synchronization should use this mutex. + static std::mutex MOPSDestroyMutex; + + static void initializeObjCGetImageName(); + static std::mutex ObjCGetImageNameMutex; + static objc_hook_getImageName PrevObjCGetImageName; using InitSectionHandler = Error (*)(const std::vector &Sections, @@ -279,6 +311,7 @@ const std::vector> InitSections = {{"__DATA,__objc_selrefs", registerObjCSelectors}, {"__DATA,__objc_classlist", registerObjCClasses}, + {"__DATA,__objc_data", registerObjCData}, {"__TEXT,__swift5_protos", registerSwift5Protocols}, {"__TEXT,__swift5_proto", registerSwift5ProtocolConformances}, {"__TEXT,__swift5_types", registerSwift5Types}, @@ -290,12 +323,14 @@ std::recursive_mutex JDStatesMutex; std::unordered_map JDStates; std::unordered_map JDNameToHeader; + ExecutorAddrRangeMap SectionToHeader; std::mutex ThreadDataSectionsMutex; std::map ThreadDataSections; }; MachOPlatformRuntimeState *MachOPlatformRuntimeState::MOPS = nullptr; +std::mutex MachOPlatformRuntimeState::MOPSDestroyMutex; void MachOPlatformRuntimeState::initialize() { assert(!MOPS && "MachOPlatformRuntimeState should be null"); @@ -308,8 +343,64 @@ } void MachOPlatformRuntimeState::destroy() { + std::lock_guard Lock(MOPSDestroyMutex); assert(MOPS && "MachOPlatformRuntimeState not initialized"); delete MOPS; + MOPS = nullptr; +} + +std::mutex MachOPlatformRuntimeState::ObjCGetImageNameMutex; +objc_hook_getImageName MachOPlatformRuntimeState::PrevObjCGetImageName = + nullptr; + +void MachOPlatformRuntimeState::initializeObjCGetImageName() { + // If PrevObjCGetImageName is set, the handler is either installed or is + // in the process of being installed, and libobjc is responsible for its + // atomicity. + if (PrevObjCGetImageName) + return; + std::lock_guard Lock(ObjCGetImageNameMutex); + // Double-check after locking to prevent double-installing the handler, which + // would create a cycle in the handler chain. + if (PrevObjCGetImageName) + return; + if (!objc_setHook_getImageName) { + // Will never be called, but lets us fail early. + PrevObjCGetImageName = [](Class cls, const char **OutImageName) -> BOOL { + return false; + }; + ORC_RT_DEBUG(printdbg("objc_setHook_getImageName not available; " + "class_getImageName will not work for JITDylibs")); + return; + } + + objc_setHook_getImageName( + [](Class cls, const char **OutImageName) -> BOOL { + if (cls && OutImageName) { + std::lock_guard Lock(MOPSDestroyMutex); + if (MOPS) { + if (const char *Name = + MOPS->lookupImageNameContaining((void *)cls)) { + *OutImageName = Name; + return true; + } + } + } + return PrevObjCGetImageName(cls, OutImageName); + }, + &PrevObjCGetImageName); +} + +const char *MachOPlatformRuntimeState::lookupImageNameContaining(void *Addr) { + std::lock_guard Lock(JDStatesMutex); + auto I = SectionToHeader.find(ExecutorAddr::fromPtr(Addr)); + if (I == SectionToHeader.end()) + return nullptr; + auto *JDS = getJITDylibStateByHeaderAddr(I->second); + assert(JDS && JDS->Name); + if (!JDS) + return nullptr; + return JDS->Name; } Error MachOPlatformRuntimeState::registerThreadDataSection( @@ -446,10 +537,13 @@ // If this entry hasn't been created yet. if (!JDS.Header) { - assert(!JDNameToHeader.count(MOJDIs.Name) && + auto Insert = JDNameToHeader.insert(std::make_pair(MOJDIs.Name, Header)); + assert(Insert.second && "JITDylib has header map entry but no name map entry"); JDNameToHeader[MOJDIs.Name] = Header; JDS.Header = Header; + // unordered_map keys are not destroyed during rehash, so don't copy. + JDS.Name = Insert.first->first.c_str(); } return JDS; @@ -514,6 +608,7 @@ auto &JDS = getOrCreateJITDylibState(MOJDIs); ++JDS.RefCount; + bool HasObjc = false; for (auto &KV : InitSections) { const auto &Name = KV.first; const auto &Handler = KV.second; @@ -521,9 +616,16 @@ if (I != MOJDIs.InitSections.end()) { if (auto Err = Handler(I->second, MOJDIs)) return Err; + for (auto &Section : I->second) + SectionToHeader.insert(std::make_pair(Section, JDS.Header)); + if (strstr(Name, "__objc") != nullptr) + HasObjc = true; } } + if (HasObjc) + initializeObjCGetImageName(); + return Error::success(); } Index: compiler-rt/test/orc/TestCases/Darwin/x86-64/Inputs/FooClass.S =================================================================== --- /dev/null +++ compiler-rt/test/orc/TestCases/Darwin/x86-64/Inputs/FooClass.S @@ -0,0 +1,66 @@ + .section __TEXT,__text,regular,pure_instructions + .section __TEXT,__objc_classname,cstring_literals +L_OBJC_CLASS_NAME_: ## @OBJC_CLASS_NAME_ + .asciz "Foo" + + .section __DATA,__objc_const + .p2align 3 ## @"_OBJC_METACLASS_RO_$_Foo" +__OBJC_METACLASS_RO_$_Foo: + .long 129 ## 0x81 + .long 40 ## 0x28 + .long 40 ## 0x28 + .space 4 + .quad 0 + .quad L_OBJC_CLASS_NAME_ + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + + .section __DATA,__objc_data + .globl _OBJC_METACLASS_$_Foo ## @"OBJC_METACLASS_$_Foo" + .p2align 3 +_OBJC_METACLASS_$_Foo: + .quad _OBJC_METACLASS_$_NSObject + .quad _OBJC_METACLASS_$_NSObject + .quad __objc_empty_cache + .quad 0 + .quad __OBJC_METACLASS_RO_$_Foo + + .section __DATA,__objc_const + .p2align 3 ## @"_OBJC_CLASS_RO_$_Foo" +__OBJC_CLASS_RO_$_Foo: + .long 128 ## 0x80 + .long 8 ## 0x8 + .long 8 ## 0x8 + .space 4 + .quad 0 + .quad L_OBJC_CLASS_NAME_ + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + + .section __DATA,__objc_data + .globl _OBJC_CLASS_$_Foo ## @"OBJC_CLASS_$_Foo" + .p2align 3 +_OBJC_CLASS_$_Foo: + .quad _OBJC_METACLASS_$_Foo + .quad _OBJC_CLASS_$_NSObject + .quad __objc_empty_cache + .quad 0 + .quad __OBJC_CLASS_RO_$_Foo + + .section __DATA,__objc_classlist,regular,no_dead_strip + .p2align 3 ## @"OBJC_LABEL_CLASS_$" +l_OBJC_LABEL_CLASS_$: + .quad _OBJC_CLASS_$_Foo + + .section __DATA,__objc_imageinfo,regular,no_dead_strip +L_OBJC_IMAGE_INFO: + .long 0 + .long 64 + +.subsections_via_symbols Index: compiler-rt/test/orc/TestCases/Darwin/x86-64/objc-class_getImageName.S =================================================================== --- /dev/null +++ compiler-rt/test/orc/TestCases/Darwin/x86-64/objc-class_getImageName.S @@ -0,0 +1,144 @@ +# RUN: rm -rf %t && mkdir -p %t +# RUN: %clang -c %S/Inputs/FooClass.S -o %t/FooClass.o +# RUN: %clang -c %s -o %t/main.o +# RUN: %llvm_jitlink -preload libobjc.A.dylib %t/main.o -lFoo -jd=Foo %t/FooClass.o | FileCheck %s + +# CHECK: foo image: Foo +# CHECK: bar image: main +# CHECK: NSObject image: /usr/lib/libobjc.A.dylib + + .section __TEXT,__text,regular,pure_instructions + .globl _main ## -- Begin function main + .p2align 4, 0x90 +_main: ## @main + .cfi_startproc +## %bb.0: + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + subq $16, %rsp + movl $0, -4(%rbp) + movq _OBJC_CLASSLIST_REFERENCES_$_(%rip), %rdi + callq _objc_opt_class + movq %rax, %rdi + callq _class_getImageName + movq %rax, %rsi + leaq L_.str(%rip), %rdi + movb $0, %al + callq _printf + movq _OBJC_CLASSLIST_REFERENCES_$_.2(%rip), %rdi + callq _objc_opt_class + movq %rax, %rdi + callq _class_getImageName + movq %rax, %rsi + leaq L_.str.1(%rip), %rdi + movb $0, %al + callq _printf + movq _OBJC_CLASSLIST_REFERENCES_$_.4(%rip), %rdi + callq _objc_opt_class + movq %rax, %rdi + callq _class_getImageName + movq %rax, %rsi + leaq L_.str.3(%rip), %rdi + movb $0, %al + callq _printf + xorl %eax, %eax + addq $16, %rsp + popq %rbp + retq + .cfi_endproc + ## -- End function + .section __TEXT,__objc_classname,cstring_literals +L_OBJC_CLASS_NAME_: ## @OBJC_CLASS_NAME_ + .asciz "Bar" + + .section __DATA,__objc_const + .p2align 3 ## @"_OBJC_METACLASS_RO_$_Bar" +__OBJC_METACLASS_RO_$_Bar: + .long 129 ## 0x81 + .long 40 ## 0x28 + .long 40 ## 0x28 + .space 4 + .quad 0 + .quad L_OBJC_CLASS_NAME_ + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + + .section __DATA,__objc_data + .globl _OBJC_METACLASS_$_Bar ## @"OBJC_METACLASS_$_Bar" + .p2align 3 +_OBJC_METACLASS_$_Bar: + .quad _OBJC_METACLASS_$_NSObject + .quad _OBJC_METACLASS_$_NSObject + .quad __objc_empty_cache + .quad 0 + .quad __OBJC_METACLASS_RO_$_Bar + + .section __DATA,__objc_const + .p2align 3 ## @"_OBJC_CLASS_RO_$_Bar" +__OBJC_CLASS_RO_$_Bar: + .long 128 ## 0x80 + .long 8 ## 0x8 + .long 8 ## 0x8 + .space 4 + .quad 0 + .quad L_OBJC_CLASS_NAME_ + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + + .section __DATA,__objc_data + .globl _OBJC_CLASS_$_Bar ## @"OBJC_CLASS_$_Bar" + .p2align 3 +_OBJC_CLASS_$_Bar: + .quad _OBJC_METACLASS_$_Bar + .quad _OBJC_CLASS_$_NSObject + .quad __objc_empty_cache + .quad 0 + .quad __OBJC_CLASS_RO_$_Bar + + .section __TEXT,__cstring,cstring_literals +L_.str: ## @.str + .asciz "foo image: %s\n" + + .section __DATA,__objc_classrefs,regular,no_dead_strip + .p2align 3 ## @"OBJC_CLASSLIST_REFERENCES_$_" +_OBJC_CLASSLIST_REFERENCES_$_: + .quad _OBJC_CLASS_$_Foo + + .section __TEXT,__cstring,cstring_literals +L_.str.1: ## @.str.1 + .asciz "bar image: %s\n" + + .section __DATA,__objc_classrefs,regular,no_dead_strip + .p2align 3 ## @"OBJC_CLASSLIST_REFERENCES_$_.2" +_OBJC_CLASSLIST_REFERENCES_$_.2: + .quad _OBJC_CLASS_$_Bar + + .section __TEXT,__cstring,cstring_literals +L_.str.3: ## @.str.3 + .asciz "NSObject image: %s\n" + + .section __DATA,__objc_classrefs,regular,no_dead_strip + .p2align 3 ## @"OBJC_CLASSLIST_REFERENCES_$_.4" +_OBJC_CLASSLIST_REFERENCES_$_.4: + .quad _OBJC_CLASS_$_NSObject + + .section __DATA,__objc_classlist,regular,no_dead_strip + .p2align 3 ## @"OBJC_LABEL_CLASS_$" +l_OBJC_LABEL_CLASS_$: + .quad _OBJC_CLASS_$_Bar + + .section __DATA,__objc_imageinfo,regular,no_dead_strip +L_OBJC_IMAGE_INFO: + .long 0 + .long 64 + +.subsections_via_symbols Index: compiler-rt/test/orc/lit.cfg.py =================================================================== --- compiler-rt/test/orc/lit.cfg.py +++ compiler-rt/test/orc/lit.cfg.py @@ -29,5 +29,7 @@ # Default test suffixes. config.suffixes = ['.c', '.cpp', '.S'] +config.excludes = ['Inputs'] + if config.host_os not in ['Darwin', 'FreeBSD', 'Linux']: config.unsupported = True Index: llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp =================================================================== --- llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp +++ llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp @@ -133,6 +133,7 @@ StringRef EHFrameSectionName = "__TEXT,__eh_frame"; StringRef ModInitFuncSectionName = "__DATA,__mod_init_func"; StringRef ObjCClassListSectionName = "__DATA,__objc_classlist"; +StringRef ObjCDataSectionName = "__DATA,__objc_data"; StringRef ObjCImageInfoSectionName = "__DATA,__objc_image_info"; StringRef ObjCSelRefsSectionName = "__DATA,__objc_selrefs"; StringRef Swift5ProtoSectionName = "__TEXT,__swift5_proto"; @@ -143,8 +144,9 @@ StringRef ThreadVarsSectionName = "__DATA,__thread_vars"; StringRef InitSectionNames[] = { - ModInitFuncSectionName, ObjCSelRefsSectionName, ObjCClassListSectionName, - Swift5ProtosSectionName, Swift5ProtoSectionName, Swift5TypesSectionName}; + ModInitFuncSectionName, ObjCSelRefsSectionName, ObjCClassListSectionName, + ObjCDataSectionName, Swift5ProtosSectionName, Swift5ProtoSectionName, + Swift5TypesSectionName}; } // end anonymous namespace Index: llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp =================================================================== --- llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp +++ llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp @@ -69,6 +69,7 @@ for (auto &Sec : G.sections()) if (Sec.getName() == "__DATA,__obj_selrefs" || Sec.getName() == "__DATA,__objc_classlist" || + Sec.getName() == "__DATA,__objc_data" || Sec.getName() == "__TEXT,__swift5_protos" || Sec.getName() == "__TEXT,__swift5_proto" || Sec.getName() == "__TEXT,__swift5_types" ||