diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 7aa1357b9aad0..a99f2dc6eb3f2 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -37,6 +37,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/TypeSize.h" #include @@ -1277,6 +1278,11 @@ class ASTContext : public RefCountedBase { /// space. QualType removeAddrSpaceQualType(QualType T) const; + /// Return the "other" discriminator used for the pointer auth schema used for + /// vtable pointers in instances of the requested type. + uint16_t + getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *RD); + /// Apply Objective-C protocol qualifiers to the given type. /// \param allowOnPointerType specifies if we can apply protocol /// qualifiers on ObjCObjectPointerType. It can be set to true when @@ -3438,12 +3444,21 @@ OPT_LIST(V) /// Whether a C++ static variable or CUDA/HIP kernel should be externalized. bool shouldExternalize(const Decl *D) const; + /// Resolve the root record to be used to derive the vtable pointer + /// authentication policy for the specified record. + const CXXRecordDecl * + baseForVTableAuthentication(const CXXRecordDecl *ThisClass); + bool useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl, + StringRef MangledName); + StringRef getCUIDHash() const; private: /// All OMPTraitInfo objects live in this collection, one per /// `pragma omp [begin] declare variant` directive. SmallVector, 4> OMPTraitInfoVector; + + llvm::DenseMap> ThunksToBeAbbreviated; }; /// Insertion operator for diagnostics. diff --git a/clang/include/clang/AST/GlobalDecl.h b/clang/include/clang/AST/GlobalDecl.h index 88abba28c991d..386693cabb1fb 100644 --- a/clang/include/clang/AST/GlobalDecl.h +++ b/clang/include/clang/AST/GlobalDecl.h @@ -145,6 +145,10 @@ class GlobalDecl { LHS.MultiVersionIndex == RHS.MultiVersionIndex; } + bool operator!=(const GlobalDecl &Other) const { + return !(*this == Other); + } + void *getAsOpaquePtr() const { return Value.getOpaqueValue(); } explicit operator bool() const { return getAsOpaquePtr(); } diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h index e586b0cec43df..d5f6c0f6cc67d 100644 --- a/clang/include/clang/AST/Mangle.h +++ b/clang/include/clang/AST/Mangle.h @@ -130,15 +130,15 @@ class MangleContext { // FIXME: consider replacing raw_ostream & with something like SmallString &. void mangleName(GlobalDecl GD, raw_ostream &); virtual void mangleCXXName(GlobalDecl GD, raw_ostream &) = 0; - virtual void mangleThunk(const CXXMethodDecl *MD, - const ThunkInfo &Thunk, - raw_ostream &) = 0; + virtual void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk, + bool ElideOverrideInfo, raw_ostream &) = 0; virtual void mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type, - const ThisAdjustment &ThisAdjustment, - raw_ostream &) = 0; + const ThunkInfo &Thunk, + bool ElideOverrideInfo, raw_ostream &) = 0; virtual void mangleReferenceTemporary(const VarDecl *D, unsigned ManglingNumber, raw_ostream &) = 0; + virtual void mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &) = 0; virtual void mangleCXXRTTI(QualType T, raw_ostream &) = 0; virtual void mangleCXXRTTIName(QualType T, raw_ostream &, bool NormalizeIntegers = false) = 0; @@ -192,7 +192,6 @@ class ItaniumMangleContext : public MangleContext { bool IsAux = false) : MangleContext(C, D, MK_Itanium, IsAux) {} - virtual void mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &) = 0; virtual void mangleCXXVTT(const CXXRecordDecl *RD, raw_ostream &) = 0; virtual void mangleCXXCtorVTable(const CXXRecordDecl *RD, int64_t Offset, const CXXRecordDecl *Type, diff --git a/clang/include/clang/AST/VTableBuilder.h b/clang/include/clang/AST/VTableBuilder.h index fbf6c041a1ec1..a5de41dbc22f1 100644 --- a/clang/include/clang/AST/VTableBuilder.h +++ b/clang/include/clang/AST/VTableBuilder.h @@ -361,6 +361,10 @@ class VTableContextBase { }; class ItaniumVTableContext : public VTableContextBase { +public: + typedef llvm::DenseMap + OriginalMethodMapTy; + private: /// Contains the index (relative to the vtable address point) @@ -384,6 +388,10 @@ class ItaniumVTableContext : public VTableContextBase { VirtualBaseClassOffsetOffsetsMapTy; VirtualBaseClassOffsetOffsetsMapTy VirtualBaseClassOffsetOffsets; + /// Map from a virtual method to the nearest method in the primary base class + /// chain that it overrides. + OriginalMethodMapTy OriginalMethodMap; + void computeVTableRelatedInformation(const CXXRecordDecl *RD) override; public: @@ -425,6 +433,27 @@ class ItaniumVTableContext : public VTableContextBase { CharUnits getVirtualBaseOffsetOffset(const CXXRecordDecl *RD, const CXXRecordDecl *VBase); + /// Return the method that added the v-table slot that will be used to call + /// the given method. + /// + /// In the Itanium ABI, where overrides always cause methods to be added to + /// the primary v-table if they're not already there, this will be the first + /// declaration in the primary base class chain for which the return type + /// adjustment is trivial. + GlobalDecl findOriginalMethod(GlobalDecl GD); + + const CXXMethodDecl *findOriginalMethodInMap(const CXXMethodDecl *MD) const; + + void setOriginalMethod(const CXXMethodDecl *Key, const CXXMethodDecl *Val) { + OriginalMethodMap[Key] = Val; + } + + /// This method is reserved for the implementation and shouldn't be used + /// directly. + const OriginalMethodMapTy &getOriginalMethodMap() { + return OriginalMethodMap; + } + static bool classof(const VTableContextBase *VT) { return !VT->isMicrosoft(); } diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 0c469e389eff0..452cd1810f653 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -686,6 +686,10 @@ class Attr { bit PragmaAttributeSupport; // Set to true if this attribute accepts parameter pack expansion expressions. bit AcceptsExprPack = 0; + // To support multiple enum parameters to an attribute without breaking + // our existing general parsing we need to have a separate flag that + // opts an attribute into strict parsing of attribute parameters + bit StrictEnumParameters = 0; // Lists language options, one of which is required to be true for the // attribute to be applicable. If empty, no language options are required. list LangOpts = []; @@ -4576,6 +4580,31 @@ def NoRandomizeLayout : InheritableAttr { } def : MutualExclusions<[RandomizeLayout, NoRandomizeLayout]>; +def VTablePointerAuthentication : InheritableAttr { + let Spellings = [Clang<"ptrauth_vtable_pointer">]; + let Subjects = SubjectList<[CXXRecord]>; + let Documentation = [Undocumented]; + let StrictEnumParameters = 1; + let Args = [EnumArgument<"Key", "VPtrAuthKeyType", /*is_string=*/ true, + ["default_key", "no_authentication", "process_dependent", + "process_independent"], + ["DefaultKey", "NoKey", "ProcessDependent", + "ProcessIndependent"]>, + EnumArgument<"AddressDiscrimination", "AddressDiscriminationMode", + /*is_string=*/ true, + ["default_address_discrimination", "no_address_discrimination", + "address_discrimination"], + ["DefaultAddressDiscrimination", "NoAddressDiscrimination", + "AddressDiscrimination"]>, + EnumArgument<"ExtraDiscrimination", "ExtraDiscrimination", + /*is_string=*/ true, + ["default_extra_discrimination", "no_extra_discrimination", + "type_discrimination", "custom_discrimination"], + ["DefaultExtraDiscrimination", "NoExtraDiscrimination", + "TypeDiscrimination", "CustomDiscrimination"]>, + IntArgument<"CustomDiscriminationValue", 1>]; +} + def FunctionReturnThunks : InheritableAttr, TargetSpecificAttr { let Spellings = [GCC<"function_return">]; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 46ad359751d7d..46e07db835274 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -940,6 +940,13 @@ def warn_ptrauth_auth_null_pointer : def err_ptrauth_string_not_literal : Error< "argument must be a string literal%select{| of char type}0">; +def note_ptrauth_virtual_function_pointer_incomplete_arg_ret : + Note<"cannot take an address of a virtual member function if its return or " + "argument types are incomplete">; +def note_ptrauth_virtual_function_incomplete_arg_ret_type : + Note<"%0 is incomplete">; + + /// main() // static main() is not an error in C, just in C++. def warn_static_main : Warning<"'main' should not be declared static">, @@ -12215,6 +12222,30 @@ def warn_cuda_maxclusterrank_sm_90 : Warning< "maxclusterrank requires sm_90 or higher, CUDA arch provided: %0, ignoring " "%1 attribute">, InGroup; +// VTable pointer authentication errors +def err_non_polymorphic_vtable_pointer_auth : Error< + "cannot set vtable pointer authentication on monomorphic type %0">; +def err_incomplete_type_vtable_pointer_auth : Error< + "cannot set vtable pointer authentication on an incomplete type %0">; +def err_non_top_level_vtable_pointer_auth : Error< + "cannot set vtable pointer authentication on %0 which is a subclass of polymorphic type %1">; +def err_duplicated_vtable_pointer_auth : Error< + "multiple vtable pointer authentication policies on %0">; +def err_invalid_authentication_key : Error< + "invalid authentication key %0">; +def err_invalid_address_discrimination : Error< + "invalid address discrimination mode %0">; +def err_invalid_extra_discrimination : Error< + "invalid extra discrimination selection %0">; +def err_invalid_custom_discrimination : Error< + "invalid custom discrimination">; +def err_missing_custom_discrimination : Error< + "missing custom discrimination">; +def err_no_default_vtable_pointer_auth : Error< + "cannot specify a default vtable pointer authentication " + "%select{key|address discrimination mode|discriminator}0 with no default set" +>; + def err_bit_int_bad_size : Error<"%select{signed|unsigned}0 _BitInt must " "have a bit size of at least %select{2|1}0">; def err_bit_int_max_size : Error<"%select{signed|unsigned}0 _BitInt of bit " diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index aaad4a2b2b5ae..197d63642ca6d 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -47,6 +47,12 @@ class PointerAuthSchema { /// No additional discrimination. None, + /// Include a hash of the entity's type. + Type, + + /// Include a hash of the entity's identity. + Decl, + /// Discriminate using a constant value. Constant, }; @@ -150,6 +156,25 @@ class PointerAuthSchema { struct PointerAuthOptions { /// The ABI for C function pointers. PointerAuthSchema FunctionPointers; + + /// The ABI for C++ virtual table pointers (the pointer to the table + /// itself) as installed in an actual class instance. + PointerAuthSchema CXXVTablePointers; + + /// TypeInfo has external ABI requirements and is emitted without + /// actually having parsed the libcxx definition, so we can't simply + /// perform a look up. The settings for this should match the exact + /// specification in type_info.h + PointerAuthSchema CXXTypeInfoVTablePointer; + + /// The ABI for C++ virtual table pointers as installed in a VTT. + PointerAuthSchema CXXVTTVTablePointers; + + /// The ABI for most C++ virtual function pointers, i.e. v-table entries. + PointerAuthSchema CXXVirtualFunctionPointers; + + /// The ABI for variadic C++ virtual function pointers. + PointerAuthSchema CXXVirtualVariadicFunctionPointers; }; } // end namespace clang diff --git a/clang/include/clang/Basic/Thunk.h b/clang/include/clang/Basic/Thunk.h index 0247e279408f0..af4afb2d2ac4d 100644 --- a/clang/include/clang/Basic/Thunk.h +++ b/clang/include/clang/Basic/Thunk.h @@ -162,20 +162,24 @@ struct ThunkInfo { /// Holds a pointer to the overridden method this thunk is for, /// if needed by the ABI to distinguish different thunks with equal - /// adjustments. Otherwise, null. + /// adjustments. + /// In the Itanium ABI, this field can hold the method that created the + /// vtable entry for this thunk. + /// Otherwise, null. /// CAUTION: In the unlikely event you need to sort ThunkInfos, consider using /// an ABI-specific comparator. const CXXMethodDecl *Method; + const Type *ThisType; - ThunkInfo() : Method(nullptr) {} + ThunkInfo() : Method(nullptr), ThisType(nullptr) {} ThunkInfo(const ThisAdjustment &This, const ReturnAdjustment &Return, - const CXXMethodDecl *Method = nullptr) - : This(This), Return(Return), Method(Method) {} + const Type *ThisT, const CXXMethodDecl *Method = nullptr) + : This(This), Return(Return), Method(Method), ThisType(ThisT) {} friend bool operator==(const ThunkInfo &LHS, const ThunkInfo &RHS) { return LHS.This == RHS.This && LHS.Return == RHS.Return && - LHS.Method == RHS.Method; + LHS.Method == RHS.Method && LHS.ThisType == RHS.ThisType; } bool isEmpty() const { diff --git a/clang/include/clang/CodeGen/CodeGenABITypes.h b/clang/include/clang/CodeGen/CodeGenABITypes.h index fda0855dc8683..d4822dc160082 100644 --- a/clang/include/clang/CodeGen/CodeGenABITypes.h +++ b/clang/include/clang/CodeGen/CodeGenABITypes.h @@ -42,6 +42,7 @@ class CXXConstructorDecl; class CXXDestructorDecl; class CXXRecordDecl; class CXXMethodDecl; +class GlobalDecl; class ObjCMethodDecl; class ObjCProtocolDecl; @@ -104,6 +105,9 @@ llvm::Type *convertTypeForMemory(CodeGenModule &CGM, QualType T); unsigned getLLVMFieldNumber(CodeGenModule &CGM, const RecordDecl *RD, const FieldDecl *FD); +/// Return a declaration discriminator for the given global decl. +uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD); + /// Given the language and code-generation options that Clang was configured /// with, set the default LLVM IR attributes for a function definition. /// The attributes set here are mostly global target-configuration and diff --git a/clang/include/clang/CodeGen/ConstantInitBuilder.h b/clang/include/clang/CodeGen/ConstantInitBuilder.h index 498acfd380131..28d4764b6d60b 100644 --- a/clang/include/clang/CodeGen/ConstantInitBuilder.h +++ b/clang/include/clang/CodeGen/ConstantInitBuilder.h @@ -25,8 +25,11 @@ #include namespace clang { -namespace CodeGen { +class GlobalDecl; +class PointerAuthSchema; +class QualType; +namespace CodeGen { class CodeGenModule; /// A convenience builder class for complex constant initializers, @@ -199,6 +202,11 @@ class ConstantAggregateBuilderBase { add(llvm::ConstantInt::get(intTy, value, isSigned)); } + /// Add a signed pointer using the given pointer authentication schema. + void addSignedPointer(llvm::Constant *Pointer, + const PointerAuthSchema &Schema, GlobalDecl CalleeDecl, + QualType CalleeType); + /// Add a null pointer of a specific type. void addNullPointer(llvm::PointerType *ptrTy) { add(llvm::ConstantPointerNull::get(ptrTy)); diff --git a/clang/include/clang/InstallAPI/Visitor.h b/clang/include/clang/InstallAPI/Visitor.h index 9ac948ded3e33..3680ee566ca87 100644 --- a/clang/include/clang/InstallAPI/Visitor.h +++ b/clang/include/clang/InstallAPI/Visitor.h @@ -60,8 +60,8 @@ class InstallAPIVisitor final : public ASTConsumer, std::string getMangledName(const NamedDecl *D) const; std::string getBackendMangledName(llvm::Twine Name) const; std::string getMangledCXXVTableName(const CXXRecordDecl *D) const; - std::string getMangledCXXThunk(const GlobalDecl &D, - const ThunkInfo &Thunk) const; + std::string getMangledCXXThunk(const GlobalDecl &D, const ThunkInfo &Thunk, + bool ElideOverrideInfo) const; std::string getMangledCXXRTTI(const CXXRecordDecl *D) const; std::string getMangledCXXRTTIName(const CXXRecordDecl *D) const; std::string getMangledCtorDtor(const CXXMethodDecl *D, int Type) const; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2e7af0f691cbb..ab3cbd19e1ff0 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4566,6 +4566,10 @@ class Sema final : public SemaBase { /// conditions that are needed for the attribute to have an effect. void checkIllFormedTrivialABIStruct(CXXRecordDecl &RD); + /// Check that VTable Pointer authentication is only being set on the first + /// first instantiation of the vtable + void checkIncorrectVTablePointerAuthenticationAttribute(CXXRecordDecl &RD); + void ActOnFinishCXXMemberSpecification(Scope *S, SourceLocation RLoc, Decl *TagDecl, SourceLocation LBrac, SourceLocation RBrac, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 1b5d16bd176f3..84deaf5429df7 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -86,6 +86,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/SipHash.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/AArch64TargetParser.h" #include "llvm/TargetParser/Triple.h" @@ -3128,6 +3129,17 @@ QualType ASTContext::removeAddrSpaceQualType(QualType T) const { return QualType(TypeNode, Quals.getFastQualifiers()); } +uint16_t +ASTContext::getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *RD) { + assert(RD->isPolymorphic() && + "Attempted to get vtable pointer discriminator on a monomorphic type"); + std::unique_ptr MC(createMangleContext()); + SmallString<256> Str; + llvm::raw_svector_ostream Out(Str); + MC->mangleCXXVTable(RD, Out); + return llvm::getPointerAuthStableSipHash(Str); +} + QualType ASTContext::getObjCGCQualType(QualType T, Qualifiers::GC GCAttr) const { QualType CanT = getCanonicalType(T); @@ -13894,3 +13906,74 @@ StringRef ASTContext::getCUIDHash() const { CUIDHash = llvm::utohexstr(llvm::MD5Hash(LangOpts.CUID), /*LowerCase=*/true); return CUIDHash; } + +const CXXRecordDecl * +ASTContext::baseForVTableAuthentication(const CXXRecordDecl *ThisClass) { + assert(ThisClass); + assert(ThisClass->isPolymorphic()); + const CXXRecordDecl *PrimaryBase = ThisClass; + while (1) { + assert(PrimaryBase); + assert(PrimaryBase->isPolymorphic()); + auto &Layout = getASTRecordLayout(PrimaryBase); + auto Base = Layout.getPrimaryBase(); + if (!Base || Base == PrimaryBase || !Base->isPolymorphic()) + break; + PrimaryBase = Base; + } + return PrimaryBase; +} + +bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl, + StringRef MangledName) { + auto *Method = cast(VirtualMethodDecl.getDecl()); + assert(Method->isVirtual()); + bool DefaultIncludesPointerAuth = + LangOpts.PointerAuthCalls || LangOpts.PointerAuthIntrinsics; + + if (!DefaultIncludesPointerAuth) + return true; + + auto Existing = ThunksToBeAbbreviated.find(VirtualMethodDecl); + if (Existing != ThunksToBeAbbreviated.end()) + return Existing->second.contains(MangledName.str()); + + std::unique_ptr Mangler(createMangleContext()); + llvm::StringMap> Thunks; + auto VtableContext = getVTableContext(); + if (const auto *ThunkInfos = VtableContext->getThunkInfo(VirtualMethodDecl)) { + auto *Destructor = dyn_cast(Method); + for (const auto &Thunk : *ThunkInfos) { + SmallString<256> ElidedName; + llvm::raw_svector_ostream ElidedNameStream(ElidedName); + if (Destructor) + Mangler->mangleCXXDtorThunk(Destructor, VirtualMethodDecl.getDtorType(), + Thunk, /* elideOverrideInfo */ true, + ElidedNameStream); + else + Mangler->mangleThunk(Method, Thunk, /* elideOverrideInfo */ true, + ElidedNameStream); + SmallString<256> MangledName; + llvm::raw_svector_ostream mangledNameStream(MangledName); + if (Destructor) + Mangler->mangleCXXDtorThunk(Destructor, VirtualMethodDecl.getDtorType(), + Thunk, /* elideOverrideInfo */ false, + mangledNameStream); + else + Mangler->mangleThunk(Method, Thunk, /* elideOverrideInfo */ false, + mangledNameStream); + + if (Thunks.find(ElidedName) == Thunks.end()) + Thunks[ElidedName] = {}; + Thunks[ElidedName].push_back(std::string(MangledName)); + } + } + llvm::StringSet<> SimplifiedThunkNames; + for (auto &ThunkList : Thunks) { + llvm::sort(ThunkList.second); + SimplifiedThunkNames.insert(ThunkList.second[0]); + } + bool Result = SimplifiedThunkNames.contains(MangledName); + ThunksToBeAbbreviated[VirtualMethodDecl] = std::move(SimplifiedThunkNames); + return Result; +} diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 2cac03ba163f9..5444dcf027fe2 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -99,11 +99,10 @@ class ItaniumMangleContextImpl : public ItaniumMangleContext { } void mangleCXXName(GlobalDecl GD, raw_ostream &) override; - void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk, + void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk, bool, raw_ostream &) override; void mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type, - const ThisAdjustment &ThisAdjustment, - raw_ostream &) override; + const ThunkInfo &Thunk, bool, raw_ostream &) override; void mangleReferenceTemporary(const VarDecl *D, unsigned ManglingNumber, raw_ostream &) override; void mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &) override; @@ -468,6 +467,7 @@ class CXXNameMangler { void mangleNameOrStandardSubstitution(const NamedDecl *ND); void mangleLambdaSig(const CXXRecordDecl *Lambda); void mangleModuleNamePrefix(StringRef Name, bool IsPartition = false); + void mangleVendorQualifier(StringRef Name); private: @@ -559,7 +559,6 @@ class CXXNameMangler { StringRef Prefix = ""); void mangleOperatorName(DeclarationName Name, unsigned Arity); void mangleOperatorName(OverloadedOperatorKind OO, unsigned Arity); - void mangleVendorQualifier(StringRef qualifier); void mangleQualifiers(Qualifiers Quals, const DependentAddressSpaceType *DAST = nullptr); void mangleRefQualifier(RefQualifierKind RefQualifier); @@ -7044,8 +7043,78 @@ void ItaniumMangleContextImpl::mangleCXXDtorComdat(const CXXDestructorDecl *D, Mangler.mangle(GlobalDecl(D, Dtor_Comdat)); } +/// Mangles the pointer authentication override attribute for classes +/// that have explicit overrides for the vtable authentication schema. +/// +/// The override is mangled as a parameterized vendor extension as follows +/// +/// ::= U "__vtptrauth" I +/// +/// +/// +/// E +/// +/// The extra discriminator encodes the explicit value derived from the +/// override schema, e.g. if the override has specified type based +/// discrimination the encoded value will be the discriminator derived from the +/// type name. +static void mangleOverrideDiscrimination(CXXNameMangler &Mangler, + ASTContext &Context, + const ThunkInfo &Thunk) { + auto &LangOpts = Context.getLangOpts(); + const CXXRecordDecl *ThisRD = Thunk.ThisType->getPointeeCXXRecordDecl(); + const CXXRecordDecl *PtrauthClassRD = + Context.baseForVTableAuthentication(ThisRD); + unsigned TypedDiscriminator = + Context.getPointerAuthVTablePointerDiscriminator(ThisRD); + Mangler.mangleVendorQualifier("__vtptrauth"); + auto &ManglerStream = Mangler.getStream(); + ManglerStream << "I"; + if (const auto *ExplicitAuth = + PtrauthClassRD->getAttr()) { + ManglerStream << "Lj" << ExplicitAuth->getKey(); + + if (ExplicitAuth->getAddressDiscrimination() == + VTablePointerAuthenticationAttr::DefaultAddressDiscrimination) + ManglerStream << "Lb" << LangOpts.PointerAuthVTPtrAddressDiscrimination; + else + ManglerStream << "Lb" + << (ExplicitAuth->getAddressDiscrimination() == + VTablePointerAuthenticationAttr::AddressDiscrimination); + + switch (ExplicitAuth->getExtraDiscrimination()) { + case VTablePointerAuthenticationAttr::DefaultExtraDiscrimination: { + if (LangOpts.PointerAuthVTPtrTypeDiscrimination) + ManglerStream << "Lj" << TypedDiscriminator; + else + ManglerStream << "Lj" << 0; + break; + } + case VTablePointerAuthenticationAttr::TypeDiscrimination: + ManglerStream << "Lj" << TypedDiscriminator; + break; + case VTablePointerAuthenticationAttr::CustomDiscrimination: + ManglerStream << "Lj" << ExplicitAuth->getCustomDiscriminationValue(); + break; + case VTablePointerAuthenticationAttr::NoExtraDiscrimination: + ManglerStream << "Lj" << 0; + break; + } + } else { + ManglerStream << "Lj" + << (unsigned)VTablePointerAuthenticationAttr::DefaultKey; + ManglerStream << "Lb" << LangOpts.PointerAuthVTPtrAddressDiscrimination; + if (LangOpts.PointerAuthVTPtrTypeDiscrimination) + ManglerStream << "Lj" << TypedDiscriminator; + else + ManglerStream << "Lj" << 0; + } + ManglerStream << "E"; +} + void ItaniumMangleContextImpl::mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk, + bool ElideOverrideInfo, raw_ostream &Out) { // ::= T // # base is the nominal target function of thunk @@ -7071,21 +7140,28 @@ void ItaniumMangleContextImpl::mangleThunk(const CXXMethodDecl *MD, Thunk.Return.Virtual.Itanium.VBaseOffsetOffset); Mangler.mangleFunctionEncoding(MD); + if (!ElideOverrideInfo) + mangleOverrideDiscrimination(Mangler, getASTContext(), Thunk); } -void ItaniumMangleContextImpl::mangleCXXDtorThunk( - const CXXDestructorDecl *DD, CXXDtorType Type, - const ThisAdjustment &ThisAdjustment, raw_ostream &Out) { +void ItaniumMangleContextImpl::mangleCXXDtorThunk(const CXXDestructorDecl *DD, + CXXDtorType Type, + const ThunkInfo &Thunk, + bool ElideOverrideInfo, + raw_ostream &Out) { // ::= T // # base is the nominal target function of thunk CXXNameMangler Mangler(*this, Out, DD, Type); Mangler.getStream() << "_ZT"; + auto &ThisAdjustment = Thunk.This; // Mangle the 'this' pointer adjustment. Mangler.mangleCallOffset(ThisAdjustment.NonVirtual, ThisAdjustment.Virtual.Itanium.VCallOffsetOffset); Mangler.mangleFunctionEncoding(GlobalDecl(DD, Type)); + if (!ElideOverrideInfo) + mangleOverrideDiscrimination(Mangler, getASTContext(), Thunk); } /// Returns the mangled name for a guard variable for the passed in VarDecl. diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp index 4fbf0e3b42dbc..75f6e2161a633 100644 --- a/clang/lib/AST/Mangle.cpp +++ b/clang/lib/AST/Mangle.cpp @@ -513,10 +513,20 @@ class ASTNameGenerator::Implementation { } } else if (const auto *MD = dyn_cast_or_null(ND)) { Manglings.emplace_back(getName(ND)); - if (MD->isVirtual()) - if (const auto *TIV = Ctx.getVTableContext()->getThunkInfo(MD)) - for (const auto &T : *TIV) - Manglings.emplace_back(getMangledThunk(MD, T)); + if (MD->isVirtual()) { + if (const auto *TIV = Ctx.getVTableContext()->getThunkInfo(MD)) { + for (const auto &T : *TIV) { + std::string ThunkName; + std::string ContextualizedName = + getMangledThunk(MD, T, /* ElideOverrideInfo */ false); + if (Ctx.useAbbreviatedThunkName(MD, ContextualizedName)) + ThunkName = getMangledThunk(MD, T, /* ElideOverrideInfo */ true); + else + ThunkName = ContextualizedName; + Manglings.emplace_back(ThunkName); + } + } + } } return Manglings; @@ -569,11 +579,12 @@ class ASTNameGenerator::Implementation { return BOS.str(); } - std::string getMangledThunk(const CXXMethodDecl *MD, const ThunkInfo &T) { + std::string getMangledThunk(const CXXMethodDecl *MD, const ThunkInfo &T, + bool ElideOverrideInfo) { std::string FrontendBuf; llvm::raw_string_ostream FOS(FrontendBuf); - MC->mangleThunk(MD, T, FOS); + MC->mangleThunk(MD, T, ElideOverrideInfo, FOS); std::string BackendBuf; llvm::raw_string_ostream BOS(BackendBuf); diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 3923d34274703..55404ca128280 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -159,9 +159,9 @@ class MicrosoftMangleContextImpl : public MicrosoftMangleContext { const MethodVFTableLocation &ML, raw_ostream &Out) override; void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk, - raw_ostream &) override; + bool ElideOverrideInfo, raw_ostream &) override; void mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type, - const ThisAdjustment &ThisAdjustment, + const ThunkInfo &Thunk, bool ElideOverrideInfo, raw_ostream &) override; void mangleCXXVFTable(const CXXRecordDecl *Derived, ArrayRef BasePath, @@ -169,6 +169,8 @@ class MicrosoftMangleContextImpl : public MicrosoftMangleContext { void mangleCXXVBTable(const CXXRecordDecl *Derived, ArrayRef BasePath, raw_ostream &Out) override; + + void mangleCXXVTable(const CXXRecordDecl *, raw_ostream &) override; void mangleCXXVirtualDisplacementMap(const CXXRecordDecl *SrcRD, const CXXRecordDecl *DstRD, raw_ostream &Out) override; @@ -3715,6 +3717,7 @@ void MicrosoftMangleContextImpl::mangleVirtualMemPtrThunk( void MicrosoftMangleContextImpl::mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk, + bool /*ElideOverrideInfo*/, raw_ostream &Out) { msvc_hashing_ostream MHO(Out); MicrosoftCXXNameMangler Mangler(*this, MHO); @@ -3736,9 +3739,11 @@ void MicrosoftMangleContextImpl::mangleThunk(const CXXMethodDecl *MD, DeclForFPT->getType()->castAs(), MD); } -void MicrosoftMangleContextImpl::mangleCXXDtorThunk( - const CXXDestructorDecl *DD, CXXDtorType Type, - const ThisAdjustment &Adjustment, raw_ostream &Out) { +void MicrosoftMangleContextImpl::mangleCXXDtorThunk(const CXXDestructorDecl *DD, + CXXDtorType Type, + const ThunkInfo &Thunk, + bool /*ElideOverrideInfo*/, + raw_ostream &Out) { // FIXME: Actually, the dtor thunk should be emitted for vector deleting // dtors rather than scalar deleting dtors. Just use the vector deleting dtor // mangling manually until we support both deleting dtor types. @@ -3747,6 +3752,7 @@ void MicrosoftMangleContextImpl::mangleCXXDtorThunk( MicrosoftCXXNameMangler Mangler(*this, MHO, DD, Type); Mangler.getStream() << "??_E"; Mangler.mangleName(DD->getParent()); + auto &Adjustment = Thunk.This; mangleThunkThisAdjustment(DD->getAccess(), Adjustment, Mangler, MHO); Mangler.mangleFunctionType(DD->getType()->castAs(), DD); } @@ -3771,6 +3777,12 @@ void MicrosoftMangleContextImpl::mangleCXXVFTable( Mangler.getStream() << '@'; } +void MicrosoftMangleContextImpl::mangleCXXVTable(const CXXRecordDecl *Derived, + raw_ostream &Out) { + // TODO: Determine appropriate mangling for MSABI + mangleCXXVFTable(Derived, {}, Out); +} + void MicrosoftMangleContextImpl::mangleCXXVBTable( const CXXRecordDecl *Derived, ArrayRef BasePath, raw_ostream &Out) { diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp index a956ca5b37acf..e941c3bedb0a7 100644 --- a/clang/lib/AST/VTableBuilder.cpp +++ b/clang/lib/AST/VTableBuilder.cpp @@ -1147,11 +1147,41 @@ void ItaniumVTableBuilder::ComputeThisAdjustments() { continue; // Add it. - VTableThunks[VTableIndex].This = ThisAdjustment; + auto SetThisAdjustmentThunk = [&](uint64_t Idx) { + // If a this pointer adjustment is required, record the method that + // created the vtable entry. MD is not necessarily the method that + // created the entry since derived classes overwrite base class + // information in MethodInfoMap, hence findOriginalMethodInMap is called + // here. + // + // For example, in the following class hierarchy, if MD = D1::m and + // Overrider = D2:m, the original method that created the entry is B0:m, + // which is what findOriginalMethodInMap(MD) returns: + // + // struct B0 { int a; virtual void m(); }; + // struct D0 : B0 { int a; void m() override; }; + // struct D1 : B0 { int a; void m() override; }; + // struct D2 : D0, D1 { int a; void m() override; }; + // + // We need to record the method because we cannot + // call findOriginalMethod to find the method that created the entry if + // the method in the entry requires adjustment. + // + // Do not set ThunkInfo::Method if Idx is already in VTableThunks. This + // can happen when covariant return adjustment is required too. + if (!VTableThunks.count(Idx)) { + const CXXMethodDecl *Method = VTables.findOriginalMethodInMap(MD); + VTableThunks[Idx].Method = Method; + VTableThunks[Idx].ThisType = Method->getThisType().getTypePtr(); + } + VTableThunks[Idx].This = ThisAdjustment; + }; + + SetThisAdjustmentThunk(VTableIndex); if (isa(MD)) { // Add an adjustment for the deleting destructor as well. - VTableThunks[VTableIndex + 1].This = ThisAdjustment; + SetThisAdjustmentThunk(VTableIndex + 1); } } @@ -1509,6 +1539,8 @@ void ItaniumVTableBuilder::AddMethods( FindNearestOverriddenMethod(MD, PrimaryBases)) { if (ComputeReturnAdjustmentBaseOffset(Context, MD, OverriddenMD).isEmpty()) { + VTables.setOriginalMethod(MD, OverriddenMD); + // Replace the method info of the overridden method with our own // method. assert(MethodInfoMap.count(OverriddenMD) && @@ -1547,7 +1579,8 @@ void ItaniumVTableBuilder::AddMethods( // This is a virtual thunk for the most derived class, add it. AddThunk(Overrider.Method, - ThunkInfo(ThisAdjustment, ReturnAdjustment)); + ThunkInfo(ThisAdjustment, ReturnAdjustment, + OverriddenMD->getThisType().getTypePtr())); } } @@ -1615,6 +1648,15 @@ void ItaniumVTableBuilder::AddMethods( ReturnAdjustment ReturnAdjustment = ComputeReturnAdjustment(ReturnAdjustmentOffset); + // If a return adjustment is required, record the method that created the + // vtable entry. We need to record the method because we cannot call + // findOriginalMethod to find the method that created the entry if the + // method in the entry requires adjustment. + if (!ReturnAdjustment.isEmpty()) { + VTableThunks[Components.size()].Method = MD; + VTableThunks[Components.size()].ThisType = MD->getThisType().getTypePtr(); + } + AddMethod(Overrider.Method, ReturnAdjustment); } } @@ -1890,11 +1932,31 @@ void ItaniumVTableBuilder::LayoutVTablesForVirtualBases( } } +static void printThunkMethod(const ThunkInfo &Info, raw_ostream &Out) { + if (!Info.Method) + return; + std::string Str = PredefinedExpr::ComputeName( + PredefinedIdentKind::PrettyFunctionNoVirtual, Info.Method); + Out << " method: " << Str; +} + /// dumpLayout - Dump the vtable layout. void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { // FIXME: write more tests that actually use the dumpLayout output to prevent // ItaniumVTableBuilder regressions. + Out << "Original map\n"; + + for (const auto &P : VTables.getOriginalMethodMap()) { + std::string Str0 = + PredefinedExpr::ComputeName(PredefinedIdentKind::PrettyFunctionNoVirtual, + P.first); + std::string Str1 = + PredefinedExpr::ComputeName(PredefinedIdentKind::PrettyFunctionNoVirtual, + P.second); + Out << " " << Str0 << " -> " << Str1 << "\n"; + } + if (isBuildingConstructorVTable()) { Out << "Construction vtable for ('"; MostDerivedClass->printQualifiedName(Out); @@ -1978,6 +2040,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { } Out << ']'; + printThunkMethod(Thunk, Out); } // If this function pointer has a 'this' pointer adjustment, dump it. @@ -1991,6 +2054,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { } Out << ']'; + printThunkMethod(Thunk, Out); } } @@ -2027,6 +2091,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { Out << ']'; } + printThunkMethod(Thunk, Out); } break; @@ -2125,7 +2190,6 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { ThunkInfoVectorTy ThunksVector = Thunks[MD]; llvm::sort(ThunksVector, [](const ThunkInfo &LHS, const ThunkInfo &RHS) { - assert(LHS.Method == nullptr && RHS.Method == nullptr); return std::tie(LHS.This, LHS.Return) < std::tie(RHS.This, RHS.Return); }); @@ -2314,6 +2378,35 @@ ItaniumVTableContext::getVirtualBaseOffsetOffset(const CXXRecordDecl *RD, return I->second; } +GlobalDecl ItaniumVTableContext::findOriginalMethod(GlobalDecl GD) { + const auto *MD = cast(GD.getDecl()); + computeVTableRelatedInformation(MD->getParent()); + const CXXMethodDecl *OriginalMD = findOriginalMethodInMap(MD); + + if (const auto *DD = dyn_cast(OriginalMD)) + return GlobalDecl(DD, GD.getDtorType()); + return OriginalMD; +} + +const CXXMethodDecl * +ItaniumVTableContext::findOriginalMethodInMap(const CXXMethodDecl *MD) const { + // Traverse the chain of virtual methods until we find the method that added + // the v-table slot. + while (true) { + auto I = OriginalMethodMap.find(MD); + + // MD doesn't exist in OriginalMethodMap, so it must be the method we are + // looking for. + if (I == OriginalMethodMap.end()) + break; + + // Set MD to the overridden method. + MD = I->second; + } + + return MD; +} + static std::unique_ptr CreateVTableLayout(const ItaniumVTableBuilder &Builder) { SmallVector @@ -3094,9 +3187,9 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth, ReturnAdjustmentOffset.VirtualBase); } } - + auto ThisType = (OverriddenMD ? OverriddenMD : MD)->getThisType().getTypePtr(); AddMethod(FinalOverriderMD, - ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment, + ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment, ThisType, ForceReturnAdjustmentMangling ? MD : nullptr)); } } diff --git a/clang/lib/CodeGen/CGCXX.cpp b/clang/lib/CodeGen/CGCXX.cpp index e95a735f92f74..23ebbee19bf79 100644 --- a/clang/lib/CodeGen/CGCXX.cpp +++ b/clang/lib/CodeGen/CGCXX.cpp @@ -263,7 +263,16 @@ static CGCallee BuildAppleKextVirtualCall(CodeGenFunction &CGF, CGF.Builder.CreateConstInBoundsGEP1_64(Ty, VTable, VTableIndex, "vfnkxt"); llvm::Value *VFunc = CGF.Builder.CreateAlignedLoad( Ty, VFuncPtr, llvm::Align(CGF.PointerAlignInBytes)); - CGCallee Callee(GD, VFunc); + + CGPointerAuthInfo PointerAuth; + if (auto &Schema = + CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers) { + GlobalDecl OrigMD = + CGM.getItaniumVTableContext().findOriginalMethod(GD.getCanonicalDecl()); + PointerAuth = CGF.EmitPointerAuthInfo(Schema, VFuncPtr, OrigMD, QualType()); + } + + CGCallee Callee(GD, VFunc, PointerAuth); return Callee; } diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h index 104a20db8efaf..7dcc539111996 100644 --- a/clang/lib/CodeGen/CGCXXABI.h +++ b/clang/lib/CodeGen/CGCXXABI.h @@ -504,13 +504,15 @@ class CGCXXABI { virtual void setThunkLinkage(llvm::Function *Thunk, bool ForVTable, GlobalDecl GD, bool ReturnAdjustment) = 0; - virtual llvm::Value *performThisAdjustment(CodeGenFunction &CGF, - Address This, - const ThisAdjustment &TA) = 0; + virtual llvm::Value * + performThisAdjustment(CodeGenFunction &CGF, Address This, + const CXXRecordDecl *UnadjustedClass, + const ThunkInfo &TI) = 0; - virtual llvm::Value *performReturnAdjustment(CodeGenFunction &CGF, - Address Ret, - const ReturnAdjustment &RA) = 0; + virtual llvm::Value * + performReturnAdjustment(CodeGenFunction &CGF, Address Ret, + const CXXRecordDecl *UnadjustedClass, + const ReturnAdjustment &RA) = 0; virtual void EmitReturnFromThunk(CodeGenFunction &CGF, RValue RV, QualType ResultType); diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 5a032bdbf9379..0a595bb998d26 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -2588,6 +2588,11 @@ void CodeGenFunction::InitializeVTablePointer(const VPtr &Vptr) { // the same addr space. Note that this might not be LLVM address space 0. VTableField = VTableField.withElementType(PtrTy); + if (auto AuthenticationInfo = CGM.getVTablePointerAuthInfo( + this, Vptr.Base.getBase(), VTableField.emitRawPointer(*this))) + VTableAddressPoint = + EmitPointerAuthSign(*AuthenticationInfo, VTableAddressPoint); + llvm::StoreInst *Store = Builder.CreateStore(VTableAddressPoint, VTableField); TBAAAccessInfo TBAAInfo = CGM.getTBAAVTablePtrAccessInfo(PtrTy); CGM.DecorateInstructionWithTBAA(Store, TBAAInfo); @@ -2681,12 +2686,35 @@ void CodeGenFunction::InitializeVTablePointers(const CXXRecordDecl *RD) { llvm::Value *CodeGenFunction::GetVTablePtr(Address This, llvm::Type *VTableTy, - const CXXRecordDecl *RD) { + const CXXRecordDecl *RD, + VTableAuthMode AuthMode) { Address VTablePtrSrc = This.withElementType(VTableTy); llvm::Instruction *VTable = Builder.CreateLoad(VTablePtrSrc, "vtable"); TBAAAccessInfo TBAAInfo = CGM.getTBAAVTablePtrAccessInfo(VTableTy); CGM.DecorateInstructionWithTBAA(VTable, TBAAInfo); + if (auto AuthenticationInfo = + CGM.getVTablePointerAuthInfo(this, RD, This.emitRawPointer(*this))) { + if (AuthMode != VTableAuthMode::UnsafeUbsanStrip) { + VTable = cast( + EmitPointerAuthAuth(*AuthenticationInfo, VTable)); + if (AuthMode == VTableAuthMode::MustTrap) { + // This is clearly suboptimal but until we have an ability + // to rely on the authentication intrinsic trapping and force + // an authentication to occur we don't really have a choice. + VTable = + cast(Builder.CreateBitCast(VTable, Int8PtrTy)); + Builder.CreateLoad(RawAddress(VTable, Int8Ty, CGM.getPointerAlign()), + /* IsVolatile */ true); + } + } else { + VTable = cast(EmitPointerAuthAuth( + CGPointerAuthInfo(0, PointerAuthenticationMode::Strip, false, false, + nullptr), + VTable)); + } + } + if (CGM.getCodeGenOpts().OptimizationLevel > 0 && CGM.getCodeGenOpts().StrictVTablePointers) CGM.DecorateInstructionWithInvariantGroup(VTable, RD); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 534f46da74862..23e5deee32581 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -830,8 +830,14 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, // Load the vptr, and mix it with TypeHash. llvm::Value *TypeHash = llvm::ConstantInt::get(Int64Ty, xxh3_64bits(Out.str())); + + llvm::Type *VPtrTy = llvm::PointerType::get(IntPtrTy, 0); Address VPtrAddr(Ptr, IntPtrTy, getPointerAlign()); - llvm::Value *VPtrVal = Builder.CreateLoad(VPtrAddr); + llvm::Value *VPtrVal = GetVTablePtr(VPtrAddr, VPtrTy, + Ty->getAsCXXRecordDecl(), + VTableAuthMode::UnsafeUbsanStrip); + VPtrVal = Builder.CreateBitOrPointerCast(VPtrVal, IntPtrTy); + llvm::Value *Hash = emitHashMix(Builder, TypeHash, Builder.CreateZExt(VPtrVal, Int64Ty)); Hash = Builder.CreateTrunc(Hash, IntPtrTy); diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 0f3e29707cb79..1fec587b5c4c7 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -803,6 +803,13 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD, llvm::Constant *VTableAddressPoint = CGM.getCXXABI().getVTableAddressPoint(BaseSubobject(CD, Offset), VTableClass); + if (auto Authentication = + CGM.getVTablePointerAuthentication(VTableClass)) { + VTableAddressPoint = Emitter.tryEmitConstantSignedPointer( + VTableAddressPoint, *Authentication); + if (!VTableAddressPoint) + return false; + } if (!AppendBytes(Offset, VTableAddressPoint)) return false; } @@ -1647,7 +1654,7 @@ namespace { // messing around with llvm::Constant structures, which never itself // does anything that should be visible in compiler output. for (auto &entry : Locations) { - assert(entry.first->getParent() == nullptr && "not a placeholder!"); + assert(entry.first->getName() == "" && "not a placeholder!"); entry.first->replaceAllUsesWith(entry.second); entry.first->eraseFromParent(); } @@ -1811,6 +1818,43 @@ llvm::Constant *ConstantEmitter::tryEmitPrivateForMemory(const APValue &value, return (C ? emitForMemory(C, destType) : nullptr); } +/// Try to emit a constant signed pointer, given a raw pointer and the +/// destination ptrauth qualifier. +/// +/// This can fail if the qualifier needs address discrimination and the +/// emitter is in an abstract mode. +llvm::Constant * +ConstantEmitter::tryEmitConstantSignedPointer(llvm::Constant *UnsignedPointer, + PointerAuthQualifier Schema) { + assert(Schema && "applying trivial ptrauth schema"); + + if (Schema.hasKeyNone()) + return UnsignedPointer; + + unsigned Key = Schema.getKey(); + + // Create an address placeholder if we're using address discrimination. + llvm::GlobalValue *StorageAddress = nullptr; + if (Schema.isAddressDiscriminated()) { + // We can't do this if the emitter is in an abstract state. + if (isAbstract()) + return nullptr; + + StorageAddress = getCurrentAddrPrivate(); + } + + llvm::ConstantInt *Discriminator = + llvm::ConstantInt::get(CGM.IntPtrTy, Schema.getExtraDiscriminator()); + + llvm::Constant *SignedPointer = CGM.getConstantSignedPointer( + UnsignedPointer, Key, StorageAddress, Discriminator); + + if (Schema.isAddressDiscriminated()) + registerCurrentAddrPrivate(SignedPointer, StorageAddress); + + return SignedPointer; +} + llvm::Constant *ConstantEmitter::emitForMemory(CodeGenModule &CGM, llvm::Constant *C, QualType destType) { diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index f0819b0467489..673f6e60bfc31 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -11,12 +11,56 @@ // //===----------------------------------------------------------------------===// +#include "CodeGenFunction.h" #include "CodeGenModule.h" #include "clang/CodeGen/CodeGenABITypes.h" +#include "clang/CodeGen/ConstantInitBuilder.h" +#include "llvm/Support/SipHash.h" using namespace clang; using namespace CodeGen; +/// Given a pointer-authentication schema, return a concrete "other" +/// discriminator for it. +llvm::ConstantInt *CodeGenModule::getPointerAuthOtherDiscriminator( + const PointerAuthSchema &Schema, GlobalDecl Decl, QualType Type) { + switch (Schema.getOtherDiscrimination()) { + case PointerAuthSchema::Discrimination::None: + return nullptr; + + case PointerAuthSchema::Discrimination::Type: + llvm_unreachable("type discrimination not implemented yet"); + + case PointerAuthSchema::Discrimination::Decl: + assert(Decl.getDecl() && + "declaration not provided for decl-discriminated schema"); + return llvm::ConstantInt::get(IntPtrTy, + getPointerAuthDeclDiscriminator(Decl)); + + case PointerAuthSchema::Discrimination::Constant: + return llvm::ConstantInt::get(IntPtrTy, Schema.getConstantDiscrimination()); + } + llvm_unreachable("bad discrimination kind"); +} + +uint16_t CodeGen::getPointerAuthDeclDiscriminator(CodeGenModule &CGM, + GlobalDecl Declaration) { + return CGM.getPointerAuthDeclDiscriminator(Declaration); +} + +/// Return the "other" decl-specific discriminator for the given decl. +uint16_t +CodeGenModule::getPointerAuthDeclDiscriminator(GlobalDecl Declaration) { + uint16_t &EntityHash = PtrAuthDiscriminatorHashes[Declaration]; + + if (EntityHash == 0) { + StringRef Name = getMangledName(Declaration); + EntityHash = llvm::getPointerAuthStableSipHash(Name); + } + + return EntityHash; +} + /// Return the abstract pointer authentication schema for a pointer to the given /// function type. CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) { @@ -35,6 +79,41 @@ CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) { /*Discriminator=*/nullptr); } +llvm::Value * +CodeGenFunction::EmitPointerAuthBlendDiscriminator(llvm::Value *StorageAddress, + llvm::Value *Discriminator) { + StorageAddress = Builder.CreatePtrToInt(StorageAddress, IntPtrTy); + auto Intrinsic = CGM.getIntrinsic(llvm::Intrinsic::ptrauth_blend); + return Builder.CreateCall(Intrinsic, {StorageAddress, Discriminator}); +} + +/// Emit the concrete pointer authentication informaton for the +/// given authentication schema. +CGPointerAuthInfo CodeGenFunction::EmitPointerAuthInfo( + const PointerAuthSchema &Schema, llvm::Value *StorageAddress, + GlobalDecl SchemaDecl, QualType SchemaType) { + if (!Schema) + return CGPointerAuthInfo(); + + llvm::Value *Discriminator = + CGM.getPointerAuthOtherDiscriminator(Schema, SchemaDecl, SchemaType); + + if (Schema.isAddressDiscriminated()) { + assert(StorageAddress && + "address not provided for address-discriminated schema"); + + if (Discriminator) + Discriminator = + EmitPointerAuthBlendDiscriminator(StorageAddress, Discriminator); + else + Discriminator = Builder.CreatePtrToInt(StorageAddress, IntPtrTy); + } + + return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(), + Schema.isIsaPointer(), + Schema.authenticatesNullValues(), Discriminator); +} + llvm::Constant * CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key, llvm::Constant *StorageAddress, @@ -60,6 +139,29 @@ CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key, IntegerDiscriminator, AddressDiscriminator); } +/// Does a given PointerAuthScheme require us to sign a value +bool CodeGenModule::shouldSignPointer(const PointerAuthSchema &Schema) { + auto AuthenticationMode = Schema.getAuthenticationMode(); + return AuthenticationMode == PointerAuthenticationMode::SignAndStrip || + AuthenticationMode == PointerAuthenticationMode::SignAndAuth; +} + +/// Sign a constant pointer using the given scheme, producing a constant +/// with the same IR type. +llvm::Constant *CodeGenModule::getConstantSignedPointer( + llvm::Constant *Pointer, const PointerAuthSchema &Schema, + llvm::Constant *StorageAddress, GlobalDecl SchemaDecl, + QualType SchemaType) { + assert(shouldSignPointer(Schema)); + llvm::ConstantInt *OtherDiscriminator = + getPointerAuthOtherDiscriminator(Schema, SchemaDecl, SchemaType); + + return getConstantSignedPointer(Pointer, Schema.getKey(), StorageAddress, + OtherDiscriminator); +} + +/// If applicable, sign a given constant function pointer with the ABI rules for +/// functionType. llvm::Constant *CodeGenModule::getFunctionPointer(llvm::Constant *Pointer, QualType FunctionType) { assert(FunctionType->isFunctionType() || @@ -80,3 +182,113 @@ llvm::Constant *CodeGenModule::getFunctionPointer(GlobalDecl GD, QualType FuncType = FD->getType(); return getFunctionPointer(getRawFunctionPointer(GD, Ty), FuncType); } + +std::optional +CodeGenModule::computeVTPointerAuthentication(const CXXRecordDecl *ThisClass) { + auto DefaultAuthentication = getCodeGenOpts().PointerAuth.CXXVTablePointers; + if (!DefaultAuthentication) + return std::nullopt; + const CXXRecordDecl *PrimaryBase = + Context.baseForVTableAuthentication(ThisClass); + + unsigned Key = DefaultAuthentication.getKey(); + bool AddressDiscriminated = DefaultAuthentication.isAddressDiscriminated(); + auto DefaultDiscrimination = DefaultAuthentication.getOtherDiscrimination(); + unsigned TypeBasedDiscriminator = + Context.getPointerAuthVTablePointerDiscriminator(PrimaryBase); + unsigned Discriminator; + if (DefaultDiscrimination == PointerAuthSchema::Discrimination::Type) { + Discriminator = TypeBasedDiscriminator; + } else if (DefaultDiscrimination == + PointerAuthSchema::Discrimination::Constant) { + Discriminator = DefaultAuthentication.getConstantDiscrimination(); + } else { + assert(DefaultDiscrimination == PointerAuthSchema::Discrimination::None); + Discriminator = 0; + } + if (auto ExplicitAuthentication = + PrimaryBase->getAttr()) { + auto ExplicitAddressDiscrimination = + ExplicitAuthentication->getAddressDiscrimination(); + auto ExplicitDiscriminator = + ExplicitAuthentication->getExtraDiscrimination(); + + unsigned ExplicitKey = ExplicitAuthentication->getKey(); + if (ExplicitKey == VTablePointerAuthenticationAttr::NoKey) + return std::nullopt; + + if (ExplicitKey != VTablePointerAuthenticationAttr::DefaultKey) { + if (ExplicitKey == VTablePointerAuthenticationAttr::ProcessIndependent) + Key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDA; + else { + assert(ExplicitKey == + VTablePointerAuthenticationAttr::ProcessDependent); + Key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDB; + } + } + + if (ExplicitAddressDiscrimination != + VTablePointerAuthenticationAttr::DefaultAddressDiscrimination) + AddressDiscriminated = + ExplicitAddressDiscrimination == + VTablePointerAuthenticationAttr::AddressDiscrimination; + + if (ExplicitDiscriminator == + VTablePointerAuthenticationAttr::TypeDiscrimination) + Discriminator = TypeBasedDiscriminator; + else if (ExplicitDiscriminator == + VTablePointerAuthenticationAttr::CustomDiscrimination) + Discriminator = ExplicitAuthentication->getCustomDiscriminationValue(); + else if (ExplicitDiscriminator == + VTablePointerAuthenticationAttr::NoExtraDiscrimination) + Discriminator = 0; + } + return PointerAuthQualifier::Create(Key, AddressDiscriminated, Discriminator, + PointerAuthenticationMode::SignAndAuth, + /* IsIsaPointer */ false, + /* AuthenticatesNullValues */ false); +} + +std::optional +CodeGenModule::getVTablePointerAuthentication(const CXXRecordDecl *Record) { + if (!Record->getDefinition() || !Record->isPolymorphic()) + return std::nullopt; + + auto Existing = VTablePtrAuthInfos.find(Record); + std::optional Authentication; + if (Existing != VTablePtrAuthInfos.end()) { + Authentication = Existing->getSecond(); + } else { + Authentication = computeVTPointerAuthentication(Record); + VTablePtrAuthInfos.insert(std::make_pair(Record, Authentication)); + } + return Authentication; +} + +std::optional +CodeGenModule::getVTablePointerAuthInfo(CodeGenFunction *CGF, + const CXXRecordDecl *Record, + llvm::Value *StorageAddress) { + auto Authentication = getVTablePointerAuthentication(Record); + if (!Authentication) + return std::nullopt; + + llvm::Value *Discriminator = nullptr; + if (auto ExtraDiscriminator = Authentication->getExtraDiscriminator()) + Discriminator = llvm::ConstantInt::get(IntPtrTy, ExtraDiscriminator); + + if (Authentication->isAddressDiscriminated()) { + assert(StorageAddress && + "address not provided for address-discriminated schema"); + if (Discriminator) + Discriminator = + CGF->EmitPointerAuthBlendDiscriminator(StorageAddress, Discriminator); + else + Discriminator = CGF->Builder.CreatePtrToInt(StorageAddress, IntPtrTy); + } + + return CGPointerAuthInfo(Authentication->getKey(), + PointerAuthenticationMode::SignAndAuth, + /* IsIsaPointer */ false, + /* AuthenticatesNullValues */ false, Discriminator); +} diff --git a/clang/lib/CodeGen/CGVTT.cpp b/clang/lib/CodeGen/CGVTT.cpp index 4cebb750c89e8..20bd2c2fc2c64 100644 --- a/clang/lib/CodeGen/CGVTT.cpp +++ b/clang/lib/CodeGen/CGVTT.cpp @@ -90,6 +90,11 @@ CodeGenVTables::EmitVTTDefinition(llvm::GlobalVariable *VTT, llvm::Constant *Init = llvm::ConstantExpr::getGetElementPtr( VTable->getValueType(), VTable, Idxs, /*InBounds=*/true, InRange); + if (const auto &Schema = + CGM.getCodeGenOpts().PointerAuth.CXXVTTVTablePointers) + Init = CGM.getConstantSignedPointer(Init, Schema, nullptr, GlobalDecl(), + QualType()); + VTTComponents.push_back(Init); } diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp index 55c3032dc9332..3e88cd73e8c1f 100644 --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -95,7 +95,7 @@ static RValue PerformReturnAdjustment(CodeGenFunction &CGF, CGF, Address(ReturnValue, CGF.ConvertTypeForMem(ResultType->getPointeeType()), ClassAlign), - Thunk.Return); + ClassDecl, Thunk.Return); if (NullCheckValue) { CGF.Builder.CreateBr(AdjustEnd); @@ -219,8 +219,10 @@ CodeGenFunction::GenerateVarArgsThunk(llvm::Function *Fn, "Store of this should be in entry block?"); // Adjust "this", if necessary. Builder.SetInsertPoint(&*ThisStore); - llvm::Value *AdjustedThisPtr = - CGM.getCXXABI().performThisAdjustment(*this, ThisPtr, Thunk.This); + + const CXXRecordDecl *ThisValueClass = Thunk.ThisType->getPointeeCXXRecordDecl(); + llvm::Value *AdjustedThisPtr = CGM.getCXXABI().performThisAdjustment( + *this, ThisPtr, ThisValueClass, Thunk); AdjustedThisPtr = Builder.CreateBitCast(AdjustedThisPtr, ThisStore->getOperand(0)->getType()); ThisStore->setOperand(0, AdjustedThisPtr); @@ -307,10 +309,15 @@ void CodeGenFunction::EmitCallAndReturnForThunk(llvm::FunctionCallee Callee, const CXXMethodDecl *MD = cast(CurGD.getDecl()); // Adjust the 'this' pointer if necessary + const CXXRecordDecl *ThisValueClass = + MD->getThisType()->getPointeeCXXRecordDecl(); + if (Thunk) + ThisValueClass = Thunk->ThisType->getPointeeCXXRecordDecl(); + llvm::Value *AdjustedThisPtr = - Thunk ? CGM.getCXXABI().performThisAdjustment( - *this, LoadCXXThisAddress(), Thunk->This) - : LoadCXXThis(); + Thunk ? CGM.getCXXABI().performThisAdjustment(*this, LoadCXXThisAddress(), + ThisValueClass, *Thunk) + : LoadCXXThis(); // If perfect forwarding is required a variadic method, a method using // inalloca, or an unprototyped thunk, use musttail. Emit an error if this @@ -504,10 +511,22 @@ llvm::Constant *CodeGenVTables::maybeEmitThunk(GlobalDecl GD, SmallString<256> Name; MangleContext &MCtx = CGM.getCXXABI().getMangleContext(); llvm::raw_svector_ostream Out(Name); - if (const CXXDestructorDecl *DD = dyn_cast(MD)) - MCtx.mangleCXXDtorThunk(DD, GD.getDtorType(), TI.This, Out); - else - MCtx.mangleThunk(MD, TI, Out); + + if (const CXXDestructorDecl *DD = dyn_cast(MD)) { + MCtx.mangleCXXDtorThunk(DD, GD.getDtorType(), TI, + /* elideOverrideInfo */ false, Out); + } else + MCtx.mangleThunk(MD, TI, /* elideOverrideInfo */ false, Out); + + if (CGM.getContext().useAbbreviatedThunkName(GD, Name.str())) { + Name = ""; + if (const CXXDestructorDecl *DD = dyn_cast(MD)) + MCtx.mangleCXXDtorThunk(DD, GD.getDtorType(), TI, + /* elideOverrideInfo */ true, Out); + else + MCtx.mangleThunk(MD, TI, /* elideOverrideInfo */ true, Out); + } + llvm::Type *ThunkVTableTy = CGM.getTypes().GetFunctionTypeForVTable(GD); llvm::Constant *Thunk = CGM.GetAddrOfThunk(Name, ThunkVTableTy, GD); @@ -819,11 +838,17 @@ void CodeGenVTables::addVTableComponent(ConstantArrayBuilder &builder, nextVTableThunkIndex++; fnPtr = maybeEmitThunk(GD, thunkInfo, /*ForVTable=*/true); + if (CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers) { + assert(thunkInfo.Method && "Method not set"); + GD = GD.getWithDecl(thunkInfo.Method); + } // Otherwise we can use the method definition directly. } else { llvm::Type *fnTy = CGM.getTypes().GetFunctionTypeForVTable(GD); fnPtr = CGM.GetAddrOfFunction(GD, fnTy, /*ForVTable=*/true); + if (CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers) + GD = getItaniumVTableContext().findOriginalMethod(GD); } if (useRelativeLayout()) { @@ -841,6 +866,9 @@ void CodeGenVTables::addVTableComponent(ConstantArrayBuilder &builder, if (FnAS != GVAS) fnPtr = llvm::ConstantExpr::getAddrSpaceCast(fnPtr, CGM.GlobalsInt8PtrTy); + if (const auto &Schema = + CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers) + return builder.addSignedPointer(fnPtr, Schema, GD, QualType()); return builder.add(fnPtr); } } diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 650c5662539f9..26deeca95d326 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -3063,3 +3063,66 @@ void CodeGenFunction::EmitPointerAuthOperandBundle( llvm::Value *Args[] = {Key, Discriminator}; Bundles.emplace_back("ptrauth", Args); } + +static llvm::Value *EmitPointerAuthCommon(CodeGenFunction &CGF, + const CGPointerAuthInfo &PointerAuth, + llvm::Value *Pointer, + unsigned IntrinsicID) { + if (!PointerAuth) + return Pointer; + + auto Key = CGF.Builder.getInt32(PointerAuth.getKey()); + + llvm::Value *Discriminator = PointerAuth.getDiscriminator(); + if (!Discriminator) { + Discriminator = CGF.Builder.getSize(0); + } + + // Convert the pointer to intptr_t before signing it. + auto OrigType = Pointer->getType(); + Pointer = CGF.Builder.CreatePtrToInt(Pointer, CGF.IntPtrTy); + + // call i64 @llvm.ptrauth.sign.i64(i64 %pointer, i32 %key, i64 %discriminator) + auto Intrinsic = CGF.CGM.getIntrinsic(IntrinsicID); + Pointer = CGF.EmitRuntimeCall(Intrinsic, {Pointer, Key, Discriminator}); + + // Convert back to the original type. + Pointer = CGF.Builder.CreateIntToPtr(Pointer, OrigType); + return Pointer; +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthSign(const CGPointerAuthInfo &PointerAuth, + llvm::Value *Pointer) { + if (!PointerAuth.shouldSign()) + return Pointer; + return EmitPointerAuthCommon(*this, PointerAuth, Pointer, + llvm::Intrinsic::ptrauth_sign); +} + +static llvm::Value *EmitStrip(CodeGenFunction &CGF, + const CGPointerAuthInfo &PointerAuth, + llvm::Value *Pointer) { + auto StripIntrinsic = CGF.CGM.getIntrinsic(llvm::Intrinsic::ptrauth_strip); + + auto Key = CGF.Builder.getInt32(PointerAuth.getKey()); + // Convert the pointer to intptr_t before signing it. + auto OrigType = Pointer->getType(); + Pointer = CGF.EmitRuntimeCall( + StripIntrinsic, {CGF.Builder.CreatePtrToInt(Pointer, CGF.IntPtrTy), Key}); + return CGF.Builder.CreateIntToPtr(Pointer, OrigType); +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthAuth(const CGPointerAuthInfo &PointerAuth, + llvm::Value *Pointer) { + if (PointerAuth.shouldStrip()) { + return EmitStrip(*this, PointerAuth, Pointer); + } + if (!PointerAuth.shouldAuth()) { + return Pointer; + } + + return EmitPointerAuthCommon(*this, PointerAuth, Pointer, + llvm::Intrinsic::ptrauth_auth); +} diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index a9c497bde6871..13f12b5d878a6 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2453,10 +2453,20 @@ class CodeGenFunction : public CodeGenTypeCache { void InitializeVTablePointers(const CXXRecordDecl *ClassDecl); + // VTableTrapMode - whether we guarantee that loading the + // vtable is guaranteed to trap on authentication failure, + // even if the resulting vtable pointer is unused. + enum class VTableAuthMode { + Authenticate, + MustTrap, + UnsafeUbsanStrip // Should only be used for Vptr UBSan check + }; /// GetVTablePtr - Return the Value of the vtable pointer member pointed /// to by This. - llvm::Value *GetVTablePtr(Address This, llvm::Type *VTableTy, - const CXXRecordDecl *VTableClass); + llvm::Value * + GetVTablePtr(Address This, llvm::Type *VTableTy, + const CXXRecordDecl *VTableClass, + VTableAuthMode AuthMode = VTableAuthMode::Authenticate); enum CFITypeCheckKind { CFITCK_VCall, @@ -4417,6 +4427,19 @@ class CodeGenFunction : public CodeGenTypeCache { bool isPointerKnownNonNull(const Expr *E); + /// Create the discriminator from the storage address and the entity hash. + llvm::Value *EmitPointerAuthBlendDiscriminator(llvm::Value *StorageAddress, + llvm::Value *Discriminator); + CGPointerAuthInfo EmitPointerAuthInfo(const PointerAuthSchema &Schema, + llvm::Value *StorageAddress, + GlobalDecl SchemaDecl, + QualType SchemaType); + llvm::Value *EmitPointerAuthSign(QualType PointeeType, llvm::Value *Pointer); + llvm::Value *EmitPointerAuthSign(const CGPointerAuthInfo &Info, + llvm::Value *Pointer); + llvm::Value *EmitPointerAuthAuth(const CGPointerAuthInfo &Info, + llvm::Value *Pointer); + void EmitPointerAuthOperandBundle( const CGPointerAuthInfo &Info, SmallVectorImpl &Bundles); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 9913304757caa..22b2b314c316c 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -609,6 +609,13 @@ class CodeGenModule : public CodeGenTypeCache { std::pair, const TopLevelStmtDecl *> GlobalTopLevelStmtBlockInFlight; + llvm::DenseMap PtrAuthDiscriminatorHashes; + + llvm::DenseMap> + VTablePtrAuthInfos; + std::optional + computeVTPointerAuthentication(const CXXRecordDecl *ThisClass); + public: CodeGenModule(ASTContext &C, IntrusiveRefCntPtr FS, const HeaderSearchOptions &headersearchopts, @@ -957,11 +964,33 @@ class CodeGenModule : public CodeGenTypeCache { CGPointerAuthInfo getFunctionPointerAuthInfo(QualType T); + bool shouldSignPointer(const PointerAuthSchema &Schema); + llvm::Constant *getConstantSignedPointer(llvm::Constant *Pointer, + const PointerAuthSchema &Schema, + llvm::Constant *StorageAddress, + GlobalDecl SchemaDecl, + QualType SchemaType); + llvm::Constant * getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key, llvm::Constant *StorageAddress, llvm::ConstantInt *OtherDiscriminator); + llvm::ConstantInt * + getPointerAuthOtherDiscriminator(const PointerAuthSchema &Schema, + GlobalDecl SchemaDecl, QualType SchemaType); + + uint16_t getPointerAuthDeclDiscriminator(GlobalDecl GD); + std::optional + getVTablePointerAuthInfo(CodeGenFunction *Context, + const CXXRecordDecl *Record, + llvm::Value *StorageAddress); + + std::optional + getVTablePointerAuthentication(const CXXRecordDecl *thisClass); + + CGPointerAuthInfo EmitPointerAuthInfo(const RecordDecl *RD); + // Return whether RTTI information should be emitted for this target. bool shouldEmitRTTI(bool ForEH = false) { return (ForEH || getLangOpts().RTTI) && !getLangOpts().CUDAIsDevice && diff --git a/clang/lib/CodeGen/ConstantEmitter.h b/clang/lib/CodeGen/ConstantEmitter.h index a55da0dcad792..eff0a8d52891f 100644 --- a/clang/lib/CodeGen/ConstantEmitter.h +++ b/clang/lib/CodeGen/ConstantEmitter.h @@ -113,6 +113,9 @@ class ConstantEmitter { llvm::Constant *tryEmitAbstract(const APValue &value, QualType T); llvm::Constant *tryEmitAbstractForMemory(const APValue &value, QualType T); + llvm::Constant *tryEmitConstantSignedPointer(llvm::Constant *Ptr, + PointerAuthQualifier Auth); + llvm::Constant *tryEmitConstantExpr(const ConstantExpr *CE); llvm::Constant *emitNullForMemory(QualType T) { diff --git a/clang/lib/CodeGen/ConstantInitBuilder.cpp b/clang/lib/CodeGen/ConstantInitBuilder.cpp index 3cf69f3b6415e..549d5dd66b123 100644 --- a/clang/lib/CodeGen/ConstantInitBuilder.cpp +++ b/clang/lib/CodeGen/ConstantInitBuilder.cpp @@ -296,3 +296,21 @@ ConstantAggregateBuilderBase::finishStruct(llvm::StructType *ty) { buffer.erase(buffer.begin() + Begin, buffer.end()); return constant; } + +/// Sign the given pointer and add it to the constant initializer +/// currently being built. +void ConstantAggregateBuilderBase::addSignedPointer( + llvm::Constant *Pointer, const PointerAuthSchema &Schema, + GlobalDecl CalleeDecl, QualType CalleeType) { + if (!Schema || !Builder.CGM.shouldSignPointer(Schema)) + return add(Pointer); + + llvm::Constant *StorageAddress = nullptr; + if (Schema.isAddressDiscriminated()) { + StorageAddress = getAddrOfCurrentPosition(Pointer->getType()); + } + + llvm::Constant *SignedPointer = Builder.CGM.getConstantSignedPointer( + Pointer, Schema, StorageAddress, CalleeDecl, CalleeType); + add(SignedPointer); +} diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 01a735c1437e1..63e36e1b83893 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -23,6 +23,7 @@ #include "CGVTables.h" #include "CodeGenFunction.h" #include "CodeGenModule.h" +#include "ConstantEmitter.h" #include "TargetInfo.h" #include "clang/AST/Attr.h" #include "clang/AST/Mangle.h" @@ -336,9 +337,11 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { bool exportThunk() override { return true; } llvm::Value *performThisAdjustment(CodeGenFunction &CGF, Address This, - const ThisAdjustment &TA) override; + const CXXRecordDecl *UnadjustedThisClass, + const ThunkInfo &TI) override; llvm::Value *performReturnAdjustment(CodeGenFunction &CGF, Address Ret, + const CXXRecordDecl *UnadjustedRetClass, const ReturnAdjustment &RA) override; size_t getSrcArgforCopyCtor(const CXXConstructorDecl *, @@ -1477,10 +1480,22 @@ llvm::Value *ItaniumCXXABI::emitDynamicCastCall( computeOffsetHint(CGF.getContext(), SrcDecl, DestDecl).getQuantity()); // Emit the call to __dynamic_cast. - llvm::Value *Args[] = {ThisAddr.emitRawPointer(CGF), SrcRTTI, DestRTTI, - OffsetHint}; - llvm::Value *Value = - CGF.EmitNounwindRuntimeCall(getItaniumDynamicCastFn(CGF), Args); + llvm::Value *Value = ThisAddr.emitRawPointer(CGF); + if (CGM.getCodeGenOpts().PointerAuth.CXXVTablePointers) { + // We perform a no-op load of the vtable pointer here to force an + // authentication. In environments that do not support pointer + // authentication this is a an actual no-op that will be elided. When + // pointer authentication is supported and enforced on vtable pointers this + // load can trap. + llvm::Value *Vtable = + CGF.GetVTablePtr(ThisAddr, CGM.Int8PtrTy, SrcDecl, + CodeGenFunction::VTableAuthMode::MustTrap); + assert(Vtable); + (void)Vtable; + } + + llvm::Value *args[] = {Value, SrcRTTI, DestRTTI, OffsetHint}; + Value = CGF.EmitNounwindRuntimeCall(getItaniumDynamicCastFn(CGF), args); /// C++ [expr.dynamic.cast]p9: /// A failed cast to reference type throws std::bad_cast @@ -1955,8 +1970,18 @@ llvm::Value *ItaniumCXXABI::getVTableAddressPointInStructorWithVTT( VirtualPointerIndex); // And load the address point from the VTT. - return CGF.Builder.CreateAlignedLoad(CGF.GlobalsVoidPtrTy, VTT, - CGF.getPointerAlign()); + llvm::Value *AP = + CGF.Builder.CreateAlignedLoad(CGF.GlobalsVoidPtrTy, VTT, + CGF.getPointerAlign()); + + if (auto &Schema = CGF.CGM.getCodeGenOpts().PointerAuth.CXXVTTVTablePointers) { + CGPointerAuthInfo PointerAuth = CGF.EmitPointerAuthInfo(Schema, VTT, + GlobalDecl(), + QualType()); + AP = CGF.EmitPointerAuthAuth(PointerAuth, AP); + } + + return AP; } llvm::GlobalVariable *ItaniumCXXABI::getAddrOfVTable(const CXXRecordDecl *RD, @@ -2008,8 +2033,9 @@ CGCallee ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, llvm::Value *VTable = CGF.GetVTablePtr(This, PtrTy, MethodDecl->getParent()); uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD); - llvm::Value *VFunc; - if (CGF.ShouldEmitVTableTypeCheckedLoad(MethodDecl->getParent())) { + llvm::Value *VFunc, *VTableSlotPtr = nullptr; + auto &Schema = CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers; + if (!Schema && CGF.ShouldEmitVTableTypeCheckedLoad(MethodDecl->getParent())) { VFunc = CGF.EmitVTableTypeCheckedLoad( MethodDecl->getParent(), VTable, PtrTy, VTableIndex * @@ -2024,7 +2050,7 @@ CGCallee ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, CGM.getIntrinsic(llvm::Intrinsic::load_relative, {CGM.Int32Ty}), {VTable, llvm::ConstantInt::get(CGM.Int32Ty, 4 * VTableIndex)}); } else { - llvm::Value *VTableSlotPtr = CGF.Builder.CreateConstInBoundsGEP1_64( + VTableSlotPtr = CGF.Builder.CreateConstInBoundsGEP1_64( PtrTy, VTable, VTableIndex, "vfn"); VFuncLoad = CGF.Builder.CreateAlignedLoad(PtrTy, VTableSlotPtr, CGF.getPointerAlign()); @@ -2048,7 +2074,13 @@ CGCallee ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, VFunc = VFuncLoad; } - CGCallee Callee(GD, VFunc); + CGPointerAuthInfo PointerAuth; + if (Schema) { + assert(VTableSlotPtr && "virtual function pointer not set"); + GD = CGM.getItaniumVTableContext().findOriginalMethod(GD.getCanonicalDecl()); + PointerAuth = CGF.EmitPointerAuthInfo(Schema, VTableSlotPtr, GD, QualType()); + } + CGCallee Callee(GD, VFunc, PointerAuth); return Callee; } @@ -2147,6 +2179,7 @@ bool ItaniumCXXABI::canSpeculativelyEmitVTable(const CXXRecordDecl *RD) const { } static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF, Address InitialPtr, + const CXXRecordDecl *UnadjustedClass, int64_t NonVirtualAdjustment, int64_t VirtualAdjustment, bool IsReturnAdjustment) { @@ -2164,8 +2197,8 @@ static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF, // Perform the virtual adjustment if we have one. llvm::Value *ResultPtr; if (VirtualAdjustment) { - Address VTablePtrPtr = V.withElementType(CGF.Int8PtrTy); - llvm::Value *VTablePtr = CGF.Builder.CreateLoad(VTablePtrPtr); + llvm::Value *VTablePtr = + CGF.GetVTablePtr(V, CGF.Int8PtrTy, UnadjustedClass); llvm::Value *Offset; llvm::Value *OffsetPtr = CGF.Builder.CreateConstInBoundsGEP1_64( @@ -2200,18 +2233,20 @@ static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF, return ResultPtr; } -llvm::Value *ItaniumCXXABI::performThisAdjustment(CodeGenFunction &CGF, - Address This, - const ThisAdjustment &TA) { - return performTypeAdjustment(CGF, This, TA.NonVirtual, - TA.Virtual.Itanium.VCallOffsetOffset, +llvm::Value * +ItaniumCXXABI::performThisAdjustment(CodeGenFunction &CGF, Address This, + const CXXRecordDecl *UnadjustedClass, + const ThunkInfo &TI) { + return performTypeAdjustment(CGF, This, UnadjustedClass, TI.This.NonVirtual, + TI.This.Virtual.Itanium.VCallOffsetOffset, /*IsReturnAdjustment=*/false); } llvm::Value * ItaniumCXXABI::performReturnAdjustment(CodeGenFunction &CGF, Address Ret, + const CXXRecordDecl *UnadjustedClass, const ReturnAdjustment &RA) { - return performTypeAdjustment(CGF, Ret, RA.NonVirtual, + return performTypeAdjustment(CGF, Ret, UnadjustedClass, RA.NonVirtual, RA.Virtual.Itanium.VBaseOffsetOffset, /*IsReturnAdjustment=*/true); } @@ -3694,6 +3729,10 @@ void ItaniumRTTIBuilder::BuildVTablePointer(const Type *Ty) { VTable, Two); } + if (auto &Schema = CGM.getCodeGenOpts().PointerAuth.CXXTypeInfoVTablePointer) + VTable = CGM.getConstantSignedPointer(VTable, Schema, nullptr, GlobalDecl(), + QualType(Ty, 0)); + Fields.push_back(VTable); } diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index 9ab634fa6ce2e..cc6740edabcd3 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -415,9 +415,11 @@ class MicrosoftCXXABI : public CGCXXABI { bool exportThunk() override { return false; } llvm::Value *performThisAdjustment(CodeGenFunction &CGF, Address This, - const ThisAdjustment &TA) override; + const CXXRecordDecl * /*UnadjustedClass*/, + const ThunkInfo &TI) override; llvm::Value *performReturnAdjustment(CodeGenFunction &CGF, Address Ret, + const CXXRecordDecl * /*UnadjustedClass*/, const ReturnAdjustment &RA) override; void EmitThreadLocalInitFuncs( @@ -2223,9 +2225,10 @@ void MicrosoftCXXABI::emitVBTableDefinition(const VPtrInfo &VBT, GV->setLinkage(llvm::GlobalVariable::AvailableExternallyLinkage); } -llvm::Value *MicrosoftCXXABI::performThisAdjustment(CodeGenFunction &CGF, - Address This, - const ThisAdjustment &TA) { +llvm::Value *MicrosoftCXXABI::performThisAdjustment( + CodeGenFunction &CGF, Address This, + const CXXRecordDecl * /*UnadjustedClass*/, const ThunkInfo &TI) { + const ThisAdjustment &TA = TI.This; if (TA.isEmpty()) return This.emitRawPointer(CGF); @@ -2275,9 +2278,10 @@ llvm::Value *MicrosoftCXXABI::performThisAdjustment(CodeGenFunction &CGF, return V; } -llvm::Value * -MicrosoftCXXABI::performReturnAdjustment(CodeGenFunction &CGF, Address Ret, - const ReturnAdjustment &RA) { +llvm::Value *MicrosoftCXXABI::performReturnAdjustment( + CodeGenFunction &CGF, Address Ret, + const CXXRecordDecl * /*UnadjustedClass*/, const ReturnAdjustment &RA) { + if (RA.isEmpty()) return Ret.emitRawPointer(CGF); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index a6d9f42ace9cc..f42e28ba7e629 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1468,6 +1468,17 @@ void CompilerInvocation::setDefaultPointerAuthOptions( // If you change anything here, be sure to update . Opts.FunctionPointers = PointerAuthSchema(Key::ASIA, false, Discrimination::None); + + Opts.CXXVTablePointers = PointerAuthSchema( + Key::ASDA, LangOpts.PointerAuthVTPtrAddressDiscrimination, + LangOpts.PointerAuthVTPtrTypeDiscrimination ? Discrimination::Type + : Discrimination::None); + Opts.CXXTypeInfoVTablePointer = + PointerAuthSchema(Key::ASDA, false, Discrimination::None); + Opts.CXXVTTVTablePointers = + PointerAuthSchema(Key::ASDA, false, Discrimination::None); + Opts.CXXVirtualFunctionPointers = Opts.CXXVirtualVariadicFunctionPointers = + PointerAuthSchema(Key::ASIA, true, Discrimination::Decl); } } diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h index 1a4bd02933ea2..40ac6dcac2ab8 100644 --- a/clang/lib/Headers/ptrauth.h +++ b/clang/lib/Headers/ptrauth.h @@ -32,6 +32,10 @@ typedef enum { The extra data is always 0. */ ptrauth_key_function_pointer = ptrauth_key_process_independent_code, + /* The key used to sign C++ v-table pointers. + The extra data is always 0. */ + ptrauth_key_cxx_vtable_pointer = ptrauth_key_process_independent_data, + /* Other pointers signed under the ABI use private ABI rules. */ } ptrauth_key; @@ -205,6 +209,12 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; #define ptrauth_sign_generic_data(__value, __data) \ __builtin_ptrauth_sign_generic_data(__value, __data) +/* C++ vtable pointer signing class attribute */ +#define ptrauth_cxx_vtable_pointer(key, address_discrimination, \ + extra_discrimination...) \ + [[clang::ptrauth_vtable_pointer(key, address_discrimination, \ + extra_discrimination)]] + #else #define ptrauth_strip(__value, __key) \ @@ -271,6 +281,10 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; ((ptrauth_generic_signature_t)0); \ }) + +#define ptrauth_cxx_vtable_pointer(key, address_discrimination, \ + extra_discrimination...) + #endif /* __has_feature(ptrauth_intrinsics) */ #endif /* __PTRAUTH_H */ diff --git a/clang/lib/InstallAPI/Visitor.cpp b/clang/lib/InstallAPI/Visitor.cpp index 367ae53b208b6..a73ea0b0d124c 100644 --- a/clang/lib/InstallAPI/Visitor.cpp +++ b/clang/lib/InstallAPI/Visitor.cpp @@ -447,16 +447,16 @@ InstallAPIVisitor::getMangledCXXVTableName(const CXXRecordDecl *D) const { return getBackendMangledName(Name); } -std::string -InstallAPIVisitor::getMangledCXXThunk(const GlobalDecl &D, - const ThunkInfo &Thunk) const { +std::string InstallAPIVisitor::getMangledCXXThunk( + const GlobalDecl &D, const ThunkInfo &Thunk, bool ElideOverrideInfo) const { SmallString<256> Name; raw_svector_ostream NameStream(Name); const auto *Method = cast(D.getDecl()); if (const auto *Dtor = dyn_cast(Method)) - MC->mangleCXXDtorThunk(Dtor, D.getDtorType(), Thunk.This, NameStream); + MC->mangleCXXDtorThunk(Dtor, D.getDtorType(), Thunk, ElideOverrideInfo, + NameStream); else - MC->mangleThunk(Method, Thunk, NameStream); + MC->mangleThunk(Method, Thunk, ElideOverrideInfo, NameStream); return getBackendMangledName(Name); } @@ -500,7 +500,8 @@ void InstallAPIVisitor::emitVTableSymbols(const CXXRecordDecl *D, return; for (const auto &Thunk : *Thunks) { - const std::string Name = getMangledCXXThunk(GD, Thunk); + const std::string Name = + getMangledCXXThunk(GD, Thunk, /*ElideOverrideInfo=*/true); auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported, GlobalRecord::Kind::Function, Avail, GD.getDecl(), Access); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index c528917437332..a07f7ad2233fe 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -368,6 +368,27 @@ static bool attributeIsTypeArgAttr(const IdentifierInfo &II) { #undef CLANG_ATTR_TYPE_ARG_LIST } +/// Determine whether the given attribute takes identifier arguments. +static bool attributeHasStrictIdentifierArgs(const IdentifierInfo &II) { +#define CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST + return (llvm::StringSwitch(normalizeAttrName(II.getName())) +#include "clang/Parse/AttrParserStringSwitches.inc" + .Default(0)) != 0; +#undef CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST +} + +/// Determine whether the given attribute takes an identifier argument at a +/// specific index +static bool attributeHasStrictIdentifierArgAtIndex(const IdentifierInfo &II, + size_t argIndex) { +#define CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST + return (llvm::StringSwitch(normalizeAttrName(II.getName())) +#include "clang/Parse/AttrParserStringSwitches.inc" + .Default(0)) & + (1ull << argIndex); +#undef CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST +} + /// Determine whether the given attribute requires parsing its arguments /// in an unevaluated context or not. static bool attributeParsedArgsUnevaluated(const IdentifierInfo &II) { @@ -546,7 +567,8 @@ unsigned Parser::ParseAttributeArgsCommon( } if (T.isUsable()) TheParsedType = T.get(); - } else if (AttributeHasVariadicIdentifierArg) { + } else if (AttributeHasVariadicIdentifierArg || + attributeHasStrictIdentifierArgs(*AttrName)) { // Parse variadic identifier arg. This can either consume identifiers or // expressions. Variadic identifier args do not support parameter packs // because those are typically used for attributes with enumeration @@ -557,6 +579,12 @@ unsigned Parser::ParseAttributeArgsCommon( if (ChangeKWThisToIdent && Tok.is(tok::kw_this)) Tok.setKind(tok::identifier); + if (Tok.is(tok::identifier) && attributeHasStrictIdentifierArgAtIndex( + *AttrName, ArgExprs.size())) { + ArgExprs.push_back(ParseIdentifierLoc()); + continue; + } + ExprResult ArgExpr; if (Tok.is(tok::identifier)) { ArgExprs.push_back(ParseIdentifierLoc()); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 99b400307d644..7e6c7d776588a 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6281,6 +6281,116 @@ EnforceTCBLeafAttr *Sema::mergeEnforceTCBLeafAttr( *this, D, AL); } +static void handleVTablePointerAuthentication(Sema &S, Decl *D, + const ParsedAttr &AL) { + CXXRecordDecl *Decl = cast(D); + const uint32_t NumArgs = AL.getNumArgs(); + if (NumArgs > 4) { + S.Diag(AL.getLoc(), diag::err_attribute_too_many_arguments) << AL << 4; + AL.setInvalid(); + } + + if (NumArgs == 0) { + S.Diag(AL.getLoc(), diag::err_attribute_too_few_arguments) << AL; + AL.setInvalid(); + return; + } + + if (D->getAttr()) { + S.Diag(AL.getLoc(), diag::err_duplicated_vtable_pointer_auth) << Decl; + AL.setInvalid(); + } + + auto KeyType = VTablePointerAuthenticationAttr::VPtrAuthKeyType::DefaultKey; + if (AL.isArgIdent(0)) { + IdentifierLoc *IL = AL.getArgAsIdent(0); + if (!VTablePointerAuthenticationAttr::ConvertStrToVPtrAuthKeyType( + IL->Ident->getName(), KeyType)) { + S.Diag(IL->Loc, diag::err_invalid_authentication_key) << IL->Ident; + AL.setInvalid(); + } + if (KeyType == VTablePointerAuthenticationAttr::DefaultKey && + !S.getLangOpts().PointerAuthCalls) { + S.Diag(AL.getLoc(), diag::err_no_default_vtable_pointer_auth) << 0; + AL.setInvalid(); + } + } else { + S.Diag(AL.getLoc(), diag::err_attribute_argument_type) + << AL << AANT_ArgumentIdentifier; + return; + } + + auto AddressDiversityMode = VTablePointerAuthenticationAttr:: + AddressDiscriminationMode::DefaultAddressDiscrimination; + if (AL.getNumArgs() > 1) { + if (AL.isArgIdent(1)) { + IdentifierLoc *IL = AL.getArgAsIdent(1); + if (!VTablePointerAuthenticationAttr:: + ConvertStrToAddressDiscriminationMode(IL->Ident->getName(), + AddressDiversityMode)) { + S.Diag(IL->Loc, diag::err_invalid_address_discrimination) << IL->Ident; + AL.setInvalid(); + } + if (AddressDiversityMode == + VTablePointerAuthenticationAttr::DefaultAddressDiscrimination && + !S.getLangOpts().PointerAuthCalls) { + S.Diag(IL->Loc, diag::err_no_default_vtable_pointer_auth) << 1; + AL.setInvalid(); + } + } else { + S.Diag(AL.getLoc(), diag::err_attribute_argument_type) + << AL << AANT_ArgumentIdentifier; + } + } + + auto ED = VTablePointerAuthenticationAttr::ExtraDiscrimination:: + DefaultExtraDiscrimination; + if (AL.getNumArgs() > 2) { + if (AL.isArgIdent(2)) { + IdentifierLoc *IL = AL.getArgAsIdent(2); + if (!VTablePointerAuthenticationAttr::ConvertStrToExtraDiscrimination( + IL->Ident->getName(), ED)) { + S.Diag(IL->Loc, diag::err_invalid_extra_discrimination) << IL->Ident; + AL.setInvalid(); + } + if (ED == VTablePointerAuthenticationAttr::DefaultExtraDiscrimination && + !S.getLangOpts().PointerAuthCalls) { + S.Diag(AL.getLoc(), diag::err_no_default_vtable_pointer_auth) << 2; + AL.setInvalid(); + } + } else { + S.Diag(AL.getLoc(), diag::err_attribute_argument_type) + << AL << AANT_ArgumentIdentifier; + } + } + + uint32_t CustomDiscriminationValue = 0; + if (ED == VTablePointerAuthenticationAttr::CustomDiscrimination) { + if (NumArgs < 4) { + S.Diag(AL.getLoc(), diag::err_missing_custom_discrimination) << AL << 4; + AL.setInvalid(); + return; + } + if (NumArgs > 4) { + S.Diag(AL.getLoc(), diag::err_attribute_too_many_arguments) << AL << 4; + AL.setInvalid(); + } + + if (!AL.isArgExpr(3) || !S.checkUInt32Argument(AL, AL.getArgAsExpr(3), + CustomDiscriminationValue)) { + S.Diag(AL.getLoc(), diag::err_invalid_custom_discrimination); + AL.setInvalid(); + } + } else if (NumArgs > 3) { + S.Diag(AL.getLoc(), diag::err_attribute_too_many_arguments) << AL << 3; + AL.setInvalid(); + } + + Decl->addAttr(::new (S.Context) VTablePointerAuthenticationAttr( + S.Context, AL, KeyType, AddressDiversityMode, ED, + CustomDiscriminationValue)); +} + //===----------------------------------------------------------------------===// // Top Level Sema Entry Points //===----------------------------------------------------------------------===// @@ -7150,6 +7260,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_TypeNullable: handleNullableTypeAttr(S, D, AL); break; + + case ParsedAttr::AT_VTablePointerAuthentication: + handleVTablePointerAuthentication(S, D, AL); + break; } } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 9b220103247dd..ffa6bcac44cbd 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -7116,6 +7116,10 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) { return false; }; + if (!Record->isInvalidDecl() && + Record->hasAttr()) + checkIncorrectVTablePointerAuthenticationAttribute(*Record); + auto CompleteMemberFunction = [&](CXXMethodDecl *M) { // Check whether the explicitly-defaulted members are valid. bool Incomplete = CheckForDefaultedFunction(M); @@ -10500,6 +10504,39 @@ void Sema::checkIllFormedTrivialABIStruct(CXXRecordDecl &RD) { } } +void Sema::checkIncorrectVTablePointerAuthenticationAttribute( + CXXRecordDecl &RD) { + if (RequireCompleteType(RD.getLocation(), Context.getRecordType(&RD), + diag::err_incomplete_type_vtable_pointer_auth)) + return; + + const CXXRecordDecl *PrimaryBase = &RD; + if (PrimaryBase->hasAnyDependentBases()) + return; + + while (1) { + assert(PrimaryBase); + const CXXRecordDecl *Base = nullptr; + for (auto BasePtr : PrimaryBase->bases()) { + if (!BasePtr.getType()->getAsCXXRecordDecl()->isDynamicClass()) + continue; + Base = BasePtr.getType()->getAsCXXRecordDecl(); + break; + } + if (!Base || Base == PrimaryBase || !Base->isPolymorphic()) + break; + Diag(RD.getAttr()->getLocation(), + diag::err_non_top_level_vtable_pointer_auth) + << &RD << Base; + PrimaryBase = Base; + } + + if (!RD.isPolymorphic()) + Diag(RD.getAttr()->getLocation(), + diag::err_non_polymorphic_vtable_pointer_auth) + << &RD; +} + void Sema::ActOnFinishCXXMemberSpecification( Scope *S, SourceLocation RLoc, Decl *TagDecl, SourceLocation LBrac, SourceLocation RBrac, const ParsedAttributesView &AttrList) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 4a2f3a65eac96..db44cfe1288b6 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -14217,6 +14217,39 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { QualType MPTy = Context.getMemberPointerType( op->getType(), Context.getTypeDeclType(MD->getParent()).getTypePtr()); + + if (getLangOpts().PointerAuthCalls && MD->isVirtual() && + !isUnevaluatedContext() && !MPTy->isDependentType()) { + // When pointer authentication is enabled, argument and return types of + // vitual member functions must be complete. This is because vitrual + // member function pointers are implemented using virtual dispatch + // thunks and the thunks cannot be emitted if the argument or return + // types are incomplete. + auto ReturnOrParamTypeIsIncomplete = [&](QualType T, + SourceLocation DeclRefLoc, + SourceLocation RetArgTypeLoc) { + if (RequireCompleteType(DeclRefLoc, T, diag::err_incomplete_type)) { + Diag(DeclRefLoc, + diag::note_ptrauth_virtual_function_pointer_incomplete_arg_ret); + Diag(RetArgTypeLoc, + diag::note_ptrauth_virtual_function_incomplete_arg_ret_type) + << T; + return true; + } + return false; + }; + QualType RetTy = MD->getReturnType(); + bool IsIncomplete = + !RetTy->isVoidType() && + ReturnOrParamTypeIsIncomplete( + RetTy, OpLoc, MD->getReturnTypeSourceRange().getBegin()); + for (auto *PVD : MD->parameters()) + IsIncomplete |= ReturnOrParamTypeIsIncomplete(PVD->getType(), OpLoc, + PVD->getBeginLoc()); + if (IsIncomplete) + return QualType(); + } + // Under the MS ABI, lock down the inheritance model now. if (Context.getTargetInfo().getCXXABI().isMicrosoft()) (void)isCompleteType(OpLoc, MPTy); diff --git a/clang/test/CodeGen/ptrauth-ubsan-vptr.cpp b/clang/test/CodeGen/ptrauth-ubsan-vptr.cpp new file mode 100644 index 0000000000000..6c36004641477 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-ubsan-vptr.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple arm64e-apple-ios15 -fsanitize=vptr -O0 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64e-apple-ios15 -fsanitize=vptr -O2 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s + +struct S { + S() {} + ~S() {} + virtual int v() { return 0; } + int a; +}; + +struct T : public S { + virtual int v(); +}; + +// CHECK-LABEL: foo1 +int foo1(void* Buffer) { + T *p = reinterpret_cast(Buffer); + return p->v(); +} +// CHECK-NOT: call {{.*}} @llvm.ptrauth.auth{{.*}} +// CHECK-NOT: call {{.*}} @llvm.ptrauth.strip{{.*}} + +// CHECK-LABEL: foo2 +int foo2(S* s) { + T *p = dynamic_cast(s); + return p->v(); +} + +// CHECK-NOT: call {{.*}} @llvm.ptrauth.auth{{.*}} +// CHECK-NOT: call {{.*}} @llvm.ptrauth.strip{{.*}} diff --git a/clang/test/CodeGenCXX/catch-undef-behavior.cpp b/clang/test/CodeGenCXX/catch-undef-behavior.cpp index 1a0e98a661375..0c14c97dacca9 100644 --- a/clang/test/CodeGenCXX/catch-undef-behavior.cpp +++ b/clang/test/CodeGenCXX/catch-undef-behavior.cpp @@ -56,8 +56,8 @@ void member_access(S *p) { // (1b) Check that 'p' actually points to an 'S'. - // CHECK: %[[VPTR:.*]] = load i64, ptr - // + // CHECK: %[[VTABLE:.*]] = load ptr, ptr %0 + // CHECK: %[[VPTR:.*]] = ptrtoint ptr %[[VTABLE]] to i64 // hash_16_bytes: // // If this number changes, it indicates that either the mangled name of ::S @@ -110,7 +110,8 @@ void member_access(S *p) { // (3b) Check that 'p' actually points to an 'S' - // CHECK: load i64, ptr + // CHECK: [[VTABLE2:%.*]] = load ptr, ptr + // CHECK: ptrtoint ptr [[VTABLE2]] to i64 // CHECK-NEXT: mul i64 %[[#]], -4658895280553007687, !nosanitize // [...] // CHECK: getelementptr inbounds [128 x i64], ptr @__ubsan_vptr_type_cache, i32 0, i64 % diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp new file mode 100644 index 0000000000000..c2f20d56b0a6b --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp @@ -0,0 +1,105 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fapple-kext -fno-rtti -emit-llvm -o - %s | FileCheck %s + +// CHECK: @_ZTV1A = unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr null, ptr ptrauth (ptr @_ZNK1A3abcEv, i32 0, i64 12401, ptr getelementptr inbounds ({ [4 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 2)), ptr null] }, align 8 +// CHECK: @_ZTV4Base = unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr null, ptr ptrauth (ptr @_ZNK4Base3abcEv, i32 0, i64 64320, ptr getelementptr inbounds ({ [4 x ptr] }, ptr @_ZTV4Base, i32 0, i32 0, i32 2)), ptr null] }, align 8 +// CHECK: @_ZTV8Derived2 = unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr null, ptr null, ptr ptrauth (ptr @_ZNK8Derived23efgEv, i32 0, i64 36603, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV8Derived2, i32 0, i32 0, i32 3)), ptr null] }, align 8 +// CHECK: @_ZTV2D2 = unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr null, ptr null, ptr ptrauth (ptr @_ZNK2D23abcEv, i32 0, i64 20222, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 3)), ptr null] }, align 8 + +struct A { + virtual const char* abc(void) const; +}; + +const char* A::abc(void) const {return "A"; }; + +struct B : virtual A { + virtual void VF(); +}; + +void B::VF() {} + +void FUNC(B* p) { +// CHECK: [[T1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV1A, i64 2) +// CHECK-NEXT: [[BT1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV1A, i64 2) to i64), i64 12401) +// CHECK-NEXT: [[T2:%.*]] = call noundef ptr [[T1]](ptr noundef {{.*}}) [ "ptrauth"(i32 0, i64 [[BT1]]) ] + const char* c = p->A::abc(); +} + + +// Test2 +struct Base { virtual char* abc(void) const; }; + +char* Base::abc() const { return 0; } + +struct Derived : public Base { +}; + +void FUNC1(Derived* p) { +// CHECK: [[U1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV4Base, i64 2) +// CHECK-NEXT: [[BU1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV4Base, i64 2) to i64), i64 64320) +// CHECK-NEXT: [[U2:%.*]] = call noundef ptr [[U1]](ptr noundef {{.*}}) [ "ptrauth"(i32 0, i64 [[BU1]]) ] + char* c = p->Base::abc(); +} + + +// Test3 +struct Base2 { }; + +struct Derived2 : virtual Base2 { + virtual char* efg(void) const; +}; + +char* Derived2::efg(void) const { return 0; } + +void FUNC2(Derived2* p) { +// CHECK: [[V1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV8Derived2, i64 3) +// CHECK-NEXT: [[BV1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV8Derived2, i64 3) to i64), i64 36603) +// CHECK-NEXT: [[V2:%.*]] = call noundef ptr [[V1]](ptr noundef {{.*}}) [ "ptrauth"(i32 0, i64 [[BV1]]) ] + char* c = p->Derived2::efg(); +} + +// Test4 +struct Base3 { }; + +struct D1 : virtual Base3 { +}; + +struct D2 : virtual Base3 { + virtual char *abc(void) const; +}; + +struct Sub : D1, D2 { +}; + +char* D2::abc(void) const { return 0; } + +void FUNC3(Sub* p) { +// CHECK: [[W1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV2D2, i64 3) +// CHECK-NEXT: [[BW1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV2D2, i64 3) to i64), i64 20222) +// CHECK-NEXT: [[W2:%.*]] = call noundef ptr [[W1]](ptr noundef {{.*}}) [ "ptrauth"(i32 0, i64 [[BW1]]) ] + char* c = p->D2::abc(); +} + + +// Test4 +struct Base4 { virtual void abc(); }; + +void Base4::abc() {} + +struct Derived4 : public Base4 { + void abc() override; +}; + +void Derived4::abc() {} + +void FUNC4(Derived4* p) { +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 426) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + p->abc(); +} diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp new file mode 100644 index 0000000000000..996829a14d7d1 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fapple-kext -emit-llvm -o - %s | FileCheck %s + +// CHECK: @_ZTV5TemplIiE = internal unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTI5TemplIiE, ptr ptrauth (ptr @_ZN5TemplIiE1fEv, i32 0, i64 22189, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 2)), ptr ptrauth (ptr @_ZN5TemplIiE1gEv, i32 0, i64 9912, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 3)), ptr null] }, align 8 + +struct Base { + virtual void abc(void) const; +}; + +void Base::abc(void) const {} + +void FUNC(Base* p) { + p->Base::abc(); +} + +// CHECK: getelementptr inbounds (ptr, ptr @_ZTV4Base, i64 2) +// CHECK-NOT: call void @_ZNK4Base3abcEv + +template +struct Templ { + virtual void f() {} + virtual void g() {} +}; +template +struct SubTempl : public Templ { + virtual void f() {} // override + virtual void g() {} // override +}; + +void f(SubTempl* t) { + // Qualified calls go through the (qualified) vtable in apple-kext mode. + // Since t's this pointer points to SubTempl's vtable, the call needs + // to load Templ's vtable. Hence, Templ::g needs to be + // instantiated in this TU, for it's referenced by the vtable. + // (This happens only in apple-kext mode; elsewhere virtual calls can always + // use the vtable pointer off this instead of having to load the vtable + // symbol.) + t->Templ::f(); +} + +// CHECK: getelementptr inbounds (ptr, ptr @_ZTV5TemplIiE, i64 2) +// CHECK: define internal void @_ZN5TemplIiE1fEv(ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %this) +// CHECK: define internal void @_ZN5TemplIiE1gEv(ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %this) diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp new file mode 100644 index 0000000000000..7bcf1fbfdb9de --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++98 -fptrauth-calls -fapple-kext -fno-rtti -disable-O0-optnone -emit-llvm -o - %s | FileCheck %s + +// CHECK: @_ZTV5TemplIiE = internal unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr null, ptr ptrauth (ptr @_ZN5TemplIiED1Ev, i32 0, i64 57986, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 2)), ptr ptrauth (ptr @_ZN5TemplIiED0Ev, i32 0, i64 22856, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 3)), ptr ptrauth (ptr @_ZN5TemplIiE1fEv, i32 0, i64 22189, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 4)), ptr ptrauth (ptr @_ZN5TemplIiE1gEv, i32 0, i64 9912, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 5)), ptr null] }, align 8 + +struct B1 { + virtual ~B1(); +}; + +B1::~B1() {} + +void DELETE(B1 *pb1) { + pb1->B1::~B1(); +} +// CHECK-LABEL: define void @_ZN2B1D0Ev +// CHECK: [[T1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV2B1, i64 2) +// CHECK-NEXT: [[B1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV2B1, i64 2) to i64), i64 14635) +// CHECK-NEXT: call noundef ptr [[T1]](ptr noundef nonnull align 8 dereferenceable(8) [[T2:%.*]]) [ "ptrauth"(i32 0, i64 [[B1]]) ] +// CHECK-LABEL: define void @_Z6DELETEP2B1 +// CHECK: [[T3:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV2B1, i64 2) +// CHECK-NEXT: [[B3:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV2B1, i64 2) to i64), i64 14635) +// CHECK-NEXT: call noundef ptr [[T3]](ptr noundef nonnull align 8 dereferenceable(8) [[T4:%.*]]) [ "ptrauth"(i32 0, i64 [[B3]]) + +template +struct Templ { + virtual ~Templ(); // Out-of-line so that the destructor doesn't cause a vtable + virtual void f() {} + virtual void g() {} +}; +template +struct SubTempl : public Templ { + virtual ~SubTempl() {} // override + virtual void f() {} // override + virtual void g() {} // override +}; + +void f(SubTempl* t) { + // Qualified calls go through the (qualified) vtable in apple-kext mode. + // Since t's this pointer points to SubTempl's vtable, the call needs + // to load Templ's vtable. Hence, Templ::g needs to be + // instantiated in this TU, for it's referenced by the vtable. + // (This happens only in apple-kext mode; elsewhere virtual calls can always + // use the vtable pointer off this instead of having to load the vtable + // symbol.) + t->Templ::~Templ(); +} + +// CHECK: getelementptr inbounds (ptr, ptr @_ZTV5TemplIiE, i64 2) +// CHECK: declare void @_ZN5TemplIiED0Ev(ptr noundef nonnull align 8 dereferenceable(8)) +// CHECK: define internal void @_ZN5TemplIiE1fEv(ptr noundef nonnull align 8 dereferenceable(8) %this) +// CHECK: define internal void @_ZN5TemplIiE1gEv(ptr noundef nonnull align 8 dereferenceable(8) %this) diff --git a/clang/test/CodeGenCXX/ptrauth-explicit-vtable-pointer-control.cpp b/clang/test/CodeGenCXX/ptrauth-explicit-vtable-pointer-control.cpp new file mode 100644 index 0000000000000..e6497b3f152aa --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-explicit-vtable-pointer-control.cpp @@ -0,0 +1,564 @@ +// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -emit-llvm -o - | FileCheck --check-prefixes=CHECK,NODISC %s + +// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -fptrauth-vtable-pointer-type-discrimination \ +// RUN: -emit-llvm -o - | FileCheck --check-prefixes=CHECK,TYPE %s + +// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -fptrauth-vtable-pointer-address-discrimination \ +// RUN: -emit-llvm -o - | FileCheck --check-prefixes=CHECK,ADDR %s + +// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -fptrauth-vtable-pointer-type-discrimination \ +// RUN: -fptrauth-vtable-pointer-address-discrimination \ +// RUN: -emit-llvm -o - | FileCheck --check-prefixes=CHECK,BOTH %s + +#include + +namespace test1 { + +#define authenticated(a...) ptrauth_cxx_vtable_pointer(a) + +struct NoExplicitAuth { + virtual ~NoExplicitAuth(); + virtual void f(); + virtual void g(); +}; + +struct authenticated(no_authentication, no_address_discrimination, no_extra_discrimination) ExplicitlyDisableAuth { + virtual ~ExplicitlyDisableAuth(); + virtual void f(); + virtual void g(); +}; + +struct authenticated(default_key, address_discrimination, default_extra_discrimination) ExplicitAddressDiscrimination { + virtual ~ExplicitAddressDiscrimination(); + virtual void f(); + virtual void g(); +}; + +struct authenticated(default_key, no_address_discrimination, default_extra_discrimination) ExplicitNoAddressDiscrimination { + virtual ~ExplicitNoAddressDiscrimination(); + virtual void f(); + virtual void g(); +}; + +struct authenticated(default_key, default_address_discrimination, no_extra_discrimination) ExplicitNoExtraDiscrimination { + virtual ~ExplicitNoExtraDiscrimination(); + virtual void f(); + virtual void g(); +}; + +struct authenticated(default_key, default_address_discrimination, type_discrimination) ExplicitTypeDiscrimination { + virtual ~ExplicitTypeDiscrimination(); + virtual void f(); + virtual void g(); +}; + +struct authenticated(default_key, default_address_discrimination, custom_discrimination, 42424) ExplicitCustomDiscrimination { + virtual ~ExplicitCustomDiscrimination(); + virtual void f(); + virtual void g(); +}; + +template +struct SubClass : T { + virtual void g(); + virtual T *h(); +}; + +template +SubClass *make_subclass(T *); + +struct authenticated(default_key, address_discrimination, type_discrimination) BasicStruct { + virtual ~BasicStruct(); +}; + +template +struct PrimaryBasicStruct : BasicStruct, T {}; +template +struct PrimaryBasicStruct *make_multiple_primary(T *); + +template +struct VirtualSubClass : virtual T { + virtual void g(); + virtual T *h(); +}; +template +struct VirtualPrimaryStruct : virtual T, VirtualSubClass {}; +template +struct VirtualPrimaryStruct *make_virtual_primary(T *); + +extern "C" { + +// CHECK: @TVDisc_NoExplicitAuth = global i32 [[DISC_DEFAULT:49565]], align 4 +int TVDisc_NoExplicitAuth = ptrauth_string_discriminator("_ZTVN5test114NoExplicitAuthE"); + +// CHECK: @TVDisc_ExplicitlyDisableAuth = global i32 [[DISC_DISABLED:24369]], align 4 +int TVDisc_ExplicitlyDisableAuth = ptrauth_string_discriminator("_ZTVN5test121ExplicitlyDisableAuthE"); + +// CHECK: @TVDisc_ExplicitAddressDiscrimination = global i32 [[DISC_ADDR:56943]], align 4 +int TVDisc_ExplicitAddressDiscrimination = ptrauth_string_discriminator("_ZTVN5test129ExplicitAddressDiscriminationE"); + +// CHECK: @TVDisc_ExplicitNoAddressDiscrimination = global i32 [[DISC_NO_ADDR:6022]], align 4 +int TVDisc_ExplicitNoAddressDiscrimination = ptrauth_string_discriminator("_ZTVN5test131ExplicitNoAddressDiscriminationE"); + +// CHECK: @TVDisc_ExplicitNoExtraDiscrimination = global i32 [[DISC_NO_EXTRA:9072]], align 4 +int TVDisc_ExplicitNoExtraDiscrimination = ptrauth_string_discriminator("_ZTVN5test129ExplicitNoExtraDiscriminationE"); + +// CHECK: @TVDisc_ExplicitTypeDiscrimination = global i32 [[DISC_TYPE:6177]], align 4 +int TVDisc_ExplicitTypeDiscrimination = ptrauth_string_discriminator("_ZTVN5test126ExplicitTypeDiscriminationE"); + + +// CHECK-LABEL: define void @test_default(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8 +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_DEFAULT]]) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_DEFAULT]]) +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +void test_default(NoExplicitAuth *a) { + a->f(); +} + +// CHECK-LABEL: define void @test_disabled(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8 +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// CHECK-NOT: call i64 @llvm.ptrauth.auth +void test_disabled(ExplicitlyDisableAuth *a) { + a->f(); +} + +// CHECK-LABEL: define void @test_addr_disc(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8 +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]]) +// +// TYPE: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// TYPE: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_ADDR]]) +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_ADDR]]) +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +void test_addr_disc(ExplicitAddressDiscrimination *a) { + a->f(); +} + +// CHECK-LABEL: define void @test_no_addr_disc(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8 +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_NO_ADDR]]) +// +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0) +// +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_NO_ADDR]]) +void test_no_addr_disc(ExplicitNoAddressDiscrimination *a) { + a->f(); +} + +// CHECK-LABEL: define void @test_no_extra_disc(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8 +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]]) +void test_no_extra_disc(ExplicitNoExtraDiscrimination *a) { + a->f(); +} + +// CHECK-LABEL: define void @test_type_disc(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8 +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_TYPE]]) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_TYPE]]) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_TYPE]]) +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_TYPE]]) +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +void test_type_disc(ExplicitTypeDiscrimination *a) { + a->f(); +} + +// CHECK-LABEL: define void @test_custom_disc(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8 +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424) +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424) +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +void test_custom_disc(ExplicitCustomDiscrimination *a) { + a->f(); +} + +// +// Test some simple single inheritance cases. +// Codegen should be the same as the simple cases above once we have a vtable. +// + +// CHECK-LABEL: define void @test_subclass_default(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_DEFAULT]]) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_DEFAULT]]) +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +void test_subclass_default(NoExplicitAuth *a) { + make_subclass(a)->f(); +} + +// CHECK-LABEL: define void @test_subclass_disabled(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// CHECK-NOT: call i64 @llvm.ptrauth.auth +void test_subclass_disabled(ExplicitlyDisableAuth *a) { + make_subclass(a)->f(); +} + +// CHECK-LABEL: define void @test_subclass_addr_disc(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]]) +// +// TYPE: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// TYPE: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_ADDR]]) +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_ADDR]]) +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +void test_subclass_addr_disc(ExplicitAddressDiscrimination *a) { + make_subclass(a)->f(); +} + +// CHECK-LABEL: define void @test_subclass_no_addr_disc(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_NO_ADDR]]) +// +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0) +// +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_NO_ADDR]]) +void test_subclass_no_addr_disc(ExplicitNoAddressDiscrimination *a) { + make_subclass(a)->f(); +} + +// CHECK-LABEL: define void @test_subclass_no_extra_disc(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]]) +void test_subclass_no_extra_disc(ExplicitNoExtraDiscrimination *a) { + make_subclass(a)->f(); +} + +// CHECK-LABEL: define void @test_subclass_type_disc(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_TYPE]]) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_TYPE]]) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_TYPE]]) +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_TYPE]]) +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +void test_subclass_type_disc(ExplicitTypeDiscrimination *a) { + make_subclass(a)->f(); +} + +// CHECK-LABEL: define void @test_subclass_custom_disc(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424) +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424) +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +void test_subclass_custom_disc(ExplicitCustomDiscrimination *a) { + make_subclass(a)->f(); +} + + +// +// Test some simple multiple inheritance cases. +// Codegen should be the same as the simple cases above once we have a vtable. +// + +// CHECK-LABEL: define void @test_multiple_default(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[CALL:%.*]] = call noundef ptr @_ZN5test121make_multiple_primary +// CHECK: [[VTADDR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8 +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_DEFAULT]]) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_DEFAULT]]) +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +void test_multiple_default(NoExplicitAuth *a) { + make_multiple_primary(a)->f(); +} + +// CHECK-LABEL: define void @test_multiple_disabled(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[CALL:%.*]] = call noundef ptr @_ZN5test121make_multiple_primary +// CHECK: [[VTADDR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8 +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// CHECK-NOT: call i64 @llvm.ptrauth.auth +void test_multiple_disabled(ExplicitlyDisableAuth *a) { + make_multiple_primary(a)->f(); +} + +// CHECK-LABEL: define void @test_multiple_custom_disc(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[CALL:%.*]] = call noundef ptr @_ZN5test121make_multiple_primary +// CHECK: [[VTADDR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8 +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424) +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424) +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +void test_multiple_custom_disc(ExplicitCustomDiscrimination *a) { + make_multiple_primary(a)->f(); +} + +// +// Test some virtual inheritance cases. +// Codegen should be the same as the simple cases above once we have a vtable, +// but twice for vtt/vtable. The names in the vtt version have "VTT" prefixes. +// + +// CHECK-LABEL: define void @test_virtual_default(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTTADDR:%.*]] = call noundef ptr @_ZN5test120make_virtual_primary +// CHECK: [[VTTABLE:%.*]] = load ptr, ptr [[VTTADDR]], align 8 +// +// NODISC: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 0) +// +// TYPE: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 [[DISC_DEFAULT]]) +// +// ADDR: [[VTTADDRI64:%.*]] = ptrtoint ptr [[VTTADDR]] to i64 +// ADDR: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 [[VTTADDRI64]]) +// +// BOTH: [[VTTADDRI64:%.*]] = ptrtoint ptr [[VTTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTTADDRI64]], i64 [[DISC_DEFAULT]]) +// BOTH: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 [[BLEND]]) + +// CHECK: [[AUTHEDPTR:%.*]] = inttoptr i64 [[AUTHED]] to ptr +// CHECK: [[VBOFFPTR:%.*]] = getelementptr i8, ptr [[AUTHEDPTR]], i64 -48 +// CHECK: [[VBOFFSET:%.*]] = load i64, ptr [[VBOFFPTR]] +// CHECK: [[VTADDR:%.*]] = getelementptr inbounds i8, ptr [[VTTADDR]], i64 [[VBOFFSET]] +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_DEFAULT]]) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_DEFAULT]]) +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +void test_virtual_default(NoExplicitAuth *a) { + make_virtual_primary(a)->f(); +} + +// CHECK-LABEL: define void @test_virtual_disabled(ptr noundef {{%.*}}) {{#.*}} { +// CHECK-NOT: call i64 @llvm.ptrauth.auth +void test_virtual_disabled(ExplicitlyDisableAuth *a) { + make_virtual_primary(a)->f(); +} + +// CHECK-LABEL: define void @test_virtual_custom_disc(ptr noundef {{%.*}}) {{#.*}} { +// CHECK: [[VTTADDR:%.*]] = call noundef ptr @_ZN5test120make_virtual_primary +// CHECK: [[VTTABLE:%.*]] = load ptr, ptr [[VTTADDR]], align 8 +// +// NODISC: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 42424) +// +// TYPE: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 42424) +// +// ADDR: [[VTTADDRI64:%.*]] = ptrtoint ptr [[VTTADDR]] to i64 +// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTTADDRI64]], i64 42424) +// ADDR: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 [[BLEND]]) +// +// BOTH: [[VTTADDRI64:%.*]] = ptrtoint ptr [[VTTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTTADDRI64]], i64 42424) +// BOTH: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 [[BLEND]]) + +// CHECK: [[AUTHEDPTR:%.*]] = inttoptr i64 [[AUTHED]] to ptr +// CHECK: [[VBOFFPTR:%.*]] = getelementptr i8, ptr [[AUTHEDPTR]], i64 -48 +// CHECK: [[VBOFFSET:%.*]] = load i64, ptr [[VBOFFPTR]] +// CHECK: [[VTADDR:%.*]] = getelementptr inbounds i8, ptr [[VTTADDR]], i64 [[VBOFFSET]] +// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8 +// +// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424) +// +// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424) +// +// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424) +// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +// +// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64 +// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424) +// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]]) +void test_virtual_custom_disc(ExplicitCustomDiscrimination *a) { + make_virtual_primary(a)->f(); +} + +} // extern "C" +} // namespace test1 diff --git a/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp b/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp new file mode 100644 index 0000000000000..b4a8784a33d8c --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 %s -I%S -triple=arm64-apple-ios -fptrauth-calls -std=c++11 -emit-llvm -o - | FileCheck %s +#include + +struct A { int a; }; + +// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE = external global [0 x ptr] +// CHECK: @_ZTS1A = linkonce_odr hidden constant [3 x i8] c"1A\00" +// CHECK: @_ZTI1A = linkonce_odr hidden constant { ptr, ptr } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), i32 2), ptr inttoptr (i64 add (i64 ptrtoint (ptr @_ZTS1A to i64), i64 -9223372036854775808) to ptr) } + +auto ATI = typeid(A); diff --git a/clang/test/CodeGenCXX/ptrauth-thunks.cpp b/clang/test/CodeGenCXX/ptrauth-thunks.cpp new file mode 100644 index 0000000000000..a85c8c4c065c4 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-thunks.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - | FileCheck %s + +namespace Test1 { + struct B1 { + virtual void* foo1() { + return 0; + } + }; + struct Pad1 { + virtual ~Pad1() {} + }; + struct Proxy1 : Pad1, B1 { + virtual ~Proxy1() {} + }; + struct D : virtual Proxy1 { + virtual ~D() {} + virtual void* foo1(); + }; + void* D::foo1() { + return (void*)this; + } +} + +// CHECK-LABEL: define linkonce_odr void @_ZTv0_n24_N5Test11DD0Ev(ptr noundef %this) +// CHECK: %[[This:.*]] = load ptr +// CHECK: %[[SignedVTable:.*]] = load ptr, ptr %[[This]], align 8 +// CHECK: %[[SignedVTableAsInt:.*]] = ptrtoint ptr %[[SignedVTable]] to i64 +// CHECK: %[[VTable:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[SignedVTableAsInt]], i32 2, i64 0) diff --git a/clang/test/CodeGenCXX/ptrauth-virtual-function.cpp b/clang/test/CodeGenCXX/ptrauth-virtual-function.cpp new file mode 100644 index 0000000000000..563d60780769b --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-virtual-function.cpp @@ -0,0 +1,581 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - | FileCheck %s + +// Check virtual function pointers in vtables are signed. + +// CHECK: %[[CLASS_B1:.*]] = type { ptr } + +// CHECK: @_ZTV2B1 = unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI2B1, ptr ptrauth (ptr @_ZN2B12m0Ev, i32 0, i64 58196, ptr getelementptr inbounds ({ [3 x ptr] }, ptr @_ZTV2B1, i32 0, i32 0, i32 2))] }, align 8 + +// CHECK: @g_B1 = global %class.B1 { ptr ptrauth (ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr] }, ptr @_ZTV2B1, i32 0, i32 0, i32 2), i32 2) }, align 8 + +// CHECK: @_ZTV2B0 = unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr @_ZTI2B0, +// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 2)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2B0D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2B0D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 6))] }, align 8 + +// CHECK: @_ZTV2D0 = unnamed_addr constant { [9 x ptr] } { [9 x ptr] [ptr null, ptr @_ZTI2D0, +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D02m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 2)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTch0_h4_N2D02m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D0D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D0D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D02m1Ev, i32 0, i64 35045, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D02m3Ev, i32 0, i64 10565, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 8))] }, align 8 + +// CHECK: @_ZTV2D1 = unnamed_addr constant { [8 x ptr] } { [8 x ptr] [ptr null, ptr @_ZTI2D1, +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D12m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 2)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTch0_h4_N2D12m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D1D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D1D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D12m1Ev, i32 0, i64 52864, ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 7))] }, align 8 + +// CHECK: @_ZTV2D2 = unnamed_addr constant { [9 x ptr], [8 x ptr] } { [9 x ptr] [ptr null, ptr @_ZTI2D2, +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D22m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 2)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTch0_h4_N2D22m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D2D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D2D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D22m1Ev, i32 0, i64 35045, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D22m3Ev, i32 0, i64 10565, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 8))], +// CHECK-SAME: [8 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr @_ZTI2D2, +// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D22m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 2)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTchn16_h4_N2D22m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D2D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D2D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D22m1Ev, i32 0, i64 52864, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 7))] }, align 8 + +// CHECK: @_ZTV2D3 = unnamed_addr constant { [7 x ptr], [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 32 to ptr), ptr null, ptr @_ZTI2D3, +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D32m0Ev, i32 0, i64 44578, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D32m1Ev, i32 0, i64 30766, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D3D1Ev, i32 0, i64 57279, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 0, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2D3D0Ev, i32 0, i64 62452, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 0, i32 6))], +// CHECK-SAME: [7 x ptr] [ptr inttoptr (i64 16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr @_ZTI2D3, +// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D32m0Ev, i32 0, i64 49430, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 1, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D32m1Ev, i32 0, i64 57119, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 1, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D3D1Ev, i32 0, i64 60799, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 1, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D3D0Ev, i32 0, i64 52565, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 1, i32 6))], +// CHECK-SAME: [11 x ptr] [ptr inttoptr (i64 -32 to ptr), ptr null, ptr inttoptr (i64 -32 to ptr), ptr inttoptr (i64 -32 to ptr), ptr inttoptr (i64 -32 to ptr), ptr @_ZTI2D3, +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n24_N2D32m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTcv0_n32_h4_N2D32m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 8)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N2D3D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 9)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N2D3D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 10))] }, align 8 + +// CHECK: @_ZTC2D30_2V0 = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 32 to ptr), ptr null, ptr @_ZTI2V0, +// CHECK-SAME: ptr ptrauth (ptr @_ZN2V02m0Ev, i32 0, i64 44578, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2V02m1Ev, i32 0, i64 30766, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2V0D1Ev, i32 0, i64 57279, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2V0D0Ev, i32 0, i64 62452, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 6))], +// CHECK-SAME: [11 x ptr] [ptr inttoptr (i64 -32 to ptr), ptr null, ptr inttoptr (i64 -32 to ptr), ptr inttoptr (i64 -32 to ptr), ptr inttoptr (i64 -32 to ptr), ptr @_ZTI2V0, +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n24_N2V02m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTcv0_n32_h4_N2V02m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 8)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N2V0D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 9)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N2V0D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 10))] }, align 8 + +// CHECK: @_ZTC2D316_2V1 = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 16 to ptr), ptr null, ptr @_ZTI2V1, +// CHECK-SAME: ptr ptrauth (ptr @_ZN2V12m0Ev, i32 0, i64 49430, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2V12m1Ev, i32 0, i64 57119, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2V1D1Ev, i32 0, i64 60799, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2V1D0Ev, i32 0, i64 52565, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 6))], +// CHECK-SAME: [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr null, ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr @_ZTI2V1, +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n24_N2V12m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTcv0_n32_h4_N2V12m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 8)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N2V1D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 9)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N2V1D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 10))] }, align 8 + + +struct S0 { + int f; +}; + +struct S1 { + int f; +}; + +struct S2 : S0, S1 { + int f; +}; + +class B0 { +public: + virtual void m0(); + virtual S1 *m1(); + virtual void m2(); + virtual ~B0(); + int f; +}; + +class B1 { +public: + virtual void m0(); +}; + +class D0 : public B0 { +public: + void m0() override; + S2 *m1() override; + virtual void m3(); + int f; +}; + +class D1 : public B0 { +public: + void m0() override; + S2 *m1() override; + int f; +}; + +class D2 : public D0, public D1 { +public: + void m0() override; + S2 *m1() override; + void m3() override; + int f; +}; + +class V0 : public virtual B0 { +public: + void m0() override; + S2 *m1() override; + int f; +}; + +class V1 : public virtual B0 { +public: + void m0() override; + S2 *m1() override; + ~V1(); + int f; +}; + +class D3 : public V0, public V1 { +public: + void m0() override; + S2 *m1() override; + int f; +}; + +B1 g_B1; + +void B0::m0() {} + +void B1::m0() {} + +void D0::m0() {} + +void D1::m0() {} + +void D2::m0() {} + +void D3::m0() {} + +V1::~V1() { + m1(); +} + +// Check sign/authentication of vtable pointers and authentication of virtual +// functions. + +// CHECK-LABEL: define noundef ptr @_ZN2V1D2Ev( +// CHECK: %[[THIS1:.*]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T1:[0-9]+]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[T2:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T1]], i32 2, i64 0) +// CHECK: %[[T3:[0-9]+]] = inttoptr i64 %[[T2]] to ptr +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[T3]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 %[[T6]], i32 2, i64 0) +// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T7]] to ptr +// CHECK: store ptr %[[SIGNED_VTADDR]], ptr %[[THIS1]] + +// CHECK-LABEL: define void @_Z8testB0m0P2B0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 53119) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testB0m0(B0 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testB0m1P2B0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 1 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 15165) +// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testB0m1(B0 *a) { + a->m1(); +} + +// CHECK-LABEL: define void @_Z8testB0m2P2B0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 2 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 43073) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testB0m2(B0 *a) { + a->m2(); +} + +// CHECK-LABEL: define void @_Z8testD0m0P2D0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 53119) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD0m0(D0 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testD0m1P2D0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 5 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 35045) +// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD0m1(D0 *a) { + a->m1(); +} + +// CHECK-LABEL: define void @_Z8testD0m2P2D0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 2 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 43073) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD0m2(D0 *a) { + a->m2(); +} + +// CHECK-LABEL: define void @_Z8testD0m3P2D0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 6 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 10565) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD0m3(D0 *a) { + a->m3(); +} + + +// CHECK-LABEL: define void @_Z8testD1m0P2D1( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 53119) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD1m0(D1 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testD1m1P2D1( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 5 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 52864) +// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD1m1(D1 *a) { + a->m1(); +} + +// CHECK-LABEL: define void @_Z8testD1m2P2D1( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 2 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 43073) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD1m2(D1 *a) { + a->m2(); +} + + +// CHECK-LABEL: define void @_Z8testD2m0P2D2( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 53119) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(36) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD2m0(D2 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testD2m1P2D2( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 5 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 35045) +// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(36) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD2m1(D2 *a) { + a->m1(); +} + +// CHECK-LABEL: define void @_Z10testD2m2D0P2D2( +// CHECK: call void @_ZN2B02m2Ev(ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}){{$}} + +void testD2m2D0(D2 *a) { + a->D0::m2(); +} + +// CHECK-LABEL: define void @_Z10testD2m2D1P2D2( +// CHECK: call void @_ZN2B02m2Ev(ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}){{$}} + +void testD2m2D1(D2 *a) { + a->D1::m2(); +} + +// CHECK-LABEL: define void @_Z8testD2m3P2D2( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 6 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 10565) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(36) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD2m3(D2 *a) { + a->m3(); +} + +// CHECK-LABEL: define void @_Z8testD3m0P2D3( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 44578) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD3m0(D3 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testD3m1P2D3( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 1 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 30766) +// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD3m1(D3 *a) { + a->m1(); +} + +// CHECK: define void @_Z8testD3m2P2D3(ptr noundef %[[A:.*]]) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[A]], ptr %[[A_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %[[V0]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 2, i64 0) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[VBASE_OFFSET_PTR:.*]] = getelementptr i8, ptr %[[V3]], i64 -24 +// CHECK: %[[VBASE_OFFSET:.*]] = load i64, ptr %[[VBASE_OFFSET_PTR]], align 8 +// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 %[[VBASE_OFFSET]] +// CHECK: %[[VTABLE1:.*]] = load ptr, ptr %[[ADD_PTR]], align 8 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[VTABLE1]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V4]], i32 2, i64 0) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[VFN:.*]] = getelementptr inbounds ptr, ptr %[[V6]], i64 2 +// CHECK: %[[V7:.*]] = load ptr, ptr %[[VFN]], align 8 +// CHECK: %[[V8:.*]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V8]], i64 43073) +// CHECK: call void %[[V7]](ptr noundef nonnull align 8 dereferenceable(12) %[[ADD_PTR]]) [ "ptrauth"(i32 0, i64 %[[V9]]) ] + +void testD3m2(D3 *a) { + a->m2(); +} + +// CHECK-LABEL: define void @_Z17testD3Destructor0P2D3( +// CHECK: load ptr, ptr +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %{{.*}} +// CHECK: %[[T2:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T2]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 3 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 62452) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD3Destructor0(D3 *a) { + delete a; +} + +// CHECK-LABEL: define void @_Z17testD3Destructor1P2D3( +// CHECK: %[[T6:.*]] = load ptr, ptr % +// CHECK: %[[VTABLE0:[a-z0-9]+]] = load ptr, ptr % +// CHECK: %[[T2:[0-9]+]] = ptrtoint ptr %[[VTABLE0]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T2]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[COMPLETE_OFFSET_PTR:.*]] = getelementptr inbounds i64, ptr %[[T4]], i64 -2 +// CHECK: %[[T5:[0-9]+]] = load i64, ptr %[[COMPLETE_OFFSET_PTR]] +// CHECK: %[[T7:[0-9]+]] = getelementptr inbounds i8, ptr %[[T6]], i64 %[[T5]] +// CHECK: %[[VTABLE1:[a-z0-9]+]] = load ptr, ptr %[[T6]] +// CHECK: %[[T9:[0-9]+]] = ptrtoint ptr %[[VTABLE1]] to i64 +// CHECK: %[[T10:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T9]], i32 2, i64 0) +// CHECK: %[[T11:[0-9]+]] = inttoptr i64 %[[T10]] to ptr +// CHECK: %[[VFN:[a-z0-9]+]] = getelementptr inbounds ptr, ptr %[[T11]], i64 2 +// CHECK: %[[T12:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T13:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T14:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T13]], i64 57279) +// CHECK: %call = call noundef ptr %[[T12]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 %[[T14]]) ] +// CHECK: call void @_ZdlPv(ptr noundef %[[T7]]) + +void testD3Destructor1(D3 *a) { + ::delete a; +} + +// CHECK-LABEL: define void @_Z17testD3Destructor2P2D3( +// CHECK: load ptr, ptr +// CHECK: %[[VTABLE:.*]] = load ptr, ptr % +// CHECK: %[[T2:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[T2]], i32 2, i64 0) +// CHECK: %[[T4:.*]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:.*]] = getelementptr inbounds ptr, ptr %[[T4]], i64 2 +// CHECK: %[[T5:.*]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:.*]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 57279) +// CHECK: %call = call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD3Destructor2(D3 *a) { + a->~D3(); +} + +void materializeConstructors() { + B0 B0; + B1 B1; + D0 D0; + D1 D1; + D2 D2; + D3 D3; + V0 V0; + V1 V1; +} + +// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2B0C2Ev( +// CHECK: %[[THIS:.*]] = load ptr, ptr % +// CHECK: %[[T0:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 40) ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T0]] to ptr +// CHECK: store ptr %[[SIGNED_VTADDR]], ptr %[[THIS]] + +// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2D0C2Ev( +// CHECK: %[[T0:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 56) ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T0]] to ptr +// CHECK: store ptr %[[SIGNED_VTADDR]], ptr %[[THIS]] + +// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2D1C2Ev( +// CHECK: %[[T0:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 48) ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T0]] to ptr +// CHECK: store ptr %[[SIGNED_VTADDR]], ptr %[[THIS]] + +// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2D2C2Ev( +// CHECK: %[[SLOT0:.*]] = load ptr, ptr +// CHECK: %[[SIGN_VTADDR0:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 56) ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[T1:[0-9]+]] = inttoptr i64 %[[SIGN_VTADDR0]] to ptr +// CHECK: store ptr %[[T1]], ptr %[[SLOT0]] +// CHECK: %[[T3:[a-z0-9.]+]] = getelementptr inbounds i8, ptr %[[SLOT0]], i64 16 +// CHECK: %[[SIGN_VTADDR1:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 48) ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[T5:[0-9]+]] = inttoptr i64 %[[SIGN_VTADDR1]] to ptr +// CHECK: store ptr %[[T5]], ptr %[[T3]] + +// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2V0C2Ev( +// CHECK: %[[THIS1]] = load ptr, ptr % +// CHECK: %[[VTT:[a-z0-9]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = load ptr, ptr %[[VTT]] +// CHECK: %[[T1:[0-9]+]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[T2:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T1]], i32 2, i64 0) +// CHECK: %[[T3:[0-9]+]] = inttoptr i64 %[[T2]] to ptr +// CHECK: %[[VTADDR0:[0-9]+]] = ptrtoint ptr %[[T3]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 %[[VTADDR0]], i32 2, i64 0) +// CHECK: %[[SIGN_VTADDR0:[0-9]+]] = inttoptr i64 %[[T7]] to ptr +// CHECK: store ptr %[[SIGN_VTADDR0]], ptr %[[SLOT0]] +// CHECK: %[[T9:[0-9]+]] = getelementptr inbounds ptr, ptr %[[VTT]], i64 1 +// CHECK: %[[T10:[0-9]+]] = load ptr, ptr %[[T9]] +// CHECK: %[[T11:[0-9]+]] = ptrtoint ptr %[[T10]] to i64 +// CHECK: %[[T12:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T11]], i32 2, i64 0) +// CHECK: %[[T13:[0-9]+]] = inttoptr i64 %[[T12]] to ptr +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %[[THIS1]] +// CHECK: %[[T15:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T16:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T15]], i32 2, i64 0) +// CHECK: %[[T17:[0-9]+]] = inttoptr i64 %[[T16]] to ptr +// CHECK: %[[VBASE_OFFSET_PTR:[a-z.]+]] = getelementptr i8, ptr %[[T17]], i64 -24 +// CHECK: %[[VBASE_OFFSET:[a-z.]+]] = load i64, ptr %[[VBASE_OFFSET_PTR]] +// CHECK: %[[T20:[a-z.]+]] = getelementptr inbounds i8, ptr %[[THIS1]], i64 %[[VBASE_OFFSET]] +// CHECK: %[[VTADDR1:[0-9]+]] = ptrtoint ptr %[[T13]] to i64 +// CHECK: %[[T23:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 %[[VTADDR1]], i32 2, i64 0) +// CHECK: %[[SIGN_VTADDR1:[0-9]+]] = inttoptr i64 %[[T23]] to ptr +// CHECK: store ptr %[[SIGN_VTADDR1]], ptr %[[T20]] diff --git a/clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk.cpp b/clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk.cpp new file mode 100644 index 0000000000000..00b1cbd06e0f0 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk.cpp @@ -0,0 +1,309 @@ +// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-intrinsics -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -emit-llvm -O0 -disable-llvm-passes -o - | FileCheck --check-prefix=CHECK %s + +// The actual vtable construction + +// CHECK: @_ZTV1C = unnamed_addr constant { [5 x ptr], [11 x ptr] } { [5 x ptr] [ptr inttoptr (i64 8 to ptr), ptr null, ptr @_ZTI1C, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1CD1Ev, i32 0, i64 31214, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1CD0Ev, i32 0, i64 8507, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 0, i32 4))], +// CHECK-SAME: [11 x ptr] [ptr inttoptr (i64 -8 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1C, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 8)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1CD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 9)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1CD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 10))] }, align 8 + +// CHECK: @_ZTT1C = unnamed_addr constant [2 x ptr] [ptr ptrauth (ptr getelementptr inbounds inrange(-24, 16) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 0, i32 3), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 6), i32 2)], align 8 + +// CHECK: @_ZTV1D = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 8 to ptr), ptr null, ptr @_ZTI1D, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1DD1Ev, i32 0, i64 59423, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1DD0Ev, i32 0, i64 25900, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1gEv, i32 0, i64 59070, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1hEz, i32 0, i64 65100, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 6))], +// CHECK-SAME: [11 x ptr] [ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr null, ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1D, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 8)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1DD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 9)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1DD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 10))] }, align 8 + +// CHECK: @_ZTT1D = unnamed_addr constant [2 x ptr] [ptr ptrauth (ptr getelementptr inbounds inrange(-24, 32) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 3), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 6), i32 2)], align 8 + +// CHECK: @_ZTV1F = unnamed_addr constant { [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] } { [6 x ptr] [ptr inttoptr (i64 32 to ptr), ptr inttoptr (i64 16 to ptr), ptr null, ptr @_ZTI1F, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1FD1Ev, i32 0, i64 31214, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1FD0Ev, i32 0, i64 8507, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 0, i32 5))], [7 x ptr] [ptr inttoptr (i64 8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1F, +// CHECK-SAME: ptr ptrauth (ptr @_ZThn8_N1FD1Ev, i32 0, i64 59423, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZThn8_N1FD0Ev, i32 0, i64 25900, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1gEv, i32 0, i64 59070, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1hEz, i32 0, i64 65100, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 6))], [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr null, ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1F, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 8)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1FD1EvU11__vtptrauthILj0Lb0Lj62866E, i32 0, i64 2043, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 9)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1FD0EvU11__vtptrauthILj0Lb0Lj62866E, i32 0, i64 63674, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 10))], [11 x ptr] [ptr inttoptr (i64 -32 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -32 to ptr), ptr @_ZTI1F, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1fEv, i32 0, i64 28408, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1gEv, i32 0, i64 22926, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1hEz, i32 0, i64 9832, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 8)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1FD1Ev, i32 0, i64 5817, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 9)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1FD0Ev, i32 0, i64 26464, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 10))] }, align 8 + +// CHECK: @_ZTT1F = unnamed_addr constant [8 x ptr] [ptr ptrauth (ptr getelementptr inbounds inrange(-32, 16) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 0, i32 4), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-24, 16) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 0, i32 3), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 6), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-24, 32) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 3), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 6), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 6), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-24, 32) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 3), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 6), i32 2)], align 8 + +// CHECK: @_ZTV1G = unnamed_addr constant { [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] } { [6 x ptr] [ptr inttoptr (i64 16 to ptr), ptr inttoptr (i64 24 to ptr), ptr null, ptr @_ZTI1G, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1GD1Ev, i32 0, i64 31214, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1GD0Ev, i32 0, i64 8507, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 0, i32 5))], [7 x ptr] [ptr inttoptr (i64 16 to ptr), ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1G, +// CHECK-SAME: ptr ptrauth (ptr @_ZThn8_N1GD1Ev, i32 0, i64 59423, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZThn8_N1GD0Ev, i32 0, i64 25900, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1gEv, i32 0, i64 59070, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1hEz, i32 0, i64 65100, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 6))], [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1G, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1fEv, i32 0, i64 28408, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1gEv, i32 0, i64 22926, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1hEz, i32 0, i64 9832, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 8)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1GD1Ev, i32 0, i64 5817, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 9)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1GD0Ev, i32 0, i64 26464, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 10))], [11 x ptr] [ptr inttoptr (i64 -24 to ptr), ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr null, ptr inttoptr (i64 -24 to ptr), ptr @_ZTI1G, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 8)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1GD1EvU11__vtptrauthILj0Lb0Lj62866E, i32 0, i64 2043, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 9)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1GD0EvU11__vtptrauthILj0Lb0Lj62866E, i32 0, i64 63674, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 10))] }, align 8 + +// CHECK: @_ZTT1G = unnamed_addr constant [8 x ptr] [ptr ptrauth (ptr getelementptr inbounds inrange(-32, 16) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 0, i32 4), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-24, 16) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 0, i32 3), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 6), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-24, 32) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 3), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 6), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 6), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 6), i32 2), +// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-24, 32) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 3), i32 2)], align 8 + +// CHECK: @_ZTV1A = unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr @_ZTI1A, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 2)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1AD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1AD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 6))] }, align 8 + +// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE = external global [0 x ptr] + +// CHECK: @_ZTS1A = constant [3 x i8] c"1A\00", align 1 + +// CHECK: @_ZTI1A = constant { ptr, ptr } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), i32 2), ptr @_ZTS1A }, align 8 + +// CHECK: @_ZTVN10__cxxabiv121__vmi_class_type_infoE = external global [0 x ptr] + +// CHECK: @_ZTS1C = constant [3 x i8] c"1C\00", align 1 + +// CHECK: @_ZTVN10__cxxabiv120__si_class_type_infoE = external global [0 x ptr] + +// CHECK: @_ZTS1B = linkonce_odr hidden constant [3 x i8] c"1B\00", align 1 + +// CHECK: @_ZTI1B = linkonce_odr hidden constant { ptr, ptr, ptr } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv120__si_class_type_infoE, i64 2), i32 2), ptr inttoptr (i64 add (i64 ptrtoint (ptr @_ZTS1B to i64), i64 -9223372036854775808) to ptr), ptr @_ZTI1A }, align 8 + +// CHECK: @_ZTI1C = constant { ptr, ptr, i32, i32, ptr, i64 } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 2), i32 2), ptr @_ZTS1C, i32 0, i32 1, ptr @_ZTI1B, i64 -6141 }, align 8 + +// CHECK: @_ZTS1D = constant [3 x i8] c"1D\00", align 1 + +// CHECK: @_ZTI1D = constant { ptr, ptr, i32, i32, ptr, i64 } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 2), i32 2), ptr @_ZTS1D, i32 0, i32 1, ptr @_ZTI1B, i64 -6141 }, align 8 + +// CHECK: @_ZTV1E = unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr @_ZTI1E, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1fEv, i32 0, i64 28408, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 2)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1gEv, i32 0, i64 22926, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1hEz, i32 0, i64 9832, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1ED1Ev, i32 0, i64 5817, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1ED0Ev, i32 0, i64 26464, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 6))] }, align 8 + +// CHECK: @_ZTS1E = constant [3 x i8] c"1E\00", align 1 + +// CHECK: @_ZTI1E = constant { ptr, ptr } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), i32 2), ptr @_ZTS1E }, align 8 + +// CHECK: @_ZTC1F0_1C = unnamed_addr constant { [5 x ptr], [11 x ptr] } { [5 x ptr] [ptr inttoptr (i64 16 to ptr), ptr null, ptr @_ZTI1C, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1CD1Ev, i32 0, i64 31214, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1CD0Ev, i32 0, i64 8507, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 0, i32 4))], [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1C, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 8)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1CD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 9)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1CD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 10))] }, align 8 + +// CHECK: @_ZTC1F8_1D = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 8 to ptr), ptr null, ptr @_ZTI1D, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1DD1Ev, i32 0, i64 59423, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1DD0Ev, i32 0, i64 25900, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1gEv, i32 0, i64 59070, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1hEz, i32 0, i64 65100, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 6))], [11 x ptr] [ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr null, ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1D, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 8)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1DD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 9)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1DD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 10))] }, align 8 + +// CHECK: @_ZTS1F = constant [3 x i8] c"1F\00", align 1 + +// CHECK: @_ZTI1F = constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64, ptr, i64 } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 2), i32 2), ptr @_ZTS1F, i32 3, i32 3, ptr @_ZTI1C, i64 2, ptr @_ZTI1D, i64 2050, ptr @_ZTI1E, i64 -8189 }, align 8 + +// CHECK: @_ZTC1G0_1C = unnamed_addr constant { [5 x ptr], [11 x ptr] } { [5 x ptr] [ptr inttoptr (i64 24 to ptr), ptr null, ptr @_ZTI1C, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1CD1Ev, i32 0, i64 31214, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1CD0Ev, i32 0, i64 8507, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 0, i32 4))], [11 x ptr] [ptr inttoptr (i64 -24 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -24 to ptr), ptr @_ZTI1C, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 8)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1CD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 9)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1CD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 10))] }, align 8 + +// CHECK: @_ZTC1G8_1D = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 16 to ptr), ptr null, ptr @_ZTI1D, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1DD1Ev, i32 0, i64 59423, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1DD0Ev, i32 0, i64 25900, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1gEv, i32 0, i64 59070, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1hEz, i32 0, i64 65100, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 6))], [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr null, ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1D, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 6)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 7)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 8)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1DD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 9)), +// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1DD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 10))] }, align 8 + +// CHECK: @_ZTS1G = constant [3 x i8] c"1G\00", align 1 + +// CHECK: @_ZTI1G = constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64, ptr, i64 } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 2), i32 2), ptr @_ZTS1G, i32 3, i32 3, ptr @_ZTI1E, i64 -8189, ptr @_ZTI1C, i64 2, ptr @_ZTI1D, i64 2050 }, align 8 + +// CHECK: @_ZTV1B = linkonce_odr unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr @_ZTI1B, +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 2)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 4)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1BD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 5)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN1BD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 6))] }, align 8 + +extern "C" int printf(const char *format, ...); + +class A { +public: + A() {} + virtual int f(); + virtual int g(); + virtual int h(...); + virtual ~A() {} + +public: + bool necessary_field; +}; + +class B : public A { +public: + B() : A() {} + virtual ~B() {} +}; + +class C : public virtual B { +public: + C() : B() {} + ~C(); +}; + +class D : public virtual B { +public: + D() : B() {} + ~D(); + virtual int g(); + virtual int h(...); +}; + +class E { +public: + virtual int f(); + virtual int g(); + virtual int h(...); + virtual ~E(){}; +}; + +class F : public C, public D, public virtual E { + ~F(); +}; + +class G : public virtual E, public C, public D { + ~G(); +}; + +C::~C() {} +D::~D() {} +F::~F() {} +G::~G() {} +int E::f() { return 1; } +int A::f() { return 0; } +int E::g() { return 1; } +int A::g() { return 0; } +int D::g() { return 0; } + +int E::h(...) { return 1; } +int A::h(...) { return 0; } +int D::h(...) { return 0; } + +int main() { + A *ans = new C(); + delete ans; + + B *b = new D(); + b->f(); + b->g(); + b->h(1,2,3); + b = new C(); + b->f(); + b->h(1,2,3); + b = new C(); + b->f(); + b->h(1,2,3); + b = new F(); + b->f(); + b->g(); + b->h(1,2,3); + + ans = new B(); + delete ans; + + ans = new F(); + ans->f(); + ans->g(); + ans->h(1,2,3); + delete ans; + + E *e = new F(); + e->f(); + e->g(); + e->h(1,2,3); + delete e; + e = new G(); + e->f(); + e->g(); + e->h(1,2,3); + delete e; +} + +// And check the thunks +// CHECK: ptr @_ZTv0_n48_N1CD1Ev(ptr noundef %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866) + +// CHECK: void @_ZTv0_n48_N1CD0Ev(ptr noundef %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866) + +// CHECK: ptr @_ZTv0_n48_N1DD1Ev(ptr noundef %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866) + +// CHECK: void @_ZTv0_n48_N1DD0Ev(ptr noundef %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866) + +// CHECK: void @_ZTv0_n48_N1FD0EvU11__vtptrauthILj0Lb0Lj62866E(ptr noundef %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866) + +// CHECK: void @_ZTv0_n48_N1FD0Ev(ptr noundef %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 12810) + +// CHECK: void @_ZTv0_n48_N1GD0Ev(ptr noundef %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 12810) + +// CHECK: void @_ZTv0_n48_N1GD0EvU11__vtptrauthILj0Lb0Lj62866E(ptr noundef %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866) diff --git a/clang/test/CodeGenCXX/ubsan-vtable-checks.cpp b/clang/test/CodeGenCXX/ubsan-vtable-checks.cpp index c70316214cf66..72c59cb41c34b 100644 --- a/clang/test/CodeGenCXX/ubsan-vtable-checks.cpp +++ b/clang/test/CodeGenCXX/ubsan-vtable-checks.cpp @@ -2,6 +2,7 @@ // RUN: %clang_cc1 -std=c++11 -triple x86_64-windows -emit-llvm -fsanitize=null %s -o - | FileCheck %s --check-prefix=CHECK-NULL --check-prefix=MSABI // RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux -emit-llvm -fsanitize=null,vptr %s -o - | FileCheck %s --check-prefix=CHECK-VPTR --check-prefix=ITANIUM // RUN: %clang_cc1 -std=c++11 -triple x86_64-windows -emit-llvm -fsanitize=null,vptr %s -o - | FileCheck %s --check-prefix=CHECK-VPTR --check-prefix=MSABI --check-prefix=CHECK-VPTR-MS +// RUN: %clang_cc1 -std=c++11 -triple arm64e-ios-13 -emit-llvm -fptrauth-intrinsics -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -fptrauth-vtable-pointer-address-discrimination -fsanitize=null,vptr %s -o - | FileCheck %s --check-prefix=CHECK-VPTR --check-prefix=ITANIUM --check-prefix=CHECK-PTRAUTH struct T { virtual ~T() {} virtual int v() { return 1; } @@ -26,18 +27,49 @@ int get_v(T* t) { // CHECK-NULL: call void @__ubsan_handle_type_mismatch_v1_abort // Second, we check that vtable is actually loaded once the type check is done. // CHECK-NULL: load ptr, ptr {{.*}} + + // CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-PTRAUTH: [[STRIPPED_VTABLE:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[CAST_VTABLE]], i32 0), !nosanitize !2 + // CHECK-PTRAUTH: [[STRIPPED_PTR:%.*]] = inttoptr i64 [[STRIPPED_VTABLE]] to ptr + // CHECK-PTRAUTH: [[STRIPPED_INT:%.*]] = ptrtoint ptr [[STRIPPED_PTR]] to i64 + // Make sure authed vtable pointer feeds into hashing + // CHECK-PTRAUTH: {{%.*}} = mul i64 [[STRIPPED_INT]], {{.*}} + + // Verify that we authenticate for the actual vcall + // CHECK-PTRAUTH: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 17113) + // CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr %vtable2 to i64 + // CHECK-PTRAUTH: [[AUTHED_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VTABLE]], i32 2, i64 [[BLENDED]]) + // CHECK-PTRAUTH: [[AUTHED_PTR:%.*]] = inttoptr i64 [[AUTHED_INT]] to ptr + // CHECK-PTRAUTH: {{%.*}} = getelementptr inbounds ptr, ptr [[AUTHED_PTR]], i64 2 return t->v(); } // ITANIUM: define{{.*}} void @_Z9delete_itP1T // MSABI: define dso_local void @"?delete_it void delete_it(T *t) { - // First, we check that vtable is not loaded before a type check. // CHECK-VPTR-NOT: load {{.*}} (ptr{{.*}})**, {{.*}} (ptr{{.*}})*** // CHECK-VPTR: br i1 {{.*}} label %{{.*}} - // CHECK-VPTR: call void @__ubsan_handle_dynamic_type_cache_miss_abort + // CHECK-VPTR: call void @__ubsan_handle_type_mismatch_v1_abort // Second, we check that vtable is actually loaded once the type check is done. // CHECK-VPTR: load ptr, ptr {{.*}} + + // First, we check that vtable is not loaded before a type check. + // CHECK-PTRAUTH: ptrtoint ptr {{%.*}} to i64 + // CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr [[VTABLE:%.*]] to i64 + // CHECK-PTRAUTH: [[STRIPPED_VTABLE:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[CAST_VTABLE]], i32 0) + // CHECK-PTRAUTH: [[STRIPPED_PTR:%.*]] = inttoptr i64 [[STRIPPED_VTABLE]] to ptr + // CHECK-PTRAUTH: [[STRIPPED_INT:%.*]] = ptrtoint ptr [[STRIPPED_PTR]] to i64 + // CHECK-PTRAUTH: {{%.*}} = mul i64 [[STRIPPED_INT]], {{.*}} + // CHECK-PTRAUTH: call void @__ubsan_handle_dynamic_type_cache_miss_abort( + // Second, we check that vtable is actually loaded once the type check is done. + // ptrauth for the virtual function load + // CHECK-PTRAUTH: [[VTABLE2:%.*]] = load ptr, ptr {{.*}} + // CHECK-PTRAUTH: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 %{{.*}}, i64 17113) + // CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr [[VTABLE2]] to i64 + // CHECK-PTRAUTH: [[AUTHED_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VTABLE]], i32 2, i64 [[BLENDED]]) + // CHECK-PTRAUTH: [[AUTHED_PTR:%.*]] = inttoptr i64 [[AUTHED_INT]] to ptr + // CHECK-PTRAUTH: getelementptr inbounds ptr, ptr + // CHECK-PTRAUTH {{%.*}} = getelementptr inbounds ptr, ptr [[AUTHED_PTR]], i64 1 delete t; } @@ -47,7 +79,19 @@ U* dyncast(T *t) { // First, we check that dynamic_cast is not called before a type check. // CHECK-VPTR-NOT: call ptr @__{{dynamic_cast|RTDynamicCast}} // CHECK-VPTR: br i1 {{.*}} label %{{.*}} + // CHECK-PTRAUTH: [[V0:%.*]] = ptrtoint ptr {{%.*}} to i64 + // CHECK-PTRAUTH: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[V0]], i64 17113) + // CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr {{%.*}} to i64 + // CHECK-PTRAUTH: [[STRIPPED_VTABLE:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[CAST_VTABLE]], i32 0) + // CHECK-PTRAUTH: [[STRIPPED_PTR:%.*]] = inttoptr i64 [[STRIPPED_VTABLE]] to ptr + // CHECK-PTRAUTH: [[STRIPPED_INT:%.*]] = ptrtoint ptr [[STRIPPED_PTR]] to i64 + // CHECK-PTRAUTH: {{%.*}} = mul i64 [[STRIPPED_INT]], {{.*}} // CHECK-VPTR: call void @__ubsan_handle_dynamic_type_cache_miss_abort + // CHECK-PTRAUTH: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 17113) + // CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr %vtable1 to i64 + // CHECK-PTRAUTH: [[AUTHED_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VTABLE]], i32 2, i64 [[BLENDED]]) + // CHECK-PTRAUTH: [[AUTHED_PTR:%.*]] = inttoptr i64 [[AUTHED_INT]] to ptr + // CHECK-PTRAUTH: {{%.*}} = load volatile i8, ptr [[AUTHED_PTR]], align 8 // Second, we check that dynamic_cast is actually called once the type check is done. // CHECK-VPTR: call ptr @__{{dynamic_cast|RTDynamicCast}} return dynamic_cast(t); diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 99732694f72a5..28df04c5e33ef 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -201,6 +201,7 @@ // CHECK-NEXT: Uninitialized (SubjectMatchRule_variable_is_local) // CHECK-NEXT: UnsafeBufferUsage (SubjectMatchRule_function) // CHECK-NEXT: UseHandle (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: VTablePointerAuthentication (SubjectMatchRule_record) // CHECK-NEXT: VecReturn (SubjectMatchRule_record) // CHECK-NEXT: VecTypeHint (SubjectMatchRule_function) // CHECK-NEXT: WarnUnused (SubjectMatchRule_record) diff --git a/clang/test/SemaCXX/ptrauth-incomplete-virtual-member-function-return-arg-type.cpp b/clang/test/SemaCXX/ptrauth-incomplete-virtual-member-function-return-arg-type.cpp new file mode 100644 index 0000000000000..41bbba0e832fd --- /dev/null +++ b/clang/test/SemaCXX/ptrauth-incomplete-virtual-member-function-return-arg-type.cpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++17 -Wno-vla -fsyntax-only -verify -fptrauth-intrinsics -fptrauth-calls %s + +struct Incomplete0; // expected-note 3 {{forward declaration of 'Incomplete0'}} + +template +struct Incomplete1; // expected-note {{template is declared here}} + +struct Complete0 { +}; + +template +struct Complete1 { +}; + +struct S { + virtual int foo(); + virtual Incomplete0 virtual0(); // expected-note 2 {{'Incomplete0' is incomplete}} + virtual void virtual1(Incomplete1); // expected-note {{'Incomplete1' is incomplete}} + virtual Complete0 virtual2(); + virtual Complete1 virtual3(); + Incomplete0 nonvirtual0(); + template + void m0() { + (void)&S::virtual0; // expected-error {{incomplete type 'Incomplete0'}} expected-note {{cannot take an address of a virtual}} + } +}; + +template +struct S2 { + virtual Incomplete0 virtual0() noexcept(T); // expected-note {{'Incomplete0' is incomplete}} + + void m0() { + (void)&S2::virtual0; + } + + void m1() { + (void)&S2::virtual0; // expected-error {{incomplete type 'Incomplete0'}} expected-note {{cannot take an address of a virtual}} + } +}; + +void test_incomplete_virtual_member_function_return_arg_type() { + (void)&S::virtual0; // expected-error {{incomplete type 'Incomplete0}} expected-note {{cannot take an address of a virtual member function}} + (void)&S::virtual1; // expected-error {{implicit instantiation of undefined template 'Incomplete1'}} expected-note {{cannot take an address of a virtual member function}} + (void)&S::virtual2; + (void)&S::virtual3; + (void)&S::nonvirtual0; + int s = sizeof(&S::virtual0); + S2().m1(); // expected-note {{in instantiation of}} +} + diff --git a/clang/test/SemaCXX/vtable_pointer_authentication_attribute.cpp b/clang/test/SemaCXX/vtable_pointer_authentication_attribute.cpp new file mode 100644 index 0000000000000..3a3386196fbfa --- /dev/null +++ b/clang/test/SemaCXX/vtable_pointer_authentication_attribute.cpp @@ -0,0 +1,225 @@ +// RUN: %clang_cc1 -fsyntax-only -triple arm64-apple-ios -verify -fptrauth-calls -std=c++2a %s + +namespace basic { + +#define authenticated(a, b, c...) [[clang::ptrauth_vtable_pointer(a, b, c)]] + +// Basic sanity tests +#define TEST_AUTH(name, auth...) \ + struct [[clang::ptrauth_vtable_pointer(auth)]] name { \ + virtual ~name() {} \ + } + +TEST_AUTH(NoParams); +// expected-error@-1{{'ptrauth_vtable_pointer' attribute takes at least 3 arguments}} +TEST_AUTH(NoAuth, no_authentication, default_address_discrimination, default_extra_discrimination); +TEST_AUTH(InvalidKey, wat, default_address_discrimination, default_extra_discrimination); +// expected-error@-1{{invalid authentication key 'wat'}} +TEST_AUTH(InvalidAddressDiscrimination, no_authentication, wat, default_extra_discrimination); +// expected-error@-1{{invalid address discrimination mode 'wat'}} +TEST_AUTH(InvalidExtraDiscrimination, no_authentication, default_address_discrimination, wat); +// expected-error@-1{{invalid extra discrimination selection 'wat'}} +TEST_AUTH(InvalidNoCustomDiscrimination, no_authentication, default_address_discrimination, custom_discrimination); +// expected-error@-1{{missing custom discrimination}} +TEST_AUTH(InvalidCustomDiscrimination, no_authentication, default_address_discrimination, custom_discrimination, wat); +// expected-error@-1{{invalid custom discrimination}} +TEST_AUTH(Default, default_key, default_address_discrimination, default_extra_discrimination); +TEST_AUTH(InvalidDefaultExtra, default_key, default_address_discrimination, default_extra_discrimination, 1); +// expected-error@-1{{'ptrauth_vtable_pointer' attribute takes no more than 3 arguments}} +TEST_AUTH(ProcessDependentKey, process_dependent, default_address_discrimination, default_extra_discrimination); +TEST_AUTH(ProcessIndependentKey, process_independent, default_address_discrimination, default_extra_discrimination); +TEST_AUTH(DefaultAddressDiscrimination, process_independent, default_address_discrimination, default_extra_discrimination); +TEST_AUTH(NoAddressDiscrimination, process_independent, no_address_discrimination, default_extra_discrimination); +TEST_AUTH(AddressDiscrimination, process_independent, address_discrimination, default_extra_discrimination); +TEST_AUTH(DefaultExtraDiscrimination, process_independent, default_address_discrimination, default_extra_discrimination); +TEST_AUTH(NoExtraDiscrimination, process_independent, default_address_discrimination, no_extra_discrimination); +TEST_AUTH(TypeExtraDiscrimination, process_independent, default_address_discrimination, type_discrimination); +TEST_AUTH(InvalidCustomExtraDiscrimination, process_independent, default_address_discrimination, custom_discrimination); +// expected-error@-1{{missing custom discrimination}} +TEST_AUTH(ValidCustomExtraDiscrimination, process_independent, default_address_discrimination, custom_discrimination, 1); + +// Basic valid authentication configuration +#define generic_authenticated \ + authenticated(process_independent, address_discrimination, type_discrimination) + +struct generic_authenticated ForwardDecl; + +struct generic_authenticated generic_authenticated InvalidDuplicateAttribute { + // expected-error@-1{{multiple vtable pointer authentication policies on 'InvalidDuplicateAttribute'}} + virtual ~InvalidDuplicateAttribute(){}; +}; +struct generic_authenticated ValidPolymorphic { + virtual ~ValidPolymorphic(){}; +}; +struct generic_authenticated InvalidMonomorphic { // expected-error{{cannot set vtable pointer authentication on monomorphic type 'InvalidMonomorphic'}} +}; +struct ValidMonomorphic { +}; + +struct ValidSubclass : ValidPolymorphic {}; +struct generic_authenticated InvalidSubclass : ValidPolymorphic {}; // expected-error{{cannot set vtable pointer authentication on 'InvalidSubclass' which is a subclass of polymorphic type 'ValidPolymorphic'}} + +// Awful template time +template +struct generic_authenticated ExplicitlyAuthedMonomorphicTemplateClass : T {}; +// expected-error@-1{{cannot set vtable pointer authentication on 'ExplicitlyAuthedMonomorphicTemplateClass' which is a subclass of polymorphic type 'ValidPolymorphic'}} +// expected-error@-2{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedMonomorphicTemplateClass'}} +template +struct generic_authenticated ExplicitlyAuthedPolymorphicTemplateClass : T { // expected-error{{cannot set vtable pointer authentication on 'ExplicitlyAuthedPolymorphicTemplateClass' which is a subclass of polymorphic type 'ValidPolymorphic'}} + virtual ~ExplicitlyAuthedPolymorphicTemplateClass(){}; +}; +template +struct UnauthedMonomorphicTemplateClass : T {}; +template +struct UnauthedPolymorphicTemplateClass : T { + virtual ~UnauthedPolymorphicTemplateClass(){}; +}; + +ExplicitlyAuthedMonomorphicTemplateClass test1; +// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedMonomorphicTemplateClass' requested here}} +ExplicitlyAuthedMonomorphicTemplateClass test2; +// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedMonomorphicTemplateClass' requested here}} +ExplicitlyAuthedPolymorphicTemplateClass test3; +// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedPolymorphicTemplateClass' requested here}} +ExplicitlyAuthedPolymorphicTemplateClass test4; + +UnauthedMonomorphicTemplateClass test5; +UnauthedMonomorphicTemplateClass test6; +UnauthedPolymorphicTemplateClass test7; +UnauthedPolymorphicTemplateClass test8; + +// Just use a different policy from the generic macro to verify we won't complain +// about the insanity +struct authenticated(process_independent, no_address_discrimination, type_discrimination) SecondAuthenticatedPolymorphic { + virtual ~SecondAuthenticatedPolymorphic(){}; +}; +struct UnauthenticatedPolymorphic { + virtual ~UnauthenticatedPolymorphic(){}; +}; + +struct MultipleParents1 : ValidPolymorphic, SecondAuthenticatedPolymorphic, UnauthenticatedPolymorphic {}; +struct MultipleParents2 : UnauthenticatedPolymorphic, ValidPolymorphic, SecondAuthenticatedPolymorphic {}; +struct generic_authenticated InvalidMultipleParents : UnauthenticatedPolymorphic, ValidPolymorphic, SecondAuthenticatedPolymorphic {}; +// expected-error@-1{{cannot set vtable pointer authentication on 'InvalidMultipleParents' which is a subclass of polymorphic type 'UnauthenticatedPolymorphic'}} + +template +struct generic_authenticated ExplicitlyAuthedPolymorphicTemplateClassNoBase { + virtual ~ExplicitlyAuthedPolymorphicTemplateClassNoBase(); +}; + +ExplicitlyAuthedPolymorphicTemplateClassNoBase v; + +struct ValidSubclassOfTemplate : ExplicitlyAuthedPolymorphicTemplateClassNoBase { +}; + +struct generic_authenticated InvalidSubclassOfTemplate : ExplicitlyAuthedPolymorphicTemplateClassNoBase { + // expected-error@-1{{cannot set vtable pointer authentication on 'InvalidSubclassOfTemplate' which is a subclass of polymorphic type 'ExplicitlyAuthedPolymorphicTemplateClassNoBase'}} +}; + +template +struct generic_authenticated ExplicitlyAuthedMonomorphicTemplateClassNoBase { + // expected-error@-1{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedMonomorphicTemplateClassNoBase'}} + // expected-error@-2{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedMonomorphicTemplateClassNoBase'}} +}; + +ExplicitlyAuthedMonomorphicTemplateClassNoBase X; +// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedMonomorphicTemplateClassNoBase' requested here}} + +template +struct generic_authenticated ExplicitlyAuthedTemplateClassValidBase : ValidMonomorphic { + // expected-error@-1{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedTemplateClassValidBase'}} + // expected-error@-2{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedTemplateClassValidBase'}} +}; + +ExplicitlyAuthedTemplateClassValidBase Y; +// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedTemplateClassValidBase' requested here}} + +template +struct generic_authenticated ExplicitlyAuthedTemplateClassInvalidBase : ValidPolymorphic { + // expected-error@-1{{cannot set vtable pointer authentication on 'ExplicitlyAuthedTemplateClassInvalidBase' which is a subclass of polymorphic type 'ValidPolymorphic'}} + // expected-error@-2{{cannot set vtable pointer authentication on 'ExplicitlyAuthedTemplateClassInvalidBase' which is a subclass of polymorphic type 'ValidPolymorphic'}} +}; + +ExplicitlyAuthedTemplateClassInvalidBase Z; +// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedTemplateClassInvalidBase' requested here}} + +template +class generic_authenticated TestPolymorphicTemplateSpecialization; + +template <> +class TestPolymorphicTemplateSpecialization { + MissingDecl *zl; + // expected-error@-1 {{unknown type name 'MissingDecl'}} +public: + virtual ~TestPolymorphicTemplateSpecialization(); +}; +template +class generic_authenticated TestPolymorphicTemplateSpecialization +// expected-error@-1 {{cannot set vtable pointer authentication on monomorphic type 'TestPolymorphicTemplateSpecialization'}} +// expected-error@-2 {{cannot set vtable pointer authentication on monomorphic type 'TestPolymorphicTemplateSpecialization'}} +{ +}; + +TestPolymorphicTemplateSpecialization b; +TestPolymorphicTemplateSpecialization b2; +// expected-note@-1 {{in instantiation of template class 'basic::TestPolymorphicTemplateSpecialization' requested here}} + +template class generic_authenticated TestMonomorphic {}; +// expected-error@-1 {{cannot set vtable pointer authentication on monomorphic type 'TestMonomorphic'}} +// expected-error@-2 {{cannot set vtable pointer authentication on monomorphic type 'TestMonomorphic'}} + +template <> class generic_authenticated TestMonomorphic { +public: + virtual ~TestMonomorphic(); +}; + +struct TestMonomorphicSubclass : TestMonomorphic { +}; +template struct generic_authenticated TestMonomorphicSubclass2 : TestMonomorphic { + // expected-error@-1 {{cannot set vtable pointer authentication on 'TestMonomorphicSubclass2' which is a subclass of polymorphic type 'TestMonomorphic'}} + // expected-error@-2 {{cannot set vtable pointer authentication on monomorphic type 'TestMonomorphicSubclass2'}} + // expected-note@-3 {{in instantiation of template class 'basic::TestMonomorphic' requested here}} +}; + +TestMonomorphicSubclass tms_1; +TestMonomorphicSubclass2 tms2_1; +// expected-note@-1 {{in instantiation of template class 'basic::TestMonomorphicSubclass2' requested here}} +TestMonomorphicSubclass2 tms2_2; +// expected-note@-1 {{in instantiation of template class 'basic::TestMonomorphicSubclass2' requested here}} +// expected-note@-2 {{in instantiation of template class 'basic::TestMonomorphicSubclass2' requested here}} + +template +class generic_authenticated dependent_type { + // expected-error@-1 {{cannot set vtable pointer authentication on monomorphic type 'dependent_type'}} + static constexpr unsigned small_object_size = 1; + char _model[small_object_size]; +}; + +template +class generic_authenticated dependent_type2 : public T... { + // expected-error@-1 {{cannot set vtable pointer authentication on 'dependent_type2' which is a subclass of polymorphic type 'Foo'}} + static constexpr unsigned small_object_size = 1; + char _model[small_object_size]; +}; + +struct Foo { + virtual ~Foo(); +}; + +dependent_type2 thing; +// expected-note@-1 {{in instantiation of template class 'basic::dependent_type2' requested here}} + +template +class task; +template struct alignedthing { + char buffer[align]; +}; + +template +class generic_authenticated task { + // expected-error@-1 {{cannot set vtable pointer authentication on monomorphic type 'task'}} + static constexpr __SIZE_TYPE__ small_object_size = 256; + alignedthing _model; +}; + +} // namespace basic diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 307334a65b1fe..7bf6091e63d18 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -2578,6 +2578,32 @@ static void emitClangAttrIdentifierArgList(RecordKeeper &Records, raw_ostream &O OS << "#endif // CLANG_ATTR_IDENTIFIER_ARG_LIST\n\n"; } +// Emits the indexed-argument-is-identifier property for attributes. +static void emitClangAttrStrictIdentifierArgAtIndexList(RecordKeeper &Records, + raw_ostream &OS) { + OS << "#if defined(CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST)\n"; + std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); + + for (const auto *Attr : Attrs) { + if (!Attr->getValueAsBit("StrictEnumParameters")) + continue; + // Determine whether the first argument is an identifier. + std::vector Args = Attr->getValueAsListOfDefs("Args"); + uint64_t enumAtIndex = 0; + for (size_t i = 0; i < Args.size(); i++) { + enumAtIndex |= ((uint64_t)isIdentifierArgument(Args[0])) << i; + } + if (!enumAtIndex) + continue; + + // All these spellings take an identifier argument. + forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.name() << "\", " << enumAtIndex << "ull)\n"; + }); + } + OS << "#endif // CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST\n\n"; +} + static bool keywordThisIsaIdentifierInArgument(const Record *Arg) { return !Arg->getSuperClasses().empty() && llvm::StringSwitch( @@ -4959,6 +4985,7 @@ void EmitClangAttrParserStringSwitches(RecordKeeper &Records, raw_ostream &OS) { emitClangAttrTypeArgList(Records, OS); emitClangAttrLateParsedList(Records, OS); emitClangAttrLateParsedExperimentalList(Records, OS); + emitClangAttrStrictIdentifierArgAtIndexList(Records, OS); } void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records,