| //===--- GenPointerAuth.cpp - IRGen for pointer authentication ------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 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 implements general support for pointer authentication in Swift. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "GenPointerAuth.h" |
| #include "Callee.h" |
| #include "ConstantBuilder.h" |
| #include "GenType.h" |
| #include "IRGenFunction.h" |
| #include "IRGenMangler.h" |
| #include "IRGenModule.h" |
| #include "swift/AST/GenericEnvironment.h" |
| #include "swift/SIL/TypeLowering.h" |
| #include "clang/CodeGen/CodeGenABITypes.h" |
| #include "llvm/ADT/APInt.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| using namespace swift; |
| using namespace irgen; |
| |
| /**************************** INTRINSIC OPERATIONS ****************************/ |
| |
| /// Return the key and discriminator value for the given auth info, |
| /// as demanded by the ptrauth intrinsics. |
| static std::pair<llvm::Constant*, llvm::Value*> |
| getPointerAuthPair(IRGenFunction &IGF, const PointerAuthInfo &authInfo) { |
| auto key = llvm::ConstantInt::get(IGF.IGM.Int32Ty, authInfo.getKey()); |
| llvm::Value *discriminator = authInfo.getDiscriminator(); |
| if (discriminator->getType()->isPointerTy()) { |
| discriminator = IGF.Builder.CreatePtrToInt(discriminator, IGF.IGM.IntPtrTy); |
| } |
| return { key, discriminator }; |
| } |
| |
| llvm::Value *irgen::emitPointerAuthBlend(IRGenFunction &IGF, |
| llvm::Value *address, |
| llvm::Value *other) { |
| address = IGF.Builder.CreatePtrToInt(address, IGF.IGM.IntPtrTy); |
| auto intrinsic = |
| llvm::Intrinsic::getDeclaration(&IGF.IGM.Module, |
| llvm::Intrinsic::ptrauth_blend, |
| { IGF.IGM.IntPtrTy }); |
| return IGF.Builder.CreateCall(intrinsic, {address, other}); |
| } |
| |
| llvm::Value *irgen::emitPointerAuthStrip(IRGenFunction &IGF, |
| llvm::Value *fnPtr, |
| unsigned Key) { |
| auto fnVal = IGF.Builder.CreatePtrToInt(fnPtr, IGF.IGM.IntPtrTy); |
| auto keyArg = llvm::ConstantInt::get(IGF.IGM.Int32Ty, Key); |
| auto intrinsic = |
| llvm::Intrinsic::getDeclaration(&IGF.IGM.Module, |
| llvm::Intrinsic::ptrauth_strip, |
| { IGF.IGM.IntPtrTy }); |
| auto strippedPtr = IGF.Builder.CreateCall(intrinsic, {fnVal, keyArg}); |
| return IGF.Builder.CreateIntToPtr(strippedPtr, fnPtr->getType()); |
| } |
| |
| FunctionPointer irgen::emitPointerAuthResign(IRGenFunction &IGF, |
| const FunctionPointer &fn, |
| const PointerAuthInfo &newAuthInfo) { |
| llvm::Value *fnPtr = emitPointerAuthResign(IGF, fn.getRawPointer(), |
| fn.getAuthInfo(), newAuthInfo); |
| return FunctionPointer(fn.getKind(), fnPtr, newAuthInfo, fn.getSignature()); |
| } |
| |
| llvm::Value *irgen::emitPointerAuthResign(IRGenFunction &IGF, |
| llvm::Value *fnPtr, |
| const PointerAuthInfo &oldAuthInfo, |
| const PointerAuthInfo &newAuthInfo) { |
| // If the signatures match, there's nothing to do. |
| if (oldAuthInfo == newAuthInfo) |
| return fnPtr; |
| |
| // If the pointer is not currently signed, sign it. |
| if (!oldAuthInfo.isSigned()) { |
| return emitPointerAuthSign(IGF, fnPtr, newAuthInfo); |
| } |
| |
| // If the pointer is not supposed to be signed, auth it. |
| if (!newAuthInfo.isSigned()) { |
| return emitPointerAuthAuth(IGF, fnPtr, oldAuthInfo); |
| } |
| |
| // Otherwise, auth and resign it. |
| auto origTy = fnPtr->getType(); |
| fnPtr = IGF.Builder.CreatePtrToInt(fnPtr, IGF.IGM.IntPtrTy); |
| |
| auto oldPair = getPointerAuthPair(IGF, oldAuthInfo); |
| auto newPair = getPointerAuthPair(IGF, newAuthInfo); |
| |
| auto intrinsic = |
| llvm::Intrinsic::getDeclaration(&IGF.IGM.Module, |
| llvm::Intrinsic::ptrauth_resign, |
| { IGF.IGM.IntPtrTy }); |
| llvm::Value *args[] = { |
| fnPtr, oldPair.first, oldPair.second, newPair.first, newPair.second |
| }; |
| fnPtr = IGF.Builder.CreateCall(intrinsic, args); |
| return IGF.Builder.CreateIntToPtr(fnPtr, origTy); |
| } |
| |
| llvm::Value *irgen::emitPointerAuthAuth(IRGenFunction &IGF, llvm::Value *fnPtr, |
| const PointerAuthInfo &oldAuthInfo) { |
| auto origTy = fnPtr->getType(); |
| fnPtr = IGF.Builder.CreatePtrToInt(fnPtr, IGF.IGM.IntPtrTy); |
| |
| auto oldPair = getPointerAuthPair(IGF, oldAuthInfo); |
| |
| auto intrinsic = |
| llvm::Intrinsic::getDeclaration(&IGF.IGM.Module, |
| llvm::Intrinsic::ptrauth_auth, |
| { IGF.IGM.IntPtrTy }); |
| llvm::Value *args[] = { |
| fnPtr, oldPair.first, oldPair.second |
| }; |
| fnPtr = IGF.Builder.CreateCall(intrinsic, args); |
| return IGF.Builder.CreateIntToPtr(fnPtr, origTy); |
| } |
| |
| llvm::Value *irgen::emitPointerAuthSign(IRGenFunction &IGF, llvm::Value *fnPtr, |
| const PointerAuthInfo &newAuthInfo) { |
| if (!newAuthInfo.isSigned()) |
| return fnPtr; |
| |
| // Special-case constants. |
| if (auto constantFnPtr = dyn_cast<llvm::Constant>(fnPtr)) { |
| if (auto constantDiscriminator = |
| dyn_cast<llvm::Constant>(newAuthInfo.getDiscriminator())) { |
| llvm::Constant *other = nullptr, *address = nullptr; |
| if (constantDiscriminator->getType()->isPointerTy()) |
| address = constantDiscriminator; |
| else |
| other = constantDiscriminator; |
| return IGF.IGM.getConstantSignedPointer(constantFnPtr, |
| newAuthInfo.getKey(), |
| address, other); |
| } |
| } |
| |
| auto origTy = fnPtr->getType(); |
| fnPtr = IGF.Builder.CreatePtrToInt(fnPtr, IGF.IGM.IntPtrTy); |
| |
| auto newPair = getPointerAuthPair(IGF, newAuthInfo); |
| |
| auto intrinsic = |
| llvm::Intrinsic::getDeclaration(&IGF.IGM.Module, |
| llvm::Intrinsic::ptrauth_sign, |
| { IGF.IGM.IntPtrTy }); |
| llvm::Value *args[] = { |
| fnPtr, newPair.first, newPair.second |
| }; |
| fnPtr = IGF.Builder.CreateCall(intrinsic, args); |
| return IGF.Builder.CreateIntToPtr(fnPtr, origTy); |
| } |
| |
| /******************************* DISCRIMINATORS *******************************/ |
| |
| struct IRGenModule::PointerAuthCachesType { |
| llvm::DenseMap<SILDeclRef, llvm::ConstantInt*> Decls; |
| llvm::DenseMap<CanType, llvm::ConstantInt*> Types; |
| llvm::DenseMap<CanType, llvm::ConstantInt*> YieldTypes; |
| llvm::DenseMap<AssociatedType, llvm::ConstantInt*> AssociatedTypes; |
| llvm::DenseMap<AssociatedConformance, llvm::ConstantInt*> AssociatedConformances; |
| }; |
| |
| IRGenModule::PointerAuthCachesType &IRGenModule::getPointerAuthCaches() { |
| if (!PointerAuthCaches) |
| PointerAuthCaches = new PointerAuthCachesType(); |
| return *PointerAuthCaches; |
| } |
| |
| void IRGenModule::destroyPointerAuthCaches() { |
| delete PointerAuthCaches; |
| } |
| |
| static const PointerAuthSchema &getFunctionPointerSchema(IRGenModule &IGM, |
| CanSILFunctionType fnType) { |
| auto &options = IGM.getOptions().PointerAuth; |
| switch (fnType->getRepresentation()) { |
| case SILFunctionTypeRepresentation::CFunctionPointer: |
| return options.FunctionPointers; |
| |
| case SILFunctionTypeRepresentation::Thin: |
| case SILFunctionTypeRepresentation::Thick: |
| case SILFunctionTypeRepresentation::Method: |
| case SILFunctionTypeRepresentation::WitnessMethod: |
| case SILFunctionTypeRepresentation::Closure: |
| return options.SwiftFunctionPointers; |
| |
| case SILFunctionTypeRepresentation::ObjCMethod: |
| case SILFunctionTypeRepresentation::Block: |
| llvm_unreachable("not just a function pointer"); |
| } |
| llvm_unreachable("bad representation"); |
| } |
| |
| PointerAuthInfo PointerAuthInfo::forFunctionPointer(IRGenModule &IGM, |
| CanSILFunctionType fnType) { |
| auto &schema = getFunctionPointerSchema(IGM, fnType); |
| |
| // If the target doesn't sign function pointers, we're done. |
| if (!schema) return PointerAuthInfo(); |
| |
| assert(!schema.isAddressDiscriminated() && |
| "function pointer cannot be address-discriminated"); |
| |
| auto discriminator = getOtherDiscriminator(IGM, schema, fnType); |
| return PointerAuthInfo(schema.getKey(), discriminator); |
| } |
| |
| PointerAuthInfo PointerAuthInfo::emit(IRGenFunction &IGF, |
| const PointerAuthSchema &schema, |
| llvm::Value *storageAddress, |
| const PointerAuthEntity &entity) { |
| if (!schema) return PointerAuthInfo(); |
| |
| unsigned key = schema.getKey(); |
| |
| // Produce the 'other' discriminator. |
| auto otherDiscriminator = getOtherDiscriminator(IGF.IGM, schema, entity); |
| llvm::Value *discriminator = otherDiscriminator; |
| |
| // Factor in the address. |
| if (schema.isAddressDiscriminated()) { |
| assert(storageAddress && |
| "no storage address for address-discriminated schema"); |
| |
| if (!otherDiscriminator->isZero()) { |
| discriminator = emitPointerAuthBlend(IGF, storageAddress, discriminator); |
| } else { |
| discriminator = |
| IGF.Builder.CreatePtrToInt(storageAddress, IGF.IGM.IntPtrTy); |
| } |
| } |
| |
| return PointerAuthInfo(key, discriminator); |
| } |
| |
| llvm::ConstantInt * |
| PointerAuthInfo::getOtherDiscriminator(IRGenModule &IGM, |
| const PointerAuthSchema &schema, |
| const PointerAuthEntity &entity) { |
| assert(schema); |
| switch (schema.getOtherDiscrimination()) { |
| case PointerAuthSchema::Discrimination::None: |
| return llvm::ConstantInt::get(IGM.IntPtrTy, 0); |
| |
| case PointerAuthSchema::Discrimination::Decl: |
| return entity.getDeclDiscriminator(IGM); |
| |
| case PointerAuthSchema::Discrimination::Type: |
| return entity.getTypeDiscriminator(IGM); |
| |
| case PointerAuthSchema::Discrimination::Constant: |
| return llvm::ConstantInt::get(IGM.IntPtrTy, |
| schema.getConstantDiscrimination()); |
| } |
| llvm_unreachable("bad kind"); |
| } |
| |
| static llvm::ConstantInt *getDiscriminatorForHash(IRGenModule &IGM, |
| uint64_t rawHash) { |
| uint16_t reducedHash = (rawHash % 0xFFFF) + 1; |
| return IGM.getSize(Size(reducedHash)); |
| } |
| |
| static llvm::ConstantInt *getDiscriminatorForString(IRGenModule &IGM, |
| StringRef string) { |
| uint64_t rawHash = clang::CodeGen::computeStableStringHash(string); |
| return getDiscriminatorForHash(IGM, rawHash); |
| } |
| |
| static std::string mangle(AssociatedType association) { |
| return IRGenMangler() |
| .mangleAssociatedTypeAccessFunctionDiscriminator(association); |
| } |
| |
| static std::string mangle(const AssociatedConformance &association) { |
| return IRGenMangler() |
| .mangleAssociatedTypeWitnessTableAccessFunctionDiscriminator(association); |
| } |
| |
| llvm::ConstantInt * |
| PointerAuthEntity::getDeclDiscriminator(IRGenModule &IGM) const { |
| switch (StoredKind) { |
| case Kind::None: |
| case Kind::CanSILFunctionType: |
| case Kind::CoroutineYieldTypes: |
| llvm_unreachable("no declaration for schema using decl discrimination"); |
| |
| case Kind::Special: { |
| auto getSpecialDiscriminator = [](Special special) -> uint16_t { |
| switch (special) { |
| case Special::HeapDestructor: |
| return SpecialPointerAuthDiscriminators::HeapDestructor; |
| case Special::TypeDescriptor: |
| case Special::TypeDescriptorAsArgument: |
| return SpecialPointerAuthDiscriminators::TypeDescriptor; |
| case Special::ProtocolConformanceDescriptor: |
| case Special::ProtocolConformanceDescriptorAsArgument: |
| return SpecialPointerAuthDiscriminators::ProtocolConformanceDescriptor; |
| case Special::PartialApplyCapture: |
| return PointerAuthDiscriminator_PartialApplyCapture; |
| case Special::KeyPathDestroy: |
| return SpecialPointerAuthDiscriminators::KeyPathDestroy; |
| case Special::KeyPathCopy: |
| return SpecialPointerAuthDiscriminators::KeyPathCopy; |
| case Special::KeyPathEquals: |
| return SpecialPointerAuthDiscriminators::KeyPathEquals; |
| case Special::KeyPathHash: |
| return SpecialPointerAuthDiscriminators::KeyPathHash; |
| case Special::KeyPathGetter: |
| return SpecialPointerAuthDiscriminators::KeyPathGetter; |
| case Special::KeyPathNonmutatingSetter: |
| return SpecialPointerAuthDiscriminators::KeyPathNonmutatingSetter; |
| case Special::KeyPathMutatingSetter: |
| return SpecialPointerAuthDiscriminators::KeyPathMutatingSetter; |
| case Special::KeyPathGetLayout: |
| return SpecialPointerAuthDiscriminators::KeyPathGetLayout; |
| case Special::KeyPathInitializer: |
| return SpecialPointerAuthDiscriminators::KeyPathInitializer; |
| case Special::KeyPathMetadataAccessor: |
| return SpecialPointerAuthDiscriminators::KeyPathMetadataAccessor; |
| case Special::DynamicReplacementKey: |
| return SpecialPointerAuthDiscriminators::DynamicReplacementKey; |
| case Special::BlockCopyHelper: |
| case Special::BlockDisposeHelper: |
| llvm_unreachable("no known discriminator for these foreign entities"); |
| } |
| llvm_unreachable("bad kind"); |
| }; |
| auto specialKind = Storage.get<Special>(StoredKind); |
| return IGM.getSize(Size(getSpecialDiscriminator(specialKind))); |
| } |
| |
| case Kind::ValueWitness: { |
| auto getValueWitnessDiscriminator = [](ValueWitness witness) -> uint16_t { |
| switch (witness) { |
| #define WANT_ALL_VALUE_WITNESSES |
| #define DATA_VALUE_WITNESS(LOWER, UPPER, TYPE) |
| #define FUNCTION_VALUE_WITNESS(LOWER, ID, RET, PARAMS) \ |
| case ValueWitness::ID: return SpecialPointerAuthDiscriminators::ID; |
| #include "swift/ABI/ValueWitness.def" |
| case ValueWitness::Size: |
| case ValueWitness::Flags: |
| case ValueWitness::Stride: |
| case ValueWitness::ExtraInhabitantCount: |
| llvm_unreachable("not a function value witness"); |
| } |
| llvm_unreachable("bad kind"); |
| }; |
| auto witness = Storage.get<ValueWitness>(StoredKind); |
| return IGM.getSize(Size(getValueWitnessDiscriminator(witness))); |
| } |
| |
| case Kind::AssociatedType: { |
| auto association = Storage.get<AssociatedType>(StoredKind); |
| llvm::ConstantInt *&cache = |
| IGM.getPointerAuthCaches().AssociatedTypes[association]; |
| if (cache) return cache; |
| |
| auto mangling = mangle(association); |
| cache = getDiscriminatorForString(IGM, mangling); |
| return cache; |
| } |
| |
| case Kind::AssociatedConformance: { |
| auto conformance = Storage.get<AssociatedConformance>(StoredKind); |
| llvm::ConstantInt *&cache = |
| IGM.getPointerAuthCaches().AssociatedConformances[conformance]; |
| if (cache) return cache; |
| |
| auto mangling = mangle(conformance); |
| cache = getDiscriminatorForString(IGM, mangling); |
| return cache; |
| } |
| |
| case Kind::SILDeclRef: { |
| auto constant = Storage.get<SILDeclRef>(StoredKind); |
| llvm::ConstantInt *&cache = IGM.getPointerAuthCaches().Decls[constant]; |
| if (cache) return cache; |
| |
| // Getting the discriminator for a foreign SILDeclRef just means |
| // converting it to a foreign declaration and asking Clang IRGen |
| // for the corresponding discriminator, but that's not completely |
| // trivial. |
| assert(!constant.isForeign && |
| "discriminator for foreign declaration not supported yet!"); |
| |
| // Special case: methods that are witnesses to Actor.enqueue(partialTask:) |
| // have their own discriminator, which is shared across all actor classes. |
| if (constant.hasFuncDecl()) { |
| auto func = dyn_cast<FuncDecl>(constant.getFuncDecl()); |
| if (func->isActorEnqueuePartialTaskWitness()) { |
| cache = IGM.getSize( |
| Size(SpecialPointerAuthDiscriminators::ActorEnqueuePartialTask)); |
| return cache; |
| } |
| } |
| |
| auto mangling = constant.mangle(); |
| cache = getDiscriminatorForString(IGM, mangling); |
| return cache; |
| } |
| case Kind::SILFunction: { |
| auto fn = Storage.get<SILFunction*>(StoredKind); |
| return getDiscriminatorForString(IGM, fn->getName()); |
| } |
| } |
| llvm_unreachable("bad kind"); |
| } |
| |
| static void hashStringForFunctionType(IRGenModule &IGM, CanSILFunctionType type, |
| raw_ostream &Out, |
| GenericEnvironment *genericEnv); |
| |
| static void hashStringForType(IRGenModule &IGM, CanType Ty, raw_ostream &Out, |
| GenericEnvironment *genericEnv) { |
| if (Ty->isAnyClassReferenceType()) { |
| // Any class type has to be hashed opaquely. |
| Out << "-class"; |
| } else if (isa<AnyMetatypeType>(Ty)) { |
| // Any metatype has to be hashed opaquely. |
| Out << "-metatype"; |
| } else if (auto UnwrappedTy = Ty->getOptionalObjectType()) { |
| if (UnwrappedTy->isBridgeableObjectType()) { |
| // Optional<T> is compatible with T when T is class-based. |
| hashStringForType(IGM, UnwrappedTy->getCanonicalType(), Out, genericEnv); |
| } else if (UnwrappedTy->is<MetatypeType>()) { |
| // Optional<T> is compatible with T when T is a metatype. |
| hashStringForType(IGM, UnwrappedTy->getCanonicalType(), Out, genericEnv); |
| } else { |
| // Optional<T> is direct if and only if T is. |
| Out << "Optional<"; |
| hashStringForType(IGM, UnwrappedTy->getCanonicalType(), Out, genericEnv); |
| Out << ">"; |
| } |
| } else if (auto GTy = dyn_cast<AnyGenericType>(Ty)) { |
| // For generic and non-generic value types, use the mangled declaration |
| // name, and ignore all generic arguments. |
| NominalTypeDecl *nominal = cast<NominalTypeDecl>(GTy->getDecl()); |
| Out << Mangle::ASTMangler().mangleNominalType(nominal); |
| } else if (auto FTy = dyn_cast<SILFunctionType>(Ty)) { |
| Out << "("; |
| hashStringForFunctionType(IGM, FTy, Out, genericEnv); |
| Out << ")"; |
| } else { |
| Out << "-"; |
| } |
| } |
| |
| template <class T> |
| static void hashStringForList(IRGenModule &IGM, const ArrayRef<T> &list, |
| raw_ostream &Out, GenericEnvironment *genericEnv, |
| const SILFunctionType *fnType) { |
| for (auto paramOrRetVal : list) { |
| if (paramOrRetVal.isFormalIndirect()) { |
| // Indirect params and return values have to be opaque. |
| Out << "-indirect"; |
| } else { |
| CanType Ty = paramOrRetVal.getArgumentType( |
| IGM.getSILModule(), fnType, IGM.getMaximalTypeExpansionContext()); |
| if (Ty->hasTypeParameter()) |
| Ty = genericEnv->mapTypeIntoContext(Ty)->getCanonicalType(); |
| hashStringForType(IGM, Ty, Out, genericEnv); |
| } |
| Out << ":"; |
| } |
| } |
| |
| static void hashStringForList(IRGenModule &IGM, |
| const ArrayRef<SILResultInfo> &list, |
| raw_ostream &Out, GenericEnvironment *genericEnv, |
| const SILFunctionType *fnType) { |
| for (auto paramOrRetVal : list) { |
| if (paramOrRetVal.isFormalIndirect()) { |
| // Indirect params and return values have to be opaque. |
| Out << "-indirect"; |
| } else { |
| CanType Ty = paramOrRetVal.getReturnValueType( |
| IGM.getSILModule(), fnType, IGM.getMaximalTypeExpansionContext()); |
| if (Ty->hasTypeParameter()) |
| Ty = genericEnv->mapTypeIntoContext(Ty)->getCanonicalType(); |
| hashStringForType(IGM, Ty, Out, genericEnv); |
| } |
| Out << ":"; |
| } |
| } |
| |
| static void hashStringForFunctionType(IRGenModule &IGM, CanSILFunctionType type, |
| raw_ostream &Out, |
| GenericEnvironment *genericEnv) { |
| Out << (type->isCoroutine() ? "coroutine" : "function") << ":"; |
| Out << type->getNumParameters() << ":"; |
| hashStringForList(IGM, type->getParameters(), Out, genericEnv, type); |
| Out << type->getNumResults() << ":"; |
| hashStringForList(IGM, type->getResults(), Out, genericEnv, type); |
| if (type->isCoroutine()) { |
| Out << type->getNumYields() << ":"; |
| hashStringForList(IGM, type->getYields(), Out, genericEnv, type); |
| } |
| } |
| |
| static uint64_t getTypeHash(IRGenModule &IGM, CanSILFunctionType type) { |
| // The hash we need to do here ignores: |
| // - thickness, so that we can promote thin-to-thick without rehashing; |
| // - error results, so that we can promote nonthrowing-to-throwing |
| // without rehashing; |
| // - types of indirect arguments/retvals, so they can be substituted freely; |
| // - types of class arguments/retvals |
| // - types of metatype arguments/retvals |
| // See isABICompatibleWith and areABICompatibleParamsOrReturns in |
| // SILFunctionType.cpp. |
| |
| SmallString<32> Buffer; |
| llvm::raw_svector_ostream Out(Buffer); |
| auto genericSig = type->getInvocationGenericSignature(); |
| hashStringForFunctionType( |
| IGM, type, Out, |
| genericSig ? genericSig->getCanonicalSignature()->getGenericEnvironment() |
| : nullptr); |
| return clang::CodeGen::computeStableStringHash(Out.str()); |
| } |
| |
| static uint64_t getYieldTypesHash(IRGenModule &IGM, CanSILFunctionType type) { |
| SmallString<32> buffer; |
| llvm::raw_svector_ostream out(buffer); |
| auto genericSig = type->getInvocationGenericSignature(); |
| GenericEnvironment *genericEnv = |
| genericSig ? genericSig->getCanonicalSignature()->getGenericEnvironment() |
| : nullptr; |
| |
| out << [&]() -> StringRef { |
| switch (type->getCoroutineKind()) { |
| case SILCoroutineKind::YieldMany: return "yield_many:"; |
| case SILCoroutineKind::YieldOnce: return "yield_once:"; |
| case SILCoroutineKind::None: llvm_unreachable("not a coroutine"); |
| } |
| llvm_unreachable("bad coroutine kind"); |
| }(); |
| |
| out << type->getNumYields() << ":"; |
| |
| for (auto yield: type->getYields()) { |
| // We can't mangle types on inout and indirect yields because they're |
| // absractable. |
| if (yield.isIndirectInOut()) { |
| out << "inout"; |
| } else if (yield.isFormalIndirect()) { |
| out << "indirect"; |
| } else { |
| CanType Ty = yield.getArgumentType(IGM.getSILModule(), type, |
| IGM.getMaximalTypeExpansionContext()); |
| if (Ty->hasTypeParameter()) |
| Ty = genericEnv->mapTypeIntoContext(Ty)->getCanonicalType(); |
| hashStringForType(IGM, Ty, out, genericEnv); |
| } |
| out << ":"; |
| } |
| |
| return clang::CodeGen::computeStableStringHash(out.str()); |
| } |
| |
| llvm::ConstantInt * |
| PointerAuthEntity::getTypeDiscriminator(IRGenModule &IGM) const { |
| auto getTypeDiscriminator = [&](CanSILFunctionType fnType) { |
| switch (fnType->getRepresentation()) { |
| // Swift function types are type-discriminated. |
| case SILFunctionTypeRepresentation::Thick: |
| case SILFunctionTypeRepresentation::Thin: |
| case SILFunctionTypeRepresentation::Method: |
| case SILFunctionTypeRepresentation::WitnessMethod: |
| case SILFunctionTypeRepresentation::Closure: { |
| llvm::ConstantInt *&cache = IGM.getPointerAuthCaches().Types[fnType]; |
| if (cache) return cache; |
| |
| auto hash = getTypeHash(IGM, fnType); |
| cache = getDiscriminatorForHash(IGM, hash); |
| return cache; |
| } |
| |
| // C function pointers are undiscriminated. |
| case SILFunctionTypeRepresentation::CFunctionPointer: |
| return llvm::ConstantInt::get(IGM.Int64Ty, 0); |
| |
| case SILFunctionTypeRepresentation::ObjCMethod: |
| case SILFunctionTypeRepresentation::Block: { |
| llvm_unreachable("not type discriminated"); |
| } |
| } |
| llvm_unreachable("invalid representation"); |
| }; |
| |
| auto getCoroutineYieldTypesDiscriminator = [&](CanSILFunctionType fnType) { |
| llvm::ConstantInt *&cache = IGM.getPointerAuthCaches().Types[fnType]; |
| if (cache) return cache; |
| |
| auto hash = getYieldTypesHash(IGM, fnType); |
| cache = getDiscriminatorForHash(IGM, hash); |
| return cache; |
| }; |
| |
| switch (StoredKind) { |
| case Kind::None: |
| case Kind::Special: |
| case Kind::ValueWitness: |
| case Kind::AssociatedType: |
| case Kind::AssociatedConformance: |
| case Kind::SILFunction: |
| llvm_unreachable("no type for schema using type discriminiation"); |
| |
| case Kind::CoroutineYieldTypes: { |
| auto fnType = Storage.get<CanSILFunctionType>(StoredKind); |
| return getCoroutineYieldTypesDiscriminator(fnType); |
| } |
| |
| case Kind::CanSILFunctionType: { |
| auto fnType = Storage.get<CanSILFunctionType>(StoredKind); |
| return getTypeDiscriminator(fnType); |
| } |
| |
| case Kind::SILDeclRef: { |
| SILDeclRef decl = Storage.get<SILDeclRef>(StoredKind); |
| auto fnType = IGM.getSILTypes().getConstantFunctionType( |
| TypeExpansionContext::minimal(), decl); |
| return getTypeDiscriminator(fnType); |
| } |
| } |
| llvm_unreachable("bad kind"); |
| } |
| |
| llvm::Constant * |
| IRGenModule::getConstantSignedFunctionPointer(llvm::Constant *fn, |
| CanSILFunctionType fnType) { |
| if (auto &schema = getFunctionPointerSchema(*this, fnType)) { |
| return getConstantSignedPointer(fn, schema, fnType, nullptr); |
| } |
| return fn; |
| } |
| |
| llvm::Constant * |
| IRGenModule::getConstantSignedCFunctionPointer(llvm::Constant *fn) { |
| if (auto &schema = getOptions().PointerAuth.FunctionPointers) { |
| assert(!schema.hasOtherDiscrimination()); |
| return getConstantSignedPointer(fn, schema, PointerAuthEntity(), nullptr); |
| } |
| return fn; |
| } |
| |
| llvm::Constant *IRGenModule::getConstantSignedPointer(llvm::Constant *pointer, |
| unsigned key, |
| llvm::Constant *storageAddress, |
| llvm::Constant *otherDiscriminator) { |
| return clang::CodeGen::getConstantSignedPointer(getClangCGM(), pointer, key, |
| storageAddress, |
| otherDiscriminator); |
| } |
| |
| llvm::Constant *IRGenModule::getConstantSignedPointer(llvm::Constant *pointer, |
| const PointerAuthSchema &schema, |
| const PointerAuthEntity &entity, |
| llvm::Constant *storageAddress) { |
| // If the schema doesn't sign pointers, do nothing. |
| if (!schema) |
| return pointer; |
| |
| auto otherDiscriminator = |
| PointerAuthInfo::getOtherDiscriminator(*this, schema, entity); |
| return getConstantSignedPointer(pointer, schema.getKey(), storageAddress, |
| otherDiscriminator); |
| } |
| |
| void ConstantAggregateBuilderBase::addSignedPointer(llvm::Constant *pointer, |
| const PointerAuthSchema &schema, |
| const PointerAuthEntity &entity) { |
| // If the schema doesn't sign pointers, do nothing. |
| if (!schema) |
| return add(pointer); |
| |
| auto otherDiscriminator = |
| PointerAuthInfo::getOtherDiscriminator(IGM(), schema, entity); |
| addSignedPointer(pointer, schema.getKey(), schema.isAddressDiscriminated(), |
| otherDiscriminator); |
| } |
| |
| void ConstantAggregateBuilderBase::addSignedPointer(llvm::Constant *pointer, |
| const PointerAuthSchema &schema, |
| uint16_t otherDiscriminator) { |
| // If the schema doesn't sign pointers, do nothing. |
| if (!schema) |
| return add(pointer); |
| |
| addSignedPointer(pointer, schema.getKey(), schema.isAddressDiscriminated(), |
| llvm::ConstantInt::get(IGM().IntPtrTy, otherDiscriminator)); |
| } |