/*
* IntraproceduralCFG.java - This file is part of the Jakstab project.
* Copyright 2007-2015 Johannes Kinder <jk@jakstab.org>
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
*/
package org.jakstab.cfa;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.jakstab.Options;
import org.jakstab.Program;
import org.jakstab.asm.AbsoluteAddress;
import org.jakstab.rtl.statements.RTLAssume;
import org.jakstab.rtl.statements.RTLCallReturn;
import org.jakstab.rtl.statements.RTLStatement;
import org.jakstab.util.FastSet;
import org.jakstab.util.Logger;
import org.jakstab.util.Worklist;
public class IntraproceduralCFG extends ControlFlowGraph {
private static final Logger logger = Logger.getLogger(IntraproceduralCFG.class);
public IntraproceduralCFG(ControlFlowGraph cfg, String procName) {
this(cfg, Program.getProgram().getAddressForSymbol(procName));
}
public IntraproceduralCFG(ControlFlowGraph cfg, AbsoluteAddress procedureHead) {
super();
if (procedureHead == null) {
logger.error("IntraproceduralCFG constructor called with NULL procedure head");
return;
}
Location headLoc = findLocation(procedureHead, cfg);
if (headLoc == null)
return;
Set<CFAEdge> edges = filterEdges(cfg, headLoc);
buildFromEdgeSet(edges);
}
private Location findLocation(AbsoluteAddress a, ControlFlowGraph cfg) {
SortedSet<Location> candidates = new TreeSet<Location>();
for (Location l : cfg.getNodes()) {
if (a.equals(l.getAddress()))
candidates.add(l);
}
logger.verbose("Found " + candidates.size() + " candidates for procedure entry, returning first one of " + candidates);
if (candidates.size() == 0) {
logger.error("Did not find procedure in CFG!");
return null;
}
return candidates.iterator().next();
}
private Set<CFAEdge> filterEdges(ControlFlowGraph cfg, Location procHead) {
Worklist<Location> worklist = new FastSet<Location>(procHead);
Set<Location> visited = new HashSet<Location>();
Set<CFAEdge> result = new HashSet<CFAEdge>();
while (!worklist.isEmpty()) {
Location loc = worklist.pick();
for (CFAEdge e : cfg.getOutEdges(loc)) {
RTLStatement stmt = (RTLStatement)e.getTransformer();
if (stmt instanceof RTLAssume) {
RTLAssume a = (RTLAssume)stmt;
if (a.isCall()) {
if (Options.procedureAbstraction.getValue() != 1) {
if (a.getSource().getNextLabel() != null) {
Location returnLoc = findReturnLocation(cfg, e, a.getSource().getNextLabel());
if (returnLoc == null) {
logger.verbose("Non-returning call to procedure " +
Program.getProgram().getSymbolFor(e.getTarget().getLabel()));
} else {
RTLCallReturn callReturn = new RTLCallReturn();
callReturn.setLabel(a.getLabel());
callReturn.setNextLabel(returnLoc.getLabel());
CFAEdge fallThroughEdge = new CFAEdge(e.getSource(), returnLoc, callReturn);
logger.debug("Generated fall-through edge " + fallThroughEdge);
// Replace e with fallthrough edge to add it further down
e = fallThroughEdge;
}
}
} else {
// don't add fall through in interproc analysis, the callReturn edge should be enough
// TODO: Check if this works with VPC CFGs
continue;
}
} else if (a.isReturn()) {
// don't add/follow the return edge, it delimits the procedure
continue;
}
}
result.add(e);
if (visited.contains(e.getTarget()))
continue;
else {
visited.add(e.getTarget());
worklist.add(e.getTarget());
}
}
}
return result;
}
private Location findReturnLocation(ControlFlowGraph cfg, CFAEdge callEdge, RTLLabel returnLabel) {
Worklist<Location> worklist = new FastSet<Location>(callEdge.getTarget());
Set<Location> visited = new HashSet<Location>();
while (!worklist.isEmpty()) {
Location l = worklist.pick();
for (CFAEdge e : cfg.getOutEdges(l)) {
Location targetLoc = e.getTarget();
if (visited.contains(targetLoc))
continue;
visited.add(targetLoc);
if (targetLoc.getLabel().equals(returnLabel))
return targetLoc;
worklist.add(targetLoc);
}
}
logger.debug("Could not find return location for call " + callEdge + " supposed to return to " + returnLabel);
return null;
}
}