001/* 002 * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.code; 024 025import java.io.*; 026import java.util.*; 027import java.util.regex.*; 028 029import jdk.internal.jvmci.code.*; 030import jdk.internal.jvmci.code.CompilationResult.*; 031 032/** 033 * A HexCodeFile is a textual format for representing a chunk of machine code along with extra 034 * information that can be used to enhance a disassembly of the code. 035 * 036 * A pseudo grammar for a HexCodeFile is given below. 037 * 038 * <pre> 039 * HexCodeFile ::= Platform Delim HexCode Delim (OptionalSection Delim)* 040 * 041 * OptionalSection ::= Comment | OperandComment | JumpTable | LookupTable 042 * 043 * Platform ::= "Platform" ISA WordWidth 044 * 045 * HexCode ::= "HexCode" StartAddress HexDigits 046 * 047 * Comment ::= "Comment" Position String 048 * 049 * OperandComment ::= "OperandComment" Position String 050 * 051 * JumpTable ::= "JumpTable" Position EntrySize Low High 052 * 053 * LookupTable ::= "LookupTable" Position NPairs KeySize OffsetSize 054 * 055 * Position, EntrySize, Low, High, NPairs KeySize OffsetSize ::= int 056 * 057 * Delim := "<||@" 058 * </pre> 059 * 060 * There must be exactly one HexCode and Platform part in a HexCodeFile. The length of HexDigits 061 * must be even as each pair of digits represents a single byte. 062 * <p> 063 * Below is an example of a valid Code input: 064 * 065 * <pre> 066 * 067 * Platform AMD64 64 <||@ 068 * HexCode 0 e8000000009090904883ec084889842410d0ffff48893c24e800000000488b3c24488bf0e8000000004883c408c3 <||@ 069 * Comment 24 frame-ref-map: +0 {0} 070 * at java.lang.String.toLowerCase(String.java:2496) [bci: 1] 071 * |0 072 * locals: |stack:0:a 073 * stack: |stack:0:a 074 * <||@ 075 * OperandComment 24 {java.util.Locale.getDefault()} <||@ 076 * Comment 36 frame-ref-map: +0 {0} 077 * at java.lang.String.toLowerCase(String.java:2496) [bci: 4] 078 * |0 079 * locals: |stack:0:a 080 * <||@ 081 * OperandComment 36 {java.lang.String.toLowerCase(Locale)} lt;||@ 082 * 083 * </pre> 084 */ 085public class HexCodeFile { 086 087 public static final String NEW_LINE = CodeUtil.NEW_LINE; 088 public static final String SECTION_DELIM = " <||@"; 089 public static final String COLUMN_END = " <|@"; 090 public static final Pattern SECTION = Pattern.compile("(\\S+)\\s+(.*)", Pattern.DOTALL); 091 public static final Pattern COMMENT = Pattern.compile("(\\d+)\\s+(.*)", Pattern.DOTALL); 092 public static final Pattern OPERAND_COMMENT = COMMENT; 093 public static final Pattern JUMP_TABLE = Pattern.compile("(\\d+)\\s+(\\d+)\\s+(-{0,1}\\d+)\\s+(-{0,1}\\d+)\\s*"); 094 public static final Pattern LOOKUP_TABLE = Pattern.compile("(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s*"); 095 public static final Pattern HEX_CODE = Pattern.compile("(\\p{XDigit}+)(?:\\s+(\\p{XDigit}*))?"); 096 public static final Pattern PLATFORM = Pattern.compile("(\\S+)\\s+(\\S+)", Pattern.DOTALL); 097 098 /** 099 * Delimiter placed before a HexCodeFile when embedded in a string/stream. 100 */ 101 public static final String EMBEDDED_HCF_OPEN = "<<<HexCodeFile"; 102 103 /** 104 * Delimiter placed after a HexCodeFile when embedded in a string/stream. 105 */ 106 public static final String EMBEDDED_HCF_CLOSE = "HexCodeFile>>>"; 107 108 /** 109 * Map from a machine code position to a list of comments for the position. 110 */ 111 public final Map<Integer, List<String>> comments = new TreeMap<>(); 112 113 /** 114 * Map from a machine code position to a comment for the operands of the instruction at the 115 * position. 116 */ 117 public final Map<Integer, String> operandComments = new TreeMap<>(); 118 119 public final byte[] code; 120 121 public final ArrayList<JumpTable> jumpTables = new ArrayList<>(); 122 123 public final String isa; 124 125 public final int wordWidth; 126 127 public final long startAddress; 128 129 public HexCodeFile(byte[] code, long startAddress, String isa, int wordWidth) { 130 this.code = code; 131 this.startAddress = startAddress; 132 this.isa = isa; 133 this.wordWidth = wordWidth; 134 } 135 136 /** 137 * Parses a string in the format produced by {@link #toString()} to produce a 138 * {@link HexCodeFile} object. 139 */ 140 public static HexCodeFile parse(String input, int sourceOffset, String source, String sourceName) { 141 return new Parser(input, sourceOffset, source, sourceName).hcf; 142 } 143 144 /** 145 * Formats this HexCodeFile as a string that can be parsed with 146 * {@link #parse(String, int, String, String)}. 147 */ 148 @Override 149 public String toString() { 150 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 151 writeTo(baos); 152 return baos.toString(); 153 } 154 155 public String toEmbeddedString() { 156 return EMBEDDED_HCF_OPEN + NEW_LINE + toString() + EMBEDDED_HCF_CLOSE; 157 } 158 159 public void writeTo(OutputStream out) { 160 PrintStream ps = out instanceof PrintStream ? (PrintStream) out : new PrintStream(out); 161 ps.printf("Platform %s %d %s%n", isa, wordWidth, SECTION_DELIM); 162 ps.printf("HexCode %x %s %s%n", startAddress, HexCodeFile.hexCodeString(code), SECTION_DELIM); 163 164 for (JumpTable table : jumpTables) { 165 ps.printf("JumpTable %d %d %d %d %s%n", table.position, table.entrySize, table.low, table.high, SECTION_DELIM); 166 } 167 168 for (Map.Entry<Integer, List<String>> e : comments.entrySet()) { 169 int pos = e.getKey(); 170 for (String comment : e.getValue()) { 171 ps.printf("Comment %d %s %s%n", pos, comment, SECTION_DELIM); 172 } 173 } 174 175 for (Map.Entry<Integer, String> e : operandComments.entrySet()) { 176 ps.printf("OperandComment %d %s %s%n", e.getKey(), e.getValue(), SECTION_DELIM); 177 } 178 ps.flush(); 179 } 180 181 /** 182 * Formats a byte array as a string of hex digits. 183 */ 184 public static String hexCodeString(byte[] code) { 185 if (code == null) { 186 return ""; 187 } else { 188 StringBuilder sb = new StringBuilder(code.length * 2); 189 for (int b : code) { 190 String hex = Integer.toHexString(b & 0xff); 191 if (hex.length() == 1) { 192 sb.append('0'); 193 } 194 sb.append(hex); 195 } 196 return sb.toString(); 197 } 198 } 199 200 /** 201 * Adds a comment to the list of comments for a given position. 202 */ 203 public void addComment(int pos, String comment) { 204 List<String> list = comments.get(pos); 205 if (list == null) { 206 list = new ArrayList<>(); 207 comments.put(pos, list); 208 } 209 list.add(encodeString(comment)); 210 } 211 212 /** 213 * Sets an operand comment for a given position. 214 * 215 * @return the previous operand comment for {@code pos} 216 */ 217 public String addOperandComment(int pos, String comment) { 218 return operandComments.put(pos, encodeString(comment)); 219 } 220 221 /** 222 * Adds any jump tables, lookup tables or code comments from a list of code annotations. 223 */ 224 public static void addAnnotations(HexCodeFile hcf, List<CodeAnnotation> annotations) { 225 if (annotations == null || annotations.isEmpty()) { 226 return; 227 } 228 for (CodeAnnotation a : annotations) { 229 if (a instanceof JumpTable) { 230 JumpTable table = (JumpTable) a; 231 hcf.jumpTables.add(table); 232 } else if (a instanceof CodeComment) { 233 CodeComment comment = (CodeComment) a; 234 hcf.addComment(comment.position, comment.value); 235 } 236 } 237 } 238 239 /** 240 * Modifies a string to mangle any substrings matching {@link #SECTION_DELIM} and 241 * {@link #COLUMN_END}. 242 */ 243 public static String encodeString(String input) { 244 int index; 245 String s = input; 246 while ((index = s.indexOf(SECTION_DELIM)) != -1) { 247 s = s.substring(0, index) + " < |@" + s.substring(index + SECTION_DELIM.length()); 248 } 249 while ((index = s.indexOf(COLUMN_END)) != -1) { 250 s = s.substring(0, index) + " < @" + s.substring(index + COLUMN_END.length()); 251 } 252 return s; 253 } 254 255 /** 256 * Helper class to parse a string in the format produced by {@link HexCodeFile#toString()} and 257 * produce a {@link HexCodeFile} object. 258 */ 259 static class Parser { 260 261 final String input; 262 final String inputSource; 263 String isa; 264 int wordWidth; 265 byte[] code; 266 long startAddress; 267 HexCodeFile hcf; 268 269 Parser(String input, int sourceOffset, String source, String sourceName) { 270 this.input = input; 271 this.inputSource = sourceName; 272 parseSections(sourceOffset, source); 273 } 274 275 void makeHCF() { 276 if (hcf == null) { 277 if (isa != null && wordWidth != 0 && code != null) { 278 hcf = new HexCodeFile(code, startAddress, isa, wordWidth); 279 } 280 } 281 } 282 283 void checkHCF(String section, int offset) { 284 check(hcf != null, offset, section + " section must be after Platform and HexCode section"); 285 } 286 287 void check(boolean condition, int offset, String message) { 288 if (!condition) { 289 error(offset, message); 290 } 291 } 292 293 Error error(int offset, String message) { 294 throw new Error(errorMessage(offset, message)); 295 } 296 297 void warning(int offset, String message) { 298 PrintStream err = System.err; 299 err.println("Warning: " + errorMessage(offset, message)); 300 } 301 302 String errorMessage(int offset, String message) { 303 assert offset < input.length(); 304 InputPos inputPos = filePos(offset); 305 int lineEnd = input.indexOf(HexCodeFile.NEW_LINE, offset); 306 int lineStart = offset - inputPos.col; 307 String line = lineEnd == -1 ? input.substring(lineStart) : input.substring(lineStart, lineEnd); 308 return String.format("%s:%d: %s%n%s%n%" + (inputPos.col + 1) + "s", inputSource, inputPos.line, message, line, "^"); 309 } 310 311 static class InputPos { 312 313 final int line; 314 final int col; 315 316 public InputPos(int line, int col) { 317 this.line = line; 318 this.col = col; 319 } 320 } 321 322 InputPos filePos(int index) { 323 assert input != null; 324 int lineStart = input.lastIndexOf(HexCodeFile.NEW_LINE, index) + 1; 325 326 String l = input.substring(lineStart, lineStart + 10); 327 PrintStream out = System.out; 328 out.println("YYY" + input.substring(index, index + 10) + "..."); 329 out.println("XXX" + l + "..."); 330 331 int pos = input.indexOf(HexCodeFile.NEW_LINE, 0); 332 int line = 1; 333 while (pos > 0 && pos < index) { 334 line++; 335 pos = input.indexOf(HexCodeFile.NEW_LINE, pos + 1); 336 } 337 return new InputPos(line, index - lineStart); 338 } 339 340 void parseSections(int offset, String source) { 341 assert input.startsWith(source, offset); 342 int index = 0; 343 int endIndex = source.indexOf(SECTION_DELIM); 344 while (endIndex != -1) { 345 while (source.charAt(index) <= ' ') { 346 index++; 347 } 348 String section = source.substring(index, endIndex).trim(); 349 parseSection(offset + index, section); 350 index = endIndex + SECTION_DELIM.length(); 351 endIndex = source.indexOf(SECTION_DELIM, index); 352 } 353 } 354 355 int parseInt(int offset, String value) { 356 try { 357 return Integer.parseInt(value); 358 } catch (NumberFormatException e) { 359 throw error(offset, "Not a valid integer: " + value); 360 } 361 } 362 363 void parseSection(int offset, String section) { 364 if (section.isEmpty()) { 365 return; 366 } 367 assert input.startsWith(section, offset); 368 Matcher m = HexCodeFile.SECTION.matcher(section); 369 check(m.matches(), offset, "Section does not match pattern " + HexCodeFile.SECTION); 370 371 String header = m.group(1); 372 String body = m.group(2); 373 int headerOffset = offset + m.start(1); 374 int bodyOffset = offset + m.start(2); 375 376 if (header.equals("Platform")) { 377 check(isa == null, bodyOffset, "Duplicate Platform section found"); 378 m = HexCodeFile.PLATFORM.matcher(body); 379 check(m.matches(), bodyOffset, "Platform does not match pattern " + HexCodeFile.PLATFORM); 380 isa = m.group(1); 381 wordWidth = parseInt(bodyOffset + m.start(2), m.group(2)); 382 makeHCF(); 383 } else if (header.equals("HexCode")) { 384 check(code == null, bodyOffset, "Duplicate Code section found"); 385 m = HexCodeFile.HEX_CODE.matcher(body); 386 check(m.matches(), bodyOffset, "Code does not match pattern " + HexCodeFile.HEX_CODE); 387 String hexAddress = m.group(1); 388 startAddress = Long.valueOf(hexAddress, 16); 389 String hexCode = m.group(2); 390 if (hexCode == null) { 391 code = new byte[0]; 392 } else { 393 check((hexCode.length() % 2) == 0, bodyOffset, "Hex code length must be even"); 394 code = new byte[hexCode.length() / 2]; 395 for (int i = 0; i < code.length; i++) { 396 String hexByte = hexCode.substring(i * 2, (i + 1) * 2); 397 code[i] = (byte) Integer.parseInt(hexByte, 16); 398 } 399 } 400 makeHCF(); 401 } else if (header.equals("Comment")) { 402 checkHCF("Comment", headerOffset); 403 m = HexCodeFile.COMMENT.matcher(body); 404 check(m.matches(), bodyOffset, "Comment does not match pattern " + HexCodeFile.COMMENT); 405 int pos = parseInt(bodyOffset + m.start(1), m.group(1)); 406 String comment = m.group(2); 407 hcf.addComment(pos, comment); 408 } else if (header.equals("OperandComment")) { 409 checkHCF("OperandComment", headerOffset); 410 m = HexCodeFile.OPERAND_COMMENT.matcher(body); 411 check(m.matches(), bodyOffset, "OperandComment does not match pattern " + HexCodeFile.OPERAND_COMMENT); 412 int pos = parseInt(bodyOffset + m.start(1), m.group(1)); 413 String comment = m.group(2); 414 hcf.addOperandComment(pos, comment); 415 } else if (header.equals("JumpTable")) { 416 checkHCF("JumpTable", headerOffset); 417 m = HexCodeFile.JUMP_TABLE.matcher(body); 418 check(m.matches(), bodyOffset, "JumpTable does not match pattern " + HexCodeFile.JUMP_TABLE); 419 int pos = parseInt(bodyOffset + m.start(1), m.group(1)); 420 int entrySize = parseInt(bodyOffset + m.start(2), m.group(2)); 421 int low = parseInt(bodyOffset + m.start(3), m.group(3)); 422 int high = parseInt(bodyOffset + m.start(4), m.group(4)); 423 hcf.jumpTables.add(new JumpTable(pos, low, high, entrySize)); 424 } else { 425 error(offset, "Unknown section header: " + header); 426 } 427 } 428 } 429}