Index: lib/AST/MicrosoftMangle.cpp =================================================================== --- lib/AST/MicrosoftMangle.cpp +++ lib/AST/MicrosoftMangle.cpp @@ -2761,7 +2761,18 @@ void MicrosoftMangleContextImpl::mangleCXXVTableBitSet(const CXXRecordDecl *RD, raw_ostream &Out) { - llvm::report_fatal_error("Cannot mangle bitsets yet"); + Linkage L = RD->getLinkageInternal(); + if (L == InternalLinkage || L == UniqueExternalLinkage) { + // This part of the identifier needs to be unique across all translation + // units in the linked program. The scheme fails if multiple translation + // units are compiled using the same relative source file path, or if + // multiple translation units are built from the same source file. + SourceManager &SM = getASTContext().getSourceManager(); + Out << "[" << SM.getFileEntryForID(SM.getMainFileID())->getName() << "]"; + } + + MicrosoftCXXNameMangler mangler(*this, Out); + mangler.mangleName(RD); } MicrosoftMangleContext * Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -205,6 +205,9 @@ CXXDtorType Type, bool ForVirtualBase, bool Delegating, llvm::Value *This) override; + void emitVTableBitSetEntries(VPtrInfo *Info, const CXXRecordDecl *RD, + llvm::GlobalVariable *VTable); + void emitVTableDefinitions(CodeGenVTables &CGVT, const CXXRecordDecl *RD) override; @@ -1401,6 +1404,58 @@ getFromDtorType(Type)); } +void MicrosoftCXXABI::emitVTableBitSetEntries(VPtrInfo *Info, + const CXXRecordDecl *RD, + llvm::GlobalVariable *VTable) { + if (!getContext().getLangOpts().Sanitize.has(SanitizerKind::CFIVCall) && + !getContext().getLangOpts().Sanitize.has(SanitizerKind::CFINVCall) && + !getContext().getLangOpts().Sanitize.has(SanitizerKind::CFIDerivedCast) && + !getContext().getLangOpts().Sanitize.has(SanitizerKind::CFIUnrelatedCast)) + return; + + llvm::NamedMDNode *BitsetsMD = + CGM.getModule().getOrInsertNamedMetadata("llvm.bitsets"); + CharUnits PointerWidth = getContext().toCharUnitsFromBits( + getContext().getTargetInfo().getPointerWidth(0)); + + // FIXME: Add blacklisting scheme. + + if (Info->PathToBaseWithVPtr.empty()) { + BitsetsMD->addOperand( + CGM.CreateVTableBitSetEntry(VTable, PointerWidth, RD)); + return; + } + + // Add a bitset entry for the least derived base belonging to this vftable. + BitsetsMD->addOperand(CGM.CreateVTableBitSetEntry( + VTable, PointerWidth, Info->PathToBaseWithVPtr.back())); + + // Add a bitset entry for each derived class that is laid out at the same + // offset as the least derived base. + for (unsigned I = Info->PathToBaseWithVPtr.size() - 1; I != 0; --I) { + const CXXRecordDecl *DerivedRD = Info->PathToBaseWithVPtr[I - 1]; + const CXXRecordDecl *BaseRD = Info->PathToBaseWithVPtr[I]; + + const ASTRecordLayout &Layout = + getContext().getASTRecordLayout(DerivedRD); + CharUnits Offset; + auto VBI = Layout.getVBaseOffsetsMap().find(BaseRD); + if (VBI == Layout.getVBaseOffsetsMap().end()) + Offset = Layout.getBaseClassOffset(BaseRD); + else + Offset = VBI->second.VBaseOffset; + if (!Offset.isZero()) + return; + BitsetsMD->addOperand( + CGM.CreateVTableBitSetEntry(VTable, PointerWidth, DerivedRD)); + } + + // Finally do the same for the most derived class. + if (Info->FullOffsetInMDC.isZero()) + BitsetsMD->addOperand( + CGM.CreateVTableBitSetEntry(VTable, PointerWidth, RD)); +} + void MicrosoftCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT, const CXXRecordDecl *RD) { MicrosoftVTableContext &VFTContext = CGM.getMicrosoftVTableContext(); @@ -1423,6 +1478,8 @@ VTLayout.getNumVTableThunks(), RTTI); VTable->setInitializer(Init); + + emitVTableBitSetEntries(Info, RD, VTable); } } @@ -1585,6 +1642,49 @@ return VTable; } +// Compute the identity of the most derived class whose virtual table is located +// at the given offset into RD. +static const CXXRecordDecl *getClassAtVTableLocation(ASTContext &Ctx, + const CXXRecordDecl *RD, + CharUnits Offset) { + if (Offset.isZero()) + return RD; + + const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD); + const CXXRecordDecl *MaxBase = nullptr; + CharUnits MaxBaseOffset; + for (auto &&B : RD->bases()) { + const CXXRecordDecl *Base = B.getType()->getAsCXXRecordDecl(); + CharUnits BaseOffset = Layout.getBaseClassOffset(Base); + if (BaseOffset <= Offset && BaseOffset > MaxBaseOffset) { + MaxBase = Base; + MaxBaseOffset = BaseOffset; + } + } + for (auto &&B : RD->vbases()) { + const CXXRecordDecl *Base = B.getType()->getAsCXXRecordDecl(); + CharUnits BaseOffset = Layout.getVBaseClassOffset(Base); + if (BaseOffset <= Offset && BaseOffset > MaxBaseOffset) { + MaxBase = Base; + MaxBaseOffset = BaseOffset; + } + } + assert(MaxBase); + return getClassAtVTableLocation(Ctx, MaxBase, Offset - MaxBaseOffset); +} + +// Compute the identity of the most derived class whose virtual table is located +// at the MethodVFTableLocation ML. +static const CXXRecordDecl * +getClassAtVTableLocation(ASTContext &Ctx, GlobalDecl GD, + MicrosoftVTableContext::MethodVFTableLocation &ML) { + const CXXRecordDecl *RD = ML.VBase; + if (!RD) + RD = cast(GD.getDecl())->getParent(); + + return getClassAtVTableLocation(Ctx, RD, ML.VFPtrOffset); +} + llvm::Value *MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, GlobalDecl GD, llvm::Value *This, @@ -1600,6 +1700,10 @@ MicrosoftVTableContext::MethodVFTableLocation ML = CGM.getMicrosoftVTableContext().getMethodVFTableLocation(GD); + if (CGF.SanOpts.has(SanitizerKind::CFIVCall)) + CGF.EmitVTablePtrCheck(getClassAtVTableLocation(getContext(), GD, ML), + VTable, CodeGenFunction::CFITCK_VCall, Loc); + llvm::Value *VFuncPtr = Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn"); return Builder.CreateLoad(VFuncPtr); Index: test/CodeGenCXX/cfi-vcall.cpp =================================================================== --- test/CodeGenCXX/cfi-vcall.cpp +++ test/CodeGenCXX/cfi-vcall.cpp @@ -1,6 +1,14 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NDIAG %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=DIAG --check-prefix=DIAG-ABORT %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=NDIAG %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=DIAG --check-prefix=DIAG-ABORT %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s +// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=MS --check-prefix=NDIAG %s + +// MS: @[[VTA:[0-9]*]] {{.*}} comdat($"\01??_7A@@6B@") +// MS: @[[VTB:[0-9]*]] {{.*}} comdat($"\01??_7B@@6B0@@") +// MS: @[[VTAinB:[0-9]*]] {{.*}} comdat($"\01??_7B@@6BA@@@") +// MS: @[[VTAinC:[0-9]*]] {{.*}} comdat($"\01??_7C@@6B@") +// MS: @[[VTBinD:[0-9]*]] {{.*}} comdat($"\01??_7D@?A@@6BB@@@") +// MS: @[[VTAinBinD:[0-9]*]] {{.*}} comdat($"\01??_7D@?A@@6BA@@@") struct A { A(); @@ -9,6 +17,8 @@ struct B : virtual A { B(); + virtual void g(); + virtual void h(); }; struct C : virtual A { @@ -20,6 +30,7 @@ struct D : B, C { D(); virtual void f(); + virtual void h(); }; } @@ -32,16 +43,24 @@ void A::f() { } +void B::g() { +} + void D::f() { } +void D::h() { +} + // DIAG: @[[SRC:.*]] = private unnamed_addr constant [{{.*}} x i8] c"{{.*}}test/CodeGenCXX/cfi-vcall.cpp\00", align 1 // DIAG: @[[TYPE:.*]] = private unnamed_addr constant { i16, i16, [4 x i8] } { i16 -1, i16 0, [4 x i8] c"'A'\00" } -// DIAG: @[[BADTYPESTATIC:.*]] = private unnamed_addr global { { [{{.*}} x i8]*, i32, i32 }, { i16, i16, [4 x i8] }*, i8 } { { [{{.*}} x i8]*, i32, i32 } { [{{.*}} x i8]* @[[SRC]], i32 58, i32 3 }, { i16, i16, [4 x i8] }* @[[TYPE]], i8 0 } +// DIAG: @[[BADTYPESTATIC:.*]] = private unnamed_addr global { { [{{.*}} x i8]*, i32, i32 }, { i16, i16, [4 x i8] }*, i8 } { { [{{.*}} x i8]*, i32, i32 } { [{{.*}} x i8]* @[[SRC]], i32 [[@LINE+20]], i32 3 }, { i16, i16, [4 x i8] }* @[[TYPE]], i8 0 } -// CHECK: define void @_Z2afP1A +// ITANIUM: define void @_Z2afP1A +// MS: define void @"\01?af@@YAXPEAUA@@@Z" void af(A *a) { - // CHECK: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"1A") + // ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"1A") + // MS: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"A@@") // CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ,]*]], label %[[TRAPBB:[^ ,]*]] // CHECK: [[TRAPBB]] @@ -58,20 +77,40 @@ a->f(); } -// CHECK: define internal void @_Z3df1PN12_GLOBAL__N_11DE +// ITANIUM: define internal void @_Z3df1PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?df1@@YAXPEAUD@?A@@@Z" void df1(D *d) { - // CHECK: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE") + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE") + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"A@@") d->f(); } -// CHECK: define internal void @_Z3df2PN12_GLOBAL__N_11DE +// ITANIUM: define internal void @_Z3dg1PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?dg1@@YAXPEAUD@?A@@@Z" +void dg1(D *d) { + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"B@@") + d->g(); +} + +// ITANIUM: define internal void @_Z3dh1PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?dh1@@YAXPEAUD@?A@@@Z" +void dh1(D *d) { + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE") + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]D@?A@@") + d->h(); +} + +// ITANIUM: define internal void @_Z3df2PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?df2@@YAXPEAUD@?A@@@Z" __attribute__((no_sanitize("cfi"))) void df2(D *d) { // CHECK-NOT: call i1 @llvm.bitset.test d->f(); } -// CHECK: define internal void @_Z3df3PN12_GLOBAL__N_11DE +// ITANIUM: define internal void @_Z3df3PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?df3@@YAXPEAUD@?A@@@Z" __attribute__((no_sanitize("address"))) __attribute__((no_sanitize("cfi-vcall"))) void df3(D *d) { // CHECK-NOT: call i1 @llvm.bitset.test @@ -82,20 +121,33 @@ void foo() { df1(&d); + dg1(&d); + dh1(&d); df2(&d); df3(&d); } -// CHECK-DAG: !{!"1A", [3 x i8*]* @_ZTV1A, i64 16} -// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} -// CHECK-DAG: !{!"1B", [5 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} -// CHECK-DAG: !{!"1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} -// CHECK-DAG: !{!"1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32} -// CHECK-DAG: !{!"1A", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// CHECK-DAG: !{!"1B", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// CHECK-DAG: !{!"1C", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 72} -// CHECK-DAG: !{!"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTV1B, i64 32} -// CHECK-DAG: !{!"1B", [5 x i8*]* @_ZTV1B, i64 32} -// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTV1C, i64 32} -// CHECK-DAG: !{!"1C", [5 x i8*]* @_ZTV1C, i64 32} +// Check for the expected number of elements (7 or 13 respectively). +// MS: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){6}]]} +// ITANIUM: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){12}]]} + +// ITANIUM-DAG: !{!"1A", [3 x i8*]* @_ZTV1A, i64 16} +// ITANIUM-DAG: !{!"1A", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// ITANIUM-DAG: !{!"1B", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// ITANIUM-DAG: !{!"1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} +// ITANIUM-DAG: !{!"1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32} +// ITANIUM-DAG: !{!"1A", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-DAG: !{!"1B", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-DAG: !{!"1C", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88} +// ITANIUM-DAG: !{!"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-DAG: !{!"1A", [7 x i8*]* @_ZTV1B, i64 32} +// ITANIUM-DAG: !{!"1B", [7 x i8*]* @_ZTV1B, i64 32} +// ITANIUM-DAG: !{!"1A", [5 x i8*]* @_ZTV1C, i64 32} +// ITANIUM-DAG: !{!"1C", [5 x i8*]* @_ZTV1C, i64 32} +// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTA]], i64 8} +// MS-DAG: !{!"B@@", [3 x i8*]* @[[VTB]], i64 8} +// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTAinB]], i64 8} +// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTAinC]], i64 8} +// MS-DAG: !{!"B@@", [3 x i8*]* @[[VTBinD]], i64 8} +// MS-DAG: !{!"[{{.*}}cfi-vcall.cpp]D@?A@@", [3 x i8*]* @[[VTBinD]], i64 8} +// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTAinBinD]], i64 8}