Files
llvm/clang/lib/CodeGen/CGVTT.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

195 lines
7.7 KiB
C++
Raw Normal View History

//===--- CGVTT.cpp - Emit LLVM Code for C++ VTTs --------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This contains code dealing with C++ code generation of VTTs (vtable tables).
//
//===----------------------------------------------------------------------===//
#include "CodeGenModule.h"
#include "CGCXXABI.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/VTTBuilder.h"
using namespace clang;
using namespace CodeGen;
static llvm::GlobalVariable *
GetAddrOfVTTVTable(CodeGenVTables &CGVT, CodeGenModule &CGM,
const CXXRecordDecl *MostDerivedClass,
const VTTVTable &VTable,
llvm::GlobalVariable::LinkageTypes Linkage,
VTableLayout::AddressPointsMapTy &AddressPoints) {
if (VTable.getBase() == MostDerivedClass) {
assert(VTable.getBaseOffset().isZero() &&
"Most derived class vtable must have a zero offset!");
// This is a regular vtable.
return CGM.getCXXABI().getAddrOfVTable(MostDerivedClass, CharUnits());
}
return CGVT.GenerateConstructionVTable(MostDerivedClass,
VTable.getBaseSubobject(),
VTable.isVirtual(),
Linkage,
AddressPoints);
}
void
CodeGenVTables::EmitVTTDefinition(llvm::GlobalVariable *VTT,
llvm::GlobalVariable::LinkageTypes Linkage,
const CXXRecordDecl *RD) {
VTTBuilder Builder(CGM.getContext(), RD, /*GenerateDefinition=*/true);
llvm::ArrayType *ArrayType = llvm::ArrayType::get(
CGM.GlobalsInt8PtrTy, Builder.getVTTComponents().size());
SmallVector<llvm::GlobalVariable *, 8> VTables;
SmallVector<VTableAddressPointsMapTy, 8> VTableAddressPoints;
for (const VTTVTable *i = Builder.getVTTVTables().begin(),
*e = Builder.getVTTVTables().end(); i != e; ++i) {
VTableAddressPoints.push_back(VTableAddressPointsMapTy());
VTables.push_back(GetAddrOfVTTVTable(*this, CGM, RD, *i, Linkage,
VTableAddressPoints.back()));
}
SmallVector<llvm::Constant *, 8> VTTComponents;
for (const VTTComponent *i = Builder.getVTTComponents().begin(),
*e = Builder.getVTTComponents().end(); i != e; ++i) {
const VTTVTable &VTTVT = Builder.getVTTVTables()[i->VTableIndex];
llvm::GlobalVariable *VTable = VTables[i->VTableIndex];
VTableLayout::AddressPointLocation AddressPoint;
if (VTTVT.getBase() == RD) {
// Just get the address point for the regular vtable.
AddressPoint =
getItaniumVTableContext().getVTableLayout(RD).getAddressPoint(
i->VTableBase);
} else {
AddressPoint = VTableAddressPoints[i->VTableIndex].lookup(i->VTableBase);
assert(AddressPoint.AddressPointIndex != 0 &&
"Did not find ctor vtable address point!");
}
llvm::Value *Idxs[] = {
llvm::ConstantInt::get(CGM.Int32Ty, 0),
llvm::ConstantInt::get(CGM.Int32Ty, AddressPoint.VTableIndex),
llvm::ConstantInt::get(CGM.Int32Ty, AddressPoint.AddressPointIndex),
};
// Add inrange attribute to indicate that only the VTableIndex can be
// accessed.
unsigned ComponentSize =
CGM.getDataLayout().getTypeAllocSize(getVTableComponentType());
unsigned VTableSize = CGM.getDataLayout().getTypeAllocSize(
cast<llvm::StructType>(VTable->getValueType())
->getElementType(AddressPoint.VTableIndex));
unsigned Offset = ComponentSize * AddressPoint.AddressPointIndex;
llvm::ConstantRange InRange(
llvm::APInt(32, (int)-Offset, true),
llvm::APInt(32, (int)(VTableSize - Offset), true));
llvm::Constant *Init = llvm::ConstantExpr::getGetElementPtr(
VTable->getValueType(), VTable, Idxs, /*InBounds=*/true, InRange);
[clang] Implement pointer authentication for C++ virtual functions, v-tables, and VTTs (#94056) Virtual function pointer entries in v-tables are signed with address discrimination in addition to declaration-based discrimination, where an integer discriminator the string hash (see `ptrauth_string_discriminator`) of the mangled name of the overridden method. This notably provides diversity based on the full signature of the overridden method, including the method name and parameter types. This patch introduces ItaniumVTableContext logic to find the original declaration of the overridden method. On AArch64, these pointers are signed using the `IA` key (the process-independent code key.) V-table pointers can be signed with either no discrimination, or a similar scheme using address and decl-based discrimination. In this case, the integer discriminator is the string hash of the mangled v-table identifier of the class that originally introduced the vtable pointer. On AArch64, these pointers are signed using the `DA` key (the process-independent data key.) Not using discrimination allows attackers to simply copy valid v-table pointers from one object to another. However, using a uniform discriminator of 0 does have positive performance and code-size implications on AArch64, and diversity for the most important v-table access pattern (virtual dispatch) is already better assured by the signing schemas used on the virtual functions. It is also known that some code in practice copies objects containing v-tables with `memcpy`, and while this is not permitted formally, it is something that may be invasive to eliminate. This is controlled by: ``` -fptrauth-vtable-pointer-type-discrimination -fptrauth-vtable-pointer-address-discrimination ``` In addition, this provides fine-grained controls in the ptrauth_vtable_pointer attribute, which allows overriding the default ptrauth schema for vtable pointers on a given class hierarchy, e.g.: ``` [[clang::ptrauth_vtable_pointer(no_authentication, no_address_discrimination, no_extra_discrimination)]] [[clang::ptrauth_vtable_pointer(default_key, default_address_discrimination, custom_discrimination, 0xf00d)]] ``` The override is then mangled as a parametrized vendor extension: ``` "__vtptrauth" I <key> <addressDiscriminated> <extraDiscriminator> E ``` To support this attribute, this patch adds a small extension to the attribute-emitter tablegen backend. Note that there are known areas where signing is either missing altogether or can be strengthened. Some will be addressed in later changes (e.g., member function pointers, some RTTI). `dynamic_cast` in particular is handled by emitting an artificial v-table pointer load (in a way that always authenticates it) before the runtime call itself, as the runtime doesn't have enough information today to properly authenticate it. Instead, the runtime is currently expected to strip the v-table pointer. --------- Co-authored-by: John McCall <rjmccall@apple.com> Co-authored-by: Ahmed Bougacha <ahmed@bougacha.org>
2024-06-26 20:35:10 -05:00
if (const auto &Schema =
CGM.getCodeGenOpts().PointerAuth.CXXVTTVTablePointers)
Init = CGM.getConstantSignedPointer(Init, Schema, nullptr, GlobalDecl(),
QualType());
VTTComponents.push_back(Init);
}
llvm::Constant *Init = llvm::ConstantArray::get(ArrayType, VTTComponents);
VTT->setInitializer(Init);
// Set the correct linkage.
VTT->setLinkage(Linkage);
if (CGM.supportsCOMDAT() && VTT->isWeakForLinker())
VTT->setComdat(CGM.getModule().getOrInsertComdat(VTT->getName()));
// Set the visibility. This will already have been set on the VTT declaration.
// Set it again, now that we have a definition, as the implicit visibility can
// apply differently to definitions.
CGM.setGVProperties(VTT, RD);
}
llvm::GlobalVariable *CodeGenVTables::GetAddrOfVTT(const CXXRecordDecl *RD) {
assert(RD->getNumVBases() && "Only classes with virtual bases need a VTT");
SmallString<256> OutName;
llvm::raw_svector_ostream Out(OutName);
cast<ItaniumMangleContext>(CGM.getCXXABI().getMangleContext())
.mangleCXXVTT(RD, Out);
StringRef Name = OutName.str();
2010-03-26 04:10:39 +00:00
// This will also defer the definition of the VTT.
(void) CGM.getCXXABI().getAddrOfVTable(RD, CharUnits());
VTTBuilder Builder(CGM.getContext(), RD, /*GenerateDefinition=*/false);
llvm::ArrayType *ArrayType = llvm::ArrayType::get(
CGM.GlobalsInt8PtrTy, Builder.getVTTComponents().size());
llvm::Align Align = CGM.getDataLayout().getABITypeAlign(CGM.GlobalsInt8PtrTy);
llvm::GlobalVariable *GV = CGM.CreateOrReplaceCXXRuntimeVariable(
Name, ArrayType, llvm::GlobalValue::ExternalLinkage, Align);
GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
CGM.setGVProperties(GV, RD);
return GV;
}
uint64_t CodeGenVTables::getSubVTTIndex(const CXXRecordDecl *RD,
BaseSubobject Base) {
BaseSubobjectPairTy ClassSubobjectPair(RD, Base);
2024-05-15 13:10:16 +01:00
SubVTTIndicesMapTy::iterator I = SubVTTIndices.find(ClassSubobjectPair);
if (I != SubVTTIndices.end())
return I->second;
VTTBuilder Builder(CGM.getContext(), RD, /*GenerateDefinition=*/false);
2024-05-15 13:10:16 +01:00
for (llvm::DenseMap<BaseSubobject, uint64_t>::const_iterator
I = Builder.getSubVTTIndices().begin(),
E = Builder.getSubVTTIndices().end();
I != E; ++I) {
// Insert all indices.
BaseSubobjectPairTy ClassSubobjectPair(RD, I->first);
2024-05-15 13:10:16 +01:00
SubVTTIndices.insert(std::make_pair(ClassSubobjectPair, I->second));
}
2024-05-15 13:10:16 +01:00
I = SubVTTIndices.find(ClassSubobjectPair);
assert(I != SubVTTIndices.end() && "Did not find index!");
return I->second;
}
uint64_t
CodeGenVTables::getSecondaryVirtualPointerIndex(const CXXRecordDecl *RD,
BaseSubobject Base) {
SecondaryVirtualPointerIndicesMapTy::iterator I =
SecondaryVirtualPointerIndices.find(std::make_pair(RD, Base));
if (I != SecondaryVirtualPointerIndices.end())
return I->second;
VTTBuilder Builder(CGM.getContext(), RD, /*GenerateDefinition=*/false);
// Insert all secondary vpointer indices.
for (llvm::DenseMap<BaseSubobject, uint64_t>::const_iterator I =
Builder.getSecondaryVirtualPointerIndices().begin(),
E = Builder.getSecondaryVirtualPointerIndices().end(); I != E; ++I) {
std::pair<const CXXRecordDecl *, BaseSubobject> Pair =
std::make_pair(RD, I->first);
SecondaryVirtualPointerIndices.insert(std::make_pair(Pair, I->second));
}
I = SecondaryVirtualPointerIndices.find(std::make_pair(RD, Base));
assert(I != SecondaryVirtualPointerIndices.end() && "Did not find index!");
return I->second;
}