-
Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy pathBincompat.cpp
314 lines (274 loc) · 11 KB
/
Bincompat.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
//===--- Bincompat.cpp - Binary compatibility checks. -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Checks for enabling binary compatibility workarounds.
//
//===----------------------------------------------------------------------===//
#include "swift/Runtime/Config.h"
#include "swift/Runtime/Bincompat.h"
#include "swift/Runtime/Debug.h"
#include "swift/Runtime/EnvironmentVariables.h"
#include "swift/Threading/Once.h"
#include "swift/shims/RuntimeShims.h"
#include "swift/shims/Target.h"
#include <stdint.h>
// If this is an Apple OS, use the Apple binary compatibility rules
#if __has_include(<mach-o/dyld_priv.h>) && defined(SWIFT_RUNTIME_OS_VERSIONING)
#include <mach-o/dyld_priv.h>
#ifndef BINARY_COMPATIBILITY_APPLE
#define BINARY_COMPATIBILITY_APPLE 1
#endif
#else
#undef BINARY_COMPATIBILITY_APPLE
#endif
namespace swift {
namespace runtime {
namespace bincompat {
#if BINARY_COMPATIBILITY_APPLE
enum sdk_test {
oldOS, // Can't tell the app SDK used because this is too old an OS
oldApp,
newApp
};
static enum sdk_test isAppAtLeast(dyld_build_version_t version) {
if (__builtin_available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *)) {
// Query the SDK version used to build the currently-running executable
if (dyld_program_sdk_at_least(version)) {
return newApp;
} else {
return oldApp;
}
}
// Older Apple OS lack the ability to test the SDK version of the running app
return oldOS;
}
static enum sdk_test isAppAtLeastSpring2021() {
const dyld_build_version_t spring_2021_os_versions = {0xffffffff, 0x007e50301};
return isAppAtLeast(spring_2021_os_versions);
}
static enum sdk_test isAppAtLeastFall2023() {
const dyld_build_version_t fall_2023_os_versions = {0xffffffff, 0x007e70901};
return isAppAtLeast(fall_2023_os_versions);
}
static enum sdk_test isAppAtLeastFall2024() {
const dyld_build_version_t fall_2024_os_versions = {0xffffffff, 0x007e80000};
return isAppAtLeast(fall_2024_os_versions);
}
#endif
static _SwiftStdlibVersion binCompatVersionOverride = { 0 };
static _SwiftStdlibVersion const knownVersions[] = {
{ /* 5.6.0 */0x050600 },
{ /* 5.7.0 */0x050700 },
// Note: If you add a new entry here, also add it to versionMap in
// _swift_stdlib_isExecutableLinkedOnOrAfter below.
{ 0 },
};
static bool isKnownBinCompatVersion(_SwiftStdlibVersion version) {
for (int i = 0; knownVersions[i]._value != 0; ++i) {
if (knownVersions[i]._value == version._value) {
return true;
}
}
return false;
}
static void checkBinCompatEnvironmentVariable(void *context) {
_SwiftStdlibVersion version =
{ runtime::environment::SWIFT_BINARY_COMPATIBILITY_VERSION() };
if (version._value > 0 && !isKnownBinCompatVersion(version)) {
swift::warning(RuntimeErrorFlagNone,
"Warning: ignoring unknown SWIFT_BINARY_COMPATIBILITY_VERSION %x.\n",
version._value);
return;
}
binCompatVersionOverride = version;
}
extern "C" __swift_bool _swift_stdlib_isExecutableLinkedOnOrAfter(
_SwiftStdlibVersion version
) {
static once_t getenvToken;
swift::once(getenvToken, checkBinCompatEnvironmentVariable, nullptr);
if (binCompatVersionOverride._value > 0) {
return version._value <= binCompatVersionOverride._value;
}
#if BINARY_COMPATIBILITY_APPLE
typedef struct {
_SwiftStdlibVersion stdlib;
dyld_build_version_t dyld;
} stdlib_version_map;
const dyld_build_version_t spring_2022_os_versions = {0xffffffff, 0x007e60301};
const dyld_build_version_t fall_2022_os_versions = {0xffffffff, 0x007e60901};
static stdlib_version_map const versionMap[] = {
{ { /* 5.6.0 */0x050600 }, spring_2022_os_versions },
{ { /* 5.7.0 */0x050700 }, fall_2022_os_versions },
// Note: if you add a new entry here, also add it to knownVersions above.
{ { 0 }, { 0, 0 } },
};
for (uint32_t i = 0; versionMap[i].stdlib._value != 0; ++i) {
if (versionMap[i].stdlib._value == version._value) {
return isAppAtLeast(versionMap[i].dyld) == newApp;
}
}
return false;
#else // !BINARY_COMPATIBILITY_APPLE
return isKnownBinCompatVersion(version);
#endif
}
// Should we mimic the old override behavior when scanning protocol conformance records?
// Old apps expect protocol conformances to override each other in a particular
// order. Starting with Swift 5.4, that order has changed as a result of
// significant performance improvements to protocol conformance scanning. If
// this returns `true`, the protocol conformance scan will do extra work to
// mimic the old override behavior.
bool useLegacyProtocolConformanceReverseIteration() {
#if BINARY_COMPATIBILITY_APPLE
switch (isAppAtLeastSpring2021()) {
case oldOS: return false; // New (non-legacy) behavior on old OSes
case oldApp: return true; // Legacy behavior for pre-Spring 2021 apps on new OS
case newApp: return false; // New behavior for new apps
}
#else
return false; // Never use the legacy behavior on non-Apple OSes
#endif
}
// Should the dynamic cast operation crash when it sees
// a non-nullable Obj-C pointer with a null value?
// Obj-C does not strictly enforce non-nullability in all cases, so it is
// possible for Obj-C code to pass null pointers into Swift code even when
// declared non-nullable. Such null pointers can lead to undefined behavior
// later on. Starting in Swift 5.4, these unexpected null pointers are fatal
// runtime errors, but this is selectively disabled for old apps.
bool useLegacyPermissiveObjCNullSemanticsInCasting() {
#if BINARY_COMPATIBILITY_APPLE
switch (isAppAtLeastSpring2021()) {
case oldOS: return true; // Permissive (legacy) behavior on old OS
case oldApp: return true; // Permissive (legacy) behavior for old apps
case newApp: return false; // Strict behavior for new apps
}
#else
return false; // Always use the strict behavior on non-Apple OSes
#endif
}
// Should casting a nil optional to another optional
// use the legacy semantics?
// For consistency, starting with Swift 5.4, casting Optional<Int> to
// Optional<Optional<Int>> always wraps the source in another layer
// of Optional.
// Earlier versions of the Swift runtime did not do this if the source
// optional was nil. In that case, the outer target optional would be
// set to nil.
bool useLegacyOptionalNilInjectionInCasting() {
#if BINARY_COMPATIBILITY_APPLE
switch (isAppAtLeastSpring2021()) {
case oldOS: return true; // Legacy behavior on old OS
case oldApp: return true; // Legacy behavior for old apps
case newApp: return false; // Consistent behavior for new apps
}
#else
return false; // Always use the 5.4 behavior on non-Apple OSes
#endif
}
// Should casting be strict about protocol conformance when
// boxing Swift values to pass to Obj-C?
// Earlier versions of the Swift runtime would allow you to
// cast a swift value to e.g., `NSCopying` or `NSObjectProtocol`
// even if that value did not actually conform. This was
// due to the fact that the `__SwiftValue` box type itself
// conformed to these protocols.
// But this was not really sound, as it implies for example that
// `x is NSCopying` is always `true` regardless of whether
// `x` actually has the `copyWithZone()` method required
// by that protocol.
bool useLegacyObjCBoxingInCasting() {
#if BINARY_COMPATIBILITY_APPLE
switch (isAppAtLeastFall2023()) {
case oldOS: return true; // Legacy behavior on old OS
case oldApp: return true; // Legacy behavior for old apps
case newApp: return false; // New behavior for new apps
}
#else
return false; // Always use the new behavior on non-Apple OSes
#endif
}
// Should casting be strict about protocol conformance when
// unboxing values that were boxed for Obj-C use?
// Similar to `useLegacyObjCBoxingInCasting()`, but
// this applies to the case where you have already boxed
// some Swift non-reference-type into a `__SwiftValue`
// and are now casting to a protocol.
// For example, this cast
// `x as! AnyObject as? NSCopying`
// always succeeded with the legacy semantics.
bool useLegacySwiftValueUnboxingInCasting() {
#if BINARY_COMPATIBILITY_APPLE
switch (isAppAtLeastFall2023()) {
case oldOS: return true; // Legacy behavior on old OS
case oldApp: return true; // Legacy behavior for old apps
case newApp: return false; // New behavior for new apps
}
#else
return false; // Always use the new behavior on non-Apple OSes
#endif
}
// Controls how ObjC -hashValue and -isEqual are handled
// by Swift objects.
// There are two basic semantics:
// * pointer: -hashValue returns pointer, -isEqual: tests pointer equality
// * proxy: -hashValue calls on Hashable conformance, -isEqual: calls Equatable conformance
//
// Legacy handling:
// * Swift struct/enum values that implement Hashable: proxy -hashValue and -isEqual:
// * Swift struct/enum values that implement Equatable but not Hashable: pointer semantics
// * Swift class values regardless of hashable/Equatable support: pointer semantics
//
// New behavior:
// * Swift struct/enum/class values that implement Hashable: proxy -hashValue and -isEqual:
// * Swift struct/enum/class values that implement Equatable but not Hashable: proxy -isEqual:, constant -hashValue
// * All other cases: pointer semantics
//
bool useLegacySwiftObjCHashing() {
#if BINARY_COMPATIBILITY_APPLE
switch (isAppAtLeastFall2024()) {
case oldOS: return true; // Legacy behavior on old OS
case oldApp: return true; // Legacy behavior for old apps
case newApp: return false; // New behavior for new apps
}
#else
return false; // Always use the new behavior on non-Apple OSes
#endif
}
// Controls legacy mode for the 'swift_task_isCurrentExecutorImpl' runtime function.
//
// In "legacy" / "no crash" mode:
// * The `swift_task_isCurrentExecutorImpl` cannot crash
// * This means cases where no "current" executor is present cannot be diagnosed correctly
// * The runtime can NOT use 'SerialExecutor/checkIsolated'
// * The runtime can NOT use 'dispatch_precondition' which is able to handle some dispatch and main actor edge cases
//
// New behavior in "swift6" "crash" mode:
// * The 'swift_task_isCurrentExecutorImpl' will CRASH rather than return 'false'
// * This allows the method to invoke 'SerialExecutor/checkIsolated'
// * Which is allowed to call 'dispatch_precondition' and handle "on dispatch queue but not on Swift executor" cases
//
bool swift_bincompat_useLegacyNonCrashingExecutorChecks() {
#if BINARY_COMPATIBILITY_APPLE
switch (isAppAtLeastFall2024()) {
case oldOS: return true; // Legacy behavior on old OS
case oldApp: return true; // Legacy behavior for old apps
case newApp: return false; // New behavior for new apps
}
#else
return false; // Always use the new behavior on non-Apple OSes
#endif
}
} // namespace bincompat
} // namespace runtime
} // namespace swift