[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:
Mike Ash
2019-03-01 14:02:14 -05:00
parent 7d4b72ffb5
commit fbe990481b
17 changed files with 250 additions and 10 deletions

View File

@@ -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'")

View File

@@ -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;
}

View 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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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);

View File

@@ -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"

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
@@ -561,6 +575,8 @@ int main(int argc, char *argv[]) {
printUsageAndExit();
const char *BinaryFilename = argv[1];
swift_reflection_classIsSwiftMask = computeClassIsSwiftMask();
uint16_t Version = swift_reflection_getSupportedMetadataVersion();
printf("Metadata version: %u\n", Version);

View File

@@ -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

View File

@@ -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,

View File

@@ -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: }>

View 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

View File

@@ -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;