1//===-- CrossDSOCFI.cpp - Externalize this module's CFI checks ------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This pass exports all llvm.bitset's found in the module in the form of a
10// __cfi_check function, which can be used to verify cross-DSO call targets.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Transforms/IPO/CrossDSOCFI.h"
15#include "llvm/ADT/SetVector.h"
16#include "llvm/ADT/Statistic.h"
17#include "llvm/IR/Constants.h"
18#include "llvm/IR/Function.h"
19#include "llvm/IR/GlobalObject.h"
20#include "llvm/IR/IRBuilder.h"
21#include "llvm/IR/Instructions.h"
22#include "llvm/IR/Intrinsics.h"
23#include "llvm/IR/MDBuilder.h"
24#include "llvm/IR/Module.h"
25#include "llvm/IR/ProfDataUtils.h"
26#include "llvm/TargetParser/Triple.h"
27#include "llvm/Transforms/IPO/LowerTypeTests.h"
28
29using namespace llvm;
30
31#define DEBUG_TYPE "cross-dso-cfi"
32
33STATISTIC(NumTypeIds, "Number of unique type identifiers");
34
35namespace {
36
37struct CrossDSOCFI {
38 MDNode *VeryLikelyWeights;
39
40 void buildCFICheck(Module &M);
41 bool runOnModule(Module &M);
42};
43
44} // anonymous namespace
45
46/// buildCFICheck - emits __cfi_check for the current module.
47void CrossDSOCFI::buildCFICheck(Module &M) {
48 // FIXME: verify that __cfi_check ends up near the end of the code section,
49 // but before the jump slots created in LowerTypeTests.
50 SetVector<uint64_t> TypeIds = lowertypetests::findCfiTypeIds(M);
51
52 LLVMContext &Ctx = M.getContext();
53 FunctionCallee C = M.getOrInsertFunction(
54 Name: "__cfi_check", RetTy: Type::getVoidTy(C&: Ctx), Args: Type::getInt64Ty(C&: Ctx),
55 Args: PointerType::getUnqual(C&: Ctx), Args: PointerType::getUnqual(C&: Ctx));
56 Function *F = cast<Function>(Val: C.getCallee());
57 // Take over the existing function. The frontend emits a weak stub so that the
58 // linker knows about the symbol; this pass replaces the function body.
59 F->deleteBody();
60 F->setAlignment(Align(4096));
61 // __cfi_check is synthesized by this pass, so there is no profile data for
62 // it.
63 setExplicitlyUnknownFunctionEntryCount(F&: *F, DEBUG_TYPE);
64
65 Triple T(M.getTargetTriple());
66 if (T.isARM() || T.isThumb())
67 F->addFnAttr(Kind: "target-features", Val: "+thumb-mode");
68
69 auto args = F->arg_begin();
70 Value &CallSiteTypeId = *(args++);
71 CallSiteTypeId.setName("CallSiteTypeId");
72 Value &Addr = *(args++);
73 Addr.setName("Addr");
74 Value &CFICheckFailData = *(args++);
75 CFICheckFailData.setName("CFICheckFailData");
76 assert(args == F->arg_end());
77
78 BasicBlock *BB = BasicBlock::Create(Context&: Ctx, Name: "entry", Parent: F);
79 BasicBlock *ExitBB = BasicBlock::Create(Context&: Ctx, Name: "exit", Parent: F);
80
81 BasicBlock *TrapBB = BasicBlock::Create(Context&: Ctx, Name: "fail", Parent: F);
82 IRBuilder<> IRBFail(TrapBB);
83 FunctionCallee CFICheckFailFn = M.getOrInsertFunction(
84 Name: "__cfi_check_fail", RetTy: Type::getVoidTy(C&: Ctx), Args: PointerType::getUnqual(C&: Ctx),
85 Args: PointerType::getUnqual(C&: Ctx));
86 IRBFail.CreateCall(Callee: CFICheckFailFn, Args: {&CFICheckFailData, &Addr});
87 IRBFail.CreateBr(Dest: ExitBB);
88
89 IRBuilder<> IRBExit(ExitBB);
90 IRBExit.CreateRetVoid();
91
92 IRBuilder<> IRB(BB);
93 SwitchInst *SI = IRB.CreateSwitch(V: &CallSiteTypeId, Dest: TrapBB, NumCases: TypeIds.size());
94 // The distribution of type ids at the call sites is not known.
95 setExplicitlyUnknownBranchWeights(I&: *SI, DEBUG_TYPE);
96 for (uint64_t TypeId : TypeIds) {
97 ConstantInt *CaseTypeId = ConstantInt::get(Ty: Type::getInt64Ty(C&: Ctx), V: TypeId);
98 BasicBlock *TestBB = BasicBlock::Create(Context&: Ctx, Name: "test", Parent: F);
99 IRBuilder<> IRBTest(TestBB);
100
101 Value *Test = IRBTest.CreateIntrinsic(
102 ID: Intrinsic::type_test,
103 Args: {&Addr,
104 MetadataAsValue::get(Context&: Ctx, MD: ConstantAsMetadata::get(C: CaseTypeId))});
105 CondBrInst *BI = IRBTest.CreateCondBr(Cond: Test, True: ExitBB, False: TrapBB);
106 BI->setMetadata(KindID: LLVMContext::MD_prof, Node: VeryLikelyWeights);
107
108 SI->addCase(OnVal: CaseTypeId, Dest: TestBB);
109 ++NumTypeIds;
110 }
111}
112
113bool CrossDSOCFI::runOnModule(Module &M) {
114 VeryLikelyWeights = MDBuilder(M.getContext()).createLikelyBranchWeights();
115 if (M.getModuleFlag(Key: "Cross-DSO CFI") == nullptr)
116 return false;
117 buildCFICheck(M);
118 return true;
119}
120
121PreservedAnalyses CrossDSOCFIPass::run(Module &M, ModuleAnalysisManager &AM) {
122 CrossDSOCFI Impl;
123 bool Changed = Impl.runOnModule(M);
124 if (!Changed)
125 return PreservedAnalyses::all();
126 return PreservedAnalyses::none();
127}
128