[Wasm][Runtime] Interpret absolute function pointer in runtime structures

When SWIFT_COMPACT_ABSOLUTE_FUNCTION_POINTER is enabled, relative direct
pointers whose pointees are functions will be turned into absolute
pointer at compile-time.
This commit is contained in:
Yuta Saito
2022-03-30 07:48:46 +00:00
parent 26ba09738e
commit 8c598e98f7
13 changed files with 251 additions and 44 deletions

View File

@@ -0,0 +1,62 @@
//===--- CompactFunctionPointer.h - Compact Function Pointers ---*- 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
//
//===----------------------------------------------------------------------===//
//
// Swift's runtime structures often use relative function pointers to reduce the
// size of metadata and also to minimize load-time overhead in PIC.
// This file defines pointer types whose size and interface are compatible with
// the relative pointer types for targets that do not support relative references
// to code from data.
//===----------------------------------------------------------------------===//
#ifndef SWIFT_ABI_COMPACTFUNCTIONPOINTER_H
#define SWIFT_ABI_COMPACTFUNCTIONPOINTER_H
namespace swift {
/// A compact unconditional absolute function pointer that can fit in a 32-bit
/// integer.
/// As a trade-off compared to relative pointers, this has load-time overhead in PIC
/// and is only available on 32-bit targets.
template <typename T>
class AbsoluteFunctionPointer {
T *Pointer;
static_assert(sizeof(T *) == sizeof(int32_t),
"Function pointer must be 32-bit when using compact absolute pointer");
public:
using PointerTy = T *;
PointerTy get() const & { return Pointer; }
operator PointerTy() const & { return this->get(); }
bool isNull() const & { return Pointer == nullptr; }
/// Resolve a pointer from a `base` pointer and a value loaded from `base`.
template <typename BasePtrTy, typename Value>
static PointerTy resolve(BasePtrTy *base, Value value) {
return reinterpret_cast<PointerTy>(value);
}
template <typename... ArgTy>
typename std::result_of<T *(ArgTy...)>::type operator()(ArgTy... arg) const {
static_assert(std::is_function<T>::value,
"T must be an explicit function type");
return this->get()(std::forward<ArgTy>(arg)...);
}
};
// TODO(katei): Add another pointer structure for 64-bit targets and for efficiency on PIC
} // namespace swift
#endif // SWIFT_ABI_COMPACTFUNCTIONPOINTER_H

View File

@@ -241,7 +241,7 @@ template <class AsyncSignature>
class AsyncFunctionPointer {
public:
/// The function to run.
RelativeDirectPointer<AsyncFunctionType<AsyncSignature>,
TargetCompactFunctionPointer<InProcess, AsyncFunctionType<AsyncSignature>,
/*nullable*/ false,
int32_t> Function;

View File

@@ -506,9 +506,20 @@ struct TargetMethodDescriptor {
MethodDescriptorFlags Flags;
/// The method implementation.
TargetRelativeDirectPointer<Runtime, void> Impl;
union {
TargetCompactFunctionPointer<Runtime, void> Impl;
TargetRelativeDirectPointer<Runtime, void> AsyncImpl;
};
// TODO: add method types or anything else needed for reflection.
void *getImpl() const {
if (Flags.isAsync()) {
return AsyncImpl.get();
} else {
return Impl.get();
}
}
};
using MethodDescriptor = TargetMethodDescriptor<InProcess>;
@@ -578,7 +589,20 @@ struct TargetMethodOverrideDescriptor {
TargetRelativeMethodDescriptorPointer<Runtime> Method;
/// The implementation of the override.
TargetRelativeDirectPointer<Runtime, void, /*nullable*/ true> Impl;
union {
TargetCompactFunctionPointer<Runtime, void, /*nullable*/ true> Impl;
TargetRelativeDirectPointer<Runtime, void, /*nullable*/ true> AsyncImpl;
};
void *getImpl() const {
auto *baseMethod = Method.get();
assert(baseMethod && "no base method");
if (baseMethod->Flags.isAsync()) {
return AsyncImpl.get();
} else {
return Impl.get();
}
}
};
/// Header for a class vtable override descriptor. This is a variable-sized
@@ -1523,7 +1547,18 @@ struct TargetProtocolRequirement {
// TODO: name, type
/// The optional default implementation of the protocol.
RelativeDirectPointer<void, /*nullable*/ true> DefaultImplementation;
union {
TargetCompactFunctionPointer<Runtime, void, /*nullable*/ true> DefaultFuncImplementation;
TargetRelativeDirectPointer<Runtime, void, /*nullable*/ true> DefaultImplementation;
};
void *getDefaultImplementation() const {
if (Flags.isFunctionImpl()) {
return DefaultFuncImplementation.get();
} else {
return DefaultImplementation.get();
}
}
};
using ProtocolRequirement = TargetProtocolRequirement<InProcess>;
@@ -2170,7 +2205,18 @@ using GenericBoxHeapMetadata = TargetGenericBoxHeapMetadata<InProcess>;
template <typename Runtime>
struct TargetResilientWitness {
TargetRelativeProtocolRequirementPointer<Runtime> Requirement;
RelativeDirectPointer<void> Witness;
union {
TargetRelativeDirectPointer<Runtime, void> Impl;
TargetCompactFunctionPointer<Runtime, void> FuncImpl;
};
void *getWitness(ProtocolRequirementFlags flags) const {
if (flags.isFunctionImpl()) {
return FuncImpl.get();
} else {
return Impl.get();
}
}
};
using ResilientWitness = TargetResilientWitness<InProcess>;
@@ -2233,10 +2279,13 @@ struct TargetGenericWitnessTable {
uint16_t WitnessTablePrivateSizeInWordsAndRequiresInstantiation;
/// The instantiation function, which is called after the template is copied.
RelativeDirectPointer<void(TargetWitnessTable<Runtime> *instantiatedTable,
const TargetMetadata<Runtime> *type,
const void * const *instantiationArgs),
/*nullable*/ true> Instantiator;
TargetCompactFunctionPointer<
Runtime,
void(TargetWitnessTable<Runtime> *instantiatedTable,
const TargetMetadata<Runtime> *type,
const void *const *instantiationArgs),
/*nullable*/ true>
Instantiator;
using PrivateDataType = void *[swift::NumGenericMetadataPrivateDataWords];
@@ -2968,12 +3017,12 @@ using MetadataCompleter =
template <typename Runtime>
struct TargetGenericMetadataPattern {
/// The function to call to instantiate the template.
TargetRelativeDirectPointer<Runtime, MetadataInstantiator>
TargetCompactFunctionPointer<Runtime, MetadataInstantiator>
InstantiationFunction;
/// The function to call to complete the instantiation. If this is null,
/// the instantiation function must always generate complete metadata.
TargetRelativeDirectPointer<Runtime, MetadataCompleter, /*nullable*/ true>
TargetCompactFunctionPointer<Runtime, MetadataCompleter, /*nullable*/ true>
CompletionFunction;
/// Flags describing the layout of this instantiation pattern.
@@ -3080,10 +3129,10 @@ struct TargetGenericClassMetadataPattern final :
using TargetGenericMetadataPattern<Runtime>::PatternFlags;
/// The heap-destructor function.
TargetRelativeDirectPointer<Runtime, HeapObjectDestroyer> Destroy;
TargetCompactFunctionPointer<Runtime, HeapObjectDestroyer> Destroy;
/// The ivar-destructor function.
TargetRelativeDirectPointer<Runtime, ClassIVarDestroyer, /*nullable*/ true>
TargetCompactFunctionPointer<Runtime, ClassIVarDestroyer, /*nullable*/ true>
IVarDestroyer;
/// The class flags.
@@ -3284,7 +3333,7 @@ private:
template <typename Runtime>
struct TargetForeignMetadataInitialization {
/// The completion function. The pattern will always be null.
TargetRelativeDirectPointer<Runtime, MetadataCompleter, /*nullable*/ true>
TargetCompactFunctionPointer<Runtime, MetadataCompleter, /*nullable*/ true>
CompletionFunction;
};
@@ -3329,14 +3378,14 @@ struct TargetResilientClassMetadataPattern {
///
/// If this is null, the runtime instead calls swift_relocateClassMetadata(),
/// passing in the class descriptor and this pattern.
TargetRelativeDirectPointer<Runtime, MetadataRelocator, /*nullable*/ true>
TargetCompactFunctionPointer<Runtime, MetadataRelocator, /*nullable*/ true>
RelocationFunction;
/// The heap-destructor function.
TargetRelativeDirectPointer<Runtime, HeapObjectDestroyer> Destroy;
TargetCompactFunctionPointer<Runtime, HeapObjectDestroyer> Destroy;
/// The ivar-destructor function.
TargetRelativeDirectPointer<Runtime, ClassIVarDestroyer, /*nullable*/ true>
TargetCompactFunctionPointer<Runtime, ClassIVarDestroyer, /*nullable*/ true>
IVarDestroyer;
/// The class flags.
@@ -3380,7 +3429,7 @@ struct TargetSingletonMetadataInitialization {
/// The completion function. The pattern will always be null, even
/// for a resilient class.
TargetRelativeDirectPointer<Runtime, MetadataCompleter>
TargetCompactFunctionPointer<Runtime, MetadataCompleter>
CompletionFunction;
bool hasResilientClassPattern(
@@ -3409,7 +3458,7 @@ struct TargetCanonicalSpecializedMetadatasListEntry {
template <typename Runtime>
struct TargetCanonicalSpecializedMetadataAccessorsListEntry {
TargetRelativeDirectPointer<Runtime, MetadataResponse(MetadataRequest), /*Nullable*/ false> accessor;
TargetCompactFunctionPointer<Runtime, MetadataResponse(MetadataRequest), /*Nullable*/ false> accessor;
};
template <typename Runtime>
@@ -3429,7 +3478,7 @@ public:
/// The function type here is a stand-in. You should use getAccessFunction()
/// to wrap the function pointer in an accessor that uses the proper calling
/// convention for a given number of arguments.
TargetRelativeDirectPointer<Runtime, MetadataResponse(...),
TargetCompactFunctionPointer<Runtime, MetadataResponse(...),
/*Nullable*/ true> AccessFunctionPtr;
/// A pointer to the field descriptor for the type, if any.
@@ -3694,7 +3743,7 @@ public:
using MetadataListEntry =
TargetCanonicalSpecializedMetadatasListEntry<Runtime>;
using MetadataAccessor =
TargetRelativeDirectPointer<Runtime, MetadataResponse(MetadataRequest), /*Nullable*/ false>;
TargetCompactFunctionPointer<Runtime, MetadataResponse(MetadataRequest), /*Nullable*/ false>;
using MetadataAccessorListEntry =
TargetCanonicalSpecializedMetadataAccessorsListEntry<Runtime>;
using MetadataCachingOnceToken =
@@ -4495,12 +4544,23 @@ class DynamicReplacementDescriptor {
DynamicReplacementKey *
__ptrauth_swift_dynamic_replacement_key>>
replacedFunctionKey;
RelativeDirectPointer<void, false> replacementFunction;
union {
TargetCompactFunctionPointer<InProcess, void, false> replacementFunction;
TargetRelativeDirectPointer<InProcess, void, false> replacementAsyncFunction;
};
RelativeDirectPointer<DynamicReplacementChainEntry, false> chainEntry;
uint32_t flags;
enum : uint32_t { EnableChainingMask = 0x1 };
void *getReplacementFunction() const {
if (replacedFunctionKey->isAsync()) {
return replacementAsyncFunction.get();
} else {
return replacementFunction.get();
}
}
public:
/// Enable this replacement by changing the function's replacement chain's
/// root entry.

View File

@@ -611,6 +611,21 @@ public:
int_type getIntValue() const { return Value; }
/// Is the method implementation is represented as a native function pointer?
bool isFunctionImpl() const {
switch (getKind()) {
case ProtocolRequirementFlags::Kind::Method:
case ProtocolRequirementFlags::Kind::Init:
case ProtocolRequirementFlags::Kind::Getter:
case ProtocolRequirementFlags::Kind::Setter:
case ProtocolRequirementFlags::Kind::ReadCoroutine:
case ProtocolRequirementFlags::Kind::ModifyCoroutine:
return !isAsync();
default:
return false;
}
}
enum : uintptr_t {
/// Bit used to indicate that an associated type witness is a pointer to
/// a mangled name (vs. a pointer to metadata).

View File

@@ -31,6 +31,7 @@
#include "swift/Runtime/Config.h"
#include "swift/Basic/RelativePointer.h"
#include "swift/ABI/CompactFunctionPointer.h"
namespace swift {
@@ -101,6 +102,14 @@ struct InProcess {
template <typename T, bool Nullable = true>
using RelativeDirectPointer = RelativeDirectPointer<T, Nullable>;
template <typename T, bool Nullable = true, typename Offset = int32_t>
#if SWIFT_COMPACT_ABSOLUTE_FUNCTION_POINTER
using CompactFunctionPointer = AbsoluteFunctionPointer<T>;
#else
using CompactFunctionPointer =
swift::RelativeDirectPointer<T, Nullable, Offset>;
#endif
template<typename T>
T *getStrippedSignedPointer(const T *pointer) const {
return swift_ptrauth_strip(pointer);
@@ -163,6 +172,9 @@ struct External {
template <typename T, bool Nullable = true>
using RelativeDirectPointer = int32_t;
template <typename T, bool Nullable = true, typename Offset = int32_t>
using CompactFunctionPointer = int32_t;
StoredPointer getStrippedSignedPointer(const StoredSignedPointer pointer) const {
return swift_ptrauth_strip(pointer);
}
@@ -191,6 +203,12 @@ template <typename Runtime, typename Pointee, bool Nullable = true>
using TargetRelativeIndirectablePointer
= typename Runtime::template RelativeIndirectablePointer<Pointee,Nullable>;
template <typename Runtime, typename Pointee, bool Nullable = true,
typename Offset = int32_t>
using TargetCompactFunctionPointer =
typename Runtime::template CompactFunctionPointer<Pointee, Nullable,
Offset>;
} // end namespace swift
#endif

View File

@@ -386,6 +386,10 @@ public:
/// position-independent constant data.
template<typename T, bool Nullable, typename Offset>
class RelativeDirectPointerImpl {
#if SWIFT_COMPACT_ABSOLUTE_FUNCTION_POINTER
static_assert(!std::is_function<T>::value,
"relative direct function pointer should not be used under absolute function pointer mode");
#endif
private:
/// The relative offset of the function's entry point from *this.
Offset RelativeOffset;
@@ -454,19 +458,25 @@ public:
/// Apply the offset to a parameter, instead of `this`.
PointerTy getRelative(void *base) const & {
// Check for null.
if (Nullable && RelativeOffset == 0)
return nullptr;
// The value is addressed relative to `base`.
uintptr_t absolute = detail::applyRelativeOffset(base, RelativeOffset);
return reinterpret_cast<PointerTy>(absolute);
return resolve(base, RelativeOffset);
}
/// A zero relative offset encodes a null reference.
bool isNull() const & {
return RelativeOffset == 0;
}
/// Resolve a pointer from a `base` pointer and a value loaded from `base`.
template<typename BasePtrTy>
static PointerTy resolve(BasePtrTy *base, Offset value) {
// Check for null.
if (Nullable && value == 0)
return nullptr;
// The value is addressed relative to `base`.
uintptr_t absolute = detail::applyRelativeOffset(base, value);
return reinterpret_cast<PointerTy>(absolute);
}
};
template <typename T, bool Nullable = true, typename Offset = int32_t,
@@ -502,6 +512,7 @@ public:
}
using super::isNull;
using super::resolve;
};
/// A specialization of RelativeDirectPointer for function pointers,
@@ -548,6 +559,7 @@ public:
}
using super::isNull;
using super::resolve;
};
/// A direct relative reference to an aligned object, with an additional

View File

@@ -248,6 +248,16 @@ extern uintptr_t __COMPATIBILITY_LIBRARIES_CANNOT_CHECK_THE_IS_SWIFT_BIT_DIRECTL
#define SWIFT_VFORMAT(fmt)
#endif
/// Should we use absolute function pointers instead of relative ones?
/// WebAssembly target uses it by default.
#ifndef SWIFT_COMPACT_ABSOLUTE_FUNCTION_POINTER
# if defined(__wasm__)
# define SWIFT_COMPACT_ABSOLUTE_FUNCTION_POINTER 1
# else
# define SWIFT_COMPACT_ABSOLUTE_FUNCTION_POINTER 0
# endif
#endif
// Pointer authentication.
#if __has_feature(ptrauth_calls)
#define SWIFT_PTRAUTH 1

View File

@@ -302,6 +302,10 @@ function(_add_target_variant_c_compile_flags)
list(APPEND result "-DSWIFT_OBJC_INTEROP=0")
endif()
if(SWIFT_STDLIB_COMPACT_ABSOLUTE_FUNCTION_POINTER)
list(APPEND result "-DSWIFT_COMPACT_ABSOLUTE_FUNCTION_POINTER=1")
endif()
if(SWIFT_STDLIB_STABLE_ABI)
list(APPEND result "-DSWIFT_LIBRARY_EVOLUTION=1")
else()

View File

@@ -73,6 +73,10 @@ option(SWIFT_STDLIB_STABLE_ABI
"Should stdlib be built with stable ABI (library evolution, resilience)."
"${SWIFT_STDLIB_STABLE_ABI_default}")
option(SWIFT_STDLIB_COMPACT_ABSOLUTE_FUNCTION_POINTER
"Force compact function pointer to always be absolute mainly for WebAssembly"
FALSE)
option(SWIFT_ENABLE_MODULE_INTERFACES
"Generate .swiftinterface files alongside .swiftmodule files"
"${SWIFT_STDLIB_STABLE_ABI}")

View File

@@ -269,6 +269,10 @@ function(_add_target_variant_swift_compile_flags
list(APPEND result "-D" "INTERNAL_CHECKS_ENABLED")
endif()
if(SWIFT_STDLIB_COMPACT_ABSOLUTE_FUNCTION_POINTER)
list(APPEND result "-D" "SWIFT_COMPACT_ABSOLUTE_FUNCTION_POINTER")
endif()
if(SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY)
list(APPEND result "-D" "SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY")
endif()

View File

@@ -2615,7 +2615,7 @@ internal func _resolveKeyPathGenericArgReference(
var offset: Int32 = 0
_memcpy(dest: &offset, src: pointerReference, size: 4)
let accessorPtrRaw = _resolveRelativeAddress(pointerReference, offset)
let accessorPtrRaw = _resolveCompactFunctionPointer(pointerReference, offset)
let accessorPtrSigned =
_PtrAuth.sign(pointer: accessorPtrRaw,
key: .processIndependentCode,
@@ -2714,6 +2714,16 @@ internal func _resolveRelativeIndirectableAddress(_ base: UnsafeRawPointer,
}
return _resolveRelativeAddress(base, offset)
}
internal func _resolveCompactFunctionPointer(_ base: UnsafeRawPointer, _ offset: Int32)
-> UnsafeRawPointer {
#if SWIFT_COMPACT_ABSOLUTE_FUNCTION_POINTER
return UnsafeRawPointer(bitPattern: Int(offset)).unsafelyUnwrapped
#else
return _resolveRelativeAddress(base, offset)
#endif
}
internal func _loadRelativeAddress<T>(at: UnsafeRawPointer,
fromByteOffset: Int = 0,
as: T.Type) -> T {
@@ -2780,12 +2790,12 @@ internal func _walkKeyPathPattern<W: KeyPathPatternVisitor>(
let idValue = _pop(from: &componentBuffer, as: Int32.self)
let getterBase = componentBuffer.baseAddress.unsafelyUnwrapped
let getterRef = _pop(from: &componentBuffer, as: Int32.self)
let getter = _resolveRelativeAddress(getterBase, getterRef)
let getter = _resolveCompactFunctionPointer(getterBase, getterRef)
let setter: UnsafeRawPointer?
if header.isComputedSettable {
let setterBase = componentBuffer.baseAddress.unsafelyUnwrapped
let setterRef = _pop(from: &componentBuffer, as: Int32.self)
setter = _resolveRelativeAddress(setterBase, setterRef)
setter = _resolveCompactFunctionPointer(setterBase, setterRef)
} else {
setter = nil
}
@@ -2799,7 +2809,7 @@ internal func _walkKeyPathPattern<W: KeyPathPatternVisitor>(
if header.hasComputedArguments {
let getLayoutBase = componentBuffer.baseAddress.unsafelyUnwrapped
let getLayoutRef = _pop(from: &componentBuffer, as: Int32.self)
let getLayoutRaw = _resolveRelativeAddress(getLayoutBase, getLayoutRef)
let getLayoutRaw = _resolveCompactFunctionPointer(getLayoutBase, getLayoutRef)
let getLayoutSigned = _PtrAuth.sign(pointer: getLayoutRaw,
key: .processIndependentCode,
discriminator: _PtrAuth.discriminator(for: KeyPathComputedArgumentLayoutFn.self))
@@ -2817,8 +2827,8 @@ internal func _walkKeyPathPattern<W: KeyPathPatternVisitor>(
let initializerBase = componentBuffer.baseAddress.unsafelyUnwrapped
let initializerRef = _pop(from: &componentBuffer, as: Int32.self)
let initializerRaw = _resolveRelativeAddress(initializerBase,
initializerRef)
let initializerRaw = _resolveCompactFunctionPointer(initializerBase,
initializerRef)
let initializerSigned = _PtrAuth.sign(pointer: initializerRaw,
key: .processIndependentCode,
discriminator: _PtrAuth.discriminator(for: KeyPathComputedArgumentInitializerFn.self))

View File

@@ -2762,7 +2762,7 @@ static void initClassVTable(ClassMetadata *self) {
for (unsigned i = 0, e = vtable->VTableSize; i < e; ++i) {
auto &methodDescription = descriptors[i];
swift_ptrauth_init_code_or_data(
&classWords[vtableOffset + i], methodDescription.Impl.get(),
&classWords[vtableOffset + i], methodDescription.getImpl(),
methodDescription.Flags.getExtraDiscriminator(),
!methodDescription.Flags.isAsync());
}
@@ -2802,9 +2802,8 @@ static void initClassVTable(ClassMetadata *self) {
auto baseVTable = baseClass->getVTableDescriptor();
auto offset = (baseVTable->getVTableOffset(baseClass) +
(baseMethod - baseClassMethods.data()));
swift_ptrauth_init_code_or_data(&classWords[offset],
descriptor.Impl.get(),
descriptor.getImpl(),
baseMethod->Flags.getExtraDiscriminator(),
!baseMethod->Flags.isAsync());
}
@@ -5139,7 +5138,7 @@ static void initializeResilientWitnessTable(
auto &reqt = requirements[reqDescriptor - requirements.begin()];
// This is an unsigned pointer formed from a relative address.
void *impl = witness.Witness.get();
void *impl = witness.getWitness(reqt.Flags);
initProtocolWitness(&table[witnessIndex], impl, reqt);
}
@@ -5153,7 +5152,7 @@ static void initializeResilientWitnessTable(
auto &reqt = requirements[i];
if (!table[witnessIndex]) {
// This is an unsigned pointer formed from a relative address.
void *impl = reqt.DefaultImplementation.get();
void *impl = reqt.getDefaultImplementation();
initProtocolWitness(&table[witnessIndex], impl, reqt);
}
@@ -5622,7 +5621,7 @@ static const WitnessTable *swift_getAssociatedConformanceWitnessSlowImpl(
// Resolve the relative reference to the witness function.
int32_t offset;
memcpy(&offset, mangledName.data() + 1, 4);
uintptr_t ptr = detail::applyRelativeOffset(mangledName.data() + 1, offset);
void *ptr = TargetCompactFunctionPointer<InProcess, void>::resolve(mangledName.data() + 1, offset);
// Call the witness function.
AssociatedWitnessTableAccessFunction *witnessFn;

View File

@@ -76,7 +76,16 @@ static uintptr_t resolveSymbolicReferenceOffset(SymbolicReferenceKind kind,
Directness isIndirect,
int32_t offset,
const void *base) {
auto ptr = detail::applyRelativeOffset(base, offset);
uintptr_t ptr;
// Function references may be resolved differently than other data references.
switch (kind) {
case SymbolicReferenceKind::AccessorFunctionReference:
ptr = (uintptr_t)TargetCompactFunctionPointer<InProcess, void>::resolve(base, offset);
break;
default:
ptr = detail::applyRelativeOffset(base, offset);
break;
}
// Indirect references may be authenticated in a way appropriate for the
// referent.
@@ -2600,10 +2609,10 @@ void DynamicReplacementDescriptor::enableReplacement() const {
// Link the replacement entry.
chainRoot->next = chainEntry.get();
// chainRoot->implementationFunction = replacementFunction.get();
// chainRoot->implementationFunction = getReplacementFunction();
swift_ptrauth_init_code_or_data(
reinterpret_cast<void **>(&chainRoot->implementationFunction),
reinterpret_cast<void *>(replacementFunction.get()),
reinterpret_cast<void *>(getReplacementFunction()),
replacedFunctionKey->getExtraDiscriminator(),
!replacedFunctionKey->isAsync());
}