mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Ensure that context descriptor pointers are signed in the runtime by putting the ptrauth_struct attribute on the types. We use the new __builtin_ptrauth_struct_key/disc to conditionally apply ptrauth_struct to TrailingObjects based on the signing of the base type, so that pointers to TrailingObjects get signed when used with a context descriptor pointer. We add new runtime entrypoints that take signed pointers where appropriate, and have the compiler emit calls to the new entrypoints when targeting a sufficiently new OS. rdar://111480914
389 lines
13 KiB
C++
389 lines
13 KiB
C++
//===--- MetadataRef.h - ABI for references to metadata ---------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2022 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file describes runtime metadata structures for references to
|
|
// other metadata.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_ABI_METADATAREF_H
|
|
#define SWIFT_ABI_METADATAREF_H
|
|
|
|
#include "swift/ABI/TargetLayout.h"
|
|
#include "swift/ABI/MetadataValues.h"
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
#include <objc/runtime.h>
|
|
#endif
|
|
|
|
namespace swift {
|
|
|
|
template <typename Runtime>
|
|
struct TargetAnyClassMetadata;
|
|
template <typename Runtime>
|
|
struct TargetAnyClassMetadataObjCInterop;
|
|
template <typename Runtime, typename TargetAnyClassMetadataVariant>
|
|
struct TargetClassMetadata;
|
|
template <typename Runtime>
|
|
struct swift_ptrauth_struct_context_descriptor(ContextDescriptor)
|
|
TargetContextDescriptor;
|
|
template <typename Runtime>
|
|
struct swift_ptrauth_struct_context_descriptor(ProtocolDescriptor)
|
|
TargetProtocolDescriptor;
|
|
|
|
namespace detail {
|
|
template <typename Runtime, bool ObjCInterop = Runtime::ObjCInterop>
|
|
struct TargetAnyClassMetadataTypeImpl;
|
|
|
|
template <typename Runtime>
|
|
struct TargetAnyClassMetadataTypeImpl<Runtime, /*ObjCInterop*/ true> {
|
|
using type = TargetAnyClassMetadataObjCInterop<Runtime>;
|
|
};
|
|
|
|
template <typename Runtime>
|
|
struct TargetAnyClassMetadataTypeImpl<Runtime, /*ObjCInterop*/ false> {
|
|
using type = TargetAnyClassMetadata<Runtime>;
|
|
};
|
|
}
|
|
|
|
/// A convenience typedef for the correct target-parameterized
|
|
/// AnyClassMetadata class.
|
|
template <typename Runtime>
|
|
using TargetAnyClassMetadataType =
|
|
typename detail::TargetAnyClassMetadataTypeImpl<Runtime>::type;
|
|
|
|
/// A convenience typedef for the correct target-parameterized
|
|
/// ClassMetadata class.
|
|
template <typename Runtime>
|
|
using TargetClassMetadataType =
|
|
TargetClassMetadata<Runtime, TargetAnyClassMetadataType<Runtime>>;
|
|
|
|
/// A convenience typedef for a ClassMetadata class that's forced to
|
|
/// support ObjC interop even if that's not the default for the target.
|
|
template <typename Runtime>
|
|
using TargetClassMetadataObjCInterop =
|
|
TargetClassMetadata<Runtime, TargetAnyClassMetadataObjCInterop<Runtime>>;
|
|
|
|
/// A convenience typedef for a target-parameterized pointer to a
|
|
/// target-parameterized type.
|
|
template <typename Runtime, template <typename> class Pointee>
|
|
using TargetMetadataPointer
|
|
= typename Runtime::template Pointer<Pointee<Runtime>>;
|
|
|
|
/// A convenience typedef for a target-parameterized const pointer
|
|
/// to a target-parameterized type.
|
|
template <typename Runtime, template <typename> class Pointee>
|
|
using ConstTargetMetadataPointer
|
|
= typename Runtime::template Pointer<const Pointee<Runtime>>;
|
|
|
|
/// A signed pointer to a type context descriptor, using the standard
|
|
/// signing schema.
|
|
template <typename Runtime,
|
|
template<typename> class Context = TargetContextDescriptor>
|
|
using TargetSignedContextPointer
|
|
= TargetSignedPointer<Runtime,
|
|
Context<Runtime> * __ptrauth_swift_type_descriptor>;
|
|
|
|
/// A relative pointer to a type context descriptor.
|
|
template <typename Runtime,
|
|
template<typename> class Context = TargetContextDescriptor>
|
|
using TargetRelativeContextPointer =
|
|
RelativeIndirectablePointer<const Context<Runtime>,
|
|
/*nullable*/ true, int32_t,
|
|
TargetSignedContextPointer<Runtime, Context>>;
|
|
using RelativeContextPointer = TargetRelativeContextPointer<InProcess>;
|
|
|
|
/// A relative-indirectable pointer to a type context descriptor, with
|
|
/// the low bits used to store a small additional discriminator.
|
|
template <typename Runtime, typename IntTy,
|
|
template<typename _Runtime> class Context = TargetContextDescriptor>
|
|
using RelativeContextPointerIntPair =
|
|
RelativeIndirectablePointerIntPair<const Context<Runtime>, IntTy,
|
|
/*nullable*/ true, int32_t,
|
|
TargetSignedContextPointer<Runtime, Context>>;
|
|
|
|
/// Layout of a small prefix of an Objective-C protocol, used only to
|
|
/// directly extract the name of the protocol.
|
|
template <typename Runtime>
|
|
struct TargetObjCProtocolPrefix {
|
|
/// Unused by the Swift runtime.
|
|
TargetPointer<Runtime, const void> _ObjC_Isa;
|
|
|
|
/// The mangled name of the protocol.
|
|
TargetPointer<Runtime, const char> Name;
|
|
};
|
|
|
|
/// A reference to a protocol within the runtime, which may be either
|
|
/// a Swift protocol or (when Objective-C interoperability is enabled) an
|
|
/// Objective-C protocol.
|
|
///
|
|
/// This type always contains a single target pointer, whose lowest bit is
|
|
/// used to distinguish between a Swift protocol referent and an Objective-C
|
|
/// protocol referent.
|
|
template <typename Runtime>
|
|
class TargetProtocolDescriptorRef {
|
|
using StoredPointer = typename Runtime::StoredPointer;
|
|
using ProtocolDescriptorPointer =
|
|
ConstTargetMetadataPointer<Runtime, TargetProtocolDescriptor>;
|
|
|
|
enum : StoredPointer {
|
|
// The bit used to indicate whether this is an Objective-C protocol.
|
|
IsObjCBit = 0x1U,
|
|
};
|
|
|
|
/// A direct pointer to a protocol descriptor for either an Objective-C
|
|
/// protocol (if the low bit is set) or a Swift protocol (if the low bit
|
|
/// is clear).
|
|
StoredPointer storage;
|
|
|
|
public:
|
|
constexpr TargetProtocolDescriptorRef(StoredPointer storage)
|
|
: storage(storage) { }
|
|
|
|
constexpr TargetProtocolDescriptorRef() : storage() { }
|
|
|
|
TargetProtocolDescriptorRef(
|
|
ProtocolDescriptorPointer protocol,
|
|
ProtocolDispatchStrategy dispatchStrategy) {
|
|
if (Runtime::ObjCInterop) {
|
|
storage =
|
|
reinterpret_cast<StoredPointer>(protocol) |
|
|
(dispatchStrategy == ProtocolDispatchStrategy::ObjC ? IsObjCBit : 0);
|
|
} else {
|
|
assert(dispatchStrategy == ProtocolDispatchStrategy::Swift);
|
|
storage = reinterpret_cast<StoredPointer>(protocol);
|
|
}
|
|
}
|
|
|
|
const static TargetProtocolDescriptorRef forSwift(
|
|
ProtocolDescriptorPointer protocol) {
|
|
return TargetProtocolDescriptorRef{
|
|
reinterpret_cast<StoredPointer>(protocol)};
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
constexpr static TargetProtocolDescriptorRef forObjC(Protocol *objcProtocol) {
|
|
return TargetProtocolDescriptorRef{
|
|
reinterpret_cast<StoredPointer>(objcProtocol) | IsObjCBit};
|
|
}
|
|
#endif
|
|
|
|
explicit constexpr operator bool() const {
|
|
return storage != 0;
|
|
}
|
|
|
|
/// The name of the protocol.
|
|
TargetPointer<Runtime, const char> getName() const {
|
|
#if SWIFT_OBJC_INTEROP
|
|
if (isObjC()) {
|
|
return reinterpret_cast<TargetObjCProtocolPrefix<Runtime> *>(
|
|
getObjCProtocol())->Name;
|
|
}
|
|
#endif
|
|
|
|
return getSwiftProtocol()->Name;
|
|
}
|
|
|
|
/// Determine what kind of protocol this is, Swift or Objective-C.
|
|
ProtocolDispatchStrategy getDispatchStrategy() const {
|
|
if (isObjC()) {
|
|
return ProtocolDispatchStrategy::ObjC;
|
|
}
|
|
|
|
return ProtocolDispatchStrategy::Swift;
|
|
}
|
|
|
|
/// Determine whether this protocol has a 'class' constraint.
|
|
ProtocolClassConstraint getClassConstraint() const {
|
|
if (isObjC()) {
|
|
return ProtocolClassConstraint::Class;
|
|
}
|
|
|
|
return getSwiftProtocol()->getProtocolContextDescriptorFlags()
|
|
.getClassConstraint();
|
|
}
|
|
|
|
/// Determine whether this protocol needs a witness table.
|
|
bool needsWitnessTable() const {
|
|
if (isObjC()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
SpecialProtocol getSpecialProtocol() const {
|
|
if (isObjC()) {
|
|
return SpecialProtocol::None;
|
|
}
|
|
|
|
return getSwiftProtocol()->getProtocolContextDescriptorFlags()
|
|
.getSpecialProtocol();
|
|
}
|
|
|
|
/// Retrieve the Swift protocol descriptor.
|
|
ProtocolDescriptorPointer getSwiftProtocol() const {
|
|
assert(!isObjC());
|
|
|
|
// NOTE: we explicitly use a C-style cast here because cl objects to the
|
|
// reinterpret_cast from a uintptr_t type to an unsigned type which the
|
|
// Pointer type may be depending on the instantiation. Using the C-style
|
|
// cast gives us a single path irrespective of the template type parameters.
|
|
return (ProtocolDescriptorPointer)(storage & ~IsObjCBit);
|
|
}
|
|
|
|
/// Retrieve the raw stored pointer and discriminator bit.
|
|
constexpr StoredPointer getRawData() const {
|
|
return storage;
|
|
}
|
|
|
|
/// Whether this references an Objective-C protocol.
|
|
bool isObjC() const {
|
|
if (Runtime::ObjCInterop)
|
|
return (storage & IsObjCBit) != 0;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
/// Retrieve the Objective-C protocol.
|
|
TargetPointer<Runtime, Protocol> getObjCProtocol() const {
|
|
assert(isObjC());
|
|
return reinterpret_cast<TargetPointer<Runtime, Protocol> >(
|
|
storage & ~IsObjCBit);
|
|
}
|
|
#endif
|
|
};
|
|
|
|
using ProtocolDescriptorRef = TargetProtocolDescriptorRef<InProcess>;
|
|
|
|
/// A relative pointer to a protocol descriptor, which provides the relative-
|
|
/// pointer equivalent to \c TargetProtocolDescriptorRef.
|
|
template <typename Runtime>
|
|
class RelativeTargetProtocolDescriptorPointer {
|
|
union {
|
|
/// Relative pointer to a Swift protocol descriptor.
|
|
/// The \c bool value will be false to indicate that the protocol
|
|
/// is a Swift protocol, or true to indicate that this references
|
|
/// an Objective-C protocol.
|
|
RelativeContextPointerIntPair<Runtime, bool, TargetProtocolDescriptor>
|
|
swiftPointer;
|
|
#if SWIFT_OBJC_INTEROP
|
|
/// Relative pointer to an ObjC protocol descriptor.
|
|
/// The \c bool value will be false to indicate that the protocol
|
|
/// is a Swift protocol, or true to indicate that this references
|
|
/// an Objective-C protocol.
|
|
RelativeIndirectablePointerIntPair<Protocol, bool> objcPointer;
|
|
#endif
|
|
};
|
|
|
|
bool isObjC() const {
|
|
#if SWIFT_OBJC_INTEROP
|
|
if (Runtime::ObjCInterop)
|
|
return objcPointer.getInt();
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
/// Retrieve a reference to the protocol.
|
|
TargetProtocolDescriptorRef<Runtime> getProtocol() const {
|
|
#if SWIFT_OBJC_INTEROP
|
|
if (isObjC()) {
|
|
return TargetProtocolDescriptorRef<Runtime>::forObjC(
|
|
const_cast<Protocol *>(objcPointer.getPointer()));
|
|
}
|
|
#endif
|
|
|
|
return TargetProtocolDescriptorRef<Runtime>::forSwift(
|
|
reinterpret_cast<
|
|
ConstTargetMetadataPointer<Runtime, TargetProtocolDescriptor>>(
|
|
swiftPointer.getPointer()));
|
|
}
|
|
|
|
/// Retrieve a reference to the protocol.
|
|
int32_t getUnresolvedProtocolAddress() const {
|
|
#if SWIFT_OBJC_INTEROP
|
|
if (isObjC()) {
|
|
return objcPointer.getUnresolvedOffset();
|
|
}
|
|
#endif
|
|
return swiftPointer.getUnresolvedOffset();
|
|
}
|
|
|
|
operator TargetProtocolDescriptorRef<Runtime>() const {
|
|
return getProtocol();
|
|
}
|
|
};
|
|
|
|
/// A reference to a type.
|
|
template <typename Runtime>
|
|
struct TargetTypeReference {
|
|
union {
|
|
/// A direct reference to a TypeContextDescriptor or ProtocolDescriptor.
|
|
RelativeDirectPointer<TargetContextDescriptor<Runtime>>
|
|
DirectTypeDescriptor;
|
|
|
|
/// An indirect reference to a TypeContextDescriptor or ProtocolDescriptor.
|
|
RelativeDirectPointer<
|
|
TargetSignedPointer<Runtime, TargetContextDescriptor<Runtime> * __ptrauth_swift_type_descriptor>>
|
|
IndirectTypeDescriptor;
|
|
|
|
/// An indirect reference to an Objective-C class.
|
|
RelativeDirectPointer<
|
|
ConstTargetMetadataPointer<Runtime, TargetClassMetadataType>>
|
|
IndirectObjCClass;
|
|
|
|
/// A direct reference to an Objective-C class name.
|
|
RelativeDirectPointer<const char>
|
|
DirectObjCClassName;
|
|
};
|
|
|
|
const TargetContextDescriptor<Runtime> *
|
|
getTypeDescriptor(TypeReferenceKind kind) const {
|
|
switch (kind) {
|
|
case TypeReferenceKind::DirectTypeDescriptor:
|
|
return DirectTypeDescriptor;
|
|
|
|
case TypeReferenceKind::IndirectTypeDescriptor:
|
|
return *IndirectTypeDescriptor;
|
|
|
|
case TypeReferenceKind::DirectObjCClassName:
|
|
case TypeReferenceKind::IndirectObjCClass:
|
|
return nullptr;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// If this type reference is one of the kinds that supports ObjC
|
|
/// references,
|
|
const TargetClassMetadataObjCInterop<Runtime> *
|
|
getObjCClass(TypeReferenceKind kind) const;
|
|
|
|
const TargetClassMetadataObjCInterop<Runtime> * const *
|
|
getIndirectObjCClass(TypeReferenceKind kind) const {
|
|
assert(kind == TypeReferenceKind::IndirectObjCClass);
|
|
return IndirectObjCClass.get();
|
|
}
|
|
|
|
const char *getDirectObjCClassName(TypeReferenceKind kind) const {
|
|
assert(kind == TypeReferenceKind::DirectObjCClassName);
|
|
return DirectObjCClassName.get();
|
|
}
|
|
};
|
|
using TypeReference = TargetTypeReference<InProcess>;
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|