brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.2 KiB · a848eac Raw
155 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This pass exports all llvm.bitset's found in the module in the form of a10// __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 27using namespace llvm;28 29#define DEBUG_TYPE "cross-dso-cfi"30 31STATISTIC(NumTypeIds, "Number of unique type identifiers");32 33namespace {34 35struct CrossDSOCFI {36  MDNode *VeryLikelyWeights;37 38  ConstantInt *extractNumericTypeId(MDNode *MD);39  void buildCFICheck(Module &M);40  bool runOnModule(Module &M);41};42 43} // anonymous namespace44 45/// Extracts a numeric type identifier from an MDNode containing type metadata.46ConstantInt *CrossDSOCFI::extractNumericTypeId(MDNode *MD) {47  // This check excludes vtables for classes inside anonymous namespaces.48  auto TM = dyn_cast<ValueAsMetadata>(MD->getOperand(1));49  if (!TM)50    return nullptr;51  auto C = dyn_cast_or_null<ConstantInt>(TM->getValue());52  if (!C) return nullptr;53  // We are looking for i64 constants.54  if (C->getBitWidth() != 64) return nullptr;55 56  return C;57}58 59/// buildCFICheck - emits __cfi_check for the current module.60void CrossDSOCFI::buildCFICheck(Module &M) {61  // FIXME: verify that __cfi_check ends up near the end of the code section,62  // but before the jump slots created in LowerTypeTests.63  SetVector<uint64_t> TypeIds;64  SmallVector<MDNode *, 2> Types;65  for (GlobalObject &GO : M.global_objects()) {66    Types.clear();67    GO.getMetadata(LLVMContext::MD_type, Types);68    for (MDNode *Type : Types)69      if (ConstantInt *TypeId = extractNumericTypeId(Type))70        TypeIds.insert(TypeId->getZExtValue());71  }72 73  NamedMDNode *CfiFunctionsMD = M.getNamedMetadata("cfi.functions");74  if (CfiFunctionsMD) {75    for (auto *Func : CfiFunctionsMD->operands()) {76      assert(Func->getNumOperands() >= 2);77      for (unsigned I = 2; I < Func->getNumOperands(); ++I)78        if (ConstantInt *TypeId =79                extractNumericTypeId(cast<MDNode>(Func->getOperand(I).get())))80          TypeIds.insert(TypeId->getZExtValue());81    }82  }83 84  LLVMContext &Ctx = M.getContext();85  FunctionCallee C = M.getOrInsertFunction(86      "__cfi_check", Type::getVoidTy(Ctx), Type::getInt64Ty(Ctx),87      PointerType::getUnqual(Ctx), PointerType::getUnqual(Ctx));88  Function *F = cast<Function>(C.getCallee());89  // Take over the existing function. The frontend emits a weak stub so that the90  // linker knows about the symbol; this pass replaces the function body.91  F->deleteBody();92  F->setAlignment(Align(4096));93 94  Triple T(M.getTargetTriple());95  if (T.isARM() || T.isThumb())96    F->addFnAttr("target-features", "+thumb-mode");97 98  auto args = F->arg_begin();99  Value &CallSiteTypeId = *(args++);100  CallSiteTypeId.setName("CallSiteTypeId");101  Value &Addr = *(args++);102  Addr.setName("Addr");103  Value &CFICheckFailData = *(args++);104  CFICheckFailData.setName("CFICheckFailData");105  assert(args == F->arg_end());106 107  BasicBlock *BB = BasicBlock::Create(Ctx, "entry", F);108  BasicBlock *ExitBB = BasicBlock::Create(Ctx, "exit", F);109 110  BasicBlock *TrapBB = BasicBlock::Create(Ctx, "fail", F);111  IRBuilder<> IRBFail(TrapBB);112  FunctionCallee CFICheckFailFn = M.getOrInsertFunction(113      "__cfi_check_fail", Type::getVoidTy(Ctx), PointerType::getUnqual(Ctx),114      PointerType::getUnqual(Ctx));115  IRBFail.CreateCall(CFICheckFailFn, {&CFICheckFailData, &Addr});116  IRBFail.CreateBr(ExitBB);117 118  IRBuilder<> IRBExit(ExitBB);119  IRBExit.CreateRetVoid();120 121  IRBuilder<> IRB(BB);122  SwitchInst *SI = IRB.CreateSwitch(&CallSiteTypeId, TrapBB, TypeIds.size());123  for (uint64_t TypeId : TypeIds) {124    ConstantInt *CaseTypeId = ConstantInt::get(Type::getInt64Ty(Ctx), TypeId);125    BasicBlock *TestBB = BasicBlock::Create(Ctx, "test", F);126    IRBuilder<> IRBTest(TestBB);127 128    Value *Test = IRBTest.CreateIntrinsic(129        Intrinsic::type_test,130        {&Addr,131         MetadataAsValue::get(Ctx, ConstantAsMetadata::get(CaseTypeId))});132    BranchInst *BI = IRBTest.CreateCondBr(Test, ExitBB, TrapBB);133    BI->setMetadata(LLVMContext::MD_prof, VeryLikelyWeights);134 135    SI->addCase(CaseTypeId, TestBB);136    ++NumTypeIds;137  }138}139 140bool CrossDSOCFI::runOnModule(Module &M) {141  VeryLikelyWeights = MDBuilder(M.getContext()).createLikelyBranchWeights();142  if (M.getModuleFlag("Cross-DSO CFI") == nullptr)143    return false;144  buildCFICheck(M);145  return true;146}147 148PreservedAnalyses CrossDSOCFIPass::run(Module &M, ModuleAnalysisManager &AM) {149  CrossDSOCFI Impl;150  bool Changed = Impl.runOnModule(M);151  if (!Changed)152    return PreservedAnalyses::all();153  return PreservedAnalyses::none();154}155