mirror of
https://github.com/RPCSX/llvm.git
synced 2025-04-04 01:01:37 +00:00

This intrinsic safely loads a function pointer from a virtual table pointer using type metadata. This intrinsic is used to implement control flow integrity in conjunction with virtual call optimization. The virtual call optimization pass will optimize away llvm.type.checked.load intrinsics associated with devirtualized calls, thereby removing the type check in cases where it is not needed to enforce the control flow integrity constraint. This patch also introduces the capability to copy type metadata between global variables, and teaches the virtual call optimization pass to do so. Differential Revision: http://reviews.llvm.org/D21121 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@273756 91177308-0d34-0410-b5e6-96231b3b80d8
119 lines
4.2 KiB
C++
119 lines
4.2 KiB
C++
//===- TypeMetadataUtils.cpp - Utilities related to type metadata ---------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains functions that make it easier to manipulate type metadata
|
|
// for devirtualization.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Analysis/TypeMetadataUtils.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/Intrinsics.h"
|
|
#include "llvm/IR/Module.h"
|
|
|
|
using namespace llvm;
|
|
|
|
// Search for virtual calls that call FPtr and add them to DevirtCalls.
|
|
static void
|
|
findCallsAtConstantOffset(SmallVectorImpl<DevirtCallSite> &DevirtCalls,
|
|
bool *HasNonCallUses, Value *FPtr, uint64_t Offset) {
|
|
for (const Use &U : FPtr->uses()) {
|
|
Value *User = U.getUser();
|
|
if (isa<BitCastInst>(User)) {
|
|
findCallsAtConstantOffset(DevirtCalls, HasNonCallUses, User, Offset);
|
|
} else if (auto CI = dyn_cast<CallInst>(User)) {
|
|
DevirtCalls.push_back({Offset, CI});
|
|
} else if (auto II = dyn_cast<InvokeInst>(User)) {
|
|
DevirtCalls.push_back({Offset, II});
|
|
} else if (HasNonCallUses) {
|
|
*HasNonCallUses = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Search for virtual calls that load from VPtr and add them to DevirtCalls.
|
|
static void
|
|
findLoadCallsAtConstantOffset(Module *M,
|
|
SmallVectorImpl<DevirtCallSite> &DevirtCalls,
|
|
Value *VPtr, uint64_t Offset) {
|
|
for (const Use &U : VPtr->uses()) {
|
|
Value *User = U.getUser();
|
|
if (isa<BitCastInst>(User)) {
|
|
findLoadCallsAtConstantOffset(M, DevirtCalls, User, Offset);
|
|
} else if (isa<LoadInst>(User)) {
|
|
findCallsAtConstantOffset(DevirtCalls, nullptr, User, Offset);
|
|
} else if (auto GEP = dyn_cast<GetElementPtrInst>(User)) {
|
|
// Take into account the GEP offset.
|
|
if (VPtr == GEP->getPointerOperand() && GEP->hasAllConstantIndices()) {
|
|
SmallVector<Value *, 8> Indices(GEP->op_begin() + 1, GEP->op_end());
|
|
uint64_t GEPOffset = M->getDataLayout().getIndexedOffsetInType(
|
|
GEP->getSourceElementType(), Indices);
|
|
findLoadCallsAtConstantOffset(M, DevirtCalls, User, Offset + GEPOffset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void llvm::findDevirtualizableCallsForTypeTest(
|
|
SmallVectorImpl<DevirtCallSite> &DevirtCalls,
|
|
SmallVectorImpl<CallInst *> &Assumes, CallInst *CI) {
|
|
assert(CI->getCalledFunction()->getIntrinsicID() == Intrinsic::type_test);
|
|
|
|
Module *M = CI->getParent()->getParent()->getParent();
|
|
|
|
// Find llvm.assume intrinsics for this llvm.type.test call.
|
|
for (const Use &CIU : CI->uses()) {
|
|
auto AssumeCI = dyn_cast<CallInst>(CIU.getUser());
|
|
if (AssumeCI) {
|
|
Function *F = AssumeCI->getCalledFunction();
|
|
if (F && F->getIntrinsicID() == Intrinsic::assume)
|
|
Assumes.push_back(AssumeCI);
|
|
}
|
|
}
|
|
|
|
// If we found any, search for virtual calls based on %p and add them to
|
|
// DevirtCalls.
|
|
if (!Assumes.empty())
|
|
findLoadCallsAtConstantOffset(M, DevirtCalls,
|
|
CI->getArgOperand(0)->stripPointerCasts(), 0);
|
|
}
|
|
|
|
void llvm::findDevirtualizableCallsForTypeCheckedLoad(
|
|
SmallVectorImpl<DevirtCallSite> &DevirtCalls,
|
|
SmallVectorImpl<Instruction *> &LoadedPtrs,
|
|
SmallVectorImpl<Instruction *> &Preds, bool &HasNonCallUses, CallInst *CI) {
|
|
assert(CI->getCalledFunction()->getIntrinsicID() ==
|
|
Intrinsic::type_checked_load);
|
|
|
|
auto *Offset = dyn_cast<ConstantInt>(CI->getArgOperand(1));
|
|
if (!Offset) {
|
|
HasNonCallUses = true;
|
|
return;
|
|
}
|
|
|
|
for (Use &U : CI->uses()) {
|
|
auto CIU = U.getUser();
|
|
if (auto EVI = dyn_cast<ExtractValueInst>(CIU)) {
|
|
if (EVI->getNumIndices() == 1 && EVI->getIndices()[0] == 0) {
|
|
LoadedPtrs.push_back(EVI);
|
|
continue;
|
|
}
|
|
if (EVI->getNumIndices() == 1 && EVI->getIndices()[0] == 1) {
|
|
Preds.push_back(EVI);
|
|
continue;
|
|
}
|
|
}
|
|
HasNonCallUses = true;
|
|
}
|
|
|
|
for (Value *LoadedPtr : LoadedPtrs)
|
|
findCallsAtConstantOffset(DevirtCalls, &HasNonCallUses, LoadedPtr,
|
|
Offset->getZExtValue());
|
|
}
|