/*
* AsmCFG.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.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import org.jakstab.Program;
import org.jakstab.asm.BranchInstruction;
import org.jakstab.asm.Instruction;
import org.jakstab.rtl.statements.RTLAssume;
import org.jakstab.rtl.statements.RTLGoto;
import org.jakstab.rtl.statements.RTLStatement;
import org.jakstab.util.Pair;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
public class AsmCFG {
private Map<Location, Instruction> nodes;
private SetMultimap<Location, Pair<Location, Object>> edges;
public AsmCFG(ControlFlowGraph cfg) {
/*
* When constructing an ASM CFG from a CFA, we have the following problem:
* The VPC can change within an instruction, so if we just skip intra-instruction
* edges, the VPC value for CFA edge entering the instruction can be different from
* the one for the CFA edge leaving the instruction. In that case, the instruction
* label is different, and the edges don't connect.
*
* Therefore, we define the VPC of an instruction location to be the VPC at the
* instruction entry.
*
* We can't just iterate over all VPC locations and check whether an VPC, address pair
* already has an instruction, because this might put an instruction twice if the VPC
* changes in the middle of it.
*/
nodes = new HashMap<Location, Instruction>();
edges = HashMultimap.create();
Program p = Program.getProgram();
Deque<Location> worklist = new LinkedList<Location>();
worklist.add(cfg.getEntryPoint());
while (!worklist.isEmpty()) {
Location instrEntry = worklist.pop();
if (nodes.containsKey(instrEntry))
continue;
// the instruction might be null (if it's a stub/prologue location)
Instruction instr = p.getInstruction(instrEntry.getAddress());
nodes.put(instrEntry, instr);
Set<CFAEdge> outEdges;
Location curLoc = instrEntry;
do {
outEdges = cfg.getOutEdges(curLoc);
if (outEdges.size() != 1)
break;
curLoc = outEdges.iterator().next().getTarget();
} while (curLoc.getAddress().equals(instrEntry.getAddress()) &&
curLoc.getLabel().getIndex() >= instrEntry.getLabel().getIndex());
for (CFAEdge e : outEdges) {
RTLStatement stmt = (RTLStatement)e.getTransformer();
String label = "";
if (instr instanceof BranchInstruction) {
assert stmt instanceof RTLAssume;
BranchInstruction bi = (BranchInstruction)instr;
if (bi.isConditional()) {
// Get the original goto from the program (not the converted assume)
RTLGoto rtlGoto = ((RTLAssume) stmt).getSource();
// If this is the fall-through edge, output F, otherwise T
// If the assume in the edge has the same nextlabel as Goto, then it's the fall-through
label = stmt.getNextLabel().equals(rtlGoto.getNextLabel()) ? "F" : "T";
}
}
edges.put(instrEntry, Pair.create(e.getTarget(), (Object)label));
worklist.add(e.getTarget());
}
}
}
public Instruction getInstruction(Location l) {
return nodes.get(l);
}
public void setInstruction(Location l, Instruction i) {
nodes.put(l, i);
}
public Map<Location, Instruction> getNodes() {
return nodes;
}
public SetMultimap<Location, Pair<Location, Object>> getEdges() {
return edges;
}
public Set<Pair<Location, Object>> getOutEdges(Location l) {
return edges.get(l);
}
}