mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Runtime] Dynamically select the is-Swift bit at runtime on Apple platforms.
Recent Swift uses 2 as the is-Swift bit when running on newer versions, and 1 on older versions. Since it's difficult or impossible to know what we'll be running on at build time, make the selection at runtime.
This commit is contained in:
@@ -235,8 +235,8 @@ option(SWIFT_RUNTIME_CRASH_REPORTER_CLIENT
|
||||
FALSE)
|
||||
|
||||
option(SWIFT_DARWIN_ENABLE_STABLE_ABI_BIT
|
||||
"Enable the Swift stable ABI's class marker bit"
|
||||
FALSE)
|
||||
"Enable the Swift stable ABI's class marker bit for new deployment targets"
|
||||
TRUE)
|
||||
|
||||
set(SWIFT_DARWIN_XCRUN_TOOLCHAIN "XcodeDefault" CACHE STRING
|
||||
"The name of the toolchain to pass to 'xcrun'")
|
||||
|
||||
@@ -1192,6 +1192,31 @@ public:
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/// Given a statically-emitted metadata template, this sets the correct
|
||||
/// "is Swift" bit for the current runtime. Depending on the deployment
|
||||
/// target a binary was compiled for, statically emitted metadata templates
|
||||
/// may have a different bit set from the one that this runtime canonically
|
||||
/// considers the "is Swift" bit.
|
||||
void setAsTypeMetadata() {
|
||||
// If the wrong "is Swift" bit is set, set the correct one.
|
||||
//
|
||||
// Note that the only time we should see the "new" bit set while
|
||||
// expecting the "old" one is when running a binary built for a
|
||||
// new OS on an old OS, which is not supported, however we do
|
||||
// have tests that exercise this scenario.
|
||||
auto otherSwiftBit = (3ULL - SWIFT_CLASS_IS_SWIFT_MASK);
|
||||
assert(otherSwiftBit == 1ULL || otherSwiftBit == 2ULL);
|
||||
|
||||
if ((this->Data & 3) == otherSwiftBit) {
|
||||
this->Data ^= 3;
|
||||
}
|
||||
|
||||
// Otherwise there should be nothing to do, since only the old "is
|
||||
// Swift" bit is used for backward-deployed runtimes.
|
||||
|
||||
assert(isTypeMetadata());
|
||||
}
|
||||
|
||||
static bool classof(const TargetMetadata<Runtime> *metadata) {
|
||||
return metadata->getKind() == MetadataKind::Class;
|
||||
}
|
||||
|
||||
45
include/swift/Runtime/BackDeployment.h
Normal file
45
include/swift/Runtime/BackDeployment.h
Normal file
@@ -0,0 +1,45 @@
|
||||
//===--- BackDeployment.h - Support for running on older OS versions. -----===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2019 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SWIFT_STDLIB_BACKDEPLOYMENT_H
|
||||
#define SWIFT_STDLIB_BACKDEPLOYMENT_H
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
|
||||
#include "swift/Runtime/Config.h"
|
||||
#include "../../../stdlib/public/SwiftShims/Visibility.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace swift { extern "C" {
|
||||
#endif
|
||||
|
||||
#if SWIFT_CLASS_IS_SWIFT_MASK_GLOBAL_VARIABLE
|
||||
# ifndef __cplusplus
|
||||
// This file gets included from some C/ObjC files and
|
||||
// SWIFT_RUNTIME_STDLIB_SPI doesn't imply extern in C.
|
||||
extern
|
||||
# endif
|
||||
SWIFT_RUNTIME_STDLIB_SPI unsigned long long _swift_classIsSwiftMask;
|
||||
#endif
|
||||
|
||||
/// Returns true if the current OS version, at runtime, is a back-deployment
|
||||
/// version.
|
||||
SWIFT_RUNTIME_STDLIB_INTERNAL
|
||||
int _swift_isBackDeploying();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}} // extern "C", namespace swift
|
||||
#endif
|
||||
|
||||
#endif // defined(__APPLE__) && defined(__MACH__)
|
||||
|
||||
#endif // SWIFT_STDLIB_BACKDEPLOYMENT_H
|
||||
@@ -5,5 +5,7 @@
|
||||
#define SWIFT_RUNTIME_CMAKECONFIG_H
|
||||
|
||||
#cmakedefine01 SWIFT_DARWIN_ENABLE_STABLE_ABI_BIT
|
||||
#cmakedefine01 SWIFT_BNI_OS_BUILD
|
||||
#cmakedefine01 SWIFT_BNI_XCODE_BUILD
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,2 +1,13 @@
|
||||
# Detect B&I builds.
|
||||
set(SWIFT_BNI_OS_BUILD FALSE)
|
||||
set(SWIFT_BNI_XCODE_BUILD FALSE)
|
||||
if(DEFINED ENV{RC_XBS})
|
||||
if(NOT DEFINED ENV{RC_XCODE} OR NOT "$ENV{RC_XCODE}")
|
||||
set(SWIFT_BNI_OS_BUILD TRUE)
|
||||
else()
|
||||
set(SWIFT_BNI_XCODE_BUILD TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
configure_file(CMakeConfig.h.in ${CMAKE_CURRENT_BINARY_DIR}/CMakeConfig.h
|
||||
ESCAPE_QUOTES @ONLY)
|
||||
|
||||
@@ -141,10 +141,27 @@
|
||||
/// Which bits in the class metadata are used to distinguish Swift classes
|
||||
/// from ObjC classes?
|
||||
#ifndef SWIFT_CLASS_IS_SWIFT_MASK
|
||||
# if defined(__APPLE__) && SWIFT_OBJC_INTEROP && SWIFT_DARWIN_ENABLE_STABLE_ABI_BIT
|
||||
# define SWIFT_CLASS_IS_SWIFT_MASK 2ULL
|
||||
# else
|
||||
|
||||
// Non-Apple platforms always use 1.
|
||||
# if !defined(__APPLE__)
|
||||
# define SWIFT_CLASS_IS_SWIFT_MASK 1ULL
|
||||
|
||||
// Builds for Swift-in-the-OS always use 2.
|
||||
# elif SWIFT_BNI_OS_BUILD
|
||||
# define SWIFT_CLASS_IS_SWIFT_MASK 2ULL
|
||||
|
||||
// Builds for Xcode always use 1.
|
||||
# elif SWIFT_BNI_XCODE_BUILD
|
||||
# define SWIFT_CLASS_IS_SWIFT_MASK 1ULL
|
||||
|
||||
// Other builds (such as local builds on developers' computers)
|
||||
// dynamically choose the bit at runtime based on the current OS
|
||||
// version.
|
||||
# else
|
||||
# define SWIFT_CLASS_IS_SWIFT_MASK _swift_classIsSwiftMask
|
||||
# define SWIFT_CLASS_IS_SWIFT_MASK_GLOBAL_VARIABLE 1
|
||||
# include "BackDeployment.h"
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern unsigned long long swift_reflection_classIsSwiftMask;
|
||||
|
||||
/// Get the metadata version supported by the Remote Mirror library.
|
||||
SWIFT_REMOTE_MIRROR_LINKAGE
|
||||
uint16_t swift_reflection_getSupportedMetadataVersion(void);
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define SWIFT_CLASS_IS_SWIFT_MASK swift_reflection_classIsSwiftMask
|
||||
extern "C" unsigned long long swift_reflection_classIsSwiftMask = 2;
|
||||
|
||||
#include "swift/Reflection/ReflectionContext.h"
|
||||
#include "swift/Reflection/TypeLowering.h"
|
||||
#include "swift/Remote/CMemoryReader.h"
|
||||
|
||||
64
stdlib/public/runtime/BackDeployment.cpp
Normal file
64
stdlib/public/runtime/BackDeployment.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
//===--- BackDeployment.cpp - Support for running on older OS versions. ---===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2019 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "swift/Runtime/BackDeployment.h"
|
||||
#include "swift/Runtime/Config.h"
|
||||
#include "../SwiftShims/FoundationShims.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
|
||||
#if SWIFT_CLASS_IS_SWIFT_MASK_GLOBAL_VARIABLE
|
||||
static unsigned long long computeIsSwiftMask() {
|
||||
if (swift::_swift_isBackDeploying())
|
||||
return 1ULL;
|
||||
return 2ULL;
|
||||
}
|
||||
|
||||
SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_BEGIN
|
||||
extern "C" unsigned long long
|
||||
_swift_classIsSwiftMask = computeIsSwiftMask();
|
||||
SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_END
|
||||
#endif // SWIFT_CLASS_IS_SWIFT_MASK_GLOBAL_VARIABLE
|
||||
|
||||
static swift::_SwiftNSOperatingSystemVersion swiftInOSVersion = {
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
10, 14, 4
|
||||
// WatchOS also pretends to be iOS, so check it first.
|
||||
#elif __WATCH_OS_VERSION_MIN_REQUIRED
|
||||
5, 2, 0
|
||||
#elif __IPHONE_OS_VERSION_MIN_REQUIRED || __TV_OS_VERSION_MIN_REQUIRED
|
||||
12, 2, 0
|
||||
#else
|
||||
9999, 0, 0
|
||||
#endif
|
||||
};
|
||||
|
||||
static bool versionLessThan(swift::_SwiftNSOperatingSystemVersion lhs,
|
||||
swift::_SwiftNSOperatingSystemVersion rhs) {
|
||||
if (lhs.majorVersion < rhs.majorVersion) return true;
|
||||
if (lhs.majorVersion > rhs.majorVersion) return false;
|
||||
|
||||
if (lhs.minorVersion < rhs.minorVersion) return true;
|
||||
if (lhs.minorVersion > rhs.minorVersion) return false;
|
||||
|
||||
if (lhs.patchVersion < rhs.patchVersion) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SWIFT_RUNTIME_STDLIB_INTERNAL
|
||||
int _swift_isBackDeploying() {
|
||||
auto version = swift::_swift_stdlib_operatingSystemVersion();
|
||||
return versionLessThan(version, swiftInOSVersion);
|
||||
}
|
||||
#endif
|
||||
@@ -31,6 +31,7 @@ set(swift_runtime_objc_sources
|
||||
set(swift_runtime_sources
|
||||
AnyHashableSupport.cpp
|
||||
Array.cpp
|
||||
BackDeployment.cpp
|
||||
Casting.cpp
|
||||
CompatibilityOverride.cpp
|
||||
CygwinPort.cpp
|
||||
|
||||
@@ -96,7 +96,15 @@ Metadata *TargetSingletonMetadataInitialization<InProcess>::allocate(
|
||||
// If this is a class, we have to initialize the value witness table early
|
||||
// so that two-phase initialization can proceed as if this metadata is
|
||||
// complete for layout purposes when it appears as part of an aggregate type.
|
||||
if (auto *classMetadata = dyn_cast<ClassMetadata>(metadata)) {
|
||||
//
|
||||
// Note that we can't use (dyn_)cast<ClassMetadata> here because the static
|
||||
// template may have the "wrong" isSwift bit set in its Data pointer, if the
|
||||
// binary was built to deploy back to pre-stable-Swift Objective-C runtimes.
|
||||
// Such a template will fail the `isTypeMetadata` test and we'll think that it
|
||||
// isn't Swift metadata but a plain old ObjC class instead.
|
||||
if (metadata->getKind() == MetadataKind::Class) {
|
||||
auto *classMetadata = static_cast<ClassMetadata*>(metadata);
|
||||
classMetadata->setAsTypeMetadata();
|
||||
auto *fullMetadata = asFullMetadata(metadata);
|
||||
|
||||
// Begin by initializing the value witness table; everything else is
|
||||
|
||||
@@ -551,6 +551,20 @@ int doDumpHeapInstance(const char *BinaryFilename) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#include <dlfcn.h>
|
||||
static unsigned long long computeClassIsSwiftMask(void) {
|
||||
uintptr_t *objc_debug_swift_stable_abi_bit_ptr =
|
||||
(uintptr_t *)dlsym(RTLD_DEFAULT, "objc_debug_swift_stable_abi_bit");
|
||||
return objc_debug_swift_stable_abi_bit_ptr ?
|
||||
*objc_debug_swift_stable_abi_bit_ptr : 1;
|
||||
}
|
||||
#else
|
||||
static unsigned long long computeClassIsSwiftMask(void) {
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void printUsageAndExit() {
|
||||
fprintf(stderr, "swift-reflection-test <binary filename>\n");
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -562,6 +576,8 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
const char *BinaryFilename = argv[1];
|
||||
|
||||
swift_reflection_classIsSwiftMask = computeClassIsSwiftMask();
|
||||
|
||||
uint16_t Version = swift_reflection_getSupportedMetadataVersion();
|
||||
printf("Metadata version: %u\n", Version);
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ sil_vtable C {}
|
||||
// CHECK-INDIRECT-SAME: %swift.type* null,
|
||||
// CHECK-SAME: [[OPAQUE]]* @_objc_empty_cache,
|
||||
// CHECK-SAME: [[OPAQUE]]* null,
|
||||
// CHECK-SAME: i64 add (i64 ptrtoint ({{.*}}* @_DATA__TtC5class1C to i64), i64 1)
|
||||
// CHECK-SAME: i64 add (i64 ptrtoint ({{.*}}* @_DATA__TtC5class1C to i64), i64 [[IS_SWIFT_BIT:1|2]])
|
||||
// CHECK-SAME: }>
|
||||
|
||||
// Destroying destructor
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
// -- ivar destroyer:
|
||||
// CHECK-SAME: i32 0,
|
||||
// -- flags:
|
||||
// CHECK-SAME: i32 3,
|
||||
// CHECK-SAME: i32 2,
|
||||
// -- RO data:
|
||||
// CHECK-objc-SAME: @_DATA__TtC16class_resilience14ResilientChild
|
||||
// CHECK-native-SAME: i32 0,
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// CHECK-INDIRECT-SAME: [[TYPE]]* null,
|
||||
// CHECK-SAME: [[OPAQUE]]* @_objc_empty_cache,
|
||||
// CHECK-SAME: [[OPAQUE]]* null,
|
||||
// CHECK-SAME: i64 add (i64 ptrtoint ({ {{.*}} }* @_DATA__TtC8subclass1A to i64), i64 1),
|
||||
// CHECK-SAME: i64 add (i64 ptrtoint ({ {{.*}} }* @_DATA__TtC8subclass1A to i64), i64 [[IS_SWIFT_BIT:1|2]]),
|
||||
// CHECK-SAME: i64 ([[A]]*)* @"$s8subclass1AC1fSiyF",
|
||||
// CHECK-SAME: [[A]]* ([[TYPE]]*)* @"$s8subclass1AC1gACyFZ"
|
||||
// CHECK-SAME: }>
|
||||
@@ -34,7 +34,7 @@
|
||||
// CHECK-INDIRECT-SAME: [[TYPE]]* null,
|
||||
// CHECK-SAME: [[OPAQUE]]* @_objc_empty_cache,
|
||||
// CHECK-SAME: [[OPAQUE]]* null,
|
||||
// CHECK-SAME: i64 add (i64 ptrtoint ({ {{.*}} }* @_DATA__TtC8subclass1B to i64), i64 1),
|
||||
// CHECK-SAME: i64 add (i64 ptrtoint ({ {{.*}} }* @_DATA__TtC8subclass1B to i64), i64 [[IS_SWIFT_BIT]]),
|
||||
// CHECK-SAME: i64 ([[B]]*)* @"$s8subclass1BC1fSiyF",
|
||||
// CHECK-SAME: [[A]]* ([[TYPE]]*)* @"$s8subclass1AC1gACyFZ"
|
||||
// CHECK-SAME: }>
|
||||
|
||||
29
test/Runtime/stable-bit-backward-deployment.swift
Normal file
29
test/Runtime/stable-bit-backward-deployment.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// -- Deployment target is set to pre-10.14.4 so that we use the "old"
|
||||
// Swift runtime bit in compiler-emitted classes
|
||||
// RUN: %target-build-swift -target x86_64-apple-macosx10.9 %s -module-name main -o %t/a.out
|
||||
// RUN: %target-run %t/a.out | %FileCheck %s
|
||||
|
||||
// REQUIRES: executable_test
|
||||
// REQUIRES: OS=macosx
|
||||
|
||||
import Foundation
|
||||
|
||||
// A fixed-layout class should be considered Swift metadata by the OS runtime.
|
||||
class FixedLayout { }
|
||||
|
||||
debugPrint(FixedLayout.self) // CHECK: main.FixedLayout
|
||||
|
||||
// A generic class
|
||||
class GenericBase<T> { }
|
||||
debugPrint(GenericBase<Int>.self) // CHECK-NEXT: main.GenericBase<Swift.Int>
|
||||
|
||||
// A singleton-initialized class
|
||||
class SingletonInit: GenericBase<Int> { }
|
||||
debugPrint(SingletonInit.self) // CHECK-NEXT: main.SingletonInit
|
||||
|
||||
// A resilient-heritage class
|
||||
class ResilientSubInit: JSONEncoder {}
|
||||
debugPrint(ResilientSubInit.self) // CHECK-NEXT: main.ResilientSubInit
|
||||
|
||||
print("nailed it") // CHECK-NEXT: nailed it
|
||||
@@ -38,6 +38,23 @@ using namespace swift;
|
||||
using namespace swift::remote;
|
||||
using namespace swift::remoteAST;
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#include <dlfcn.h>
|
||||
static unsigned long long computeClassIsSwiftMask(void) {
|
||||
uintptr_t *objc_debug_swift_stable_abi_bit_ptr =
|
||||
(uintptr_t *)dlsym(RTLD_DEFAULT, "objc_debug_swift_stable_abi_bit");
|
||||
return objc_debug_swift_stable_abi_bit_ptr ?
|
||||
*objc_debug_swift_stable_abi_bit_ptr : 1;
|
||||
}
|
||||
#else
|
||||
static unsigned long long computeClassIsSwiftMask(void) {
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern "C" unsigned long long _swift_classIsSwiftMask =
|
||||
computeClassIsSwiftMask();
|
||||
|
||||
/// The context for the code we're running. Set by the observer.
|
||||
static ASTContext *context = nullptr;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user