Skip to content

Commit c1f4d99

Browse files
committed
[Runtime] Fix protocol conformances on NULL types when back deploying.
Add code to the 5.0 compatibility library that scans for conformances pointing to a NULL type and rewrites them to point to a dummy type that the 5.0 protocol conformance checking code will safely ignore.
1 parent 5c0b60b commit c1f4d99

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

include/swift/ABI/Metadata.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2333,6 +2333,12 @@ struct TargetProtocolConformanceDescriptor final
23332333
return TypeRef.getTypeDescriptor(getTypeKind());
23342334
}
23352335

2336+
const TargetContextDescriptor<Runtime> **_getTypeDescriptorLocation() const {
2337+
if (getTypeKind() != TypeReferenceKind::IndirectTypeDescriptor)
2338+
return nullptr;
2339+
return TypeRef.IndirectTypeDescriptor.get();
2340+
}
2341+
23362342
/// Retrieve the context of a retroactive conformance.
23372343
const TargetContextDescriptor<Runtime> *getRetroactiveContext() const {
23382344
if (!Flags.isRetroactive()) return nullptr;

stdlib/toolchain/Compatibility50/ProtocolConformance.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,22 @@
2020
#include "Overrides.h"
2121
#include "../../public/runtime/Private.h"
2222
#include "swift/Basic/Lazy.h"
23+
#include <mach-o/dyld.h>
24+
#include <mach-o/getsect.h>
2325
#include <objc/runtime.h>
2426

2527
using namespace swift;
2628

29+
#if __POINTER_WIDTH__ == 64
30+
using mach_header_platform = mach_header_64;
31+
#else
32+
using mach_header_platform = mach_header;
33+
#endif
34+
35+
/// The Mach-O section name for the section containing protocol conformances.
36+
/// This lives within SEG_TEXT.
37+
constexpr const char ProtocolConformancesSection[] = "__swift5_proto";
38+
2739
// Clone of private function getRootSuperclass. This returns the SwiftObject
2840
// class in the ABI-stable dylib, regardless of what the local runtime build
2941
// does, since we're always patching an ABI-stable dylib.
@@ -33,6 +45,50 @@ const ClassMetadata *swift::getRootSuperclass() {
3345
return (const ClassMetadata *)theClass;
3446
}
3547

48+
// A dummy target context descriptor to use in conformance records which point
49+
// to a NULL descriptor. It doesn't have to be completely valid, just something
50+
// that code reading conformance descriptors will ignore.
51+
struct {
52+
ContextDescriptorFlags flags;
53+
int32_t offset;
54+
} DummyTargetContextDescriptor = {
55+
ContextDescriptorFlags().withKind(ContextDescriptorKind::Extension),
56+
0
57+
};
58+
59+
// Search for any protocol conformance descriptors with a NULL type descriptor
60+
// and rewrite those to point to the dummy descriptor. This occurs when an
61+
// extension is used to declare a conformance on a weakly linked type and that
62+
// type is not present at runtime.
63+
static void addImageCallback(const mach_header *mh, intptr_t vmaddr_slide) {
64+
unsigned long size;
65+
const uint8_t *section =
66+
getsectiondata(reinterpret_cast<const mach_header_platform *>(mh),
67+
SEG_TEXT, ProtocolConformancesSection,
68+
&size);
69+
if (!section)
70+
return;
71+
72+
auto recordsBegin
73+
= reinterpret_cast<const ProtocolConformanceRecord*>(section);
74+
auto recordsEnd
75+
= reinterpret_cast<const ProtocolConformanceRecord*>
76+
(section + size);
77+
for (auto record = recordsBegin; record != recordsEnd; record++) {
78+
auto descriptor = record->get();
79+
if (auto typePtr = descriptor->_getTypeDescriptorLocation()) {
80+
if (*typePtr == nullptr)
81+
*typePtr = reinterpret_cast<TargetContextDescriptor<InProcess> *>(
82+
&DummyTargetContextDescriptor);
83+
}
84+
}
85+
}
86+
87+
// Register the add image callback with dyld.
88+
static void registerAddImageCallback(void *) {
89+
_dyld_register_func_for_add_image(addImageCallback);
90+
}
91+
3692
// Clone of private helper swift::_swiftoverride_class_getSuperclass
3793
// for use in the override implementation.
3894
static const Metadata *_swift50override_class_getSuperclass(
@@ -56,6 +112,10 @@ swift::swift50override_conformsToProtocol(const Metadata *type,
56112
const ProtocolDescriptor *protocol,
57113
ConformsToProtocol_t *original_conformsToProtocol)
58114
{
115+
// Register our add image callback if necessary.
116+
static OnceToken_t token;
117+
SWIFT_ONCE_F(token, registerAddImageCallback, nullptr);
118+
59119
// The implementation of swift_conformsToProtocol in Swift 5.0 would return
60120
// a false negative answer when asking whether a subclass conforms using
61121
// a conformance from a superclass. Work around this by walking up the

0 commit comments

Comments
 (0)