/* * Options.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; import java.io.File; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.TreeMap; import org.jakstab.util.Logger; /** * Parses and holds command line options. * * @author Johannes Kinder */ public class Options { private static final Logger logger = Logger.getLogger(Options.class); private final static int lineLength = 80; private final static int indentation = 22; public static final String jakstabHome; static { // Get path of Jakstab's directory from VM String classFileName = Options.class.getResource("/org/jakstab/Options.class").getPath(); if (classFileName.startsWith("file:")) classFileName = classFileName.substring(5); classFileName = classFileName.replace("%20", " "); jakstabHome = (new File(classFileName)).getParentFile().getParentFile().getParentFile().getParent(); } private static Map<String,JOption<?>> options = new TreeMap<String,JOption<?>>(new Comparator<String>() { public int compare(String s1, String s2) { if (s1.length() != 2 && s2.length() == 2) return 1; if (s1.length() == 2 && s2.length() != 2) return -1; else return s1.compareTo(s2); } }); static void addOption(JOption<?> o) { String name = o.getName(); if (options.containsKey(name)) { logger.fatal("Option " + name + " already present!"); System.exit(1); } else { options.put(name, o); } } public static String mainFilename = null; public static List<String> moduleFilenames = new LinkedList<String>(); public static String arguments; public static JOption<String> sslFilename = JOption.create("ssl", "file", jakstabHome + "/ssl/pentium.ssl", "Use <file> instead of pentium.ssl."); public static JOption<Long> startAddress = JOption.create("a", "address", -1L, "Start analysis at given virtual address."); public static JOption<Boolean> wdm = JOption.create("wdm", "WDM mode, export main function as DriverMain."); public static JOption<Boolean> allEdges = JOption.create("all-edges", "Generate a true over-approximation and add edges to all possible addresses when over-approximating a jump (very slow!)."); public static JOption<Boolean> dumpStates = JOption.create("s", "Output all reached states after analysis."); public static JOption<Boolean> outputLocationsWithMostStates = JOption.create("toplocs", "Output the 10 locations with the highest state count."); public static JOption<Boolean> failFast = JOption.create("fail-fast", "Stop when unsound assumptions are necessary to continue."); public static JOption<Boolean> debug = JOption.create("debug", "Stop on failed assertions or weak updates to the complete stack or all store regions."); public static JOption<Boolean> asmTrace = JOption.create("asm-trace", "Output any error trace as a list of assembly instructions instead of IL statements."); public static JOption<Boolean> errorTrace = JOption.create("error-trace", "Build an abstract error trace for failed assertions and debug stops."); public static JOption<Boolean> backward = JOption.create("backward", "Perform secondary cpa as a backward analysis."); public static JOption<Boolean> background = JOption.create("b", "Background mode, i.e., disable shutdown hook on enter."); public static JOption<Boolean> graphML = JOption.create("graphML", "Produce graphML output instead of GraphViz .dot files."); public static JOption<Boolean> noGraphs = JOption.create("no-graphs", "Do not generate output graphs"); public static JOption<Boolean> heuristicEntryPoints = JOption.create("h", "Use heuristics to determine additional procedures and add pseudo-calls to include them in disassembly."); public static JOption<Boolean> ignoreWeakUpdates = JOption.create("ignore-weak-updates", "Do not perform weak store updates (unsound)."); public static JOption<Boolean> initHeapToBot = JOption.create("bot-heap", "Initialize heap cells to BOT to force strong updates."); public static JOption<Boolean> summarizeRep = JOption.create("summarize-rep", "Use summarizing transformer for string instructions."); public static JOption<Boolean> basicBlocks = JOption.create("basicblocks", "Build CFA from basic-blocks instead of single statements."); public static JOption<Integer> simplifyVCFG = JOption.create("simplifyVCFG", "l", 1, "In VPC-CFG reconstruction, simplify the reconstructed graph using (0) nothing (1) DCE (2) DCE + Expression Substitution"); public static JOption<Integer> verbosity = JOption.create("v", "level", 3, "Set verbosity to value. Default is 3."); public static JOption<Integer> timeout = JOption.create("timeout", "t", -1, "Set timeout in seconds for the analysis."); public static JOption<Integer> procedureAbstraction = JOption.create("procedures", "n", 0, "Level of procedure assumptions: " + "0: Pessimistic: No assumptions, treat calls and returns as jumps (default). " + "1: Semi-optimistic: Abstract unknown calls according to ABI contract. " + "2: Optimistic: Abstract all calls to ABI contract (fastest)."); public static JOption<Integer> getProcAddress = JOption.create("getprocaddress", "n", 2, "How to resolve GetProcAddress: 0: Always succeed, 1: Split success/fail, 2: Merge success/fail (default)"); private static AnalysisManager mgr = AnalysisManager.getInstance(); public static JOption<String> cpas = JOption.create("cpa", "{" + mgr.getShorthandsString() + "}", "x", "Configure which analyses to use for control flow reconstruction."); public static JOption<String> secondaryCPAs = JOption.create("cpa2", "{" + mgr.getShorthandsString() + "}", "", "Secondary analyses to be performed after the initial CFG reconstruction and dead code elimination are completed."); public static JOption<String> procedureGraph = JOption.create("procedure-graph", "p", "", "Generate intraprocedural CFG for procedure with give name (requires symbols)"); /** * Handle command line options. * * @param args */ public static void parseOptions(String args[]) { // Pre-load analyses so that they can register their options AnalysisManager.getInstance(); StringBuilder argStringBuilder = new StringBuilder(); for (int i = 0; i < args.length - 1; i++) { argStringBuilder.append(args[i]).append(" "); } if (args.length > 0) argStringBuilder.append(args[args.length - 1]); arguments = argStringBuilder.toString(); for (int i = 0; i < args.length; i++) { String arg = args[i]; // Dash (-) arguments if (arg.startsWith("-")) { JOption<?> opt = options.get(arg); if (opt != null) { if (opt.getDefaultValue() instanceof Boolean) { opt.setValue(Boolean.TRUE); } else if (opt.getDefaultValue() instanceof Integer) { opt.setValue(Integer.parseInt(args[++i])); } else if (opt.getDefaultValue() instanceof Long) { arg = args[++i]; if (arg.startsWith("0x")) opt.setValue(Long.parseLong(arg.substring(2), 16)); else opt.setValue(Long.parseLong(arg)); } else if (opt.getDefaultValue() instanceof String) { opt.setValue(args[++i]); } else { assert false : "Unhandled Option type " + opt.getDefaultValue().getClass().getSimpleName(); } } // Arguments which require arguments else if (arg.equals("-m")) { mainFilename = args[++i]; } else { logger.fatal("Invalid command line argument: " + arg); logger.fatal(""); Options.printOptions(); System.exit(1); } } // arguments w/o dash else { moduleFilenames.add(arg); } } if (mainFilename == null) { logger.fatal("No main file specified!"); logger.fatal(""); Options.printOptions(); System.exit(1); } } public static void printOptions() { logger.fatal("Usage: jakstab [options] -m mainfile [ modules... ]"); logger.fatal(""); logger.fatal("Options:"); for (JOption<?> o : options.values()) { StringBuilder os = new StringBuilder(lineLength); os.append(" ").append(o.getName()); if (o.getParamName() != null) os.append(' ').append(o.getParamName()); os.append(' '); printWithIndentedLineWrap(os, o.getDescription()); // Special treatment for CPAs option if (o.equals(cpas)) { String shorthands = mgr.getShorthandsString(); for (int i=0; i<shorthands.length(); i++) { Character cpa = shorthands.charAt(i); os = new StringBuilder(lineLength); os.append(" ").append(cpa); printWithIndentedLineWrap(os, mgr.getName(cpa) + ": " + mgr.getDescription(cpa)); } } } } private static void printWithIndentedLineWrap(StringBuilder os, String s) { StringTokenizer st = new StringTokenizer(s, " "); String nextWord = st.nextToken(); printLine: while (true) { // We are at the first or a new line while (os.length() < indentation) { os.append(' '); } os.append(nextWord); // Output text until end of line while (true) { if (!st.hasMoreTokens()) break printLine; nextWord = st.nextToken(); if (os.length() + 1 + nextWord.length() <= lineLength) { os.append(" ").append(nextWord); } else { logger.fatal(os.toString()); os = new StringBuilder(lineLength); // Start new line continue printLine; } } } logger.fatal(os.toString()); } }