/* * Copyright (c) 2013-2017 Chris Newland. * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki */ package org.adoptopenjdk.jitwatch.model.assembly.x86; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_CLOSE_PARENTHESES; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_CLOSE_SQUARE_BRACKET; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_COMMA; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OPEN_PARENTHESES; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OPEN_SQUARE_BRACKET; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SPACE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING_ASSEMBLY; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_EMPTY; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_HEX_POSTFIX; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_HEX_PREFIX; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_OPEN_SQUARE_BRACKET; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_PERCENT; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_SPACE; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.adoptopenjdk.jitwatch.model.assembly.AbstractAssemblyParser; import org.adoptopenjdk.jitwatch.model.assembly.Architecture; import org.adoptopenjdk.jitwatch.model.assembly.AssemblyInstruction; import org.adoptopenjdk.jitwatch.model.assembly.AssemblyLabels; import org.adoptopenjdk.jitwatch.model.assembly.AssemblyUtil; public final class AssemblyParserX86 extends AbstractAssemblyParser { // http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html private static final String PART_ADDRESS = "(" + S_HEX_PREFIX + "[a-f0-9]+):"; private static final String PART_INSTRUCTION = "([0-9a-zA-Z:_\\(\\)\\[\\]\\+\\*\\$,\\-%\\s]+)"; private static final String PART_COMMENT = "([;#].*)?"; private static final Pattern ASSEMBLY_CONSTANT = Pattern .compile("^([\\$]?(" + S_HEX_PREFIX + ")?[a-f0-9]+[" + S_HEX_POSTFIX + "]?)$"); private static final Pattern PATTERN_ASSEMBLY_INSTRUCTION = Pattern .compile("^" + PART_ADDRESS + "\\s+" + PART_INSTRUCTION + PART_COMMENT); public AssemblyParserX86(Architecture architecture) { super(architecture); } @Override public AssemblyInstruction createInstruction(final AssemblyLabels labels, final String inLine) { if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("Trying to parse instruction : {}", inLine); } String line = inLine; AssemblyInstruction instr = null; String annotation = S_EMPTY; if (!line.startsWith(S_HEX_PREFIX)) { int addressIndex = line.indexOf(' ' + S_HEX_PREFIX); if (addressIndex != -1) { annotation = line.substring(0, addressIndex) + ' '; line = line.substring(addressIndex + 1); } } Matcher matcher = PATTERN_ASSEMBLY_INSTRUCTION.matcher(line); if (matcher.find()) { if (DEBUG_LOGGING_ASSEMBLY) { for (int i = 1; i <= matcher.groupCount(); i++) { logger.debug("parts : '{}'='{}'", i, matcher.group(i)); } } if (matcher.groupCount() == 3) { String address = matcher.group(1); String instructionString = matcher.group(2); String comment = matcher.group(3); if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("Annotation : '{}'", annotation); logger.debug("Address : '{}'", address); logger.debug("Instruction: '{}'", instructionString); logger.debug("Comment : '{}'", comment); } long addressValue = AssemblyUtil.getValueFromAddress(address); if (instructionString != null && instructionString.trim().length() > 0) { instr = parseInstruction(instructionString, addressValue, comment, annotation, labels); labels.newInstruction(instr); } } } return instr; } @Override public AssemblyInstruction parseInstruction(String input, long address, String comment, String annotation, AssemblyLabels labels) { input = input.replaceAll("\\s+", S_SPACE).trim(); int length = input.length(); boolean inBrackets = false; String mnemonic = null; List<String> prefixes = new ArrayList<>(); List<String> operands = new ArrayList<>(); StringBuilder partBuilder = new StringBuilder(); for (int pos = 0; pos < length; pos++) { char c = input.charAt(pos); if (c == C_OPEN_PARENTHESES || c == C_OPEN_SQUARE_BRACKET) { inBrackets = true; } else if (c == C_CLOSE_PARENTHESES || c == C_CLOSE_SQUARE_BRACKET) { inBrackets = false; } if (c == C_SPACE && mnemonic == null) { // end of part String part = partBuilder.toString(); partBuilder.delete(0, partBuilder.length()); if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("part: '{}'", part); } if (mnemonic == null) { if ("data64".equals(part) || "data32".equals(part) || "data16".equals(part) || "data8".equals(part) || "lock".equals(part)) { prefixes.add(part); } else { mnemonic = part; if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("mnemonic: '{}'", mnemonic); } } } } else if (c == C_COMMA && !inBrackets) { String operand = partBuilder.toString(); partBuilder.delete(0, partBuilder.length()); operands.add(operand); if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("operand1: '{}'", operand); } } else { partBuilder.append(c); } } if (partBuilder.length() > 0) { String part = partBuilder.toString(); partBuilder.delete(0, partBuilder.length() - 1); if (mnemonic == null) { mnemonic = part; if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("mnemonic: '{}'", part); } } else { operands.add(part); if (DEBUG_LOGGING_ASSEMBLY) { logger.debug("operand2: '{}'", part); } } } return new AssemblyInstruction(annotation, address, prefixes, mnemonic, operands, comment, labels); } @Override public boolean isConstant(String mnemonic, String operand) { return ASSEMBLY_CONSTANT.matcher(operand).find() && !isJump(mnemonic); } @Override public boolean isRegister(String mnemonic, String operand) { return operand.startsWith(S_PERCENT) || operand.contains("(%") || operand.contains(S_OPEN_SQUARE_BRACKET) || (!isConstant(mnemonic, operand) && !isAddress(mnemonic, operand)); } @Override public boolean isJump(String mnemonic) { boolean result = false; if (mnemonic != null) { result = mnemonic.toLowerCase().startsWith("j") || mnemonic.toLowerCase().startsWith("call"); } return result; } @Override public String extractRegisterName(final String input) { String regName = input; int indexOpenParentheses = input.indexOf(C_OPEN_PARENTHESES); int indexCloseParentheses = input.indexOf(C_CLOSE_PARENTHESES); if (indexOpenParentheses != -1 && indexCloseParentheses != -1) { regName = regName.substring(indexOpenParentheses + 1, indexCloseParentheses); } int indexOpenSquareBracket = regName.indexOf(C_OPEN_SQUARE_BRACKET); int indexCloseSquareBracket = regName.indexOf(C_CLOSE_SQUARE_BRACKET); if (indexOpenSquareBracket != -1 && indexCloseSquareBracket != -1) { regName = regName.substring(indexOpenSquareBracket + 1, indexCloseSquareBracket); } if (regName.startsWith("*")) { regName = regName.substring(1); } if (regName.startsWith(S_PERCENT)) { regName = regName.substring(1); } StringBuilder builder = new StringBuilder(); for (int i = 0; i < regName.length(); i++) { char c = regName.charAt(i); if (Character.isAlphabetic(c) || Character.isDigit(c)) { builder.append(c); } else { break; } } regName = builder.toString(); return regName; } }