/* ### * IP: GHIDRA * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "callgraph.hh" #include "funcdata.hh" void CallGraphEdge::saveXml(ostream &s) const { s << " \n"; s << " "; from->getAddr().saveXml(s); s << "\n "; to->getAddr().saveXml(s); s << "\n "; callsiteaddr.saveXml(s); s << "\n \n"; } void CallGraphEdge::restoreXml(const Element *el,CallGraph *graph) { const AddrSpaceManager *manage = graph->getArch(); Address fromaddr,toaddr,siteaddr; const List &list(el->getChildren()); List::const_iterator iter; iter = list.begin(); fromaddr = Address::restoreXml(*iter,manage); ++iter; toaddr = Address::restoreXml(*iter,manage); ++iter; siteaddr = Address::restoreXml(*iter,manage); CallGraphNode *fromnode = graph->findNode(fromaddr); if (fromnode == (CallGraphNode *)0) throw LowlevelError("Could not find from node"); CallGraphNode *tonode = graph->findNode(toaddr); if (tonode == (CallGraphNode *)0) throw LowlevelError("Could not find to node"); graph->addEdge(fromnode,tonode,siteaddr); } void CallGraphNode::setFuncdata(Funcdata *f) { if ((fd != (Funcdata *)0)&&(fd != f)) throw LowlevelError("Multiple functions at one address in callgraph"); if (f->getAddress() != entryaddr) throw LowlevelError("Setting function data at wrong address in callgraph"); fd = f; } void CallGraphNode::saveXml(ostream &s) const { s << " \n "; entryaddr.saveXml(s); s << "\n \n"; } void CallGraphNode::restoreXml(const Element *el,CallGraph *graph) { int4 num = el->getNumAttributes(); string name; for(int4 i=0;igetAttributeName(i) == "name") name = el->getAttributeValue(i); } Address addr = Address::restoreXml(*el->getChildren().begin(),graph->getArch()); graph->addNode(addr,name); } bool CallGraph::findNoEntry(vector &seeds) { // Find all functions (that are not already marked) that either have no in edges at all, // or have no in edges that haven't been snipped as part of cycles map::iterator iter; CallGraphNode *lownode = (CallGraphNode *)0; bool allcovered = true; bool newseeds = false; for(iter=graph.begin();iter!=graph.end();++iter) { CallGraphNode &node( (*iter).second ); if (node.isMark()) continue; if ((node.inedge.size() == 0)||((node.flags&CallGraphNode::onlycyclein)!=0)) { seeds.push_back(&node); node.flags |= CallGraphNode::mark | CallGraphNode::entrynode; newseeds = true; } else { allcovered = false; // We need to worry about the case where everything is in a cycle, so we don't find a natural root // We use the node with the lowest number of in edges as a pseudo root if (lownode == (CallGraphNode *)0) lownode = &node; else { if (node.numInEdge() < lownode->numInEdge()) lownode = &node; } } } if ((!newseeds)&&(!allcovered)) { seeds.push_back(lownode); lownode->flags |= CallGraphNode::mark | CallGraphNode::entrynode; } return allcovered; } void CallGraph::snipCycles(CallGraphNode *node) { // Snip any cycles starting from root -node- CallGraphNode *next; vector stack; node->flags |= CallGraphNode::currentcycle; stack.push_back(LeafIterator(node)); while(!stack.empty()) { CallGraphNode *cur = stack.back().node; // Current node int4 st = stack.back().outslot; // which out edge we will follow if (st >= cur->outedge.size()) { cur->flags &= ~((uint4)CallGraphNode::currentcycle); stack.pop_back(); } else { stack.back().outslot += 1; if ((cur->outedge[st].flags&CallGraphEdge::cycle)!=0) continue; next = cur->outedge[st].to; if ((next->flags&CallGraphNode::currentcycle)!=0) { // Found a cycle snipEdge(cur,st); continue; } else if ((next->flags&CallGraphNode::mark)!=0) { // Already traced before cur->outedge[st].flags |= CallGraphEdge::dontfollow; continue; } next->parentedge = cur->outedge[st].complement; next->flags |= (CallGraphNode::currentcycle| CallGraphNode::mark); stack.push_back(LeafIterator(next)); } } } void CallGraph::snipEdge(CallGraphNode *node,int4 i) { node->outedge[i].flags |= CallGraphEdge::cycle | CallGraphEdge::dontfollow; int4 toi = node->outedge[i].complement; CallGraphNode *to = node->outedge[i].to; to->inedge[toi].flags |= CallGraphEdge::cycle; bool onlycycle = true; for(uint4 j=0;jinedge.size();++j) { if ((to->inedge[j].flags & CallGraphEdge::cycle)==0) { onlycycle = false; break; } } if (onlycycle) to->flags |= CallGraphNode::onlycyclein; } void CallGraph::clearMarks(void) { map::iterator iter; for(iter=graph.begin();iter!=graph.end();++iter) (*iter).second.clearMark(); } CallGraphEdge &CallGraph::insertBlankEdge(CallGraphNode *node,int4 slot) { node->outedge.emplace_back(); if (node->outedge.size() > 1) { for(int4 i=node->outedge.size()-2;i>=slot;--i) { int4 newi = i+1; CallGraphEdge &edge( node->outedge[newi] ); edge = node->outedge[i]; CallGraphNode *nodeout = edge.to; nodeout->inedge[ edge.complement ].complement += 1; } } return node->outedge[slot]; } CallGraphNode *CallGraph::addNode(Funcdata *f) { // Add a node, based on an existing function -f- CallGraphNode &node( graph[f->getAddress()] ); if ((node.getFuncdata() != (Funcdata *)0)&&(node.getFuncdata() != f)) throw LowlevelError("Functions with duplicate entry points: "+f->getName()+" "+node.getFuncdata()->getName()); node.entryaddr = f->getAddress(); node.name = f->getName(); node.fd = f; return &node; } CallGraphNode *CallGraph::addNode(const Address &addr,const string &nm) { CallGraphNode &node( graph[addr] ); node.entryaddr = addr; node.name = nm; return &node; } CallGraphNode *CallGraph::findNode(const Address &addr) { // Find function at given address, or return null map::iterator iter; iter = graph.find(addr); if (iter != graph.end()) return &(*iter).second; return (CallGraphNode *)0; } void CallGraph::addEdge(CallGraphNode *from,CallGraphNode *to,const Address &addr) { int4 i; for(i=0;ioutedge.size();++i) { CallGraphNode *outnode = from->outedge[i].to; if (outnode == to) return; // Already have an out edge if (to->entryaddr < outnode->entryaddr) break; } CallGraphEdge &fromedge( insertBlankEdge(from,i) ); int4 toi = to->inedge.size(); to->inedge.emplace_back(); CallGraphEdge &toedge( to->inedge.back() ); fromedge.from = from; fromedge.to = to; fromedge.callsiteaddr = addr; fromedge.complement = toi; toedge.from = from; toedge.to = to; toedge.callsiteaddr = addr; toedge.complement = i; } void CallGraph::deleteInEdge(CallGraphNode *node,int4 i) { int4 tosize = node->inedge.size(); int4 fromi = node->inedge[i].complement; CallGraphNode *from = node->inedge[i].from; int4 fromsize = from->outedge.size(); for(int4 j=i+1;jinedge[j-1] = node->inedge[j]; if (node->inedge[j-1].complement >= fromi) node->inedge[j-1].complement -= 1; } node->inedge.pop_back(); for(int4 j=fromi+1;joutedge[j-1] = from->outedge[j]; if (from->outedge[j-1].complement >= i) from->outedge[j-1].complement -= 1; } from->outedge.pop_back(); } CallGraphNode *CallGraph::popPossible(CallGraphNode *node,int4 &outslot) { if ((node->flags & CallGraphNode::entrynode)!=0) { outslot = node->parentedge; return (CallGraphNode *)0; } outslot = node->inedge[node->parentedge].complement; return node->inedge[node->parentedge].from; } CallGraphNode *CallGraph::pushPossible(CallGraphNode *node,int4 outslot) { if (node == (CallGraphNode *)0) { if (outslot >= seeds.size()) return (CallGraphNode *)0; return seeds[outslot]; } while(outslot < node->outedge.size()) { if ((node->outedge[outslot].flags & CallGraphEdge::dontfollow)!=0) outslot += 1; else return node->outedge[outslot].to; } return (CallGraphNode *)0; } CallGraphNode *CallGraph::initLeafWalk(void) { cycleStructure(); if (seeds.empty()) return (CallGraphNode *)0; CallGraphNode *node = seeds[0]; for(;;) { CallGraphNode *pushnode = pushPossible(node,0); if (pushnode == (CallGraphNode *)0) break; node = pushnode; } return node; } CallGraphNode *CallGraph::nextLeaf(CallGraphNode *node) { int4 outslot; node = popPossible(node,outslot); outslot += 1; for(;;) { CallGraphNode *pushnode = pushPossible(node,outslot); if (pushnode == (CallGraphNode *)0) break; node = pushnode; outslot = 0; } return node; } void CallGraph::cycleStructure(void) { // Generate list of seeds nodes (from which we can get to everything) if (!seeds.empty()) return; uint4 walked = 0; bool allcovered; do { allcovered = findNoEntry(seeds); while(walked < seeds.size()) { CallGraphNode *rootnode = seeds[walked]; rootnode->parentedge = walked; snipCycles(rootnode); walked += 1; } } while(!allcovered); clearMarks(); } void CallGraph::iterateScopesRecursive(Scope *scope) { if (!scope->isGlobal()) return; iterateFunctionsAddrOrder(scope); ScopeMap::const_iterator iter,enditer; iter = scope->childrenBegin(); enditer = scope->childrenEnd(); for(;iter!=enditer;++iter) { iterateScopesRecursive((*iter).second); } } void CallGraph::iterateFunctionsAddrOrder(Scope *scope) { MapIterator miter,menditer; miter = scope->begin(); menditer = scope->end(); while(miter != menditer) { Symbol *sym = (*miter)->getSymbol(); FunctionSymbol *fsym = dynamic_cast(sym); ++miter; if (fsym != (FunctionSymbol *)0) addNode(fsym->getFunction()); } } void CallGraph::buildAllNodes(void) { // Make every function symbol into a node iterateScopesRecursive(glb->symboltab->getGlobalScope()); } void CallGraph::buildEdges(Funcdata *fd) { // Build edges from a disassembled (decompiled) function CallGraphNode *fdnode = findNode(fd->getAddress()); CallGraphNode *tonode; if (fdnode == (CallGraphNode *)0) throw LowlevelError("Function is missing from callgraph"); if (fd->getFuncProto().getModelExtraPop() == ProtoModel::extrapop_unknown) fd->fillinExtrapop(); int4 numcalls = fd->numCalls(); for(int4 i=0;igetCallSpecs(i); Address addr = fs->getEntryAddress(); if (!addr.isInvalid()) { tonode = findNode(addr); if (tonode == (CallGraphNode *)0) { string name; glb->nameFunction(addr,name); tonode = addNode(addr,name); } addEdge(fdnode,tonode,fs->getOp()->getAddr()); } } } void CallGraph::saveXml(ostream &s) const { map::const_iterator iter; s << "\n"; for(iter=graph.begin();iter!=graph.end();++iter) (*iter).second.saveXml(s); // Dump all the "in" edges for(iter=graph.begin();iter!=graph.end();++iter) { const CallGraphNode &node( (*iter).second ); for(uint4 i=0;i\n"; } void CallGraph::restoreXml(const Element *el) { const List &list(el->getChildren()); List::const_iterator iter; iter = list.begin(); while(iter != list.end()) { const Element *subel = *iter; ++iter; if (subel->getName() == "edge") CallGraphEdge::restoreXml(subel,this); else CallGraphNode::restoreXml(subel,this); } }