// // Method: getUnsafeAllocsFromABC() // // Description: // Find all memory objects that are both allocated on the stack and are not // proven to be indexed in a type-safe manner according to the static array // bounds checking pass. // // Notes: // This method saves its results be remembering the set of DSNodes which are // both on the stack and potentially indexed in a type-unsafe manner. // // FIXME: // This method only considers unsafe GEP instructions; it does not consider // unsafe call instructions or other instructions deemed unsafe by the array // bounds checking pass. // void ConvertUnsafeAllocas::getUnsafeAllocsFromABC(Module & M) { UnsafeAllocaNodeListBuilder Builder(budsPass, unsafeAllocaNodes); Builder.visit(M); #if 0 // Haohui: Disable it right now since nobody using the code std::map<BasicBlock *,std::set<Instruction*>*> UnsafeGEPMap= abcPass->UnsafeGetElemPtrs; std::map<BasicBlock *,std::set<Instruction*>*>::const_iterator bCurrent = UnsafeGEPMap.begin(), bEnd = UnsafeGEPMap.end(); for (; bCurrent != bEnd; ++bCurrent) { std::set<Instruction *> * UnsafeGetElemPtrs = bCurrent->second; std::set<Instruction *>::const_iterator iCurrent = UnsafeGetElemPtrs->begin(), iEnd = UnsafeGetElemPtrs->end(); for (; iCurrent != iEnd; ++iCurrent) { if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(*iCurrent)) { Value *pointerOperand = GEP->getPointerOperand(); DSGraph * TDG = budsPass->getDSGraph(*(GEP->getParent()->getParent())); DSNode *DSN = TDG->getNodeForValue(pointerOperand).getNode(); //FIXME DO we really need this ? markReachableAllocas(DSN); if (DSN && DSN->isAllocaNode() && !DSN->isNodeCompletelyFolded()) { unsafeAllocaNodes.push_back(DSN); } } else { //call instruction add the corresponding *iCurrent->dump(); //FIXME abort(); } } } #endif }
void ListaDescritor::imprime() { //DSGRAPH DSGraph ds; ds.showCPP(this->descritor); //DSGRAPH }
void Fila::imprime() { //DSGRAPH DSGraph ds; ds.showCPP(this->inicio, 2, this->inicio, this->fim); //DSGRAPH }
// // Method: findGlobalPoolNodes() // // Description: // This method finds DSNodes that are reachable from globals and that need a // pool. The Automatic Pool Allocation transform will use the returned // information to build global pools for the DSNodes in question. // // Note that this method does not assign DSNodes to pools; it merely decides // which DSNodes are reachable from globals and will need a pool of global // scope. // // Outputs: // Nodes - The DSNodes that are both reachable from globals and which should // have global pools will be *added* to this container. // void AllHeapNodesHeuristic::findGlobalPoolNodes (DSNodeSet_t & Nodes) { // Get the globals graph for the program. DSGraph* GG = Graphs->getGlobalsGraph(); // Get all of the nodes reachable from globals. DenseSet<const DSNode*> GlobalHeapNodes; GetNodesReachableFromGlobals (GG, GlobalHeapNodes); // // Create a global pool for each global DSNode. // for (DenseSet<const DSNode *>::iterator NI = GlobalHeapNodes.begin(); NI != GlobalHeapNodes.end();++NI) { const DSNode * N = *NI; PoolMap[N] = OnePool(N); } // // Now find all DSNodes belonging to function-local DSGraphs which are // mirrored in the globals graph. These DSNodes require a global pool, too. // for (Module::iterator F = M->begin(); F != M->end(); ++F) { if (Graphs->hasDSGraph(*F)) { DSGraph* G = Graphs->getDSGraph(*F); DSGraph::NodeMapTy NodeMap; G->computeGToGGMapping (NodeMap); // // Scan through all DSNodes in the local graph. If a local DSNode has a // corresponding DSNode in the globals graph that is reachable from a // global, then add the local DSNode to the set of DSNodes reachable from // a global. // DSGraph::node_iterator ni = G->node_begin(); for (; ni != G->node_end(); ++ni) { DSNode * N = ni; DSNode * GGN = NodeMap[N].getNode(); //assert (!GGN || GlobalHeapNodes.count (GGN)); if (GGN && GlobalHeapNodes.count (GGN)) PoolMap[GGN].NodesInPool.push_back (N); } } } // // Copy the values into the output container. Note that DenseSet has no // iterator traits (or whatever allows us to treat DenseSet has a generic // container), so we have to use a loop to copy values from the DenseSet into // the output container. // for (DenseSet<const DSNode*>::iterator I = GlobalHeapNodes.begin(), E = GlobalHeapNodes.end(); I != E; ++I) { Nodes.insert (*I); } return; }
void visitGetElementPtrInst(GetElementPtrInst &GEP) { Value *pointerOperand = GEP.getPointerOperand(); DSGraph * TDG = budsPass->getDSGraph(*(GEP.getParent()->getParent())); DSNode *DSN = TDG->getNodeForValue(pointerOperand).getNode(); //FIXME DO we really need this ? markReachableAllocas(DSN); if (DSN && DSN->isAllocaNode() && !DSN->isNodeCompletelyFolded()) { unsafeAllocaNodes.push_back(DSN); } }
/** * Imprime os elementos da lista com o auxilio da biblioteca DSGraph. */ void ListaEncadeada::imprime() { No *p = this->prim; //DSGRAPH DSGraph ds; ds.showCPP(p); //DSGRAPH }
void PoolRegisterElimination::removeTypeSafeRegistrations (const char * name) { // // Scan through all uses of the registration function and see if it can be // safely removed. If so, schedule it for removal. // std::vector<CallInst*> toBeRemoved; Function * F = intrinsic->getIntrinsic(name).F; // // Look for and record all registrations that can be deleted. // for (Value::use_iterator UI=F->use_begin(), UE=F->use_end(); UI != UE; ++UI) { // // Get the pointer to the registered object. // CallInst * CI = cast<CallInst>(*UI); Value * Ptr = intrinsic->getValuePointer(CI); // Lookup the DSNode for the value in the function's DSGraph. // DSGraph * TDG = dsaPass->getDSGraph(*(CI->getParent()->getParent())); DSNodeHandle DSH = TDG->getNodeForValue(Ptr); assert ((!(DSH.isNull())) && "No DSNode for Value!\n"); // // If the DSNode is type-safe and is never used as an array, then there // will never be a need to look it up in a splay tree, so remove its // registration. // DSNode * N = DSH.getNode(); if(!N->isArrayNode() && TS->isTypeSafe(Ptr, F)){ toBeRemoved.push_back(CI); } } // // Update the statistics. // if (toBeRemoved.size()) { RemovedRegistration += toBeRemoved.size(); TypeSafeRegistrations += toBeRemoved.size(); } // // Remove the unnecesary registrations. // std::vector<CallInst*>::iterator it, end; for (it = toBeRemoved.begin(), end = toBeRemoved.end(); it != end; ++it) { (*it)->eraseFromParent(); } }
// // Method: insertHardDanglingPointers() // // Description: // Insert dangling pointer dereferences into the code. This is done by // finding instructions that store pointers to memory and free'ing those // pointers before the store. Subsequent loads and uses of the pointer will // cause a dangling pointer dereference. // // Return value: // true - The module was modified. // false - The module was left unmodified. // // Notes: // This code utilizes DSA to ensure that the pointer can point to heap // memory (although the pointer is allowed to alias global and stack memory). // bool FaultInjector::insertHardDanglingPointers (Function & F) { // // Ensure that we can get analysis information for this function. // if (!(TDPass->hasDSGraph(F))) return false; // // Scan through each instruction of the function looking for store // instructions that store a pointer to memory. Free the pointer right // before the store instruction. // DSGraph * DSG = TDPass->getDSGraph(F); for (Function::iterator fI = F.begin(), fE = F.end(); fI != fE; ++fI) { BasicBlock & BB = *fI; for (BasicBlock::iterator bI = BB.begin(), bE = BB.end(); bI != bE; ++bI) { Instruction * I = bI; // // Look to see if there is an instruction that stores a pointer to // memory. If so, then free the pointer before the store. // if (StoreInst * SI = dyn_cast<StoreInst>(I)) { if (isa<PointerType>(SI->getOperand(0)->getType())) { Value * Pointer = SI->getOperand(0); // // Check to ensure that the pointer aliases with the heap. If so, go // ahead and add the free. Note that we may introduce an invalid // free, but we're injecting errors, so I think that's okay. // DSNode * Node = DSG->getNodeForValue(Pointer).getNode(); if (Node && (Node->isHeapNode())) { // Skip if we should not insert a fault. if (!doFault()) continue; // // Print information about where the fault is being inserted. // printSourceInfo ("Hard dangling pointer", I); CallInst::Create (Free, Pointer, "", I); ++DPFaults; } } } } } return (DPFaults > 0); }
// Find the number of arguments we need to add to the functions. void CSDataRando::findFunctionArgNodes(const std::vector<const Function *> &Functions) { std::vector<DSNodeHandle> RootNodes; for (const Function *F : Functions) { DSGraph *G = DSA->getDSGraph(*F); G->getFunctionArgumentsForCall(F, RootNodes); } // No additional args to pass. if (RootNodes.size() == 0) { return; } DenseSet<const DSNode*> MarkedNodes; for (DSNodeHandle &NH : RootNodes) { if (DSNode *N = NH.getNode()) { N->markReachableNodes(MarkedNodes); } } // Remove global nodes from the arg nodes. If we are using the bottom-up // analysis then if a node is a global node all contexts will use the global map. for (auto i : GlobalNodes) { MarkedNodes.erase(i); } // Remove any nodes that are marked do not encrypt. SmallVector<const DSNode*, 8> MarkedNodeWorkList; for (auto i : MarkedNodes) { if (i->isDoNotEncryptNode()) { MarkedNodeWorkList.push_back(i); } } for (auto i : MarkedNodeWorkList) { MarkedNodes.erase(i); } if (MarkedNodes.empty()) { return; } // Create a FuncInfo entry for each of the functions with the arg nodes that // need to be passed for (const Function *F : Functions) { FuncInfo &FI = FunctionInfo[F]; FI.ArgNodes.insert(FI.ArgNodes.end(), MarkedNodes.begin(), MarkedNodes.end()); } }
/// OptimizeGlobals - This method uses information taken from DSA to optimize /// global variables. /// bool DSOpt::OptimizeGlobals(Module &M) { DSGraph* GG = TD->getGlobalsGraph(); const DSGraph::ScalarMapTy &SM = GG->getScalarMap(); bool Changed = false; for (Module::global_iterator I = M.global_begin(), E = M.global_end(); I != E; ++I) if (!I->isDeclaration()) { // Loop over all of the non-external globals... // Look up the node corresponding to this global, if it exists. DSNode *GNode = 0; DSGraph::ScalarMapTy::const_iterator SMI = SM.find(I); if (SMI != SM.end()) GNode = SMI->second.getNode(); if (GNode == 0 && I->hasInternalLinkage()) { // If there is no entry in the scalar map for this global, it was never // referenced in the program. If it has internal linkage, that means we // can delete it. We don't ACTUALLY want to delete the global, just // remove anything that references the global: later passes will take // care of nuking it. if (!I->use_empty()) { I->replaceAllUsesWith(ConstantPointerNull::get(I->getType())); ++NumGlobalsIsolated; } } else if (GNode && GNode->NodeType.isCompleteNode()) { // If the node has not been read or written, and it is not externally // visible, kill any references to it so it can be DCE'd. if (!GNode->NodeType.isModifiedNode() && !GNode->NodeType.isReadNode() &&I->hasInternalLinkage()){ if (!I->use_empty()) { I->replaceAllUsesWith(ConstantPointerNull::get(I->getType())); ++NumGlobalsIsolated; } } // We expect that there will almost always be a node for this global. // If there is, and the node doesn't have the M bit set, we can set the // 'constant' bit on the global. if (!GNode->NodeType.isModifiedNode() && !I->isConstant()) { I->setConstant(true); ++NumGlobalsConstanted; Changed = true; } } } return Changed; }
void TDDataStructures::ComputePostOrder(const Function* F, hash_set<DSGraph*> &Visited, std::vector<DSGraph*> &PostOrder) { if (F->isDeclaration()) return; DSGraph* G = getOrFetchDSGraph(F); if (Visited.count(G)) return; Visited.insert(G); // Recursively traverse all of the callee graphs. for (DSGraph::fc_iterator CI = G->fc_begin(), CE = G->fc_end(); CI != CE; ++CI){ Instruction *CallI = CI->getCallSite().getInstruction(); for (calleeTy::iterator I = callee.begin(CallI), E = callee.end(CallI); I != E; ++I) ComputePostOrder(*I, Visited, PostOrder); } PostOrder.push_back(G); }
// // Function: GetNodesReachableFromGlobals() // // Description: // This function finds all DSNodes which are reachable from globals. It finds // DSNodes both within the local DSGraph as well as in the Globals graph that // are reachable from globals. // // Inputs: // G - The Globals Graph. // // Outputs: // NodesFromGlobals - A reference to a container object in which to record // DSNodes reachable from globals. DSNodes are *added* to // this container; it is not cleared by this function. // DSNodes from both the local and globals graph are added. static void GetNodesReachableFromGlobals (DSGraph* G, DenseSet<const DSNode*> &NodesFromGlobals) { // // Ensure that G is the globals graph. // assert (G->getGlobalsGraph() == 0); DSGraph * GlobalsGraph = G; // // Find all DSNodes which are reachable in the globals graph. // for (DSGraph::node_iterator NI = GlobalsGraph->node_begin(); NI != GlobalsGraph->node_end(); ++NI) { NI->markReachableNodes(NodesFromGlobals); } }
/** * Realiza a busca de um valor na lista e retorna * o nó com este valor. * @param valor */ void ListaDescritor::busca(int valor) { No *noBusca = NULL; if(this->vazia()) { cout<<"Lista se encontra vazia."<<endl; exit(1); } else if(this->descritor->getAnt()->getInfo() == valor) { noBusca = this->descritor->getAnt(); //primeiro nó da lista } else if(this->descritor->getProx()->getInfo() == valor) { noBusca = this->descritor->getProx(); //ultimo nó da lista } else { No *l = this->descritor->getAnt(); // primeiro nó da lista while(l!= NULL && l->getInfo()!= valor) { l = l->getProx(); } noBusca = l; } //DSGRAPH DSGraph ds; //DSGRAPH if(noBusca != NULL) { //DSGRAPH showComment("Valor %d encontrado...", valor); ds.showCPP(this->descritor, 1, noBusca); //DSGRAPH } else { //DSGRAPH showComment("Valor %d nao encontrado...", valor); ds.showCPP(this->descritor); //DSGRAPH } }
// Get all nodes in all function DSGraphs and the global DSGraph that contain // global values. void CSDataRando::findGlobalNodes(Module &M) { DSGraph *GG = DSA->getGlobalsGraph(); for (auto i = GG->node_begin(), e = GG->node_end(); i != e; i++) { GlobalNodes.insert(&*i); } for (Function &F : M) { if ((!F.isDeclaration()) && DSA->hasDSGraph(F)) { DSGraph *G = DSA->getDSGraph(F); FuncInfo &FI = FunctionInfo[&F]; DSGraph::NodeMapTy NodeMap; G->computeGToGGMapping(NodeMap); for (auto i : NodeMap) { GlobalNodes.insert(i.first); FI.ToGlobalNodeMap[i.first] = i.second.getNode(); } } } }
// // Method: TransformCollapsedAllocas() // // Description: // Transform all stack allocated objects that are type-unknown // (i.e., are completely folded) to heap allocations. // void ConvertUnsafeAllocas::TransformCollapsedAllocas(Module &M) { // // Need to check if the following is incomplete because we are only looking // at scalars. // // It may be complete because every instruction actually is a scalar in // LLVM?! for (Module::iterator MI = M.begin(), ME = M.end(); MI != ME; ++MI) { if (!MI->isDeclaration()) { DSGraph *G = budsPass->getDSGraph(*MI); DSGraph::ScalarMapTy &SM = G->getScalarMap(); for (DSGraph::ScalarMapTy::iterator SMI = SM.begin(), SME = SM.end(); SMI != SME; ) { if (AllocaInst *AI = dyn_cast<AllocaInst>((Value *)(SMI->first))) { if (SMI->second.getNode()->isNodeCompletelyFolded()) { Value *AllocSize = ConstantInt::get(Int32Type, TD->getTypeAllocSize(AI->getAllocatedType())); if (AI->isArrayAllocation()) AllocSize = BinaryOperator::Create(Instruction::Mul, AllocSize, AI->getOperand(0), "sizetmp", AI); CallInst *CI = CallInst::Create (kmalloc, AllocSize, "", AI); Value * MI = castTo (CI, AI->getType(), "", AI); InsertFreesAtEnd(CI); AI->replaceAllUsesWith(MI); SMI->second.getNode()->setHeapMarker(); SM.erase(SMI++); AI->getParent()->getInstList().erase(AI); ++ConvAllocas; } else { ++SMI; } } else { ++SMI; } } } } }
// // Function: processRuntimeCheck() // // Description: // Modify a run-time check so that its return value has the same DSNode as the // checked pointer. // // Inputs: // M - The module in which calls to the function live. // name - The name of the function for which direct calls should be processed. // arg - The argument index that contains the pointer which the run-time // check returns. // void StdLibDataStructures::processRuntimeCheck (Module & M, std::string name, unsigned arg) { // // Get a pointer to the function. // Function* F = M.getFunction (name); // // If the function doesn't exist, then there is no work to do. // if (!F) return; // // Scan through all direct calls to the function (there should only be direct // calls) and process each one. // for (Value::use_iterator ii = F->use_begin(), ee = F->use_end(); ii != ee; ++ii) { if (CallInst* CI = dyn_cast<CallInst>(*ii)) { if (CI->getCalledValue() == F) { DSGraph* Graph = getDSGraph(*CI->getParent()->getParent()); DSNodeHandle & RetNode = Graph->getNodeForValue(CI); DSNodeHandle & ArgNode = Graph->getNodeForValue(CI->getArgOperand(arg)); RetNode.mergeWith(ArgNode); } } } // // Erase the DSCallSites for this function. This should prevent other DSA // passes from making the DSNodes passed to/returned from the function // from becoming Incomplete or External. // eraseCallsTo (F); return; }
// // Function: FoldNodesInDSGraph() // // Description: // This function will take the specified DSGraph and fold all DSNodes within // it that are marked with the heap flag. // static void FoldNodesInDSGraph (DSGraph & Graph) { // Worklist of heap nodes to process std::vector<DSNodeHandle> HeapNodes; // // Go find all of the heap nodes. // DSGraph::node_iterator i; DSGraph::node_iterator e = Graph.node_end(); for (i = Graph.node_begin(); i != e; ++i) { DSNode * Node = i; if (Node->isHeapNode()) HeapNodes.push_back (DSNodeHandle(Node)); } // // Fold all of the heap nodes; this makes them type-unknown. // for (unsigned i = 0; i < HeapNodes.size(); ++i) HeapNodes[i].getNode()->foldNodeCompletely(); return; }
// // Method: getFunctionTargets() // // Description: // This method finds all of the potential targets of the specified indirect // function call. // void CompleteChecks::getFunctionTargets (CallSite CS, std::vector<const Function *> & Targets) { EQTDDataStructures & dsaPass = getAnalysis<EQTDDataStructures>(); const DSCallGraph & callgraph = dsaPass.getCallGraph(); DSGraph* G = dsaPass.getGlobalsGraph(); DSGraph::ScalarMapTy& SM = G->getScalarMap(); DSCallGraph::callee_iterator csi = callgraph.callee_begin(CS); DSCallGraph::callee_iterator cse = callgraph.callee_end(CS); for (; csi != cse; ++csi) { const Function *F = *csi; DSCallGraph::scc_iterator sccii = callgraph.scc_begin(F), sccee = callgraph.scc_end(F); for (;sccii != sccee; ++sccii) { DSGraph::ScalarMapTy::const_iterator I = SM.find(SM.getLeaderForGlobal(*sccii)); if (I != SM.end() && !((*sccii)->isDeclaration())) { Targets.push_back (*sccii); } } } const Function *F1 = CS.getInstruction()->getParent()->getParent(); F1 = callgraph.sccLeader(&*F1); DSCallGraph::scc_iterator sccii = callgraph.scc_begin(F1), sccee = callgraph.scc_end(F1); for(;sccii != sccee; ++sccii) { DSGraph::ScalarMapTy::const_iterator I = SM.find(SM.getLeaderForGlobal(*sccii)); if (I != SM.end() && !((*sccii)->isDeclaration())) { Targets.push_back (*sccii); } } return; }
void TDDataStructures::ComputePostOrder(const Function &F, DenseSet<DSGraph*> &Visited, std::vector<DSGraph*> &PostOrder) { if (F.isDeclaration()) return; DSGraph* G = getOrCreateGraph(&F); if (!Visited.insert(G).second) return; // Recursively traverse all of the callee graphs. svset<const Function*> Callees; // Go through all of the callsites in this graph and find all callees // Here we're trying to capture all possible callees so that we can ensure // each function has all possible callers inlined into it. for (DSGraph::fc_iterator CI = G->fc_begin(), E = G->fc_end(); CI != E; ++CI) { // Direct calls are easy, no reason to look at DSCallGraph // or anything to do with SCC's if (CI->isDirectCall()) { ComputePostOrder(*CI->getCalleeFunc(), Visited, PostOrder); } else { // Otherwise, ask the DSCallGraph for the full set of possible // callees for this callsite. // This includes all members of the SCC's of those callees, // and well as others in F's SCC, since we must assume // any indirect call might be intra-SCC. callgraph.addFullFunctionSet(CI->getCallSite(), Callees); } } for (svset<const Function*>::iterator I = Callees.begin(), E = Callees.end(); I != E; ++I) ComputePostOrder(**I, Visited, PostOrder); PostOrder.push_back(G); }
// // Method: getDSNodeHandle() // // Description: // This method looks up the DSNodeHandle for a given LLVM value. The context // of the value is the specified function, although if it is a global value, // the DSNodeHandle may exist within the global DSGraph. // // Return value: // A DSNodeHandle for the value is returned. This DSNodeHandle could either // be in the function's DSGraph or from the GlobalsGraph. Note that the // DSNodeHandle may represent a NULL DSNode. // DSNodeHandle CompleteChecks::getDSNodeHandle (const Value * V, const Function * F) { // // Get access to the points-to results. // EQTDDataStructures & dsaPass = getAnalysis<EQTDDataStructures>(); // // Ensure that the function has a DSGraph // assert (dsaPass.hasDSGraph(*F) && "No DSGraph for function!\n"); // // Lookup the DSNode for the value in the function's DSGraph. // DSGraph * TDG = dsaPass.getDSGraph(*F); DSNodeHandle DSH = TDG->getNodeForValue(V); // // If the value wasn't found in the function's DSGraph, then maybe we can // find the value in the globals graph. // if ((DSH.isNull()) && (isa<GlobalValue>(V))) { // // Try looking up this DSNode value in the globals graph. Note that // globals are put into equivalence classes; we may need to first find the // equivalence class to which our global belongs, find the global that // represents all globals in that equivalence class, and then look up the // DSNode Handle for *that* global. // DSGraph * GlobalsGraph = TDG->getGlobalsGraph (); DSH = GlobalsGraph->getNodeForValue(V); if (DSH.isNull()) { // // DSA does not currently handle global aliases. // if (!isa<GlobalAlias>(V)) { // // We have to dig into the globalEC of the DSGraph to find the DSNode. // const GlobalValue * GV = dyn_cast<GlobalValue>(V); const GlobalValue * Leader; Leader = GlobalsGraph->getGlobalECs().getLeaderValue(GV); DSH = GlobalsGraph->getNodeForValue(Leader); } } } return DSH; }
// // Method: getLocalPoolNodes() // // Description: // For a given function, determine which DSNodes for that function should have // local pools created for them. // void Heuristic::getLocalPoolNodes (const Function & F, DSNodeList_t & Nodes) { // // Get the DSGraph of the specified function. If the DSGraph has no nodes, // then there is nothing we need to do. // DSGraph* G = Graphs->getDSGraph(F); if (G->node_begin() == G->node_end()) return; // // Calculate which DSNodes are reachable from globals. If a node is reachable // from a global, we will create a global pool for it, so no argument passage // is required. Graphs->getGlobalsGraph(); // Map all node reachable from this global to the corresponding nodes in // the globals graph. DSGraph::NodeMapTy GlobalsGraphNodeMapping; G->computeGToGGMapping(GlobalsGraphNodeMapping); // // Loop over all of the nodes which are non-escaping, adding pool-allocatable // ones to the NodesToPA vector. In other words, scan over the DSGraph and // find nodes for which a new pool must be created within this function. // for (DSGraph::node_iterator I = G->node_begin(), E = G->node_end(); I != E; ++I){ // Get the DSNode and, if applicable, its mirror in the globals graph DSNode * N = I; DSNode * GGN = GlobalsGraphNodeMapping[N].getNode(); // // Only the following nodes are pool allocated: // 1) Local Heap nodes // 2) Nodes which are mirrored in the globals graph and, in the globals // graph, are heap nodes. // if ((N->isHeapNode()) || (GGN && GGN->isHeapNode())) { if (!(GlobalPoolNodes.count (N) || GlobalPoolNodes.count (GGN))) { // Otherwise, if it was not passed in from outside the function, it must // be a local pool! assert((!N->isGlobalNode() || N->isPtrToIntNode()) && "Should be in global mapping!"); if(!N->isPtrToIntNode()) { Nodes.push_back (N); } } } } return; }
void BUDataStructures::CloneAuxIntoGlobal(DSGraph* G) { DSGraph* GG = G->getGlobalsGraph(); ReachabilityCloner RC(GG, G, 0); for(DSGraph::afc_iterator ii = G->afc_begin(), ee = G->afc_end(); ii != ee; ++ii) { //cerr << "Pushing " << ii->getCallSite().getInstruction()->getOperand(0) << "\n"; //If we can, merge with an existing call site for this instruction if (GG->hasNodeForValue(ii->getCallSite().getInstruction()->getOperand(0))) { DSGraph::afc_iterator GGii; for(GGii = GG->afc_begin(); GGii != GG->afc_end(); ++GGii) if (GGii->getCallSite().getInstruction()->getOperand(0) == ii->getCallSite().getInstruction()->getOperand(0)) break; if (GGii != GG->afc_end()) RC.cloneCallSite(*ii).mergeWith(*GGii); else GG->addAuxFunctionCall(RC.cloneCallSite(*ii)); } else { GG->addAuxFunctionCall(RC.cloneCallSite(*ii)); } } }
void initialize(const Module *M, const DataStructures *DS) { parseValue(M); assert(V && "Failed to parse value?"); if (isa<GlobalValue>(V)) { DSGraph *G = DS->getGlobalsGraph(); assert(G->hasNodeForValue(V) && "Node not in specified graph!"); NH = G->getNodeForValue(V); } else { assert(F && "No function?"); DSGraph *G = DS->getDSGraph(*F); assert(G->hasNodeForValue(V) && "Node not in specified graph!"); NH = G->getNodeForValue(V); } // Handle offsets, if any // For each offset in the offsets vector, follow the link at that offset for (OffsetVectorTy::const_iterator I = offsets.begin(), E = offsets.end(); I != E; ++I ) { assert(!NH.isNull() && "Null NodeHandle?"); assert(NH.hasLink(*I) && "Handle doesn't have link?"); // Follow the offset NH = NH.getLink(*I); } }
// run - Calculate the top down data structure graphs for each function in the // program. // bool TDDataStructures::runOnModule(Module &M) { init(useEQBU ? &getAnalysis<EquivBUDataStructures>() : &getAnalysis<BUDataStructures>(), true, true, true, false); // Figure out which functions must not mark their arguments complete because // they are accessible outside this compilation unit. Currently, these // arguments are functions which are reachable by incomplete or external // nodes in the globals graph. const DSScalarMap &GGSM = GlobalsGraph->getScalarMap(); DenseSet<DSNode*> Visited; for (DSScalarMap::global_iterator I=GGSM.global_begin(), E=GGSM.global_end(); I != E; ++I) { DSNode *N = GGSM.find(*I)->second.getNode(); if (N->isIncompleteNode() || N->isExternalNode()) markReachableFunctionsExternallyAccessible(N, Visited); } // Loop over unresolved call nodes. Any functions passed into (but not // returned!) from unresolvable call nodes may be invoked outside of the // current module. for (DSGraph::afc_iterator I = GlobalsGraph->afc_begin(), E = GlobalsGraph->afc_end(); I != E; ++I) for (unsigned arg = 0, e = I->getNumPtrArgs(); arg != e; ++arg) markReachableFunctionsExternallyAccessible(I->getPtrArg(arg).getNode(), Visited); Visited.clear(); // Clear Aux of Globals Graph to be refilled in later by post-TD unresolved // functions GlobalsGraph->getAuxFunctionCalls().clear(); // Functions without internal linkage are definitely externally callable! for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) if (!I->isDeclaration() && !I->hasInternalLinkage() && !I->hasPrivateLinkage()) ExternallyCallable.insert(I); // Debug code to print the functions that are externally callable #if 0 for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) if (ExternallyCallable.count(I)) { errs() << "ExternallyCallable: " << I->getNameStr() << "\n"; } #endif // We want to traverse the call graph in reverse post-order. To do this, we // calculate a post-order traversal, then reverse it. DenseSet<DSGraph*> VisitedGraph; std::vector<DSGraph*> PostOrder; {TIME_REGION(XXX, "td:Compute postorder"); // Calculate top-down from main... if (Function *F = M.getFunction("main")) ComputePostOrder(*F, VisitedGraph, PostOrder); // Next calculate the graphs for each unreachable function... for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) if (!I->isDeclaration()) ComputePostOrder(*I, VisitedGraph, PostOrder); VisitedGraph.clear(); // Release memory! } {TIME_REGION(XXX, "td:Inline stuff"); // Visit each of the graphs in reverse post-order now! while (!PostOrder.empty()) { InlineCallersIntoGraph(PostOrder.back()); PostOrder.pop_back(); } } // Free the IndCallMap. while (!IndCallMap.empty()) { delete IndCallMap.begin()->second; IndCallMap.erase(IndCallMap.begin()); } formGlobalECs(); ExternallyCallable.clear(); GlobalsGraph->removeTriviallyDeadNodes(); GlobalsGraph->computeExternalFlags(DSGraph::DontMarkFormalsExternal); GlobalsGraph->computeIntPtrFlags(); // Make sure each graph has updated external information about globals // in the globals graph. VisitedGraph.clear(); for (Module::iterator F = M.begin(); F != M.end(); ++F) { if (!(F->isDeclaration())){ DSGraph *Graph = getOrCreateGraph(F); if (!VisitedGraph.insert(Graph).second) continue; cloneGlobalsInto(Graph, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); Graph->computeExternalFlags(DSGraph::DontMarkFormalsExternal); Graph->computeIntPtrFlags(); // Clean up uninteresting nodes Graph->removeDeadNodes(0); } } // CBU contains the correct call graph. // Restore it, so that subsequent passes and clients can get it. restoreCorrectCallGraph(); /// Added by Zhiyuan: print out the DSGraph. if (llvm::DebugFlag) { print(errs(), &M); } return false; }
/// InlineCallersIntoGraph - Inline all of the callers of the specified DS graph /// into it, then recompute completeness of nodes in the resultant graph. void TDDataStructures::InlineCallersIntoGraph(DSGraph* DSG) { // Inline caller graphs into this graph. First step, get the list of call // sites that call into this graph. std::vector<CallerCallEdge> EdgesFromCaller; std::map<DSGraph*, std::vector<CallerCallEdge> >::iterator CEI = CallerEdges.find(DSG); if (CEI != CallerEdges.end()) { std::swap(CEI->second, EdgesFromCaller); CallerEdges.erase(CEI); } // Sort the caller sites to provide a by-caller-graph ordering. std::sort(EdgesFromCaller.begin(), EdgesFromCaller.end()); // Merge information from the globals graph into this graph. FIXME: This is // stupid. Instead of us cloning information from the GG into this graph, // then having RemoveDeadNodes clone it back, we should do all of this as a // post-pass over all of the graphs. We need to take cloning out of // removeDeadNodes and gut removeDeadNodes at the same time first though. :( cloneGlobalsInto(DSG, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); DEBUG(errs() << "[TD] Inlining callers into '" << DSG->getFunctionNames() << "'\n"); DSG->maskIncompleteMarkers(); // Iteratively inline caller graphs into this graph. while (!EdgesFromCaller.empty()) { DSGraph* CallerGraph = EdgesFromCaller.back().CallerGraph; // Iterate through all of the call sites of this graph, cloning and merging // any nodes required by the call. ReachabilityCloner RC(DSG, CallerGraph, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); // Inline all call sites from this caller graph. do { const DSCallSite &CS = *EdgesFromCaller.back().CS; const Function &CF = *EdgesFromCaller.back().CalledFunction; DEBUG(errs() << " [TD] Inlining graph into Fn '" << CF.getName().str() << "' from "); if (CallerGraph->getReturnNodes().empty()) { DEBUG(errs() << "SYNTHESIZED INDIRECT GRAPH"); } else { DEBUG(errs() << "Fn '" << CS.getCallSite().getInstruction()-> getParent()->getParent()->getName().str() << "'"); } DEBUG(errs() << ": " << CF.getFunctionType()->getNumParams() << " args\n"); // Get the formal argument and return nodes for the called function and // merge them with the cloned subgraph. DSCallSite T1 = DSG->getCallSiteForArguments(CF); RC.mergeCallSite(T1, CS); ++NumTDInlines; EdgesFromCaller.pop_back(); } while (!EdgesFromCaller.empty() && EdgesFromCaller.back().CallerGraph == CallerGraph); } // Next, now that this graph is finalized, we need to recompute the // incompleteness markers for this graph and remove unreachable nodes. // If any of the functions is externally callable, treat everything in its // SCC as externally callable. bool isExternallyCallable = false; for (DSGraph::retnodes_iterator I = DSG->retnodes_begin(), E = DSG->retnodes_end(); I != E; ++I) if (ExternallyCallable.count(I->first)) { isExternallyCallable = true; break; } // Recompute the Incomplete markers. Depends on whether args are complete unsigned IncFlags = DSGraph::IgnoreFormalArgs; IncFlags |= DSGraph::IgnoreGlobals | DSGraph::MarkVAStart; DSG->markIncompleteNodes(IncFlags); // If this graph contains functions that are externally callable, now is the time to mark // their arguments and return values as external. At this point TD is inlining all caller information, // and that means External callers too. unsigned ExtFlags = isExternallyCallable ? DSGraph::MarkFormalsExternal : DSGraph::DontMarkFormalsExternal; DSG->computeExternalFlags(ExtFlags); DSG->computeIntPtrFlags(); cloneIntoGlobals(DSG, DSGraph::DontCloneCallNodes | DSGraph::DontCloneAuxCallNodes); // // Delete dead nodes. Treat globals that are unreachable as dead also. // // FIXME: // Do not delete unreachable globals as the comment describes. For its // alignment checks on the results of load instructions, SAFECode must be // able to find the DSNode of both the result of the load as well as the // pointer dereferenced by the load. If we remove unreachable globals, then // if the dereferenced pointer is a global, its DSNode will not reachable // from the local graph's scalar map, and chaos ensues. // // So, for now, just remove dead nodes but leave the globals alone. // DSG->removeDeadNodes(0); // We are done with computing the current TD Graph! Finally, before we can // finish processing this function, we figure out which functions it calls and // records these call graph edges, so that we have them when we process the // callee graphs. if (DSG->fc_begin() == DSG->fc_end()) return; // Loop over all the call sites and all the callees at each call site, and add // edges to the CallerEdges structure for each callee. for (DSGraph::fc_iterator CI = DSG->fc_begin(), E = DSG->fc_end(); CI != E; ++CI) { // Handle direct calls efficiently. if (CI->isDirectCall()) { if (!CI->getCalleeFunc()->isDeclaration() && !DSG->getReturnNodes().count(CI->getCalleeFunc())) CallerEdges[getOrCreateGraph(CI->getCalleeFunc())] .push_back(CallerCallEdge(DSG, &*CI, CI->getCalleeFunc())); continue; } svset<const Function*> AllCallees; std::vector<const Function*> Callees; // Get the list of callees callgraph.addFullFunctionSet(CI->getCallSite(), AllCallees); // Filter all non-declarations, and calls within this DSGraph for (svset<const Function*>::iterator I = AllCallees.begin(), E = AllCallees.end(); I != E; ++I) { const Function *F = *I; if (!F->isDeclaration() && getDSGraph(**I) != DSG) Callees.push_back(F); } AllCallees.clear(); // If there is exactly one callee from this call site, remember the edge in // CallerEdges. if (Callees.size() == 1) { const Function * Callee = Callees[0]; CallerEdges[getOrCreateGraph(Callee)] .push_back(CallerCallEdge(DSG, &*CI, Callee)); } if (Callees.size() <= 1) continue; // Otherwise, there are multiple callees from this call site, so it must be // an indirect call. Chances are that there will be other call sites with // this set of targets. If so, we don't want to do M*N inlining operations, // so we build up a new, private, graph that represents the calls of all // calls to this set of functions. std::map<std::vector<const Function*>, DSGraph*>::iterator IndCallRecI = IndCallMap.lower_bound(Callees); // If we already have this graph, recycle it. if (IndCallRecI != IndCallMap.end() && IndCallRecI->first == Callees) { DEBUG(errs() << " [TD] *** Reuse of indcall graph for " << Callees.size() << " callees!\n"); DSGraph * IndCallGraph = IndCallRecI->second; assert(IndCallGraph->getFunctionCalls().size() == 1); // Merge the call into the CS already in the IndCallGraph ReachabilityCloner RC(IndCallGraph, DSG, 0); RC.mergeCallSite(IndCallGraph->getFunctionCalls().front(), *CI); } else { // Otherwise, create a new DSGraph to represent this. DSGraph* IndCallGraph = new DSGraph(DSG->getGlobalECs(), DSG->getDataLayout(), *TypeSS); // Clone over the call into the new DSGraph ReachabilityCloner RC(IndCallGraph, DSG, 0); DSCallSite ClonedCS = RC.cloneCallSite(*CI); // Add the cloned CS to the graph, as if it were an original call. IndCallGraph->getFunctionCalls().push_back(ClonedCS); // Save this graph for use later, should we need it. IndCallRecI = IndCallMap.insert(IndCallRecI, std::make_pair(Callees, IndCallGraph)); // Additionally, make sure that each of the callees inlines this graph // exactly once. DSCallSite *NCS = &IndCallGraph->getFunctionCalls().front(); for (unsigned i = 0, e = Callees.size(); i != e; ++i) { DSGraph* CalleeGraph = getDSGraph(*Callees[i]); if (CalleeGraph != DSG) CallerEdges[CalleeGraph].push_back(CallerCallEdge(IndCallGraph, NCS, Callees[i])); } } } }
bool CSDataRando::runOnModule(Module &M) { DSA = &getAnalysis<BUMarkDoNotEncrypt>(); MaskTy = TypeBuilder<mask_t, false>::get(M.getContext()); FunctionWrappers &FW = getAnalysis<FunctionWrappers>(); { // Gather statistics on the globals DenseMap<const DSNode*, unsigned int> GlobalClassSizes; DSGraph *GG = DSA->getGlobalsGraph(); for (GlobalVariable &GV : M.getGlobalList()) { if (!(GV.isDeclaration() || PointerEquivalenceAnalysis::shouldIgnoreGlobal(GV))) { GlobalClassSizes[GG->getNodeForValue(&GV).getNode()] += 1; } } NumGlobalECs = GlobalClassSizes.size(); for (auto i : GlobalClassSizes) { if (i.second > MaxSizeGlobalEC) { MaxSizeGlobalEC = i.second; } } } findGlobalNodes(M); findArgNodes(M); // Find which functions may need cloning. If we have a DSGraph for the // function, consider it a candidate for cloning. std::vector<Function *> OriginalFunctions; for (Function &F : M) { if (!F.isDeclaration() && DSA->hasDSGraph(F)) { OriginalFunctions.push_back(&F); } } // Perform cloning of the original functions Function *Main = M.getFunction("main"); for (Function *Original : OriginalFunctions) { // Handle the main function if (Main && Original == Main) { // Never clone main OldToNewFuncMap[Original] = nullptr; // If main has no uses then we can encrypt the arguments to main. To allow // the arg nodes to be encrypted we clear ArgNodes. if (Original->uses().begin() == Original->uses().end()) { FunctionInfo[Original].ArgNodes.clear(); } continue; } // Maybe make a clone, if a clone was not made nullptr is returned. OldToNewFuncMap[Original] = makeFunctionClone(Original); } // File to potentially print diagnostic information std::unique_ptr<tool_output_file> Out(nullptr); // If we will be printing diagnostic information, open the file if (!PointerEquivalenceAnalysis::PrintEquivalenceClassesTo.empty()) { std::error_code error; Out.reset(new tool_output_file(PointerEquivalenceAnalysis::PrintEquivalenceClassesTo, error, sys::fs::F_None)); if (error) { Out.release(); } } // Perform randomization DataRandomizer DR(M); RandomNumberGenerator *RNG = M.createRNG(this); FuncInfo empty; ContextSensitivePEA GGPEA(*RNG, M.getContext(), empty, *DSA->getGlobalsGraph()); DR.encryptGlobalVariables(M, GGPEA); // All original functions with DSGraphs will be in OldToNewFuncMap. If a clone // was not made, then the entry will map to nullptr. for (auto i : OldToNewFuncMap) { Function *Original = i.first; Function *Clone = i.second; FuncInfo &FI = FunctionInfo[Original]; DSGraph *Graph = DSA->getDSGraph(*Original); if (Clone) { // Perform randomization of the cloned function CloneFunctionPEA CP(*RNG, M.getContext(), FI, *Graph, &GGPEA); DR.instrumentMemoryOperations(*Clone, CP, NULL); DR.wrapLibraryFunctions(*Clone, CP, FW); replaceWithClones(Clone, FI, CP, Graph); if (Out.get()) { // Add all Instructions before dumping to make the dump more complete. addAllInstructions(Clone, CP); Out->os() << "*** Equivalence classes for: " << Clone->getName() << " ***\n"; CP.printEquivalenceClasses(Out->os()); Out->os() << "*** End of equivalence classes for: " << Clone->getName() << " ***\n"; } } // Perform randomization of the original function FunctionPEA FP(*RNG, M.getContext(), FI, *Graph, &GGPEA, !Clone); DR.instrumentMemoryOperations(*Original, FP, NULL); DR.wrapLibraryFunctions(*Original, FP, FW); replaceWithClones(Original, FI, FP, Graph); // Encrypt main args using the main function's PEA if (Main && Original == Main) { DR.encryptMainArgs(M, FP, FW); } if (Out.get()) { // Add all Instructions before dumping to make the dump more complete. addAllInstructions(Original, FP); Out->os() << "*** Equivalence classes for: " << Original->getName() << " ***\n"; FP.printEquivalenceClasses(Out->os()); Out->os() << "*** End of equivalence classes for: " << Original->getName() << " ***\n"; } } // Replace remaining uses of original functions with clones. replaceOriginalsWithClones(); if (Out.get()) { Out->os() << "*** Equivalence classes for global variables ***\n"; GGPEA.printEquivalenceClasses(Out->os()); Out->os() << "*** End of equivalence classes for global variables ***\n"; Out->keep(); } return true; }
AliasAnalysis::AliasResult DSAAA::alias(const AliasAnalysis::Location& l1, const AliasAnalysis::Location& l2) { DSAAA_TOTAL_ANSWER++; if (l1.Size == 0 || l2.Size == 0) return NoAlias; /// ? Zhiyuan: weired, l1 & l2's locations are both instructions, in my opinion, they should be operands const Value* v1 = (l1.Ptr)->stripPointerCasts(); const Value* v2 = (l2.Ptr)->stripPointerCasts(); if (!v1->getType()->isPointerTy() || !v2->getType()->isPointerTy()) return NoAlias; if (v1 == v2) return MustAlias; DSGraph *G1 = getGraphForValue(v1); DSGraph *G2 = getGraphForValue(v2); assert((!G1 || !G2 || G1 == G2) && "Alias query for 2 different functions?"); const Function *func = nullptr; /// Zhiyuan: Debug if (const Instruction *I = dyn_cast<Instruction>(v1)) { func = I->getParent()->getParent(); } else if (const Argument *A = dyn_cast<Argument>(v1)) { func = A->getParent(); } else if (const BasicBlock *BB = dyn_cast<BasicBlock>(v1)) { func = BB->getParent(); } if (func != nullptr) { //errs() << "[DSAAA Debug] We are in function [" << func->getName() << "].\n"; DEBUG_QueryFunctionSet.insert(func->getName().str()); } DSAAA_TOTAL_QUERY_FUNCTIONS = DEBUG_QueryFunctionSet.size(); // Get the graph to use... DSGraph* G = G1 ? G1 : (G2 ? G2 : TD->getGlobalsGraph()); const DSGraph::ScalarMapTy &GSM = G->getScalarMap(); DSGraph::ScalarMapTy::const_iterator I = GSM.find((Value*)v1); if (I == GSM.end()) return NoAlias; DSGraph::ScalarMapTy::const_iterator J = GSM.find((Value*)v2); if (J == GSM.end()) return NoAlias; DSNode *N1 = I->second.getNode(), *N2 = J->second.getNode(); unsigned O1 = I->second.getOffset(), O2 = J->second.getOffset(); if (N1 == nullptr || N2 == nullptr) { // Can't tell whether anything aliases null. errs() << "[DSAAA DEBUG] nullptr for this value. \n"; return AliasAnalysis::alias(l1, l2); } if (!N1->isCompleteNode() && !N2->isCompleteNode()) { // if (llvm::DebugFlag) { // errs() << "We calculate MayAlias here.\n"; // errs() << "v1 = " << *(l1.Ptr) << "; v2 = " << *(l2.Ptr) << "\n"; // errs() << "N1 = " << N1 << "; N2 = " << N2 << "\n"; // errs() << "N1 complete? " << N1->isCompleteNode() << "; N2 complete? " << N2->isCompleteNode() << "\n"; // } if (N1 == N2) { DSAAA_INCOMPLETE_SAME_NODE++; } DSAAA_INCOMPLETE_NODE++; DEBUG_IncompleteNodeSet.insert(N1); DSAAA_INCOMPLETE_NODE_COUNT = DEBUG_IncompleteNodeSet.size(); if ( llvm::DebugFlag && func != nullptr && func->getName().str() == "BZ2_decompress") { errs() << "[DSAAA DEBUG] # of referrers: " << N1->getNumReferrers() << "\n"; // errs() << "[DSAAA DEBUG] # of links: " << N1->getLinkCount() << "\n"; N1->print(errs(), G); const DSScalarMap &SM = G->getScalarMap(); int refCount = 1; for (DSScalarMap::const_iterator i = SM.begin(); i != SM.end(); i++) { if (i->second.getNode() == N1 && refCount < 240) { errs() << refCount++ <<": " << *(i->first) << "\n"; } } //exit(0); } return AliasAnalysis::alias(l1, l2); } // We can only make a judgment if one of the nodes is complete. if (N1->isCompleteNode() || N2->isCompleteNode()) { if (N1 != N2) return NoAlias; // Completely different nodes. // See if they point to different offsets... if so, we may be able to // determine that they do not alias... if (O1 != O2) { uint64_t V1Size = l1.Size; uint64_t V2Size = l2.Size; if (O2 < O1) { // Ensure that O1 <= O2 std::swap(v1, v2); std::swap(O1, O2); std::swap(V1Size, V2Size); } if (O1+V1Size <= O2) return NoAlias; } } /** * Below added by Zhiyuan */ // if (N1 == N2 && N1->isCompleteNode() && N2->isCompleteNode()) return MustAlias; // if (llvm::DebugFlag) { // errs() << "We need to consult other alias analysis for better results.\n"; // errs() << "v1 = " << *(l1.Ptr) << "; v2 = " << *(l2.Ptr) << "\n"; // errs() << "N1 = " << N1 << "; N2 = " << N2 << "\n"; // errs() << "N1 complete? " << N1->isCompleteNode() << "; N2 complete? " << N2->isCompleteNode() << "\n"; // } /** * Above added by Zhiyuan */ DSAAA_CANNOT_ANSWER++; // FIXME: we could improve on this by checking the globals graph for aliased // global queries... return AliasAnalysis::alias(l1, l2); }
void PoolRegisterElimination::removeSingletonRegistrations (const char * name) { // // Scan through all uses of the registration function and see if it can be // safely removed. If so, schedule it for removal. // std::vector<CallInst*> toBeRemoved; Function * F = intrinsic->getIntrinsic(name).F; // // Look for and record all registrations that can be deleted. // for (Value::use_iterator UI=F->use_begin(), UE=F->use_end(); UI != UE; ++UI) { // // Get the pointer to the registered object. // CallInst * CI = cast<CallInst>(*UI); Value * Ptr = intrinsic->getValuePointer(CI); // // Lookup the DSNode for the value in the function's DSGraph. // DSGraph * TDG = dsaPass->getDSGraph(*(CI->getParent()->getParent())); DSNodeHandle DSH = TDG->getNodeForValue(Ptr); assert ((!(DSH.isNull())) && "No DSNode for Value!\n"); // // If the object being registered is the same size as that found in the // DSNode, then we know it's a singleton object. The run-time doesn't need // such objects registered in the splay trees, so we can remove the // registration function. // DSNode * N = DSH.getNode(); Value * Size = intrinsic->getObjectSize (Ptr->stripPointerCasts()); if (Size) { if (ConstantInt * C = dyn_cast<ConstantInt>(Size)) { unsigned long size = C->getZExtValue(); if (size == N->getSize()) { toBeRemoved.push_back(CI); continue; } } } } // // Update the statistics. // if (toBeRemoved.size()) { RemovedRegistration += toBeRemoved.size(); SingletonRegistrations += toBeRemoved.size(); } // // Remove the unnecesary registrations. // std::vector<CallInst*>::iterator it, end; for (it = toBeRemoved.begin(), end = toBeRemoved.end(); it != end; ++it) { (*it)->eraseFromParent(); } }
// // Method: visitCallSite() // // Description: // This method transforms a call site. A call site may either be a call // instruction or an invoke instruction. // // Inputs: // CS - The call site representing the instruction that should be transformed. // void FuncTransform::visitCallSite(CallSite& CS) { const Function *CF = CS.getCalledFunction(); Instruction *TheCall = CS.getInstruction(); bool thread_creation_point = false; // // Get the value that is called at this call site. Strip away any pointer // casts that do not change the representation of the data (i.e., are // lossless casts). // Value * CalledValue = CS.getCalledValue()->stripPointerCasts(); // // The CallSite::getCalledFunction() method is not guaranteed to strip off // pointer casts. If no called function was found, manually strip pointer // casts off of the called value and see if we get a function. If so, this // is a direct call, and we want to update CF accordingly. // if (!CF) CF = dyn_cast<Function>(CalledValue); // // Do not change any inline assembly code. // if (isa<InlineAsm>(TheCall->getOperand(0))) { errs() << "INLINE ASM: ignoring. Hoping that's safe.\n"; return; } // // Ignore calls to NULL pointers or undefined values. // if ((isa<ConstantPointerNull>(CalledValue)) || (isa<UndefValue>(CalledValue))) { errs() << "WARNING: Ignoring call using NULL/Undef function pointer.\n"; return; } // If this function is one of the memory manipulating functions built into // libc, emulate it with pool calls as appropriate. if (CF && CF->isDeclaration()) { std::string Name = CF->getName(); if (Name == "free" || Name == "cfree") { visitFreeCall(CS); return; } else if (Name == "malloc") { visitMallocCall(CS); return; } else if (Name == "calloc") { visitCallocCall(CS); return; } else if (Name == "realloc") { visitReallocCall(CS); return; } else if (Name == "memalign" || Name == "posix_memalign") { visitMemAlignCall(CS); return; } else if (Name == "strdup") { visitStrdupCall(CS); return; } else if (Name == "valloc") { errs() << "VALLOC USED BUT NOT HANDLED!\n"; abort(); } else if (unsigned PoolArgc = PAInfo.getNumInitialPoolArguments(Name)) { visitRuntimeCheck(CS, PoolArgc); return; } else if (Name == "pthread_create") { thread_creation_point = true; // // Get DSNode representing the DSNode of the function pointer Value of // the pthread_create call // DSNode* thread_callee_node = G->getNodeForValue(CS.getArgument(2)).getNode(); if (!thread_callee_node) { assert(0 && "apparently you need this code"); FuncInfo *CFI = PAInfo.getFuncInfo(*CF); thread_callee_node = G->getNodeForValue(CFI->MapValueToOriginal(CS.getArgument(2))).getNode(); } // Fill in CF with the name of one of the functions in thread_callee_node CF = const_cast<Function*>(dyn_cast<Function>(*thread_callee_node->globals_begin())); } } // // We need to figure out which local pool descriptors correspond to the pool // descriptor arguments passed into the function call. Calculate a mapping // from callee DSNodes to caller DSNodes. We construct a partial isomophism // between the graphs to figure out which pool descriptors need to be passed // in. The roots of this mapping is found from arguments and return values. // DataStructures& Graphs = PAInfo.getGraphs(); DSGraph::NodeMapTy NodeMapping; Instruction *NewCall; Value *NewCallee; std::vector<const DSNode*> ArgNodes; DSGraph *CalleeGraph; // The callee graph // For indirect callees, find any callee since all DS graphs have been // merged. if (CF) { // Direct calls are nice and simple. DEBUG(errs() << " Handling direct call: " << *TheCall << "\n"); // // Do not try to add pool handles to the function if it: // a) Already calls a cloned function; or // b) Calls a function which was never cloned. // // For such a call, just replace any arguments that take original functions // with their cloned function poiner values. // FuncInfo *CFI = PAInfo.getFuncInfo(*CF); if (CFI == 0 || CFI->Clone == 0) { // Nothing to transform... visitInstruction(*TheCall); return; } // // Oh, dear. We must add pool descriptors to this direct call. // NewCallee = CFI->Clone; ArgNodes = CFI->ArgNodes; assert ((Graphs.hasDSGraph (*CF)) && "Function has no ECGraph!\n"); CalleeGraph = Graphs.getDSGraph(*CF); } else { DEBUG(errs() << " Handling indirect call: " << *TheCall << "\n"); DSGraph *G = Graphs.getGlobalsGraph(); DSGraph::ScalarMapTy& SM = G->getScalarMap(); // Here we fill in CF with one of the possible called functions. Because we // merged together all of the arguments to all of the functions in the // equivalence set, it doesn't really matter which one we pick. // (If the function was cloned, we have to map the cloned call instruction // in CS back to the original call instruction.) Instruction *OrigInst = cast<Instruction>(getOldValueIfAvailable(CS.getInstruction())); // // Attempt to get one of the function targets of this indirect call site by // looking at the call graph constructed by the points-to analysis. Be // sure to use the original call site from the original function; the // points-to analysis has no information on the clones we've created. // // Also, look for the target that has the greatest number of arguments that // have associated DSNodes. This ensures that we pass the maximum number // of pools possible and prevents us from eliding a pool because we're // examining a target that doesn't need it. // const DSCallGraph & callGraph = Graphs.getCallGraph(); DSCallGraph::callee_iterator I = callGraph.callee_begin(OrigInst); for (; I != callGraph.callee_end(OrigInst); ++I) { for(DSCallGraph::scc_iterator sccii = callGraph.scc_begin(*I), sccee = callGraph.scc_end(*I); sccii != sccee; ++sccii){ if(SM.find(SM.getLeaderForGlobal(*sccii)) == SM.end()) continue; // // Get the information for this function. Since this is coming from // DSA, it should be an original function. // // This call site calls a function, that is not defined in this module if (!(Graphs.hasDSGraph(**sccii))) return; // For all other cases Func Info must exist. PAInfo.getFuncInfo(**sccii); // // If this target takes more DSNodes than the last one we found, then // make *this* target our canonical target. // CF = *sccii; break; } } if(!CF){ const Function *F1 = OrigInst->getParent()->getParent(); F1 = callGraph.sccLeader(&*F1); for(DSCallGraph::scc_iterator sccii = callGraph.scc_begin(F1), sccee = callGraph.scc_end(F1); sccii != sccee; ++sccii){ if(SM.find(SM.getLeaderForGlobal(*sccii)) == SM.end()) continue; // // Get the information for this function. Since this is coming from DSA, // it should be an original function. // // This call site calls a function, that is not defined in this module if (!(Graphs.hasDSGraph(**sccii))) return; // For all other cases Func Info must exist. PAInfo.getFuncInfo(**sccii); // // If this target takes more DSNodes than the last one we found, then // make *this* target our canonical target. // CF = *sccii; } } // Assuming the call graph is always correct. And if the call graph reports, // no callees, we can assume that it is right. // // If we didn't find the callee in the constructed call graph, try // checking in the DSNode itself. // This isn't ideal as it means that this call site didn't have inlining // happen. // // // If we still haven't been able to find a target function of the call site // to transform, do nothing. // // One may be tempted to think that we should always have at least one // target, but this is not true. There are perfectly acceptable (but // strange) programs for which no function targets exist. Function // pointers loaded from undef values, for example, will have no targets. // if (!CF) return; // // It's possible that this program has indirect call targets that are // not defined in this module. Do not transformation for such functions. // if (!(Graphs.hasDSGraph(*CF))) return; // // Get the common graph for the set of functions this call may invoke. // assert ((Graphs.hasDSGraph(*CF)) && "Function has no DSGraph!\n"); CalleeGraph = Graphs.getDSGraph(*CF); #ifndef NDEBUG // Verify that all potential callees at call site have the same DS graph. DSCallGraph::callee_iterator E = Graphs.getCallGraph().callee_end(OrigInst); for (; I != E; ++I) { const Function * F = *I; assert (F); if (!(F)->isDeclaration()) assert(CalleeGraph == Graphs.getDSGraph(**I) && "Callees at call site do not have a common graph!"); } #endif // Find the DS nodes for the arguments that need to be added, if any. FuncInfo *CFI = PAInfo.getFuncInfo(*CF); assert(CFI && "No function info for callee at indirect call?"); ArgNodes = CFI->ArgNodes; if (ArgNodes.empty()) return; // No arguments to add? Transformation is a noop! // Cast the function pointer to an appropriate type! std::vector<Type*> ArgTys(ArgNodes.size(), PoolAllocate::PoolDescPtrTy); for (CallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end(); I != E; ++I) ArgTys.push_back((*I)->getType()); FunctionType *FTy = FunctionType::get(TheCall->getType(), ArgTys, false); PointerType *PFTy = PointerType::getUnqual(FTy); // If there are any pool arguments cast the func ptr to the right type. NewCallee = CastInst::CreatePointerCast(CS.getCalledValue(), PFTy, "tmp", TheCall); } // // FIXME: Why do we disable strict checking when calling the // DSGraph::computeNodeMapping() method? // Function::const_arg_iterator FAI = CF->arg_begin(), E = CF->arg_end(); CallSite::arg_iterator AI = CS.arg_begin() + (thread_creation_point ? 3 : 0); CallSite::arg_iterator AE = CS.arg_end(); for ( ; FAI != E && AI != AE; ++FAI, ++AI) if (!isa<Constant>(*AI)) { DSGraph::computeNodeMapping(CalleeGraph->getNodeForValue(FAI), getDSNodeHFor(*AI), NodeMapping, false); } //assert(AI == AE && "Varargs calls not handled yet!"); // Map the return value as well... if (isa<PointerType>(TheCall->getType())) DSGraph::computeNodeMapping(CalleeGraph->getReturnNodeFor(*CF), getDSNodeHFor(TheCall), NodeMapping, false); // This code seems redundant (and crashes occasionally) // There is no reason to map globals here, since they are not passed as // arguments // // Map the nodes that are pointed to by globals. // DSScalarMap &CalleeSM = CalleeGraph->getScalarMap(); // for (DSScalarMap::global_iterator GI = G.getScalarMap().global_begin(), // E = G.getScalarMap().global_end(); GI != E; ++GI) // if (CalleeSM.count(*GI)) // DSGraph::computeNodeMapping(CalleeGraph->getNodeForValue(*GI), // getDSNodeHFor(*GI), // NodeMapping, false); // // Okay, now that we have established our mapping, we can figure out which // pool descriptors to pass in... // // Note: // There used to be code here that would create a new pool before the // function call and destroy it after the function call. This could would // get triggered if bounds checking was disbled or the DSNode for the // argument was an array value. // // I believe that code was incorrect; an argument may have a NULL pool handle // (i.e., no pool handle) because the pool allocation heuristic used simply // decided not to assign that value a pool. The argument may alias data // that should not be freed after the function call is complete, so calling // pooldestroy() after the call would free data, causing dangling pointer // dereference errors. // std::vector<Value*> Args; for (unsigned i = 0, e = ArgNodes.size(); i != e; ++i) { Value *ArgVal = Constant::getNullValue(PoolAllocate::PoolDescPtrTy); if (NodeMapping.count(ArgNodes[i])) { if (DSNode *LocalNode = NodeMapping[ArgNodes[i]].getNode()) if (FI.PoolDescriptors.count(LocalNode)) ArgVal = FI.PoolDescriptors.find(LocalNode)->second; } Args.push_back(ArgVal); } // Add the rest of the arguments unless we're a thread creation point, in which case we only need the pools if(!thread_creation_point) Args.insert(Args.end(), CS.arg_begin(), CS.arg_end()); // // There are circumstances where a function is casted to another type and // then called (que horible). We need to perform a similar cast if the // type doesn't match the number of arguments. // if (Function * NewFunction = dyn_cast<Function>(NewCallee)) { FunctionType * NewCalleeType = NewFunction->getFunctionType(); if (NewCalleeType->getNumParams() != Args.size()) { std::vector<Type *> Types; Type * FuncTy = FunctionType::get (NewCalleeType->getReturnType(), Types, true); FuncTy = PointerType::getUnqual (FuncTy); NewCallee = new BitCastInst (NewCallee, FuncTy, "", TheCall); } } std::string Name = TheCall->getName(); TheCall->setName(""); if(thread_creation_point) { Module *M = CS.getInstruction()->getParent()->getParent()->getParent(); Value* pthread_replacement = M->getFunction("poolalloc_pthread_create"); std::vector<Value*> thread_args; //Push back original thread arguments through the callee thread_args.push_back(CS.getArgument(0)); thread_args.push_back(CS.getArgument(1)); thread_args.push_back(CS.getArgument(2)); //Push back the integer argument saying how many uses there are thread_args.push_back(Constant::getIntegerValue(llvm::Type::getInt32Ty(M->getContext()),APInt(32,Args.size()))); thread_args.insert(thread_args.end(),Args.begin(),Args.end()); thread_args.push_back(CS.getArgument(3)); //Make the thread creation call NewCall = CallInst::Create(pthread_replacement, thread_args, Name,TheCall); } else if (InvokeInst *II = dyn_cast<InvokeInst>(TheCall)) { NewCall = InvokeInst::Create (NewCallee, II->getNormalDest(), II->getUnwindDest(), Args, Name, TheCall); } else { NewCall = CallInst::Create (NewCallee, Args, Name, TheCall); } // Add all of the uses of the pool descriptor for (unsigned i = 0, e = ArgNodes.size(); i != e; ++i) AddPoolUse(*NewCall, Args[i], PoolUses); TheCall->replaceAllUsesWith(NewCall); DEBUG(errs() << " Result Call: " << *NewCall << "\n"); if (!TheCall->getType()->isVoidTy()) { // If we are modifying the original function, update the DSGraph... DSGraph::ScalarMapTy &SM = G->getScalarMap(); DSGraph::ScalarMapTy::iterator CII = SM.find(TheCall); if (CII != SM.end()) { SM[NewCall] = CII->second; SM.erase(CII); // Destroy the CallInst } else if (!FI.NewToOldValueMap.empty()) { // Otherwise, if this is a clone, update the NewToOldValueMap with the new // CI return value. UpdateNewToOldValueMap(TheCall, NewCall); } } else if (!FI.NewToOldValueMap.empty()) { UpdateNewToOldValueMap(TheCall, NewCall); } // // Copy over the calling convention and attributes of the original call // instruction to the new call instruction. // CallSite(NewCall).setCallingConv(CallSite(TheCall).getCallingConv()); TheCall->eraseFromParent(); visitInstruction(*NewCall); }
void CallTargetFinder<dsa>::findIndTargets(Module &M) { dsa* T = &getAnalysis<dsa>(); const DSCallGraph & callgraph = T->getCallGraph(); DSGraph* G = T->getGlobalsGraph(); DSGraph::ScalarMapTy& SM = G->getScalarMap(); for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) if (!I->isDeclaration()) for (Function::iterator F = I->begin(), FE = I->end(); F != FE; ++F) for (BasicBlock::iterator B = F->begin(), BE = F->end(); B != BE; ++B) if (isa<CallInst>(B) || isa<InvokeInst>(B)) { CallSite cs(B); AllSites.push_back(cs); Function* CF = cs.getCalledFunction(); if (isa<UndefValue>(cs.getCalledValue())) continue; if (isa<InlineAsm>(cs.getCalledValue())) continue; // // If the called function is casted from one function type to // another, peer into the cast instruction and pull out the actual // function being called. // if (!CF) CF = dyn_cast<Function>(cs.getCalledValue()->stripPointerCasts()); if (!CF) { Value * calledValue = cs.getCalledValue()->stripPointerCasts(); if (isa<ConstantPointerNull>(calledValue)) { ++DirCall; CompleteSites.insert(cs); } else { IndCall++; DSCallGraph::callee_iterator csi = callgraph.callee_begin(cs), cse = callgraph.callee_end(cs); while(csi != cse) { const Function *F = *csi; DSCallGraph::scc_iterator sccii = callgraph.scc_begin(F), sccee = callgraph.scc_end(F); for(;sccii != sccee; ++sccii) { DSGraph::ScalarMapTy::const_iterator I = SM.find(SM.getLeaderForGlobal(*sccii)); if (I != SM.end()) { IndMap[cs].push_back (*sccii); } } ++csi; } const Function *F1 = (cs).getInstruction()->getParent()->getParent(); F1 = callgraph.sccLeader(&*F1); DSCallGraph::scc_iterator sccii = callgraph.scc_begin(F1), sccee = callgraph.scc_end(F1); for(;sccii != sccee; ++sccii) { DSGraph::ScalarMapTy::const_iterator I = SM.find(SM.getLeaderForGlobal(*sccii)); if (I != SM.end()) { IndMap[cs].push_back (*sccii); } } DSNode* N = T->getDSGraph(*cs.getCaller()) ->getNodeForValue(cs.getCalledValue()).getNode(); assert (N && "CallTarget: findIndTargets: No DSNode!"); if (!N->isIncompleteNode() && !N->isExternalNode() && IndMap[cs].size()) { CompleteSites.insert(cs); ++CompleteInd; } if (!N->isIncompleteNode() && !N->isExternalNode() && !IndMap[cs].size()) { ++CompleteEmpty; DEBUG(errs() << "Call site empty: '" << cs.getInstruction()->getName() << "' In '" << cs.getInstruction()->getParent()->getParent()->getName() << "'\n"); } } } else { ++DirCall; IndMap[cs].push_back(CF); CompleteSites.insert(cs); } } //Print the indirect call Map: for(std::map<CallSite, std::vector<const Function*> >::iterator indMapIt = IndMap.begin(); indMapIt != IndMap.end(); ++indMapIt ) { CallSite CS = indMapIt->first; Instruction* Inst = CS.getInstruction(); Inst->dump(); } }