86 changes: 86 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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/Triple.h"
#include <algorithm>
Expand Down Expand Up @@ -3088,6 +3089,17 @@ QualType ASTContext::removeAddrSpaceQualType(QualType T) const {
return QualType(TypeNode, Quals.getFastQualifiers());
}

uint16_t ASTContext::getPointerAuthVTablePointerDiscriminator(
const CXXRecordDecl *record) {
assert(record->isPolymorphic() &&
"Attempted to get vtable pointer discriminator on a monomorphic type");
std::unique_ptr<MangleContext> MC(createMangleContext());
SmallString<256> Str;
llvm::raw_svector_ostream Out(Str);
MC->mangleCXXVTable(record, Out);
return llvm::getPointerAuthStableSipHash16(Str.c_str());
}

QualType ASTContext::getObjCGCQualType(QualType T,
Qualifiers::GC GCAttr) const {
QualType CanT = getCanonicalType(T);
Expand Down Expand Up @@ -13812,3 +13824,77 @@ 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<CXXMethodDecl>(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<MangleContext> mangler(createMangleContext());
llvm::StringMap<llvm::SmallVector<std::string, 2>> thunks;
auto vtableContext = getVTableContext();
if (const auto *thunkInfos = vtableContext->getThunkInfo(virtualMethodDecl)) {
auto destructor = dyn_cast<CXXDestructorDecl>(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;
}
77 changes: 70 additions & 7 deletions clang/lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 qualifier);

private:

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -7037,8 +7036,64 @@ void ItaniumMangleContextImpl::mangleCXXDtorComdat(const CXXDestructorDecl *D,
Mangler.mangle(GlobalDecl(D, Dtor_Comdat));
}

static void mangleOverrideDiscrimination(CXXNameMangler &mangler,
ASTContext &context,
const ThunkInfo &thunk) {
auto &langOpts = context.getLangOpts();
auto thisType = thunk.ThisType;
auto thisRecord = thisType->getPointeeCXXRecordDecl();
auto ptrauthClassRecord = context.baseForVTableAuthentication(thisRecord);
unsigned typedDiscriminator =
context.getPointerAuthVTablePointerDiscriminator(thisRecord);
mangler.mangleVendorQualifier("__vtptrauth");
auto &manglerStream = mangler.getStream();
manglerStream << "I";
if (auto explicitAuth =
ptrauthClassRecord->getAttr<VTablePointerAuthenticationAttr>()) {
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) {
// <special-name> ::= T <call-offset> <base encoding>
// # base is the nominal target function of thunk
Expand All @@ -7064,21 +7119,29 @@ 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) {
// <special-name> ::= T <call-offset> <base encoding>
// # 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.
Expand Down
23 changes: 17 additions & 6 deletions clang/lib/AST/Mangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,10 +514,20 @@ class ASTNameGenerator::Implementation {
}
} else if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(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;
Expand Down Expand Up @@ -570,11 +580,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);
Expand Down
23 changes: 16 additions & 7 deletions clang/lib/AST/MicrosoftMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,17 +158,18 @@ class MicrosoftMangleContextImpl : public MicrosoftMangleContext {
void mangleVirtualMemPtrThunk(const CXXMethodDecl *MD,
const MethodVFTableLocation &ML,
raw_ostream &Out) 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 mangleCXXVFTable(const CXXRecordDecl *Derived,
ArrayRef<const CXXRecordDecl *> BasePath,
raw_ostream &Out) override;
void mangleCXXVBTable(const CXXRecordDecl *Derived,
ArrayRef<const CXXRecordDecl *> BasePath,
raw_ostream &Out) override;

void mangleCXXVTable(const CXXRecordDecl *, raw_ostream &) override;
void mangleCXXVirtualDisplacementMap(const CXXRecordDecl *SrcRD,
const CXXRecordDecl *DstRD,
raw_ostream &Out) override;
Expand Down Expand Up @@ -3652,7 +3653,7 @@ void MicrosoftMangleContextImpl::mangleVirtualMemPtrThunk(
}

void MicrosoftMangleContextImpl::mangleThunk(const CXXMethodDecl *MD,
const ThunkInfo &Thunk,
const ThunkInfo &Thunk, bool,
raw_ostream &Out) {
msvc_hashing_ostream MHO(Out);
MicrosoftCXXNameMangler Mangler(*this, MHO);
Expand All @@ -3674,9 +3675,10 @@ void MicrosoftMangleContextImpl::mangleThunk(const CXXMethodDecl *MD,
DeclForFPT->getType()->castAs<FunctionProtoType>(), 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, 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.
Expand All @@ -3685,6 +3687,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<FunctionProtoType>(), DD);
}
Expand All @@ -3709,6 +3712,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<const CXXRecordDecl *> BasePath,
raw_ostream &Out) {
Expand Down
107 changes: 101 additions & 6 deletions clang/lib/AST/VTableBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
auto method = VTables.findOriginalMethodInMap(MD);
VTableThunks[Idx].Method = method;
VTableThunks[Idx].ThisType = method->getThisType().getTypePtr();
}
VTableThunks[Idx].This = ThisAdjustment;
};

SetThisAdjustmentThunk(VTableIndex);

if (isa<CXXDestructorDecl>(MD)) {
// Add an adjustment for the deleting destructor as well.
VTableThunks[VTableIndex + 1].This = ThisAdjustment;
SetThisAdjustmentThunk(VTableIndex + 1);
}
}

Expand Down Expand Up @@ -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) &&
Expand Down Expand Up @@ -1546,8 +1578,9 @@ void ItaniumVTableBuilder::AddMethods(
ComputeReturnAdjustment(ReturnAdjustmentOffset);

// This is a virtual thunk for the most derived class, add it.
AddThunk(Overrider.Method,
ThunkInfo(ThisAdjustment, ReturnAdjustment));
AddThunk(
Overrider.Method,
ThunkInfo(ThisAdjustment, ReturnAdjustment, OverriddenMD->getThisType().getTypePtr()));
}
}

Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -1890,11 +1932,32 @@ void ItaniumVTableBuilder::LayoutVTablesForVirtualBases(
}
}

static void printThunkMethod(const ThunkInfo &Info, raw_ostream &Out) {
if (Info.Method) {
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);
Expand Down Expand Up @@ -1978,6 +2041,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
}

Out << ']';
printThunkMethod(Thunk, Out);
}

// If this function pointer has a 'this' pointer adjustment, dump it.
Expand All @@ -1991,6 +2055,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
}

Out << ']';
printThunkMethod(Thunk, Out);
}
}

Expand Down Expand Up @@ -2027,6 +2092,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {

Out << ']';
}
printThunkMethod(Thunk, Out);
}

break;
Expand Down Expand Up @@ -2125,7 +2191,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);
});

Expand Down Expand Up @@ -2314,6 +2379,35 @@ ItaniumVTableContext::getVirtualBaseOffsetOffset(const CXXRecordDecl *RD,
return I->second;
}

GlobalDecl ItaniumVTableContext::findOriginalMethod(GlobalDecl GD) {
const auto *MD = cast<CXXMethodDecl>(GD.getDecl());
computeVTableRelatedInformation(MD->getParent());
const auto *OriginalMD = findOriginalMethodInMap(MD);

if (const auto *DD = dyn_cast<CXXDestructorDecl>(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<VTableLayout>
CreateVTableLayout(const ItaniumVTableBuilder &Builder) {
SmallVector<VTableLayout::VTableThunkTy, 1>
Expand Down Expand Up @@ -3094,9 +3188,10 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
ReturnAdjustmentOffset.VirtualBase);
}
}

auto thisType = (OverriddenMD ? OverriddenMD : MD)->getThisType().getTypePtr();
AddMethod(FinalOverriderMD,
ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment,
thisType,
ForceReturnAdjustmentMangling ? MD : nullptr));
}
}
Expand Down
11 changes: 10 additions & 1 deletion clang/lib/CodeGen/CGCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
auto OrigMD =
CGM.getItaniumVTableContext().findOriginalMethod(GD.getCanonicalDecl());
PointerAuth = CGF.EmitPointerAuthInfo(Schema, VFuncPtr, OrigMD, QualType());
}

CGCallee Callee(GD, VFunc, PointerAuth);
return Callee;
}

Expand Down
14 changes: 8 additions & 6 deletions clang/lib/CodeGen/CGCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -505,13 +505,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);
Expand Down
31 changes: 30 additions & 1 deletion clang/lib/CodeGen/CGClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2588,6 +2588,12 @@ 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);
Expand Down Expand Up @@ -2681,12 +2687,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<llvm::Instruction>(
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<llvm::Instruction>(Builder.CreateBitCast(VTable, Int8PtrTy));
Builder.CreateLoad(RawAddress(VTable, Int8Ty, CGM.getPointerAlign()),
/* IsVolatile */ true);
}
} else {
VTable = cast<llvm::Instruction>(EmitPointerAuthAuth(
CGPointerAuthInfo(0, PointerAuthenticationMode::Strip, false, false,
nullptr),
VTable));
}
}

if (CGM.getCodeGenOpts().OptimizationLevel > 0 &&
CGM.getCodeGenOpts().StrictVTablePointers)
CGM.DecorateInstructionWithInvariantGroup(VTable, RD);
Expand Down
7 changes: 5 additions & 2 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -838,9 +838,12 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc,

// Load the vptr, and compute hash_16_bytes(TypeHash, vptr).
llvm::Value *Low = llvm::ConstantInt::get(Int64Ty, TypeHash);
llvm::Type *VPtrTy = llvm::PointerType::get(IntPtrTy, 0);
Address VPtrAddr(Ptr, IntPtrTy, getPointerAlign());
llvm::Value *VPtrVal = Builder.CreateLoad(VPtrAddr);
llvm::Value *High = Builder.CreateZExt(VPtrVal, Int64Ty);
llvm::Value *VPtrVal = GetVTablePtr(VPtrAddr, VPtrTy,
Ty->getAsCXXRecordDecl(),
VTableAuthMode::UnsafeUbsanStrip);
llvm::Value *High = Builder.CreateBitOrPointerCast(VPtrVal, Int64Ty);

llvm::Value *Hash = emitHash16Bytes(Builder, Low, High);
Hash = Builder.CreateTrunc(Hash, IntPtrTy);
Expand Down
77 changes: 76 additions & 1 deletion clang/lib/CodeGen/CGExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,14 @@ 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;
}
Expand Down Expand Up @@ -1520,8 +1528,37 @@ llvm::GlobalValue *ConstantEmitter::getCurrentAddrPrivate() {
return global;
}

static llvm::Constant *getUnfoldableValue(llvm::Constant *C) {
// Look through any constant expressions that might get folded
while (auto CE = dyn_cast<llvm::ConstantExpr>(C)) {
switch (CE->getOpcode()) {
// Simple type changes.
case llvm::Instruction::BitCast:
case llvm::Instruction::IntToPtr:
case llvm::Instruction::PtrToInt:
break;

// GEPs, if all the indices are zero.
case llvm::Instruction::GetElementPtr:
for (unsigned i = 1, e = CE->getNumOperands(); i != e; ++i)
if (!CE->getOperand(i)->isNullValue())
return C;
break;

default:
return C;
}
C = CE->getOperand(0);
}
return C;
}

void ConstantEmitter::registerCurrentAddrPrivate(llvm::Constant *signal,
llvm::GlobalValue *placeholder) {
// Strip anything from the signal value that might get folded into other
// constant expressions in the final initializer.
signal = getUnfoldableValue(signal);

assert(!PlaceholderAddresses.empty());
assert(PlaceholderAddresses.back().first == nullptr);
assert(PlaceholderAddresses.back().second == placeholder);
Expand Down Expand Up @@ -1579,7 +1616,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();
}
Expand Down Expand Up @@ -1743,6 +1780,44 @@ 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;

auto 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();
}

// Fetch the extra discriminator.
llvm::Constant *otherDiscriminator =
llvm::ConstantInt::get(CGM.IntPtrTy, schema.getExtraDiscriminator());

auto signedPointer = CGM.getConstantSignedPointer(
unsignedPointer, key, storageAddress, otherDiscriminator);

if (schema.isAddressDiscriminated())
registerCurrentAddrPrivate(signedPointer, storageAddress);

return signedPointer;
}

llvm::Constant *ConstantEmitter::emitForMemory(CodeGenModule &CGM,
llvm::Constant *C,
QualType destType) {
Expand Down
239 changes: 239 additions & 0 deletions clang/lib/CodeGen/CGPointerAuth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,53 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/IR/ValueMap.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Support/SipHash.h"
#include <vector>

using namespace clang;
using namespace CodeGen;

/// Given a pointer-authentication schema, return a concrete "other"
/// discriminator for it.
llvm::Constant *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::getPointerAuthStableSipHash16(name);
}

return entityHash;
}

/// Return the abstract pointer authentication schema for a pointer to the given
/// function type.
CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) {
Expand All @@ -46,6 +88,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);
}

/// Build a signed-pointer "ptrauth" constant.
static llvm::ConstantPtrAuth *
buildConstantAddress(CodeGenModule &CGM, llvm::Constant *pointer, unsigned key,
Expand Down Expand Up @@ -85,6 +162,27 @@ CodeGenModule::getConstantSignedPointer(llvm::Constant *pointer,
otherDiscriminator);
}

/// Does a given PointerAuthScheme require us to sign a value
static bool 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::Constant *otherDiscriminator =
getPointerAuthOtherDiscriminator(schema, schemaDecl, schemaType);

return getConstantSignedPointer(pointer, schema.getKey(), storageAddress,
otherDiscriminator);
}

llvm::Constant *
CodeGen::getConstantSignedPointer(CodeGenModule &CGM,
llvm::Constant *pointer, unsigned key,
Expand All @@ -94,6 +192,37 @@ CodeGen::getConstantSignedPointer(CodeGenModule &CGM,
otherDiscriminator);
}

/// 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 || !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);
}

void ConstantAggregateBuilderBase::addSignedPointer(
llvm::Constant *pointer, unsigned key, bool useAddressDiscrimination,
llvm::Constant *otherDiscriminator) {
llvm::Constant *storageAddress = nullptr;
if (useAddressDiscrimination) {
storageAddress = getAddrOfCurrentPosition(pointer->getType());
}

llvm::Constant *signedPointer = Builder.CGM.getConstantSignedPointer(
pointer, key, storageAddress, otherDiscriminator);
add(signedPointer);
}

/// If applicable, sign a given constant function pointer with the ABI rules for
/// functionType.
llvm::Constant *CodeGenModule::getFunctionPointer(llvm::Constant *pointer,
Expand Down Expand Up @@ -126,3 +255,113 @@ llvm::Constant *CodeGenModule::getFunctionPointer(GlobalDecl GD,

return getFunctionPointer(getRawFunctionPointer(GD, Ty), FuncType, GD);
}

std::optional<PointerAuthQualifier>
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<VTablePointerAuthenticationAttr>()) {
auto explicitKey = explicitAuthentication->getKey();
auto explicitAddressDiscrimination =
explicitAuthentication->getAddressDiscrimination();
auto explicitDiscriminator =
explicitAuthentication->getExtraDiscrimination();
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<PointerAuthQualifier>
CodeGenModule::getVTablePointerAuthentication(const CXXRecordDecl *record) {
if (!record->getDefinition() || !record->isPolymorphic())
return std::nullopt;

auto existing = VTablePtrAuthInfos.find(record);
std::optional<PointerAuthQualifier> authentication;
if (existing != VTablePtrAuthInfos.end()) {
authentication = existing->getSecond();
} else {
authentication = computeVTPointerAuthentication(record);
VTablePtrAuthInfos.insert(std::make_pair(record, authentication));
}
return authentication;
}

std::optional<CGPointerAuthInfo>
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);
}
5 changes: 5 additions & 0 deletions clang/lib/CodeGen/CGVTT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ CodeGenVTables::EmitVTTDefinition(llvm::GlobalVariable *VTT,
llvm::Constant *Init = llvm::ConstantExpr::getGetElementPtr(
VTable->getValueType(), VTable, Idxs, /*InBounds=*/true, InRange);

if (auto &schema =
CGM.getCodeGenOpts().PointerAuth.CXXVTTVTablePointers)
Init = CGM.getConstantSignedPointer(Init, schema, nullptr, GlobalDecl(),
QualType());

VTTComponents.push_back(Init);
}

Expand Down
48 changes: 38 additions & 10 deletions clang/lib/CodeGen/CGVTables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -307,10 +309,15 @@ void CodeGenFunction::EmitCallAndReturnForThunk(llvm::FunctionCallee Callee,
const CXXMethodDecl *MD = cast<CXXMethodDecl>(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
Expand Down Expand Up @@ -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<CXXDestructorDecl>(MD))
MCtx.mangleCXXDtorThunk(DD, GD.getDtorType(), TI.This, Out);
else
MCtx.mangleThunk(MD, TI, Out);

if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(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<CXXDestructorDecl>(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);

Expand Down Expand Up @@ -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()) {
Expand All @@ -841,6 +866,9 @@ void CodeGenVTables::addVTableComponent(ConstantArrayBuilder &builder,
if (FnAS != GVAS)
fnPtr =
llvm::ConstantExpr::getAddrSpaceCast(fnPtr, CGM.GlobalsInt8PtrTy);
if (auto &schema =
CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers)
return builder.addSignedPointer(fnPtr, schema, GD, QualType());
return builder.add(fnPtr);
}
}
Expand Down
27 changes: 27 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3099,3 +3099,30 @@ CodeGenFunction::EmitPointerAuthSign(const CGPointerAuthInfo &pointerAuth,
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);
}
20 changes: 18 additions & 2 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2456,10 +2456,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,
Expand Down Expand Up @@ -4406,13 +4416,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 calleeDecl,
QualType calleeType);
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<llvm::OperandBundleDef> &bundles);
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,13 @@ class CodeGenModule : public CodeGenTypeCache {
std::pair<std::unique_ptr<CodeGenFunction>, const TopLevelStmtDecl *>
GlobalTopLevelStmtBlockInFlight;

llvm::DenseMap<GlobalDecl, uint16_t> PtrAuthDiscriminatorHashes;

llvm::DenseMap<const CXXRecordDecl *, std::optional<PointerAuthQualifier>>
VTablePtrAuthInfos;
std::optional<PointerAuthQualifier>
computeVTPointerAuthentication(const CXXRecordDecl *thisClass);

public:
CodeGenModule(ASTContext &C, IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
const HeaderSearchOptions &headersearchopts,
Expand Down Expand Up @@ -975,6 +982,18 @@ class CodeGenModule : public CodeGenTypeCache {
llvm::Constant *storageAddress,
llvm::Constant *extraDiscrim);

llvm::Constant *
getPointerAuthOtherDiscriminator(const PointerAuthSchema &schema,
GlobalDecl schemaDecl, QualType schemaType);
uint16_t getPointerAuthDeclDiscriminator(GlobalDecl GD);
std::optional<CGPointerAuthInfo>
getVTablePointerAuthInfo(CodeGenFunction *context,
const CXXRecordDecl *record,
llvm::Value *storageAddress);

std::optional<PointerAuthQualifier>
getVTablePointerAuthentication(const CXXRecordDecl *thisClass);

CGPointerAuthInfo EmitPointerAuthInfo(const RecordDecl *RD);

// Return whether RTTI information should be emitted for this target.
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/ConstantEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
72 changes: 53 additions & 19 deletions clang/lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 *,
Expand Down Expand Up @@ -1478,10 +1481,17 @@ 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) {
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
Expand Down Expand Up @@ -1956,8 +1966,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,
Expand Down Expand Up @@ -2009,8 +2029,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 *
Expand All @@ -2025,7 +2046,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());
Expand All @@ -2049,7 +2070,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;
}

Expand Down Expand Up @@ -2145,6 +2172,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) {
Expand All @@ -2162,8 +2190,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(
Expand Down Expand Up @@ -2198,18 +2226,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);
}
Expand Down Expand Up @@ -3690,6 +3720,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);
}

Expand Down
9 changes: 7 additions & 2 deletions clang/lib/CodeGen/MicrosoftCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -2227,7 +2229,9 @@ void MicrosoftCXXABI::emitVBTableDefinition(const VPtrInfo &VBT,

llvm::Value *MicrosoftCXXABI::performThisAdjustment(CodeGenFunction &CGF,
Address This,
const ThisAdjustment &TA) {
const CXXRecordDecl *,
const ThunkInfo &TI) {
auto &TA = TI.This;
if (TA.isEmpty())
return This.emitRawPointer(CGF);

Expand Down Expand Up @@ -2279,6 +2283,7 @@ llvm::Value *MicrosoftCXXABI::performThisAdjustment(CodeGenFunction &CGF,

llvm::Value *
MicrosoftCXXABI::performReturnAdjustment(CodeGenFunction &CGF, Address Ret,
const CXXRecordDecl *,
const ReturnAdjustment &RA) {
if (RA.isEmpty())
return Ret.emitRawPointer(CGF);
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,18 @@ bool CompilerInvocation::setDefaultPointerAuthOptions(
// If you change anything here, be sure to update <ptrauth.h>.
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);
}
return true;
}
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/Headers/ptrauth.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,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;
Expand Down Expand Up @@ -213,6 +217,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) \
Expand Down Expand Up @@ -279,6 +289,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 */
13 changes: 7 additions & 6 deletions clang/lib/InstallAPI/Visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CXXMethodDecl>(D.getDecl());
if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(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);
}
Expand Down Expand Up @@ -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);
Expand Down
30 changes: 29 additions & 1 deletion clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint64_t>(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<uint64_t>(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) {
Expand Down Expand Up @@ -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
Expand All @@ -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());
Expand Down
116 changes: 116 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9149,6 +9149,118 @@ EnforceTCBLeafAttr *Sema::mergeEnforceTCBLeafAttr(
*this, D, AL);
}

static void handleVTablePointerAuthentication(Sema &S, Decl *D,
const ParsedAttr &AL) {
CXXRecordDecl *decl = cast<CXXRecordDecl>(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<VTablePointerAuthenticationAttr>()) {
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 extraDiscrimination = VTablePointerAuthenticationAttr::
ExtraDiscrimination::DefaultExtraDiscrimination;
if (AL.getNumArgs() > 2) {
if (AL.isArgIdent(2)) {
IdentifierLoc *IL = AL.getArgAsIdent(2);
if (!VTablePointerAuthenticationAttr::ConvertStrToExtraDiscrimination(
IL->Ident->getName(), extraDiscrimination)) {
S.Diag(IL->Loc, diag::err_invalid_extra_discrimination) << IL->Ident;
AL.setInvalid();
}
if (extraDiscrimination ==
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 (extraDiscrimination ==
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, extraDiscrimination,
customDiscriminationValue));
}

//===----------------------------------------------------------------------===//
// Top Level Sema Entry Points
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -10093,6 +10205,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;
}
}

Expand Down
46 changes: 46 additions & 0 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7119,6 +7119,11 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
return false;
};

if (!Record->isInvalidDecl() &&
Record->hasAttr<VTablePointerAuthenticationAttr>()) {
checkIncorrectVTablePointerAuthenticationAttribute(*Record);
}

auto CompleteMemberFunction = [&](CXXMethodDecl *M) {
// Check whether the explicitly-defaulted members are valid.
bool Incomplete = CheckForDefaultedFunction(M);
Expand Down Expand Up @@ -10503,6 +10508,47 @@ 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;
if (base->isPolymorphic()) {
Diag(RD.getAttr<VTablePointerAuthenticationAttr>()->getLocation(),
diag::err_non_top_level_vtable_pointer_auth)
<< &RD << base;
} else {
break;
}
primaryBase = base;
}

if (!RD.isPolymorphic()) {
Diag(RD.getAttr<VTablePointerAuthenticationAttr>()->getLocation(),
diag::err_non_polymorphic_vtable_pointer_auth)
<< &RD;
return;
}
}

void Sema::ActOnFinishCXXMemberSpecification(
Scope *S, SourceLocation RLoc, Decl *TagDecl, SourceLocation LBrac,
SourceLocation RBrac, const ParsedAttributesView &AttrList) {
Expand Down
33 changes: 33 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14201,6 +14201,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);
Expand Down
30 changes: 30 additions & 0 deletions clang/test/CodeGen/ptrauth-ubsan-vptr.cpp
Original file line number Diff line number Diff line change
@@ -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<T*>(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<T*>(s);
return p->v();
}

// CHECK-NOT: call {{.*}} @llvm.ptrauth.auth{{.*}}
// CHECK-NOT: call {{.*}} @llvm.ptrauth.strip{{.*}}
7 changes: 4 additions & 3 deletions clang/test/CodeGenCXX/catch-undef-behavior.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -115,7 +115,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: xor i64 {{-4030275160588942838|1107558922}},
// [...]
// CHECK: getelementptr inbounds [128 x i64], ptr @__ubsan_vptr_type_cache, i32 0, i64 %
Expand Down
105 changes: 105 additions & 0 deletions clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
42 changes: 42 additions & 0 deletions clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp
Original file line number Diff line number Diff line change
@@ -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<class T>
struct Templ {
virtual void f() {}
virtual void g() {}
};
template<class T>
struct SubTempl : public Templ<T> {
virtual void f() {} // override
virtual void g() {} // override
};

void f(SubTempl<int>* 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<int>'s vtable. Hence, Templ<int>::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)
Original file line number Diff line number Diff line change
@@ -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<class T>
struct Templ {
virtual ~Templ(); // Out-of-line so that the destructor doesn't cause a vtable
virtual void f() {}
virtual void g() {}
};
template<class T>
struct SubTempl : public Templ<T> {
virtual ~SubTempl() {} // override
virtual void f() {} // override
virtual void g() {} // override
};

void f(SubTempl<int>* 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<int>'s vtable. Hence, Templ<int>::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)
Loading