blob: 9f2fad7c886a1df4b12129dcb03c453154948026 [file] [log] [blame]
//===- RegisterBounds.cpp ---------------------------------------*- C++ -*----//
//
// The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Various passes to register the bound information of variables into the pools
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "sc-register"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/InstIterator.h"
#include "safecode/RegisterBounds.h"
#include "safecode/AllocatorInfo.h"
#include "safecode/Utility.h"
#include <functional>
using namespace llvm;
namespace {
// Statistics
STATISTIC (RegisteredGVs, "Number of registered global variables");
STATISTIC (RegisteredByVals, "Number of registered byval arguments");
STATISTIC (RegisteredHeapObjs, "Number of registered heap objects");
}
namespace llvm {
char RegisterGlobalVariables::ID = 0;
char RegisterMainArgs::ID = 0;
char RegisterFunctionByvalArguments::ID=0;
char RegisterCustomizedAllocation::ID = 0;
static llvm::RegisterPass<RegisterGlobalVariables>
X1 ("reg-globals", "Register globals into pools", true);
static llvm::RegisterPass<RegisterMainArgs>
X2 ("reg-argv", "Register argv[] into pools", true);
static llvm::RegisterPass<RegisterCustomizedAllocation>
X3 ("reg-custom-alloc", "Register customized allocators", true);
static llvm::RegisterPass<RegisterFunctionByvalArguments>
X4 ("reg-byval-args", "Register byval arguments for functions", true);
//
// Method: registerGV()
//
// Description:
// This method adds code into a program to register a global variable into its
// pool.
//
void
RegisterGlobalVariables::registerGV (GlobalVariable * GV,
Instruction * InsertBefore) {
//
// Do not register the global variable if it has opaque type. This is
// because we cannot determine the size of an opaque type.
//
Type * GlobalType = GV->getType()->getElementType();
if (StructType * ST = dyn_cast<StructType>(GlobalType))
if (ST->isOpaque())
return;
//
// Get the pool into which the global should be registered.
//
Value * PH = ConstantPointerNull::get (getVoidPtrType(GV->getContext()));
Type* csiType = IntegerType::getInt32Ty(GV->getContext());
unsigned TypeSize = TD->getTypeAllocSize((GlobalType));
if (!TypeSize) {
llvm::errs() << "FIXME: Ignoring global of size zero: ";
GV->dump();
return;
}
Value * AllocSize = ConstantInt::get (csiType, TypeSize);
RegisterVariableIntoPool(PH, GV, AllocSize, InsertBefore);
// Update statistics
++RegisteredGVs;
}
bool
RegisterGlobalVariables::runOnModule(Module & M) {
init(M, "pool_register_global");
//
// Get required analysis passes.
//
TD = &getAnalysis<DataLayout>();
//
// Create a skeleton function that will register the global variables.
//
Type * VoidTy = Type::getVoidTy (M.getContext());
Constant * CF = M.getOrInsertFunction ("sc.register_globals", VoidTy, NULL);
Function * F = dyn_cast<Function>(CF);
//
// Create the basic registration function.
//
Instruction * InsertPt = CreateRegistrationFunction (F);
//
// Skip over several types of globals, including:
// llvm.used
// llvm.noinline
// llvm.global_ctors
// Any global pool descriptor
// Any global in the meta-data seciton
//
// The llvm.global_ctors requires special note. Apparently, it will not
// be code generated as the list of constructors if it has any uses
// within the program. This transform must ensure, then, that it is
// never used, even if such a use would otherwise be innocuous.
//
Module::global_iterator GI = M.global_begin(), GE = M.global_end();
for ( ; GI != GE; ++GI) {
GlobalVariable *GV = dyn_cast<GlobalVariable>(GI);
if (!GV) continue;
// Don't register external global variables
//if (GV->isDeclaration()) continue;
std::string name = GV->getName();
// Skip globals in special sections
if ((GV->getSection()) == "llvm.metadata") continue;
if (strncmp(name.c_str(), "llvm.", 5) == 0) continue;
if (strncmp(name.c_str(), "__poolalloc", 11) == 0) continue;
// Linking fails when registering objects in section exitcall.exit
// This is needed for the Linux kernel.
if (GV->getSection() == ".exitcall.exit") continue;
//
// Skip globals that may not be emitted into the final executable.
//
if (GV->hasAvailableExternallyLinkage()) continue;
registerGV(GV, InsertPt);
}
return true;
}
bool
RegisterMainArgs::runOnModule(Module & M) {
init(M, "pool_register");
Function *MainFunc = M.getFunction("main");
if (MainFunc == 0 || MainFunc->isDeclaration()) {
return false;
}
//
// If there are no argc and argv arguments, don't register them.
//
if (MainFunc->arg_size() < 2) {
return false;
}
Function::arg_iterator AI = MainFunc->arg_begin();
Value *Argc = AI;
Value *Argv = ++AI;
Instruction * InsertPt = MainFunc->front().begin();
//
// FIXME:
// This is a hack around what appears to be a DSA bug. These pointers
// should be marked incomplete, but for some reason, in at least one test
// case, they are not.
//
// Register all of the argv strings
//
Type * VoidPtrType = getVoidPtrType(M.getContext());
Type * Int32Type = IntegerType::getInt32Ty(M.getContext());
Constant * CF = M.getOrInsertFunction ("poolargvregister",
getVoidPtrType(M.getContext()),
Int32Type,
PointerType::getUnqual (VoidPtrType),
NULL);
Function * RegisterArgv = dyn_cast<Function>(CF);
std::vector<Value *> fargs;
fargs.push_back (Argc);
fargs.push_back (Argv);
CallInst::Create (RegisterArgv, fargs, "", InsertPt);
return true;
}
///
/// Methods for RegisterCustomizedAllocations
///
void
RegisterCustomizedAllocation::proceedAllocator(Module * M, AllocatorInfo * info) {
Function * allocFunc = M->getFunction(info->getAllocCallName());
if (allocFunc) {
for (Value::use_iterator it = allocFunc->use_begin(),
end = allocFunc->use_end(); it != end; ++it) {
if (CallInst * CI = dyn_cast<CallInst>(*it)) {
if (CI->getCalledValue() == allocFunc) {
registerAllocationSite(CI, info);
++RegisteredHeapObjs;
}
}
//
// If the user is a constant expression, the constant expression may be
// a cast that is used by a call instruction. Get the enclosing call
// instruction if so.
//
if (ConstantExpr * CE = dyn_cast<ConstantExpr>(*it)) {
if (CE->isCast()) {
for (Value::use_iterator iit = CE->use_begin(),
end = CE->use_end(); iit != end; ++iit) {
if (CallInst * CI = dyn_cast<CallInst>(*iit)) {
if (CI->getCalledValue() == CE) {
registerAllocationSite(CI, info);
++RegisteredHeapObjs;
}
}
}
}
}
}
}
//
// Find the deallocation function, visit all uses of it, and process all
// calls to it.
//
Function * freeFunc = M->getFunction(info->getFreeCallName());
if (freeFunc) {
for (Value::use_iterator it = freeFunc->use_begin(),
end = freeFunc->use_end(); it != end; ++it) {
if (CallInst * CI = dyn_cast<CallInst>(*it)) {
if (CI->getCalledValue() == freeFunc) {
registerFreeSite(CI, info);
}
}
//
// If the user is a constant expression, the constant expression may be
// a cast that is used by a call instruction. Get the enclosing call
// instruction if so.
//
if (ConstantExpr * CE = dyn_cast<ConstantExpr>(*it)) {
if (CE->isCast()) {
for (Value::use_iterator iit = CE->use_begin(),
end = CE->use_end(); iit != end; ++iit) {
if (CallInst * CI = dyn_cast<CallInst>(*iit)) {
if (CI->getCalledValue() == CE) {
registerFreeSite(CI, info);
}
}
}
}
}
}
}
}
void
RegisterCustomizedAllocation::proceedReallocator(Module * M, ReAllocatorInfo * info) {
Function * allocFunc = M->getFunction(info->getAllocCallName());
if (allocFunc) {
for (Value::use_iterator it = allocFunc->use_begin(),
end = allocFunc->use_end(); it != end; ++it) {
if (CallInst * CI = dyn_cast<CallInst>(*it)) {
if (CI->getCalledValue()->stripPointerCasts() == allocFunc) {
registerReallocationSite(CI, info);
++RegisteredHeapObjs;
}
}
//
// If the user is a constant expression, the constant expression may be
// a cast that is used by a call instruction. Get the enclosing call
// instruction if so.
//
if (ConstantExpr * CE = dyn_cast<ConstantExpr>(*it)) {
if (CE->isCast()) {
for (Value::use_iterator iit = CE->use_begin(),
end = CE->use_end(); iit != end; ++iit) {
if (CallInst * CI = dyn_cast<CallInst>(*iit)) {
if (CI->getCalledValue() == CE) {
registerReallocationSite(CI, info);
++RegisteredHeapObjs;
}
}
}
}
}
}
}
Function * freeFunc = M->getFunction(info->getFreeCallName());
if (freeFunc) {
for (Value::use_iterator it = freeFunc->use_begin(),
end = freeFunc->use_end(); it != end; ++it) {
if (CallInst * CI = dyn_cast<CallInst>(*it)) {
if (CI->getCalledValue()->stripPointerCasts() == freeFunc) {
registerFreeSite(CI, info);
}
}
//
// If the user is a constant expression, the constant expression may be
// a cast that is used by a call instruction. Get the enclosing call
// instruction if so.
//
if (ConstantExpr * CE = dyn_cast<ConstantExpr>(*it)) {
if (CE->isCast()) {
for (Value::use_iterator iit = CE->use_begin(),
end = CE->use_end(); iit != end; ++iit) {
if (CallInst * CI = dyn_cast<CallInst>(*iit)) {
if (CI->getCalledValue() == CE) {
registerFreeSite(CI, info);
}
}
}
}
}
}
}
}
bool
RegisterCustomizedAllocation::runOnModule(Module & M) {
init(M, "pool_register");
//
// Ensure that a prototype for strlen() exists.
//
DataLayout & TD = getAnalysis<DataLayout>();
M.getOrInsertFunction ("nullstrlen",
TD.getIntPtrType(M.getContext(), 0),
getVoidPtrType(M.getContext()),
NULL);
//
// Get the functions for reregistering and deregistering memory objects.
//
Type * Int32Type = IntegerType::getInt32Ty (M.getContext());
PoolReregisterFunc = (Function *) M.getOrInsertFunction ("pool_reregister",
Type::getVoidTy (M.getContext()),
getVoidPtrType (M),
getVoidPtrType (M),
getVoidPtrType (M),
Int32Type,
NULL);
Constant * CF = M.getOrInsertFunction ("pool_unregister",
Type::getVoidTy (M.getContext()),
getVoidPtrType(M.getContext()),
getVoidPtrType(M.getContext()),
NULL);
PoolUnregisterFunc = dyn_cast<Function>(CF);
AllocatorInfoPass & AIP = getAnalysis<AllocatorInfoPass>();
for (AllocatorInfoPass::alloc_iterator it = AIP.alloc_begin(),
end = AIP.alloc_end(); it != end; ++it) {
proceedAllocator(&M, *it);
}
for (AllocatorInfoPass::realloc_iterator it = AIP.realloc_begin(),
end = AIP.realloc_end(); it != end; ++it) {
proceedReallocator(&M, *it);
}
return true;
}
void
RegisterCustomizedAllocation::registerAllocationSite(CallInst * AllocSite, AllocatorInfo * info) {
//
// Get the pool handle for the node.
//
LLVMContext & Context = AllocSite->getContext();
Value * PH = ConstantPointerNull::get (getVoidPtrType (Context));
//
// Find a place to insert the registration.
//
BasicBlock::iterator InsertPt = AllocSite;
++InsertPt;
//
// Find or create an LLVM value representing the size. If that is not
// possible, do not register the memory object.
//
// We do not assert out here (like one would think) because autoconf scripts
// will create calls to strdup() with zero arguments.
//
Value * AllocSize = info->getOrCreateAllocSize(AllocSite);
if (!AllocSize)
return;
//
// Cast the size to the correct type.
//
if (!AllocSize->getType()->isIntegerTy(32)) {
AllocSize = CastInst::CreateIntegerCast (AllocSize,
Type::getInt32Ty(Context),
false,
AllocSize->getName(),
InsertPt);
}
//
// Create the registration of the object in the pool.
//
RegisterVariableIntoPool (PH, AllocSite, AllocSize, InsertPt);
}
void
RegisterCustomizedAllocation::registerReallocationSite(CallInst * AllocSite, ReAllocatorInfo * info) {
//
// Get the pool handle for the node.
//
LLVMContext & Context = AllocSite->getContext();
Value * PH = ConstantPointerNull::get (getVoidPtrType (Context));
//
// Find the instruction following the reallocation site; this will be where
// we insert the reallocation registration call.
//
BasicBlock::iterator InsertPt = AllocSite;
++InsertPt;
//
// Get the size of the allocation and cast it to the desired type.
//
Value * AllocSize = info->getOrCreateAllocSize(AllocSite);
if (!AllocSize->getType()->isIntegerTy(32)) {
AllocSize = CastInst::CreateIntegerCast (AllocSize,
Type::getInt32Ty(Context),
false,
AllocSize->getName(),
InsertPt);
}
//
// Get the pointers to the old and new memory buffer.
//
Value * OldPtr = castTo (info->getAllocedPointer (AllocSite),
getVoidPtrType(PH->getContext()),
(info->getAllocedPointer (AllocSite))->getName(),
InsertPt);
Value * NewPtr = castTo (AllocSite,
getVoidPtrType(PH->getContext()),
AllocSite->getName(),
InsertPt);
//
// Create the call to reregister the allocation.
//
std::vector<Value *> args;
args.push_back (PH);
args.push_back (NewPtr);
args.push_back (OldPtr);
args.push_back (AllocSize);
CallInst * CI = CallInst::Create(PoolReregisterFunc, args, "", InsertPt);
//
// If there's debug information on the allocation instruction, add it to the
// registration call.
//
if (MDNode * MD = AllocSite->getMetadata ("dbg"))
CI->setMetadata ("dbg", MD);
return;
}
void
RegisterCustomizedAllocation::registerFreeSite (CallInst * FreeSite,
AllocatorInfo * info) {
//
// Get the pointer being deallocated. Strip away casts as these may have
// been inserted after the DSA pass was executed and may, therefore, not have
// a pool handle.
//
Value * ptr = info->getFreedPointer(FreeSite)->stripPointerCasts();
//
// If the pointer is a constant NULL pointer, then don't bother inserting
// an unregister call.
//
if (isa<ConstantPointerNull>(ptr))
return;
//
// Get the pool handle for the freed pointer.
//
LLVMContext & Context = FreeSite->getContext();
Value * PH = ConstantPointerNull::get (getVoidPtrType(Context));
//
// Cast the pointer being unregistered and the pool handle into void pointer
// types.
//
Value * Casted = castTo (ptr,
getVoidPtrType(Context),
ptr->getName()+".casted",
FreeSite);
Value * PHCasted = castTo (PH,
getVoidPtrType(Context),
PH->getName()+".casted",
FreeSite);
//
// Create a call that will unregister the object.
//
std::vector<Value *> args;
args.push_back (PHCasted);
args.push_back (Casted);
CallInst::Create (PoolUnregisterFunc, args, "", FreeSite);
}
Instruction *
RegisterVariables::CreateRegistrationFunction(Function * F) {
//
// Destroy any code that currently exists in the function. We are going to
// replace it.
//
destroyFunction (F);
//
// Add a call in the new constructor function to the SAFECode initialization
// function.
//
BasicBlock * BB = BasicBlock::Create (F->getContext(), "entry", F);
//
// Add a return instruction at the end of the basic block.
//
return ReturnInst::Create (F->getContext(), BB);
}
RegisterVariables::~RegisterVariables() {}
//
// Method: init()
//
// Description:
// This method performs some initialization that is common to all subclasses
// of this pass.
//
// Inputs:
// M - The module in which to insert the function.
// registerName - The name of the function with which to register object.
//
void
RegisterVariables::init (Module & M, std::string registerName) {
//
// Create the type of the registration function.
//
Type * Int8Type = IntegerType::getInt8Ty(M.getContext());
Type * VoidTy = Type::getVoidTy(M.getContext());
std::vector<Type *> ArgTypes;
ArgTypes.push_back (PointerType::getUnqual(Int8Type));
ArgTypes.push_back (PointerType::getUnqual(Int8Type));
ArgTypes.push_back (IntegerType::getInt32Ty(M.getContext()));
FunctionType * PoolRegTy = FunctionType::get (VoidTy, ArgTypes, false);
//
// Create the function.
//
PoolRegisterFunc = dyn_cast<Function>(M.getOrInsertFunction (registerName,
PoolRegTy));
return;
}
void
RegisterVariables::RegisterVariableIntoPool(Value * PH, Value * val, Value * AllocSize, Instruction * InsertBefore) {
if (!PH) {
llvm::errs() << "pool descriptor not present for " << val->getName().str()
<< "\n";
return;
}
Value *GVCasted = castTo (val,
getVoidPtrType(PH->getContext()),
val->getName()+".casted",
InsertBefore);
Value * PHCasted = castTo (PH,
getVoidPtrType(PH->getContext()),
PH->getName()+".casted",
InsertBefore);
std::vector<Value *> args;
args.push_back (PHCasted);
args.push_back (GVCasted);
args.push_back (AllocSize);
CallInst * CI = CallInst::Create(PoolRegisterFunc, args, "", InsertBefore);
//
// If there's debug information on the allocation instruction, add it to the
// registration call.
//
if (Instruction * I = dyn_cast<Instruction>(val->stripPointerCasts()))
if (MDNode * MD = I->getMetadata ("dbg"))
CI->setMetadata ("dbg", MD);
return;
}
bool
RegisterFunctionByvalArguments::runOnModule(Module & M) {
init(M, "pool_register_stack");
//
// Fetch prerequisite analysis passes.
//
TD = &getAnalysis<DataLayout>();
//
// Insert required intrinsics.
//
Constant * CF = M.getOrInsertFunction ("pool_unregister_stack",
Type::getVoidTy (M.getContext()),
getVoidPtrType(M.getContext()),
getVoidPtrType(M.getContext()),
NULL);
StackFree = dyn_cast<Function>(CF);
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++ I) {
//
// Don't process declarations.
//
if (I->isDeclaration()) continue;
//
// Check the name of the function to see if it is a run-time function that
// we should not process.
//
if (I->hasName()) {
std::string Name = I->getName();
if ((Name.find ("__poolalloc") == 0) || (Name.find ("poolregister") == 0))
continue;
}
runOnFunction(*I);
}
return true;
}
//
// Method: runOnFunction()
//
// Description:
// Entry point for this function pass. This method will insert calls to
// register the memory allocated for the byval arguments passed into the
// specified function.
//
// Return value:
// true - The function was modified.
// false - The function was not modified.
//
bool
RegisterFunctionByvalArguments::runOnFunction (Function & F) {
//
// Scan through all arguments of the function. For each byval argument,
// insert code to register the argument into its repspective pool. Also
// record the mapping between argument and pool so that we can insert
// deregistration code at function exit.
//
typedef SmallVector<std::pair<Value*, Argument *>, 4> RegisteredArgTy;
RegisteredArgTy registeredArguments;
LLVMContext & Context = F.getContext();
for (Function::arg_iterator I = F.arg_begin(), E = F.arg_end(); I != E; ++I) {
if (I->hasByValAttr()) {
assert (isa<PointerType>(I->getType()));
PointerType * PT = cast<PointerType>(I->getType());
Type * ET = PT->getElementType();
Value * AllocSize = ConstantInt::get
(IntegerType::getInt32Ty(Context), TD->getTypeAllocSize(ET));
Value * PH = ConstantPointerNull::get (getVoidPtrType(Context));
Instruction * InsertBefore = &(F.getEntryBlock().front());
RegisterVariableIntoPool(PH, &*I, AllocSize, InsertBefore);
registeredArguments.push_back(std::make_pair<Value*, Argument*>(PH, &*I));
}
}
//
// Find all basic blocks which terminate the function.
//
SmallSet<BasicBlock *, 4> exitBlocks;
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ++I) {
if (isa<ReturnInst>(*I) || isa<ResumeInst>(*I)) {
exitBlocks.insert(I->getParent());
}
}
//
// At each function exit, insert code to deregister all byval arguments.
//
for (SmallSet<BasicBlock*, 4>::const_iterator BI = exitBlocks.begin(),
BE = exitBlocks.end();
BI != BE; ++BI) {
for (RegisteredArgTy::const_iterator I = registeredArguments.begin(),
E = registeredArguments.end();
I != E; ++I) {
SmallVector<Value *, 2> args;
Instruction * Pt = &((*BI)->back());
Value *CastPH = castTo (I->first, getVoidPtrType(Context), Pt);
Value *CastV = castTo (I->second, getVoidPtrType(Context), Pt);
args.push_back (CastPH);
args.push_back (CastV);
CallInst::Create (StackFree, args, "", Pt);
}
}
//
// Update the statistics on the number of registered byval arguments.
//
if (registeredArguments.size())
RegisteredByVals += registeredArguments.size();
return true;
}
}