From 509735ea66a42cb9d610845abb223eced1d43aa0 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 6 Jan 2020 16:01:20 -0800 Subject: [PATCH] IRGen: Work around RemoteMirror bug generating reflection info for empty builtin types. The RemoteMirror library in shipping versions of macOS/iOS/tvOS/watchOS crashes if the compiler emits a BuiltinTypeDescriptor with size zero. Although this is fixed in top-of-tree RemoteMirror, we want binaries built with the new compiler to still be inspectable when run on older OSes. Generate the metadata as an empty struct with no fields when deploying back to these older platforms, which should be functionally equivalent for most purposes. Fixes rdar://problem/57924984. --- lib/IRGen/GenReflection.cpp | 75 ++++++++++++++++++- .../Reflection/reflect_empty_struct.swift | 3 +- .../reflect_empty_struct_compat.swift | 75 +++++++++++++++++++ 3 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 validation-test/Reflection/reflect_empty_struct_compat.swift diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index 4415b5b6d6e86..558439d743391 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -671,7 +671,10 @@ class AssociatedTypeMetadataBuilder : public ReflectionMetadataBuilder { }; class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { - const uint32_t fieldRecordSize = 12; +public: + static const uint32_t FieldRecordSize = 12; + +private: const NominalTypeDecl *NTD; void addFieldDecl(const ValueDecl *value, Type type, @@ -714,7 +717,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { } B.addInt16(uint16_t(kind)); - B.addInt16(fieldRecordSize); + B.addInt16(FieldRecordSize); auto properties = NTD->getStoredProperties(); B.addInt32(properties.size()); @@ -737,7 +740,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { } B.addInt16(uint16_t(kind)); - B.addInt16(fieldRecordSize); + B.addInt16(FieldRecordSize); B.addInt32(strategy.getElementsWithPayload().size() + strategy.getElementsWithNoPayload().size()); @@ -764,7 +767,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { else Kind = FieldDescriptorKind::Protocol; B.addInt16(uint16_t(Kind)); - B.addInt16(fieldRecordSize); + B.addInt16(FieldRecordSize); B.addInt32(0); } @@ -825,6 +828,57 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder { } }; +static bool +deploymentTargetHasRemoteMirrorZeroSizedTypeDescriptorBug(IRGenModule &IGM) { + auto target = IGM.Context.LangOpts.Target; + + if (target.isMacOSX() && target.isMacOSXVersionLT(10, 16, 0)) { + return true; + } + if (target.isiOS() && target.isOSVersionLT(14)) { // includes tvOS + return true; + } + if (target.isWatchOS() && target.isOSVersionLT(7)) { + return true; + } + + return false; +} + +/// Metadata builder that emits a fixed-layout empty type as an empty struct, as +/// a workaround for a RemoteMirror crash in older OSes. +class EmptyStructMetadataBuilder : public ReflectionMetadataBuilder { + const NominalTypeDecl *NTD; + + void layout() override { + addNominalRef(NTD); + B.addInt32(0); + B.addInt16(uint16_t(FieldDescriptorKind::Struct)); + B.addInt16(FieldTypeMetadataBuilder::FieldRecordSize); + B.addInt32(0); + } + +public: + EmptyStructMetadataBuilder(IRGenModule &IGM, + const NominalTypeDecl *NTD) + : ReflectionMetadataBuilder(IGM), NTD(NTD) { + assert(IGM.getTypeInfoForUnlowered( + NTD->getDeclaredTypeInContext()->getCanonicalType()) + .isKnownEmpty(ResilienceExpansion::Maximal) + && "should only be used for known empty types"); + } + + llvm::GlobalVariable *emit() { + auto section = IGM.getFieldTypeMetadataSectionName(); + return ReflectionMetadataBuilder::emit( + [&](IRGenModule &IGM, ConstantInit definition) -> llvm::Constant* { + return IGM.getAddrOfReflectionFieldDescriptor( + NTD->getDeclaredType()->getCanonicalType(), definition); + }, + section); + } +}; + class FixedTypeMetadataBuilder : public ReflectionMetadataBuilder { ModuleDecl *module; CanType type; @@ -1338,6 +1392,19 @@ void IRGenModule::emitFieldDescriptor(const NominalTypeDecl *D) { } if (needsOpaqueDescriptor) { + // Work around an issue in the RemoteMirror library that ships in + // macOS 10.15/iOS 13 and earlier that causes it to crash on a + // BuiltinTypeDescriptor with zero size. If the type has zero size, emit it + // as an empty struct instead, which will have the same impact on the + // encoded type layout. + auto &TI = getTypeInfoForUnlowered(T); + if (deploymentTargetHasRemoteMirrorZeroSizedTypeDescriptorBug(*this) + && TI.isKnownEmpty(ResilienceExpansion::Maximal)) { + EmptyStructMetadataBuilder builder(*this, D); + builder.emit(); + return; + } + FixedTypeMetadataBuilder builder(*this, D); builder.emit(); } diff --git a/validation-test/Reflection/reflect_empty_struct.swift b/validation-test/Reflection/reflect_empty_struct.swift index 32427344a3fc9..df2e365172082 100644 --- a/validation-test/Reflection/reflect_empty_struct.swift +++ b/validation-test/Reflection/reflect_empty_struct.swift @@ -1,11 +1,12 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct +// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.99.0 // RUN: %target-codesign %t/reflect_empty_struct // RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail // REQUIRES: objc_interop // REQUIRES: executable_test +// REQUIRES: OS=macosx import SwiftReflectionTest diff --git a/validation-test/Reflection/reflect_empty_struct_compat.swift b/validation-test/Reflection/reflect_empty_struct_compat.swift new file mode 100644 index 0000000000000..93188e9f1010e --- /dev/null +++ b/validation-test/Reflection/reflect_empty_struct_compat.swift @@ -0,0 +1,75 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.15.0 +// RUN: %target-codesign %t/reflect_empty_struct +// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail + +// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.14.0 +// RUN: %target-codesign %t/reflect_empty_struct +// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail + +// REQUIRES: objc_interop +// REQUIRES: executable_test +// REQUIRES: OS=macosx + +import SwiftReflectionTest + +import EmptyStruct + +@_alignment(1) struct EmptyStruct { } +class Class { + var a = EmptyStruct() + var b: Any = EmptyStruct() + var c = EmptyStructC() + var d: Any = EmptyStructC() +} + +var obj = Class() + +reflect(object: obj) + +// CHECK-64: Reflecting an object. +// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-64: Type reference: +// CHECK-64: (class reflect_empty_struct.Class) + +// CHECK-64: Type info: +// CHECK-64: (class_instance size=80 alignment=8 stride=80 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-64: (field name=a offset=16 +// CHECK-64: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64: (field name=b offset=16 +// CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1 +// CHECK-64: (field name=metadata offset=24 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=2147483647 bitwise_takable=1)))) +// CHECK-64: (field name=c offset=48 +// CHECK-64: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-64: (field name=d offset=48 +// CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1 +// CHECK-64: (field name=metadata offset=24 +// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=2147483647 bitwise_takable=1))))) + +// CHECK-32: Reflecting an object. +// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} +// CHECK-32: Type reference: +// CHECK-32: (class reflect_empty_struct.Class) + +// CHECK-32: Type info: +// CHECK-32: (class_instance size=40 alignment=4 stride=40 num_extra_inhabitants=0 bitwise_takable=1 +// CHECK-32: (field name=a offset=8 +// CHECK-32: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-32: (field name=b offset=8 +// CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1 +// CHECK-32: (field name=metadata offset=12 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1)))) +// CHECK-32: (field name=c offset=24 +// CHECK-32: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1)) +// CHECK-32: (field name=d offset=24 +// CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1 +// CHECK-32: (field name=metadata offset=12 +// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1))))) + +doneReflecting() + +// CHECK-64: Done. + +// CHECK-32: Done.