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/TargetParser/Triple.h"
26#include "llvm/Transforms/IPO/LowerTypeTests.h"
27
28using namespace llvm;
29
30#define DEBUG_TYPE "cross-dso-cfi"
31
32STATISTIC(NumTypeIds, "Number of unique type identifiers");
33
34namespace {
35
36struct CrossDSOCFI {
37 MDNode *VeryLikelyWeights;
38
39 void buildCFICheck(Module &M);
40 bool runOnModule(Module &M);
41};
42
43} // anonymous namespace
44
45/// buildCFICheck - emits __cfi_check for the current module.
46void CrossDSOCFI::buildCFICheck(Module &M) {
47 // FIXME: verify that __cfi_check ends up near the end of the code section,
48 // but before the jump slots created in LowerTypeTests.
49 SetVector<uint64_t> TypeIds = lowertypetests::findCfiTypeIds(M);
50
51 LLVMContext &Ctx = M.getContext();
52 FunctionCallee C = M.getOrInsertFunction(
53 Name: "__cfi_check", RetTy: Type::getVoidTy(C&: Ctx), Args: Type::getInt64Ty(C&: Ctx),
54 Args: PointerType::getUnqual(C&: Ctx), Args: PointerType::getUnqual(C&: Ctx));
55 Function *F = cast<Function>(Val: C.getCallee());
56 // Take over the existing function. The frontend emits a weak stub so that the
57 // linker knows about the symbol; this pass replaces the function body.
58 F->deleteBody();
59 F->setAlignment(Align(4096));
60
61 Triple T(M.getTargetTriple());
62 if (T.isARM() || T.isThumb())
63 F->addFnAttr(Kind: "target-features", Val: "+thumb-mode");
64
65 auto args = F->arg_begin();
66 Value &CallSiteTypeId = *(args++);
67 CallSiteTypeId.setName("CallSiteTypeId");
68 Value &Addr = *(args++);
69 Addr.setName("Addr");
70 Value &CFICheckFailData = *(args++);
71 CFICheckFailData.setName("CFICheckFailData");
72 assert(args == F->arg_end());
73
74 BasicBlock *BB = BasicBlock::Create(Context&: Ctx, Name: "entry", Parent: F);
75 BasicBlock *ExitBB = BasicBlock::Create(Context&: Ctx, Name: "exit", Parent: F);
76
77 BasicBlock *TrapBB = BasicBlock::Create(Context&: Ctx, Name: "fail", Parent: F);
78 IRBuilder<> IRBFail(TrapBB);
79 FunctionCallee CFICheckFailFn = M.getOrInsertFunction(
80 Name: "__cfi_check_fail", RetTy: Type::getVoidTy(C&: Ctx), Args: PointerType::getUnqual(C&: Ctx),
81 Args: PointerType::getUnqual(C&: Ctx));
82 IRBFail.CreateCall(Callee: CFICheckFailFn, Args: {&CFICheckFailData, &Addr});
83 IRBFail.CreateBr(Dest: ExitBB);
84
85 IRBuilder<> IRBExit(ExitBB);
86 IRBExit.CreateRetVoid();
87
88 IRBuilder<> IRB(BB);
89 SwitchInst *SI = IRB.CreateSwitch(V: &CallSiteTypeId, Dest: TrapBB, NumCases: TypeIds.size());
90 for (uint64_t TypeId : TypeIds) {
91 ConstantInt *CaseTypeId = ConstantInt::get(Ty: Type::getInt64Ty(C&: Ctx), V: TypeId);
92 BasicBlock *TestBB = BasicBlock::Create(Context&: Ctx, Name: "test", Parent: F);
93 IRBuilder<> IRBTest(TestBB);
94
95 Value *Test = IRBTest.CreateIntrinsic(
96 ID: Intrinsic::type_test,
97 Args: {&Addr,
98 MetadataAsValue::get(Context&: Ctx, MD: ConstantAsMetadata::get(C: CaseTypeId))});
99 CondBrInst *BI = IRBTest.CreateCondBr(Cond: Test, True: ExitBB, False: TrapBB);
100 BI->setMetadata(KindID: LLVMContext::MD_prof, Node: VeryLikelyWeights);
101
102 SI->addCase(OnVal: CaseTypeId, Dest: TestBB);
103 ++NumTypeIds;
104 }
105}
106
107bool CrossDSOCFI::runOnModule(Module &M) {
108 VeryLikelyWeights = MDBuilder(M.getContext()).createLikelyBranchWeights();
109 if (M.getModuleFlag(Key: "Cross-DSO CFI") == nullptr)
110 return false;
111 buildCFICheck(M);
112 return true;
113}
114
115PreservedAnalyses CrossDSOCFIPass::run(Module &M, ModuleAnalysisManager &AM) {
116 CrossDSOCFI Impl;
117 bool Changed = Impl.runOnModule(M);
118 if (!Changed)
119 return PreservedAnalyses::all();
120 return PreservedAnalyses::none();
121}
122