Mercurial > hg > truffle
view graal/com.oracle.max.criutils/src/com/oracle/max/criutils/HexCodeFile.java @ 5609:282e2d94b420
Add missing file
author | Gilles Duboscq <duboscq@ssw.jku.at> |
---|---|
date | Thu, 14 Jun 2012 14:14:06 +0200 |
parents | b6617d13ea44 |
children | 4f9574b2893e |
line wrap: on
line source
/* * 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.criutils; import java.io.*; import java.lang.reflect.*; import java.util.*; import java.util.regex.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.code.CompilationResult.*; /** * A HexCodeFile is a textual format for representing a chunk of machine code along * with extra information that can be used to enhance a disassembly of the code. * * A pseudo grammar for a HexCodeFile is given below. * <pre> * HexCodeFile ::= Platform Delim HexCode Delim (OptionalSection Delim)* * * OptionalSection ::= Comment | OperandComment | JumpTable | LookupTable * * Platform ::= "Platform" ISA WordWidth * * HexCode ::= "HexCode" StartAddress HexDigits * * Comment ::= "Comment" Position String * * OperandComment ::= "OperandComment" Position String * * JumpTable ::= "JumpTable" Position EntrySize Low High * * LookupTable ::= "LookupTable" Position NPairs KeySize OffsetSize * * Position, EntrySize, Low, High, NPairs KeySize OffsetSize ::= int * * Delim := "<||@" * </pre> * * There must be exactly one HexCode and Platform part in a HexCodeFile. The length of HexDigits must be even * as each pair of digits represents a single byte. * <p> * Below is an example of a valid Code input: * <pre> * * Platform AMD64 64 <||@ * HexCode 0 e8000000009090904883ec084889842410d0ffff48893c24e800000000488b3c24488bf0e8000000004883c408c3 <||@ * Comment 24 frame-ref-map: +0 {0} * at java.lang.String.toLowerCase(String.java:2496) [bci: 1] * |0 * locals: |stack:0:a * stack: |stack:0:a * <||@ * OperandComment 24 {java.util.Locale.getDefault()} <||@ * Comment 36 frame-ref-map: +0 {0} * at java.lang.String.toLowerCase(String.java:2496) [bci: 4] * |0 * locals: |stack:0:a * <||@ * OperandComment 36 {java.lang.String.toLowerCase(Locale)} <||@ * * </pre> */ public class HexCodeFile { public static final String NEW_LINE = CodeUtil.NEW_LINE; public static final String SECTION_DELIM = " <||@"; public static final Pattern SECTION = Pattern.compile("(\\S+)\\s+(.*)", Pattern.DOTALL); public static final Pattern COMMENT = Pattern.compile("(\\d+)\\s+(.*)", Pattern.DOTALL); public static final Pattern OPERAND_COMMENT = COMMENT; public static final Pattern JUMP_TABLE = Pattern.compile("(\\d+)\\s+(\\d+)\\s+(-{0,1}\\d+)\\s+(-{0,1}\\d+)\\s*"); public static final Pattern LOOKUP_TABLE = Pattern.compile("(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s*"); public static final Pattern HEX_CODE = Pattern.compile("(\\p{XDigit}+)(?:\\s+(\\p{XDigit}*))?"); public static final Pattern PLATFORM = Pattern.compile("(\\S+)\\s+(\\S+)", Pattern.DOTALL); /** * Delimiter placed before a HexCodeFile when embedded in a string/stream. */ public static final String EMBEDDED_HCF_OPEN = "<<<HexCodeFile"; /** * Delimiter placed after a HexCodeFile when embedded in a string/stream. */ public static final String EMBEDDED_HCF_CLOSE = "HexCodeFile>>>"; /** * Map from a machine code position to a list of comments for the position. */ public final Map<Integer, List<String>> comments = new TreeMap<>(); /** * Map from a machine code position to a comment for the operands of the instruction at the position. */ public final Map<Integer, String> operandComments = new TreeMap<>(); public final byte[] code; public final ArrayList<JumpTable> jumpTables = new ArrayList<>(); public final ArrayList<LookupTable> lookupTables = new ArrayList<>(); public final String isa; public final int wordWidth; public final long startAddress; public HexCodeFile(byte[] code, long startAddress, String isa, int wordWidth) { this.code = code; this.startAddress = startAddress; this.isa = isa; this.wordWidth = wordWidth; } /** * Parses a string in the format produced by {@link #toString()} to produce a {@link HexCodeFile} object. */ public static HexCodeFile parse(String source, String sourceName) { return new Parser(source, sourceName).hcf; } /** * Formats this HexCodeFile as a string that can be parsed with {@link #parse(String, String)}. */ @Override public String toString() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); writeTo(baos); return baos.toString(); } public String toEmbeddedString() { return EMBEDDED_HCF_OPEN + NEW_LINE + toString() + EMBEDDED_HCF_CLOSE; } public void writeTo(OutputStream out) { PrintStream ps = out instanceof PrintStream ? (PrintStream) out : new PrintStream(out); ps.printf("Platform %s %d %s%n", isa, wordWidth, SECTION_DELIM); ps.printf("HexCode %x %s %s%n", startAddress, HexCodeFile.hexCodeString(code), SECTION_DELIM); for (JumpTable table : jumpTables) { ps.printf("JumpTable %d %d %d %d %s%n", table.position, table.entrySize, table.low, table.high, SECTION_DELIM); } for (LookupTable table : lookupTables) { ps.printf("LookupTable %d %d %d %d %s%n", table.position, table.npairs, table.keySize, table.keySize, SECTION_DELIM); } for (Map.Entry<Integer, List<String>> e : comments.entrySet()) { int pos = e.getKey(); for (String comment : e.getValue()) { ps.printf("Comment %d %s %s%n", pos, comment, SECTION_DELIM); } } for (Map.Entry<Integer, String> e : operandComments.entrySet()) { ps.printf("OperandComment %d %s %s%n", e.getKey(), e.getValue(), SECTION_DELIM); } ps.flush(); } /** * Formats a byte array as a string of hex digits. */ public static String hexCodeString(byte[] code) { StringBuilder sb = new StringBuilder(code.length * 2); for (int b : code) { String hex = Integer.toHexString(b & 0xff); if (hex.length() == 1) { sb.append('0'); } sb.append(hex); } return sb.toString(); } /** * Adds a comment to the list of comments for a given position. */ public void addComment(int pos, String comment) { List<String> list = comments.get(pos); if (list == null) { list = new ArrayList<>(); comments.put(pos, list); } list.add(encodeString(comment)); } /** * Sets an operand comment for a given position. * * @return the previous operand comment for {@code pos} */ public String addOperandComment(int pos, String comment) { return operandComments.put(pos, encodeString(comment)); } /** * Adds any jump tables, lookup tables or code comments from a list of code annotations. */ public static void addAnnotations(HexCodeFile hcf, List<CodeAnnotation> annotations) { if (annotations == null || annotations.isEmpty()) { return; } for (CodeAnnotation a : annotations) { if (a instanceof JumpTable) { JumpTable table = (JumpTable) a; hcf.jumpTables.add(table); } else if (a instanceof LookupTable) { LookupTable table = (LookupTable) a; hcf.lookupTables.add(table); } else if (a instanceof CodeComment) { CodeComment comment = (CodeComment) a; hcf.addComment(comment.position, comment.value); } } } /** * Modifies a string to mangle any substrings matching {@link #SECTION_DELIM}. */ public static String encodeString(String s) { int index; String result = s; while ((index = result.indexOf(SECTION_DELIM)) != -1) { result = result.substring(0, index) + " < |@" + result.substring(index + SECTION_DELIM.length()); } return result; } /** * Helper class to parse a string in the format produced by {@link HexCodeFile#toString()} * and produce a {@link HexCodeFile} object. */ static class Parser { private static final Field offsetField = stringField("offset"); private static final Field valueField = stringField("value"); static Field stringField(String name) { try { Field field = String.class.getDeclaredField(name); field.setAccessible(true); return field; } catch (Exception e) { throw new Error("Could not get reflective access to field " + String.class.getName() + "." + name); } } final String input; final String inputSource; String isa; int wordWidth; byte[] code; long startAddress; HexCodeFile hcf; Parser(String source, String sourceName) { this.input = storage(source); this.inputSource = sourceName; parseSections(source); } void makeHCF() { if (hcf == null) { if (isa != null && wordWidth != 0 && code != null) { hcf = new HexCodeFile(code, startAddress, isa, wordWidth); } } } void checkHCF(String section, String where) { check(hcf != null, where, section + " section must be after Platform and HexCode section"); } void check(boolean condition, String where, String message) { if (!condition) { error(where, message); } } int offset(String s) { try { return offsetField.getInt(s); } catch (Exception e) { throw new Error("Could not read value of field " + offsetField, e); } } /** * Gets a string corresponding to the storage char array for a given string. */ String storage(String s) { try { char[] value = (char[]) valueField.get(s); if (offset(s) == 0 && value.length == s.length()) { return s; } return new String(value); } catch (Exception e) { throw new Error("Could not read value of field " + valueField, e); } } Error error(String where, String message) { return error(offset(where), message); } Error error(int offset, String message) { throw new Error(errorMessage(offset, message)); } void warning(String where, String message) { System.err.println("Warning: " + errorMessage(offset(where), message)); } String errorMessage(int offset, String message) { assert offset < input.length(); InputPos inputPos = filePos(offset); int lineEnd = input.indexOf(HexCodeFile.NEW_LINE, offset); int lineStart = offset - inputPos.col; String line = lineEnd == -1 ? input.substring(lineStart) : input.substring(lineStart, lineEnd); return String.format("%s:%d: %s%n%s%n%" + (inputPos.col + 1) + "s", inputSource, inputPos.line, message, line, "^"); } static class InputPos { final int line; final int col; public InputPos(int line, int col) { this.line = line; this.col = col; } } InputPos filePos(int index) { assert input != null; int line = 1; int lineEnd = 0; int lineStart = 0; while ((lineEnd = input.indexOf(HexCodeFile.NEW_LINE, lineStart)) != -1) { if (lineEnd < index) { line++; lineStart = lineEnd + HexCodeFile.NEW_LINE.length(); } else { break; } } return new InputPos(line, index - lineStart); } void parseSections(String source) { int index = 0; int endIndex = source.indexOf(SECTION_DELIM); while (endIndex != -1) { String section = source.substring(index, endIndex).trim(); parseSection(section); index = endIndex + SECTION_DELIM.length(); endIndex = source.indexOf(SECTION_DELIM, index); } } int parseInt(String value) { try { return Integer.parseInt(value); } catch (NumberFormatException e) { throw error(value, "Not a valid integer: " + value); } } void parseSection(String section) { if (section.isEmpty()) { return; } Matcher m = HexCodeFile.SECTION.matcher(section); check(m.matches(), section, "Section does not match pattern " + HexCodeFile.SECTION); String header = m.group(1); String body = m.group(2); if (header.equals("Platform")) { check(isa == null, body, "Duplicate Platform section found"); m = HexCodeFile.PLATFORM.matcher(body); check(m.matches(), body, "Platform does not match pattern " + HexCodeFile.PLATFORM); isa = m.group(1); wordWidth = parseInt(m.group(2)); makeHCF(); } else if (header.equals("HexCode")) { check(code == null, body, "Duplicate Code section found"); m = HexCodeFile.HEX_CODE.matcher(body); check(m.matches(), body, "Code does not match pattern " + HexCodeFile.HEX_CODE); String hexAddress = m.group(1); startAddress = Long.valueOf(hexAddress, 16); String hexCode = m.group(2); if (hexCode == null) { code = new byte[0]; } else { check((hexCode.length() % 2) == 0, body, "Hex code length must be even"); code = new byte[hexCode.length() / 2]; for (int i = 0; i < code.length; i++) { String hexByte = hexCode.substring(i * 2, (i + 1) * 2); code[i] = (byte) Integer.parseInt(hexByte, 16); } } makeHCF(); } else if (header.equals("Comment")) { checkHCF("Comment", header); m = HexCodeFile.COMMENT.matcher(body); check(m.matches(), body, "Comment does not match pattern " + HexCodeFile.COMMENT); int pos = parseInt(m.group(1)); String comment = m.group(2); hcf.addComment(pos, comment); } else if (header.equals("OperandComment")) { checkHCF("OperandComment", header); m = HexCodeFile.OPERAND_COMMENT.matcher(body); check(m.matches(), body, "OperandComment does not match pattern " + HexCodeFile.OPERAND_COMMENT); int pos = parseInt(m.group(1)); String comment = m.group(2); hcf.addOperandComment(pos, comment); } else if (header.equals("JumpTable")) { checkHCF("JumpTable", header); m = HexCodeFile.JUMP_TABLE.matcher(body); check(m.matches(), body, "JumpTable does not match pattern " + HexCodeFile.JUMP_TABLE); int pos = parseInt(m.group(1)); int entrySize = parseInt(m.group(2)); int low = parseInt(m.group(3)); int high = parseInt(m.group(4)); hcf.jumpTables.add(new JumpTable(pos, low, high, entrySize)); } else if (header.equals("LookupTable")) { checkHCF("LookupTable", header); m = HexCodeFile.LOOKUP_TABLE.matcher(body); check(m.matches(), body, "LookupTable does not match pattern " + HexCodeFile.LOOKUP_TABLE); int pos = parseInt(m.group(1)); int npairs = parseInt(m.group(2)); int keySize = parseInt(m.group(3)); int offsetSize = parseInt(m.group(4)); hcf.lookupTables.add(new LookupTable(pos, npairs, keySize, offsetSize)); } else { error(section, "Unknown section header: " + header); } } } }