/* * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * 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, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.oracle.max.hcfdis; import static com.oracle.max.criutils.HexCodeFile.*; import java.io.*; import java.util.*; import com.oracle.max.criutils.*; import com.sun.cri.ci.CiTargetMethod.CodeAnnotation; import com.sun.cri.ci.CiTargetMethod.JumpTable; import com.sun.cri.ci.CiTargetMethod.LookupTable; import com.sun.max.asm.*; import com.sun.max.asm.InlineDataDescriptor.JumpTable32; import com.sun.max.asm.InlineDataDescriptor.LookupTable32; import com.sun.max.asm.dis.*; import com.sun.max.io.*; import com.sun.max.lang.*; import com.sun.max.program.option.*; /** * Utility for converting a {@link HexCodeFile} to a commented disassembly. */ public class HexCodeFileDis extends DisassemblyPrinter { public static final OptionSet options = new OptionSet(); public static final Option<String> commentPrefixOption = options.newStringOption("comment-prefix", ";; ", "The prefix prepended to each line of instruction comments."); public static final Option<String> hcfOpenOption = options.newStringOption("hcf-open", EMBEDDED_HCF_OPEN, "Start delimiter for HexCodeFile."); public static final Option<String> hcfCloseOption = options.newStringOption("hcf-close", EMBEDDED_HCF_CLOSE, "End delimiter for HexCodeFile."); public static final Option<Boolean> copyDelimitersOption = options.newBooleanOption("copy-delimiters", false, "Copy delimiters to output."); public static final Option<String> dirOption = options.newStringOption("d", null, "Output directory (input files are overwritten if not specified)."); public static final Option<Boolean> verboseOption = options.newBooleanOption("v", true, "Verbose operation."); /** * Prefix prepended to each instruction comment line in the disassembly. */ public String commentLinePrefix = ";; "; /** * The input being processed. */ protected String input; /** * A name for the input source to be used in error messages. */ protected String inputSource; /** * The HexCodeFile currently being processed. */ protected HexCodeFile hcf; /** * Current machine code position during disassembly. */ protected int pos; /** * Count of embedded HexCodeFiles disassembled. */ protected int hcfCount; public HexCodeFileDis(boolean includeHeader) { super(includeHeader); } /** * Decoding method called by external tools via reflection. */ public static String processEmbeddedString(String source) { if (!source.startsWith(EMBEDDED_HCF_OPEN) || !source.endsWith(EMBEDDED_HCF_CLOSE)) { throw new IllegalArgumentException("Input string is not in embedded format"); } source = source.substring(EMBEDDED_HCF_OPEN.length(), source.length() - EMBEDDED_HCF_CLOSE.length()); HexCodeFileDis dis = new HexCodeFileDis(false); HexCodeFile hcf = HexCodeFile.parse(source, 0, source, ""); return dis.process(hcf, null); } /** * Disassembles all HexCodeFiles embedded in a given input string. * * @param input some input containing 0 or more HexCodeFiles * @param inputName name for the input source to be used in error messages * @param startDelim the delimiter just before to an embedded HexCodeFile in {@code input} * @param endDelim the delimiter just after to an embedded HexCodeFile in {@code input} * @return the value of {@code input} with all embedded HexCodeFiles converted to their disassembled form */ public String processAll(String input, String inputName, String startDelim, String endDelim, boolean verbose) { ByteArrayOutputStream baos = new ByteArrayOutputStream(input.length() * 2); PrintStream out = new PrintStream(baos); int codeEnd = 0; int index; while ((index = input.indexOf(startDelim, codeEnd)) != -1) { int codeStart = index + startDelim.length(); String copy = input.substring(codeEnd, codeStart); if (!copyDelimitersOption.getValue()) { if (copy.startsWith(endDelim)) { copy = copy.substring(endDelim.length()); } if (copy.endsWith(startDelim)) { copy = copy.substring(0, copy.length() - startDelim.length()); } } out.println(copy); int endIndex = input.indexOf(endDelim, codeStart); assert endIndex != -1; String source = input.substring(codeStart, endIndex); HexCodeFile hcf = HexCodeFile.parse(input, codeStart, source, inputName); process(hcf, out); if (verbose) { System.out.print("."); } codeEnd = endIndex; } if (verbose && hcfCount != 0) { System.out.println(); } String copy = input.substring(codeEnd); if (!copyDelimitersOption.getValue()) { if (copy.startsWith(endDelim)) { copy = copy.substring(endDelim.length()); } } out.print(copy); out.flush(); return baos.toString(); } /** * Disassembles a given HexCodeFile. * * @param out if not {@code null}, this is where the HexCodeFile disassembly should be printed * @return the disassembled HexCodeFile if {@code out == null} otherwise {@code null} */ public String process(HexCodeFile hcf, PrintStream out) { final InlineDataDecoder inlineDataDecoder = makeInlineDataDecoder(hcf); ByteArrayOutputStream buf = null; if (out == null) { buf = new ByteArrayOutputStream(); out = new PrintStream(buf); } this.hcf = hcf; this.hcfCount++; Disassembler.disassemble(out, hcf.code, parseISA(hcf.isa), WordWidth.fromInt(hcf.wordWidth), hcf.startAddress, inlineDataDecoder, this); this.hcf = null; out.flush(); if (buf != null) { return buf.toString(); } return null; } public static InlineDataDecoder makeInlineDataDecoder(CodeAnnotation[] annotations) { ArrayList<InlineDataDescriptor> descriptors = new ArrayList<InlineDataDescriptor>(); for (CodeAnnotation a : annotations) { if (a instanceof JumpTable) { JumpTable table = (JumpTable) a; if (table.entrySize == 4) { descriptors.add(new JumpTable32(table.position, table.low, table.high)); } else { System.err.println("WARNING: Ignoring jump table with an entry size != 4"); } } else if (a instanceof LookupTable) { LookupTable table = (LookupTable) a; if (table.keySize == 4 && table.offsetSize == 4) { descriptors.add(new LookupTable32(table.position, table.npairs)); } else { System.err.println("WARNING: Ignoring lookup table with a key or offset size != 4"); } } } final InlineDataDecoder inlineDataDecoder = descriptors.isEmpty() ? null : new InlineDataDecoder(descriptors); return inlineDataDecoder; } public static InlineDataDecoder makeInlineDataDecoder(HexCodeFile hcf) { ArrayList<InlineDataDescriptor> descriptors = new ArrayList<InlineDataDescriptor>(); for (JumpTable table : hcf.jumpTables) { if (table.entrySize == 4) { descriptors.add(new JumpTable32(table.position, table.low, table.high)); } else { System.err.println("WARNING: Ignoring jump table with an entry size != 4"); } } for (LookupTable table : hcf.lookupTables) { if (table.keySize == 4 && table.offsetSize == 4) { descriptors.add(new LookupTable32(table.position, table.npairs)); } else { System.err.println("WARNING: Ignoring lookup table with a key or offset size != 4"); } } final InlineDataDecoder inlineDataDecoder = descriptors.isEmpty() ? null : new InlineDataDecoder(descriptors); return inlineDataDecoder; } @Override protected String disassembledObjectString(Disassembler disassembler, DisassembledObject disassembledObject) { String string = super.disassembledObjectString(disassembler, disassembledObject); String s = hcf.operandComments.get(pos); if (s != null) { string += " " + s; } return string; } @Override protected void printDisassembledObject(Disassembler disassembler, PrintStream stream, int nOffsetChars, int nLabelChars, DisassembledObject disassembledObject) { pos = disassembledObject.startPosition(); List<String> comments = hcf.comments.get(pos); if (comments != null) { for (String comment : comments) { stream.println(commentLinePrefix + comment.replace(HexCodeFile.NEW_LINE, HexCodeFile.NEW_LINE + commentLinePrefix)); } } super.printDisassembledObject(disassembler, stream, nOffsetChars, nLabelChars, disassembledObject); } ISA parseISA(String value) { try { return ISA.valueOf(value); } catch (IllegalArgumentException e) { throw new Error("Unsupported ISA - must be one of " + Arrays.toString(ISA.values())); } } public static void main(String[] args) throws IOException { options.parseArguments(args); File outDir = null; if (dirOption.getValue() != null) { outDir = new File(dirOption.getValue()); if (!outDir.isDirectory()) { if (!outDir.mkdirs()) { throw new Error("Could not create output directory " + outDir.getAbsolutePath()); } } } for (String arg : options.getArguments()) { HexCodeFileDis dis = new HexCodeFileDis(false); File inputFile = new File(arg); String input = new String(Files.toChars(inputFile)); String inputSource = inputFile.getAbsolutePath(); String output = dis.processAll(input, inputSource, hcfOpenOption.getValue(), hcfCloseOption.getValue(), verboseOption.getValue()); File outputFile; if (outDir == null) { outputFile = inputFile; } else { outputFile = new File(outDir, inputFile.getName()); } System.out.println(outputFile + ": disassembled " + dis.hcfCount + " embedded HexCodeFiles"); if (outputFile.equals(inputFile)) { if (!output.equals(input)) { Files.fill(inputFile, output); } } else { Files.fill(outputFile, output); } } } }