[ORC][ORC-RT][MachO] Use _objc_(map|load)_images for ObjC & Swift registration.
This patch drops the individual registration calls to the ObjC and Swift runtimes (for selectors, classes, etc.), and instead creates a Mach header and load commands that can be passed to _objc_map_images and _objc_load_images to trigger registration and execution of +load methods. This approach supports categories (for which there is no current registration API), and more closely follows dyld's ObjC & Swift registration path.
This commit is contained in:
@@ -36,40 +36,18 @@ using namespace __orc_rt::macho;
|
||||
ORC_RT_JIT_DISPATCH_TAG(__orc_rt_macho_push_initializers_tag)
|
||||
ORC_RT_JIT_DISPATCH_TAG(__orc_rt_macho_symbol_lookup_tag)
|
||||
|
||||
// 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 *;
|
||||
struct mach_header;
|
||||
|
||||
// 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;
|
||||
class TypeMetadataRecord;
|
||||
|
||||
extern "C" void
|
||||
swift_registerProtocols(const ProtocolRecord *begin,
|
||||
const ProtocolRecord *end) ORC_RT_WEAK_IMPORT;
|
||||
_objc_map_images(unsigned count, const char *const paths[],
|
||||
const mach_header *const mhdrs[]) ORC_RT_WEAK_IMPORT;
|
||||
|
||||
extern "C" void swift_registerProtocolConformances(
|
||||
const ProtocolConformanceRecord *begin,
|
||||
const ProtocolConformanceRecord *end) ORC_RT_WEAK_IMPORT;
|
||||
|
||||
extern "C" void swift_registerTypeMetadataRecords(
|
||||
const TypeMetadataRecord *begin,
|
||||
const TypeMetadataRecord *end) ORC_RT_WEAK_IMPORT;
|
||||
extern "C" void _objc_load_image(const char *path,
|
||||
const mach_header *mh) ORC_RT_WEAK_IMPORT;
|
||||
|
||||
// Libunwind prototypes.
|
||||
struct unw_dynamic_unwind_sections {
|
||||
@@ -290,11 +268,7 @@ private:
|
||||
std::unordered_map<void *, size_t> ZeroInitRanges;
|
||||
UnwindSectionsMap UnwindSections;
|
||||
RecordSectionsTracker<void (*)()> ModInitsSections;
|
||||
RecordSectionsTracker<void *> ObjCClassListSections;
|
||||
RecordSectionsTracker<void *> ObjCSelRefsSections;
|
||||
RecordSectionsTracker<char> Swift5ProtocolsSections;
|
||||
RecordSectionsTracker<char> Swift5ProtocolConformancesSections;
|
||||
RecordSectionsTracker<char> Swift5TypesSections;
|
||||
RecordSectionsTracker<char> ObjCRuntimeRegistrationObjects;
|
||||
|
||||
bool referenced() const {
|
||||
return LinkedAgainstRefCount != 0 || DlRefCount != 0;
|
||||
@@ -357,11 +331,7 @@ private:
|
||||
static Error registerEHFrames(span<const char> EHFrameSection);
|
||||
static Error deregisterEHFrames(span<const char> EHFrameSection);
|
||||
|
||||
static Error registerObjCSelectors(JITDylibState &JDS);
|
||||
static Error registerObjCClasses(JITDylibState &JDS);
|
||||
static Error registerSwift5Protocols(JITDylibState &JDS);
|
||||
static Error registerSwift5ProtocolConformances(JITDylibState &JDS);
|
||||
static Error registerSwift5Types(JITDylibState &JDS);
|
||||
static Error registerObjCRegistrationObjects(JITDylibState &JDS);
|
||||
static Error runModInits(std::unique_lock<std::mutex> &JDStatesLock,
|
||||
JITDylibState &JDS);
|
||||
|
||||
@@ -580,22 +550,12 @@ Error MachOPlatformRuntimeState::registerObjectPlatformSections(
|
||||
JDS->DataSectionContent[KV.second.Start.toPtr<char *>()] =
|
||||
std::vector<char>(S.begin(), S.end());
|
||||
} else if (KV.first == "__DATA,__common") {
|
||||
// fprintf(stderr, "Adding zero-init range %llx -- %llx\n",
|
||||
// KV.second.Start.getValue(), KV.second.size());
|
||||
JDS->ZeroInitRanges[KV.second.Start.toPtr<char *>()] = KV.second.size();
|
||||
} else if (KV.first == "__DATA,__thread_data") {
|
||||
if (auto Err = registerThreadDataSection(KV.second.toSpan<const char>()))
|
||||
return Err;
|
||||
} else if (KV.first == "__DATA,__objc_selrefs")
|
||||
JDS->ObjCSelRefsSections.add(KV.second.toSpan<void *>());
|
||||
else if (KV.first == "__DATA,__objc_classlist")
|
||||
JDS->ObjCClassListSections.add(KV.second.toSpan<void *>());
|
||||
else if (KV.first == "__TEXT,__swift5_protos")
|
||||
JDS->Swift5ProtocolsSections.add(KV.second.toSpan<char>());
|
||||
else if (KV.first == "__TEXT,__swift5_proto")
|
||||
JDS->Swift5ProtocolConformancesSections.add(KV.second.toSpan<char>());
|
||||
else if (KV.first == "__TEXT,__swift5_types")
|
||||
JDS->Swift5TypesSections.add(KV.second.toSpan<char>());
|
||||
} else if (KV.first == "__llvm_jitlink_ObjCRuntimeRegistrationObject")
|
||||
JDS->ObjCRuntimeRegistrationObjects.add(KV.second.toSpan<char>());
|
||||
else if (KV.first == "__DATA,__mod_init_func")
|
||||
JDS->ModInitsSections.add(KV.second.toSpan<void (*)()>());
|
||||
else {
|
||||
@@ -675,16 +635,8 @@ Error MachOPlatformRuntimeState::deregisterObjectPlatformSections(
|
||||
if (auto Err =
|
||||
deregisterThreadDataSection(KV.second.toSpan<const char>()))
|
||||
return Err;
|
||||
} else if (KV.first == "__DATA,__objc_selrefs")
|
||||
JDS->ObjCSelRefsSections.removeIfPresent(KV.second);
|
||||
else if (KV.first == "__DATA,__objc_classlist")
|
||||
JDS->ObjCClassListSections.removeIfPresent(KV.second);
|
||||
else if (KV.first == "__TEXT,__swift5_protos")
|
||||
JDS->Swift5ProtocolsSections.removeIfPresent(KV.second);
|
||||
else if (KV.first == "__TEXT,__swift5_proto")
|
||||
JDS->Swift5ProtocolConformancesSections.removeIfPresent(KV.second);
|
||||
else if (KV.first == "__TEXT,__swift5_types")
|
||||
JDS->Swift5TypesSections.removeIfPresent(KV.second);
|
||||
} else if (KV.first == "__llvm_jitlink_ObjCRuntimeRegistrationObject")
|
||||
JDS->ObjCRuntimeRegistrationObjects.removeIfPresent(KV.second);
|
||||
else if (KV.first == "__DATA,__mod_init_func")
|
||||
JDS->ModInitsSections.removeIfPresent(KV.second);
|
||||
else {
|
||||
@@ -905,115 +857,24 @@ Error MachOPlatformRuntimeState::deregisterEHFrames(
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error MachOPlatformRuntimeState::registerObjCSelectors(JITDylibState &JDS) {
|
||||
if (!JDS.ObjCSelRefsSections.hasNewSections())
|
||||
return Error::success();
|
||||
|
||||
if (ORC_RT_UNLIKELY(!sel_registerName))
|
||||
return make_error<StringError>("sel_registerName is not available");
|
||||
|
||||
JDS.ObjCSelRefsSections.processNewSections([](span<void *> SelRefs) {
|
||||
for (void *&SelEntry : SelRefs) {
|
||||
const char *SelName = reinterpret_cast<const char *>(SelEntry);
|
||||
auto Sel = sel_registerName(SelName);
|
||||
*reinterpret_cast<SEL *>(&SelEntry) = Sel;
|
||||
}
|
||||
});
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error MachOPlatformRuntimeState::registerObjCClasses(JITDylibState &JDS) {
|
||||
if (!JDS.ObjCClassListSections.hasNewSections())
|
||||
return Error::success();
|
||||
|
||||
if (ORC_RT_UNLIKELY(!objc_msgSend))
|
||||
return make_error<StringError>("objc_msgSend is not available");
|
||||
if (ORC_RT_UNLIKELY(!objc_readClassPair))
|
||||
return make_error<StringError>("objc_readClassPair is not available");
|
||||
|
||||
struct ObjCClassCompiled {
|
||||
void *Metaclass;
|
||||
void *Parent;
|
||||
void *Cache1;
|
||||
void *Cache2;
|
||||
void *Data;
|
||||
};
|
||||
|
||||
auto ClassSelector = sel_registerName("class");
|
||||
|
||||
return JDS.ObjCClassListSections.processNewSections(
|
||||
[&](span<void *> ClassPtrs) -> Error {
|
||||
for (void *ClassPtr : ClassPtrs) {
|
||||
auto *Cls = reinterpret_cast<Class>(ClassPtr);
|
||||
auto *ClassCompiled = reinterpret_cast<ObjCClassCompiled *>(ClassPtr);
|
||||
objc_msgSend(reinterpret_cast<id>(ClassCompiled->Parent),
|
||||
ClassSelector);
|
||||
auto Registered = objc_readClassPair(Cls, JDS.ObjCImageInfo);
|
||||
// FIXME: Improve diagnostic by reporting the failed class's name.
|
||||
if (Registered != Cls)
|
||||
return make_error<StringError>(
|
||||
"Unable to register Objective-C class");
|
||||
}
|
||||
return Error::success();
|
||||
});
|
||||
}
|
||||
|
||||
Error MachOPlatformRuntimeState::registerSwift5Protocols(JITDylibState &JDS) {
|
||||
|
||||
if (!JDS.Swift5ProtocolsSections.hasNewSections())
|
||||
return Error::success();
|
||||
|
||||
if (ORC_RT_UNLIKELY(!swift_registerProtocols))
|
||||
return make_error<StringError>("swift_registerProtocols is not available");
|
||||
|
||||
JDS.Swift5ProtocolsSections.processNewSections([](span<char> ProtoSec) {
|
||||
swift_registerProtocols(
|
||||
reinterpret_cast<const ProtocolRecord *>(ProtoSec.data()),
|
||||
reinterpret_cast<const ProtocolRecord *>(ProtoSec.data() +
|
||||
ProtoSec.size()));
|
||||
});
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error MachOPlatformRuntimeState::registerSwift5ProtocolConformances(
|
||||
Error MachOPlatformRuntimeState::registerObjCRegistrationObjects(
|
||||
JITDylibState &JDS) {
|
||||
|
||||
if (!JDS.Swift5ProtocolConformancesSections.hasNewSections())
|
||||
return Error::success();
|
||||
|
||||
if (ORC_RT_UNLIKELY(!swift_registerProtocolConformances))
|
||||
if (!_objc_map_images || !_objc_load_image)
|
||||
return make_error<StringError>(
|
||||
"swift_registerProtocolConformances is not available");
|
||||
"Could not register Objective-C / Swift metadata: _objc_map_images / "
|
||||
"_objc_load_image not found");
|
||||
|
||||
JDS.Swift5ProtocolConformancesSections.processNewSections(
|
||||
[](span<char> ProtoConfSec) {
|
||||
swift_registerProtocolConformances(
|
||||
reinterpret_cast<const ProtocolConformanceRecord *>(
|
||||
ProtoConfSec.data()),
|
||||
reinterpret_cast<const ProtocolConformanceRecord *>(
|
||||
ProtoConfSec.data() + ProtoConfSec.size()));
|
||||
});
|
||||
std::vector<char *> RegObjBases;
|
||||
JDS.ObjCRuntimeRegistrationObjects.processNewSections(
|
||||
[&](span<char> RegObj) { RegObjBases.push_back(RegObj.data()); });
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
std::vector<char *> Paths;
|
||||
Paths.resize(RegObjBases.size());
|
||||
_objc_map_images(RegObjBases.size(), Paths.data(),
|
||||
reinterpret_cast<mach_header **>(RegObjBases.data()));
|
||||
|
||||
Error MachOPlatformRuntimeState::registerSwift5Types(JITDylibState &JDS) {
|
||||
|
||||
if (!JDS.Swift5TypesSections.hasNewSections())
|
||||
return Error::success();
|
||||
|
||||
if (ORC_RT_UNLIKELY(!swift_registerTypeMetadataRecords))
|
||||
return make_error<StringError>(
|
||||
"swift_registerTypeMetadataRecords is not available");
|
||||
|
||||
JDS.Swift5TypesSections.processNewSections([&](span<char> TypesSec) {
|
||||
swift_registerTypeMetadataRecords(
|
||||
reinterpret_cast<const TypeMetadataRecord *>(TypesSec.data()),
|
||||
reinterpret_cast<const TypeMetadataRecord *>(TypesSec.data() +
|
||||
TypesSec.size()));
|
||||
});
|
||||
for (void *RegObjBase : RegObjBases)
|
||||
_objc_load_image(nullptr, reinterpret_cast<mach_header *>(RegObjBase));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
@@ -1151,15 +1012,7 @@ Error MachOPlatformRuntimeState::dlopenInitialize(
|
||||
}
|
||||
|
||||
// Initialize this JITDylib.
|
||||
if (auto Err = registerObjCSelectors(JDS))
|
||||
return Err;
|
||||
if (auto Err = registerObjCClasses(JDS))
|
||||
return Err;
|
||||
if (auto Err = registerSwift5Protocols(JDS))
|
||||
return Err;
|
||||
if (auto Err = registerSwift5ProtocolConformances(JDS))
|
||||
return Err;
|
||||
if (auto Err = registerSwift5Types(JDS))
|
||||
if (auto Err = registerObjCRegistrationObjects(JDS))
|
||||
return Err;
|
||||
if (auto Err = runModInits(JDStatesLock, JDS))
|
||||
return Err;
|
||||
|
||||
190
compiler-rt/test/orc/TestCases/Darwin/arm64/objc-category.S
Normal file
190
compiler-rt/test/orc/TestCases/Darwin/arm64/objc-category.S
Normal file
@@ -0,0 +1,190 @@
|
||||
// Test that we can handle calls to methods on categories.
|
||||
// The following assembly defines an ObjC class Foo with an instance method
|
||||
// -foo, then uses a category (Bar) to add an extra instance method -bar.
|
||||
// The main function calls both -foo and -bar on an instance of Foo to check
|
||||
// that the calls behave as expected.
|
||||
//
|
||||
// RUN: %clang -c -o %t.o %s
|
||||
// RUN: %llvm_jitlink -preload libobjc.A.dylib %t.o
|
||||
|
||||
.section __TEXT,__text,regular,pure_instructions
|
||||
.build_version macos, 14, 0
|
||||
.p2align 2
|
||||
"-[Foo foo]":
|
||||
mov w0, #1
|
||||
ret
|
||||
|
||||
.p2align 2
|
||||
"-[Foo(Bar) bar]":
|
||||
mov w0, #1
|
||||
ret
|
||||
|
||||
.globl _main
|
||||
.p2align 2
|
||||
_main:
|
||||
stp x20, x19, [sp, #-32]!
|
||||
stp x29, x30, [sp, #16]
|
||||
add x29, sp, #16
|
||||
Lloh0:
|
||||
adrp x8, _OBJC_CLASSLIST_REFERENCES_$_@PAGE
|
||||
Lloh1:
|
||||
ldr x0, [x8, _OBJC_CLASSLIST_REFERENCES_$_@PAGEOFF]
|
||||
bl _objc_alloc_init
|
||||
mov x19, x0
|
||||
Lloh2:
|
||||
adrp x8, _OBJC_SELECTOR_REFERENCES_@PAGE
|
||||
Lloh3:
|
||||
ldr x1, [x8, _OBJC_SELECTOR_REFERENCES_@PAGEOFF]
|
||||
bl _objc_msgSend
|
||||
cmp w0, #1
|
||||
b.ne LBB2_2
|
||||
|
||||
Lloh4:
|
||||
adrp x8, _OBJC_SELECTOR_REFERENCES_.3@PAGE
|
||||
Lloh5:
|
||||
ldr x1, [x8, _OBJC_SELECTOR_REFERENCES_.3@PAGEOFF]
|
||||
mov x0, x19
|
||||
bl _objc_msgSend
|
||||
cmp w0, #1
|
||||
cset w0, ne
|
||||
ldp x29, x30, [sp, #16]
|
||||
ldp x20, x19, [sp], #32
|
||||
ret
|
||||
LBB2_2:
|
||||
mov w0, #1
|
||||
ldp x29, x30, [sp, #16]
|
||||
ldp x20, x19, [sp], #32
|
||||
ret
|
||||
.loh AdrpLdr Lloh2, Lloh3
|
||||
.loh AdrpLdr Lloh0, Lloh1
|
||||
.loh AdrpLdr Lloh4, Lloh5
|
||||
|
||||
.section __TEXT,__objc_classname,cstring_literals
|
||||
l_OBJC_CLASS_NAME_:
|
||||
.asciz "Foo"
|
||||
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0
|
||||
__OBJC_METACLASS_RO_$_Foo:
|
||||
.long 1
|
||||
.long 40
|
||||
.long 40
|
||||
.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
|
||||
.p2align 3, 0x0
|
||||
_OBJC_METACLASS_$_Foo:
|
||||
.quad _OBJC_METACLASS_$_NSObject
|
||||
.quad _OBJC_METACLASS_$_NSObject
|
||||
.quad __objc_empty_cache
|
||||
.quad 0
|
||||
.quad __OBJC_METACLASS_RO_$_Foo
|
||||
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_METH_VAR_NAME_:
|
||||
.asciz "foo"
|
||||
|
||||
.section __TEXT,__objc_methtype,cstring_literals
|
||||
l_OBJC_METH_VAR_TYPE_:
|
||||
.asciz "i16@0:8"
|
||||
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0
|
||||
__OBJC_$_INSTANCE_METHODS_Foo:
|
||||
.long 24
|
||||
.long 1
|
||||
.quad l_OBJC_METH_VAR_NAME_
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad "-[Foo foo]"
|
||||
|
||||
.p2align 3, 0x0
|
||||
__OBJC_CLASS_RO_$_Foo:
|
||||
.long 0
|
||||
.long 8
|
||||
.long 8
|
||||
.space 4
|
||||
.quad 0
|
||||
.quad l_OBJC_CLASS_NAME_
|
||||
.quad __OBJC_$_INSTANCE_METHODS_Foo
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
|
||||
.section __DATA,__objc_data
|
||||
.globl _OBJC_CLASS_$_Foo
|
||||
.p2align 3, 0x0
|
||||
_OBJC_CLASS_$_Foo:
|
||||
.quad _OBJC_METACLASS_$_Foo
|
||||
.quad _OBJC_CLASS_$_NSObject
|
||||
.quad __objc_empty_cache
|
||||
.quad 0
|
||||
.quad __OBJC_CLASS_RO_$_Foo
|
||||
|
||||
.section __TEXT,__objc_classname,cstring_literals
|
||||
l_OBJC_CLASS_NAME_.1:
|
||||
.asciz "Bar"
|
||||
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_METH_VAR_NAME_.2:
|
||||
.asciz "bar"
|
||||
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0
|
||||
__OBJC_$_CATEGORY_INSTANCE_METHODS_Foo_$_Bar:
|
||||
.long 24
|
||||
.long 1
|
||||
.quad l_OBJC_METH_VAR_NAME_.2
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad "-[Foo(Bar) bar]"
|
||||
|
||||
.p2align 3, 0x0
|
||||
__OBJC_$_CATEGORY_Foo_$_Bar:
|
||||
.quad l_OBJC_CLASS_NAME_.1
|
||||
.quad _OBJC_CLASS_$_Foo
|
||||
.quad __OBJC_$_CATEGORY_INSTANCE_METHODS_Foo_$_Bar
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.long 64
|
||||
.space 4
|
||||
|
||||
.section __DATA,__objc_classrefs,regular,no_dead_strip
|
||||
.p2align 3, 0x0
|
||||
_OBJC_CLASSLIST_REFERENCES_$_:
|
||||
.quad _OBJC_CLASS_$_Foo
|
||||
|
||||
.section __DATA,__objc_selrefs,literal_pointers,no_dead_strip
|
||||
.p2align 3, 0x0
|
||||
_OBJC_SELECTOR_REFERENCES_:
|
||||
.quad l_OBJC_METH_VAR_NAME_
|
||||
|
||||
.p2align 3, 0x0
|
||||
_OBJC_SELECTOR_REFERENCES_.3:
|
||||
.quad l_OBJC_METH_VAR_NAME_.2
|
||||
|
||||
.section __DATA,__objc_classlist,regular,no_dead_strip
|
||||
.p2align 3, 0x0
|
||||
l_OBJC_LABEL_CLASS_$:
|
||||
.quad _OBJC_CLASS_$_Foo
|
||||
|
||||
.section __DATA,__objc_catlist,regular,no_dead_strip
|
||||
.p2align 3, 0x0
|
||||
l_OBJC_LABEL_CATEGORY_$:
|
||||
.quad __OBJC_$_CATEGORY_Foo_$_Bar
|
||||
|
||||
.section __DATA,__objc_imageinfo,regular,no_dead_strip
|
||||
L_OBJC_IMAGE_INFO:
|
||||
.long 0
|
||||
.long 64
|
||||
|
||||
.subsections_via_symbols
|
||||
@@ -156,6 +156,12 @@ private:
|
||||
ExecutorAddrRange CompactUnwindSection;
|
||||
};
|
||||
|
||||
struct ObjCImageInfo {
|
||||
uint32_t Version = 0;
|
||||
uint32_t Flags = 0;
|
||||
ExecutorAddr Addr;
|
||||
};
|
||||
|
||||
Error bootstrapPipelineStart(jitlink::LinkGraph &G);
|
||||
Error bootstrapPipelineRecordRuntimeFunctions(jitlink::LinkGraph &G);
|
||||
Error bootstrapPipelineEnd(jitlink::LinkGraph &G);
|
||||
@@ -165,8 +171,8 @@ private:
|
||||
Error associateJITDylibHeaderSymbol(jitlink::LinkGraph &G,
|
||||
MaterializationResponsibility &MR);
|
||||
|
||||
Error preserveInitSections(jitlink::LinkGraph &G,
|
||||
MaterializationResponsibility &MR);
|
||||
Error preserveImportantSections(jitlink::LinkGraph &G,
|
||||
MaterializationResponsibility &MR);
|
||||
|
||||
Error processObjCImageInfo(jitlink::LinkGraph &G,
|
||||
MaterializationResponsibility &MR);
|
||||
@@ -178,12 +184,16 @@ private:
|
||||
Error registerObjectPlatformSections(jitlink::LinkGraph &G, JITDylib &JD,
|
||||
bool InBootstrapPhase);
|
||||
|
||||
Error createObjCRuntimeObject(jitlink::LinkGraph &G);
|
||||
Error populateObjCRuntimeObject(jitlink::LinkGraph &G,
|
||||
MaterializationResponsibility &MR);
|
||||
|
||||
std::mutex PluginMutex;
|
||||
MachOPlatform &MP;
|
||||
|
||||
// FIXME: ObjCImageInfos and HeaderAddrs need to be cleared when
|
||||
// JITDylibs are removed.
|
||||
DenseMap<JITDylib *, std::pair<uint32_t, uint32_t>> ObjCImageInfos;
|
||||
DenseMap<JITDylib *, ObjCImageInfo> ObjCImageInfos;
|
||||
DenseMap<JITDylib *, ExecutorAddr> HeaderAddrs;
|
||||
InitSymbolDepMap InitSymbolDeps;
|
||||
};
|
||||
@@ -253,6 +263,10 @@ private:
|
||||
ES.intern("___orc_rt_macho_deregister_object_platform_sections")};
|
||||
RuntimeFunction CreatePThreadKey{
|
||||
ES.intern("___orc_rt_macho_create_pthread_key")};
|
||||
RuntimeFunction RegisterObjCRuntimeObject{
|
||||
ES.intern("___orc_rt_macho_register_objc_runtime_object")};
|
||||
RuntimeFunction DeregisterObjCRuntimeObject{
|
||||
ES.intern("___orc_rt_macho_deregister_objc_runtime_object")};
|
||||
|
||||
DenseMap<JITDylib *, SymbolLookupSet> RegisteredInitSymbols;
|
||||
|
||||
|
||||
@@ -19,21 +19,35 @@ namespace llvm {
|
||||
namespace orc {
|
||||
|
||||
// MachO section names.
|
||||
|
||||
extern StringRef MachODataCommonSectionName;
|
||||
extern StringRef MachODataDataSectionName;
|
||||
extern StringRef MachOEHFrameSectionName;
|
||||
extern StringRef MachOCompactUnwindInfoSectionName;
|
||||
extern StringRef MachOModInitFuncSectionName;
|
||||
extern StringRef MachOObjCCatListSectionName;
|
||||
extern StringRef MachOObjCCatList2SectionName;
|
||||
extern StringRef MachOObjCClassListSectionName;
|
||||
extern StringRef MachOObjCClassNameSectionName;
|
||||
extern StringRef MachOObjCClassRefsSectionName;
|
||||
extern StringRef MachOObjCConstSectionName;
|
||||
extern StringRef MachOObjCDataSectionName;
|
||||
extern StringRef MachOObjCImageInfoSectionName;
|
||||
extern StringRef MachOObjCMethNameSectionName;
|
||||
extern StringRef MachOObjCMethTypeSectionName;
|
||||
extern StringRef MachOObjCNLCatListSectionName;
|
||||
extern StringRef MachOObjCSelRefsSectionName;
|
||||
extern StringRef MachOSwift5ProtoSectionName;
|
||||
extern StringRef MachOSwift5ProtosSectionName;
|
||||
extern StringRef MachOSwift5TypesSectionName;
|
||||
extern StringRef MachOSwift5TypeRefSectionName;
|
||||
extern StringRef MachOSwift5FieldMetadataSectionName;
|
||||
extern StringRef MachOSwift5EntrySectionName;
|
||||
extern StringRef MachOThreadBSSSectionName;
|
||||
extern StringRef MachOThreadDataSectionName;
|
||||
extern StringRef MachOThreadVarsSectionName;
|
||||
extern StringRef MachOInitSectionNames[6];
|
||||
|
||||
extern StringRef MachOInitSectionNames[19];
|
||||
|
||||
// ELF section names.
|
||||
extern StringRef ELFEHFrameSectionName;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "llvm/ExecutionEngine/Orc/MachOPlatform.h"
|
||||
|
||||
#include "llvm/BinaryFormat/MachO.h"
|
||||
#include "llvm/ExecutionEngine/JITLink/MachO.h"
|
||||
#include "llvm/ExecutionEngine/JITLink/x86_64.h"
|
||||
#include "llvm/ExecutionEngine/Orc/DebugUtils.h"
|
||||
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
|
||||
@@ -246,6 +247,21 @@ private:
|
||||
ExecutorAddr MachOHeaderAddr;
|
||||
};
|
||||
|
||||
static StringRef ObjCRuntimeObjectSectionsData[] = {
|
||||
MachOObjCCatListSectionName, MachOObjCClassListSectionName,
|
||||
MachOObjCClassRefsSectionName, MachOObjCConstSectionName,
|
||||
MachOObjCDataSectionName, MachOObjCSelRefsSectionName};
|
||||
|
||||
static StringRef ObjCRuntimeObjectSectionsText[] = {
|
||||
MachOObjCClassNameSectionName, MachOObjCMethNameSectionName,
|
||||
MachOObjCMethTypeSectionName, MachOSwift5TypesSectionName,
|
||||
MachOSwift5TypeRefSectionName, MachOSwift5FieldMetadataSectionName,
|
||||
MachOSwift5EntrySectionName, MachOSwift5ProtoSectionName,
|
||||
MachOSwift5ProtosSectionName};
|
||||
|
||||
static StringRef ObjCRuntimeObjectSectionName =
|
||||
"__llvm_jitlink_ObjCRuntimeRegistrationObject";
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
namespace llvm {
|
||||
@@ -742,10 +758,14 @@ void MachOPlatform::MachOPlatformPlugin::modifyPassConfig(
|
||||
// If the object contains an init symbol other than the header start symbol
|
||||
// then add passes to preserve, process and register the init
|
||||
// sections/symbols.
|
||||
Config.PrePrunePasses.push_back([this, &MR](LinkGraph &G) {
|
||||
if (auto Err = preserveInitSections(G, MR))
|
||||
Config.PrePrunePasses.push_back(
|
||||
[this, &MR](LinkGraph &G) { return preserveImportantSections(G, MR); });
|
||||
Config.PostPrunePasses.push_back(
|
||||
[this](LinkGraph &G) { return createObjCRuntimeObject(G); });
|
||||
Config.PostAllocationPasses.push_back([this, &MR](LinkGraph &G) {
|
||||
if (auto Err = processObjCImageInfo(G, MR))
|
||||
return Err;
|
||||
return processObjCImageInfo(G, MR);
|
||||
return populateObjCRuntimeObject(G, MR);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -806,7 +826,10 @@ Error MachOPlatform::MachOPlatformPlugin::
|
||||
&MP.RegisterObjectPlatformSections.Addr},
|
||||
{*MP.DeregisterObjectPlatformSections.Name,
|
||||
&MP.DeregisterObjectPlatformSections.Addr},
|
||||
{*MP.CreatePThreadKey.Name, &MP.CreatePThreadKey.Addr}};
|
||||
{*MP.CreatePThreadKey.Name, &MP.CreatePThreadKey.Addr},
|
||||
{*MP.RegisterObjCRuntimeObject.Name, &MP.RegisterObjCRuntimeObject.Addr},
|
||||
{*MP.DeregisterObjCRuntimeObject.Name,
|
||||
&MP.DeregisterObjCRuntimeObject.Addr}};
|
||||
|
||||
bool RegisterMachOHeader = false;
|
||||
|
||||
@@ -875,9 +898,25 @@ Error MachOPlatform::MachOPlatformPlugin::associateJITDylibHeaderSymbol(
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error MachOPlatform::MachOPlatformPlugin::preserveInitSections(
|
||||
Error MachOPlatform::MachOPlatformPlugin::preserveImportantSections(
|
||||
jitlink::LinkGraph &G, MaterializationResponsibility &MR) {
|
||||
// __objc_imageinfo is "important": we want to preserve it and record its
|
||||
// address in the first graph that it appears in, then verify and discard it
|
||||
// in all subsequent graphs. In this pass we preserve unconditionally -- we'll
|
||||
// manually throw it away in the processObjCImageInfo pass.
|
||||
if (auto *ObjCImageInfoSec = G.findSectionByName("__DATA,__objc_imageinfo")) {
|
||||
if (ObjCImageInfoSec->blocks_size() != 1)
|
||||
return make_error<StringError>(
|
||||
"In " + G.getName() +
|
||||
"__DATA,__objc_imageinfo contains multiple blocks",
|
||||
inconvertibleErrorCode());
|
||||
G.addAnonymousSymbol(**ObjCImageInfoSec->blocks().begin(), 0, 0, false,
|
||||
true);
|
||||
}
|
||||
|
||||
// Init sections are important: We need to preserve them and so that their
|
||||
// addresses can be captured and reported to the ORC runtime in
|
||||
// registerObjectPlatformSections.
|
||||
JITLinkSymbolSet InitSectionSymbols;
|
||||
for (auto &InitSectionName : MachOInitSectionNames) {
|
||||
// Skip non-init sections.
|
||||
@@ -967,12 +1006,12 @@ Error MachOPlatform::MachOPlatformPlugin::processObjCImageInfo(
|
||||
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)
|
||||
if (ObjCImageInfoItr->second.Version != Version)
|
||||
return make_error<StringError>(
|
||||
"ObjC version in " + G.getName() +
|
||||
" does not match first registered version",
|
||||
inconvertibleErrorCode());
|
||||
if (ObjCImageInfoItr->second.second != Flags)
|
||||
if (ObjCImageInfoItr->second.Flags != Flags)
|
||||
return make_error<StringError>("ObjC flags in " + G.getName() +
|
||||
" do not match first registered flags",
|
||||
inconvertibleErrorCode());
|
||||
@@ -984,7 +1023,8 @@ Error MachOPlatform::MachOPlatformPlugin::processObjCImageInfo(
|
||||
} 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);
|
||||
ObjCImageInfos[&MR.getTargetJITDylib()] = {Version, Flags,
|
||||
ObjCImageInfoBlock.getAddress()};
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
@@ -1165,11 +1205,8 @@ Error MachOPlatform::MachOPlatformPlugin::registerObjectPlatformSections(
|
||||
|
||||
// If any platform sections were found then add an allocation action to call
|
||||
// the registration function.
|
||||
StringRef PlatformSections[] = {
|
||||
MachOModInitFuncSectionName, MachOObjCClassListSectionName,
|
||||
MachOObjCSelRefsSectionName, MachOSwift5ProtoSectionName,
|
||||
MachOSwift5ProtosSectionName, MachOSwift5TypesSectionName,
|
||||
};
|
||||
StringRef PlatformSections[] = {MachOModInitFuncSectionName,
|
||||
ObjCRuntimeObjectSectionName};
|
||||
|
||||
for (auto &SecName : PlatformSections) {
|
||||
auto *Sec = G.findSectionByName(SecName);
|
||||
@@ -1230,5 +1267,154 @@ Error MachOPlatform::MachOPlatformPlugin::registerObjectPlatformSections(
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error MachOPlatform::MachOPlatformPlugin::createObjCRuntimeObject(
|
||||
jitlink::LinkGraph &G) {
|
||||
|
||||
bool NeedTextSegment = false;
|
||||
size_t NumRuntimeSections = 0;
|
||||
|
||||
for (auto ObjCRuntimeSectionName : ObjCRuntimeObjectSectionsData)
|
||||
if (auto *Sec = G.findSectionByName(ObjCRuntimeSectionName))
|
||||
++NumRuntimeSections;
|
||||
|
||||
for (auto ObjCRuntimeSectionName : ObjCRuntimeObjectSectionsText) {
|
||||
if (auto *Sec = G.findSectionByName(ObjCRuntimeSectionName)) {
|
||||
++NumRuntimeSections;
|
||||
NeedTextSegment = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Early out for no runtime sections.
|
||||
if (NumRuntimeSections == 0)
|
||||
return Error::success();
|
||||
|
||||
// If there were any runtime sections then we need to add an __objc_imageinfo
|
||||
// section.
|
||||
++NumRuntimeSections;
|
||||
|
||||
size_t MachOSize = sizeof(MachO::mach_header_64) +
|
||||
(NeedTextSegment + 1) * sizeof(MachO::segment_command_64) +
|
||||
NumRuntimeSections * sizeof(MachO::section_64);
|
||||
|
||||
auto &Sec = G.createSection(ObjCRuntimeObjectSectionName,
|
||||
MemProt::Read | MemProt::Write);
|
||||
G.createMutableContentBlock(Sec, MachOSize, ExecutorAddr(), 16, 0, true);
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error MachOPlatform::MachOPlatformPlugin::populateObjCRuntimeObject(
|
||||
jitlink::LinkGraph &G, MaterializationResponsibility &MR) {
|
||||
|
||||
auto *ObjCRuntimeObjectSec =
|
||||
G.findSectionByName(ObjCRuntimeObjectSectionName);
|
||||
|
||||
if (!ObjCRuntimeObjectSec)
|
||||
return Error::success();
|
||||
|
||||
auto &SecBlock = **ObjCRuntimeObjectSec->blocks().begin();
|
||||
|
||||
std::vector<MachO::section_64> TextSections, DataSections;
|
||||
auto AddSection = [&](MachO::section_64 &Sec, jitlink::Section &GraphSec) {
|
||||
jitlink::SectionRange SR(GraphSec);
|
||||
StringRef FQName = GraphSec.getName();
|
||||
memset(&Sec, 0, sizeof(MachO::section_64));
|
||||
memcpy(Sec.sectname, FQName.drop_front(7).data(), FQName.size() - 7);
|
||||
memcpy(Sec.segname, FQName.data(), 6);
|
||||
Sec.addr = SR.getStart() - SecBlock.getAddress();
|
||||
Sec.size = SR.getSize();
|
||||
Sec.flags = MachO::S_REGULAR;
|
||||
};
|
||||
|
||||
// Add the __objc_imageinfo section.
|
||||
{
|
||||
DataSections.push_back({});
|
||||
auto &Sec = DataSections.back();
|
||||
memset(&Sec, 0, sizeof(Sec));
|
||||
strcpy(Sec.sectname, "__objc_imageinfo");
|
||||
strcpy(Sec.segname, "__DATA");
|
||||
std::lock_guard<std::mutex> Lock(PluginMutex);
|
||||
auto I = ObjCImageInfos.find(&MR.getTargetJITDylib());
|
||||
assert(I != ObjCImageInfos.end() && "Missing __objc_imageinfo");
|
||||
assert(std::get<2>(I->second) && "Null __objc_imageinfo");
|
||||
Sec.addr = I->second.Addr - SecBlock.getAddress();
|
||||
Sec.size = 8;
|
||||
}
|
||||
|
||||
for (auto ObjCRuntimeSectionName : ObjCRuntimeObjectSectionsData) {
|
||||
if (auto *GraphSec = G.findSectionByName(ObjCRuntimeSectionName)) {
|
||||
DataSections.push_back({});
|
||||
AddSection(DataSections.back(), *GraphSec);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto ObjCRuntimeSectionName : ObjCRuntimeObjectSectionsText) {
|
||||
if (auto *GraphSec = G.findSectionByName(ObjCRuntimeSectionName)) {
|
||||
TextSections.push_back({});
|
||||
AddSection(TextSections.back(), *GraphSec);
|
||||
}
|
||||
}
|
||||
|
||||
MachO::mach_header_64 Hdr;
|
||||
Hdr.magic = MachO::MH_MAGIC_64;
|
||||
switch (G.getTargetTriple().getArch()) {
|
||||
case Triple::aarch64:
|
||||
Hdr.cputype = MachO::CPU_TYPE_ARM64;
|
||||
Hdr.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL;
|
||||
break;
|
||||
case Triple::x86_64:
|
||||
Hdr.cputype = MachO::CPU_TYPE_X86_64;
|
||||
Hdr.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL;
|
||||
break;
|
||||
default:
|
||||
return make_error<StringError>("Unrecognized MachO arch in triple " +
|
||||
G.getTargetTriple().str(),
|
||||
inconvertibleErrorCode());
|
||||
}
|
||||
|
||||
Hdr.filetype = MachO::MH_DYLIB;
|
||||
Hdr.ncmds = 1 + !TextSections.empty();
|
||||
Hdr.sizeofcmds =
|
||||
Hdr.ncmds * sizeof(MachO::segment_command_64) +
|
||||
(TextSections.size() + DataSections.size()) * sizeof(MachO::section_64);
|
||||
Hdr.flags = 0;
|
||||
Hdr.reserved = 0;
|
||||
|
||||
assert(ObjCRuntimeObjectSec->blocks_size() == 1 &&
|
||||
"Unexpected number of blocks in runtime sections object");
|
||||
auto SecContent = SecBlock.getAlreadyMutableContent();
|
||||
|
||||
char *P = SecContent.data();
|
||||
auto WriteMachOStruct = [&](auto S) {
|
||||
if (G.getEndianness() != support::endian::system_endianness())
|
||||
MachO::swapStruct(S);
|
||||
memcpy(P, &S, sizeof(S));
|
||||
P += sizeof(S);
|
||||
};
|
||||
|
||||
auto WriteSegment = [&](StringRef Name,
|
||||
const std::vector<MachO::section_64> &Secs) {
|
||||
MachO::segment_command_64 SegLC;
|
||||
memset(&SegLC, 0, sizeof(SegLC));
|
||||
memcpy(SegLC.segname, Name.data(), Name.size());
|
||||
SegLC.cmd = MachO::LC_SEGMENT_64;
|
||||
SegLC.cmdsize = sizeof(MachO::segment_command_64) +
|
||||
Secs.size() * sizeof(MachO::section_64);
|
||||
SegLC.nsects = Secs.size();
|
||||
WriteMachOStruct(SegLC);
|
||||
for (auto &Sec : Secs)
|
||||
WriteMachOStruct(Sec);
|
||||
};
|
||||
|
||||
WriteMachOStruct(Hdr);
|
||||
if (!TextSections.empty())
|
||||
WriteSegment("__TEXT", TextSections);
|
||||
if (!DataSections.empty())
|
||||
WriteSegment("__DATA", DataSections);
|
||||
|
||||
assert(P == SecContent.end() && "Underflow writing ObjC runtime object");
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
} // End namespace orc.
|
||||
} // End namespace llvm.
|
||||
|
||||
@@ -20,20 +20,40 @@ StringRef MachODataDataSectionName = "__DATA,__data";
|
||||
StringRef MachOEHFrameSectionName = "__TEXT,__eh_frame";
|
||||
StringRef MachOCompactUnwindInfoSectionName = "__TEXT,__unwind_info";
|
||||
StringRef MachOModInitFuncSectionName = "__DATA,__mod_init_func";
|
||||
StringRef MachOObjCCatListSectionName = "__DATA,__objc_catlist";
|
||||
StringRef MachOObjCCatList2SectionName = "__DATA,__objc_catlist2";
|
||||
StringRef MachOObjCClassListSectionName = "__DATA,__objc_classlist";
|
||||
StringRef MachOObjCClassNameSectionName = "__TEXT,__objc_classname";
|
||||
StringRef MachOObjCClassRefsSectionName = "__DATA,__objc_classrefs";
|
||||
StringRef MachOObjCConstSectionName = "__DATA,__objc_const";
|
||||
StringRef MachOObjCDataSectionName = "__DATA,__objc_data";
|
||||
StringRef MachOObjCImageInfoSectionName = "__DATA,__objc_imageinfo";
|
||||
StringRef MachOObjCMethNameSectionName = "__TEXT,__objc_methname";
|
||||
StringRef MachOObjCMethTypeSectionName = "__TEXT,__objc_methtype";
|
||||
StringRef MachOObjCNLCatListSectionName = "__DATA,__objc_nlcatlist";
|
||||
StringRef MachOObjCSelRefsSectionName = "__DATA,__objc_selrefs";
|
||||
StringRef MachOSwift5ProtoSectionName = "__TEXT,__swift5_proto";
|
||||
StringRef MachOSwift5ProtosSectionName = "__TEXT,__swift5_protos";
|
||||
StringRef MachOSwift5TypesSectionName = "__TEXT,__swift5_types";
|
||||
StringRef MachOSwift5TypeRefSectionName = "__TEXT,__swift5_typeref";
|
||||
StringRef MachOSwift5FieldMetadataSectionName = "__TEXT,__swift5_fieldmd";
|
||||
StringRef MachOSwift5EntrySectionName = "__TEXT,__swift5_entry";
|
||||
StringRef MachOThreadBSSSectionName = "__DATA,__thread_bss";
|
||||
StringRef MachOThreadDataSectionName = "__DATA,__thread_data";
|
||||
StringRef MachOThreadVarsSectionName = "__DATA,__thread_vars";
|
||||
|
||||
StringRef MachOInitSectionNames[6] = {
|
||||
MachOModInitFuncSectionName, MachOObjCSelRefsSectionName,
|
||||
MachOObjCClassListSectionName, MachOSwift5ProtosSectionName,
|
||||
MachOSwift5ProtoSectionName, MachOSwift5TypesSectionName};
|
||||
StringRef MachOInitSectionNames[19] = {
|
||||
MachOModInitFuncSectionName, MachOObjCCatListSectionName,
|
||||
MachOObjCCatList2SectionName, MachOObjCClassListSectionName,
|
||||
MachOObjCClassNameSectionName, MachOObjCClassRefsSectionName,
|
||||
MachOObjCConstSectionName, MachOObjCDataSectionName,
|
||||
MachOObjCImageInfoSectionName, MachOObjCMethNameSectionName,
|
||||
MachOObjCMethTypeSectionName, MachOObjCNLCatListSectionName,
|
||||
MachOObjCSelRefsSectionName, MachOSwift5ProtoSectionName,
|
||||
MachOSwift5ProtosSectionName, MachOSwift5TypesSectionName,
|
||||
MachOSwift5TypeRefSectionName, MachOSwift5FieldMetadataSectionName,
|
||||
MachOSwift5EntrySectionName,
|
||||
};
|
||||
|
||||
StringRef ELFEHFrameSectionName = ".eh_frame";
|
||||
StringRef ELFInitArrayFuncSectionName = ".init_array";
|
||||
|
||||
Reference in New Issue
Block a user