/* * Copyright (c) 2007, 2012, 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.sun.max.tele.method; import java.io.*; import java.util.*; import com.sun.max.*; import com.sun.max.asm.*; import com.sun.max.asm.amd64.*; import com.sun.max.asm.dis.*; import com.sun.max.asm.gen.*; import com.sun.max.asm.gen.cisc.x86.*; import com.sun.max.lang.*; import com.sun.max.platform.*; import com.sun.max.program.*; import com.sun.max.tele.util.*; import com.sun.max.unsafe.*; /** * Disassembler for machine code in the VM. */ public final class TeleDisassembler { private static final int TRACE_VALUE = 1; private static String tracePrefix() { return "[TeleDisassembler: thread=" + Thread.currentThread().getName() + "] "; } private TeleDisassembler() { } /** * Causes the disassembler to load and initialize static state * on a separate thread, since it can be time consuming. * * @param platform the kind of disassembler to initialize. */ public static void initialize(final Platform platform) { final Thread thread = new Thread("TeleDisassembler initializer") { @Override public void run() { Trace.begin(TRACE_VALUE, tracePrefix() + "initializing"); final long startTimeMillis = System.currentTimeMillis(); createDisassembler(platform, Address.zero(), null); Trace.end(TRACE_VALUE, tracePrefix() + "initializing", startTimeMillis); } }; thread.start(); } /** * Disassembles a segment of compiled code. * * @param platform the hardware, including the kind of processor for which the code was compiled. * @param codeStart start location of the code in VM memory. * @param code the compiled code as bytes * @param inlineDataDecoder * * @return the code disassembled into instructions. */ public static List<TargetCodeInstruction> decode(Platform platform, Address codeStart, byte[] code, InlineDataDecoder inlineDataDecoder) { final Disassembler disassembler = createDisassembler(platform, codeStart, inlineDataDecoder); final LoadLiteralParser literalParser = createLiteralParser(platform, disassembler, codeStart, code); return create(codeStart, code, disassembler, literalParser); } // Synchronize on class to avoid use of the disassembler before the initial call made during initialization. // This might be tidier if not all static. private static synchronized Disassembler createDisassembler(final Platform platform, Address startAddress, InlineDataDecoder inlineDataDecoder) { return Disassembler.createDisassembler(platform.isa, platform.wordWidth(), startAddress.toLong(), inlineDataDecoder); } private abstract static class LoadLiteralParser { protected Disassembler disassembler; protected Address literalBase; LoadLiteralParser(Disassembler disassembler, Address literalBase) { this.disassembler = disassembler; this.literalBase = literalBase; } /** * Gets a boolean indicating whether the specified disassembled instruction is an instruction that loads a * literal value. * * @param disassembledInstruction the disassembled instruction * @return true if the instruction loads a literal value, false otherwise. */ abstract boolean loadsLiteralData(DisassembledInstruction disassembledInstruction); /** * Gets the address of the literal value loaded by an instruction. * * @param disassembledInstruction the disassembled form of the instruction * @return the address where the literal value is stored */ abstract Address literalAddress(DisassembledInstruction disassembledInstruction); } private static class AMD64LoadLiteralParser extends LoadLiteralParser { AMD64LoadLiteralParser(Disassembler disassembler, Address codeStart) { super(disassembler, codeStart); } @Override boolean loadsLiteralData(DisassembledInstruction disassembledInstruction) { if (disassembledInstruction.arguments().size() == 2 && Utils.first(disassembledInstruction.arguments()) instanceof AMD64GeneralRegister64 && Utils.last(disassembledInstruction.template().operands()) instanceof X86OffsetParameter && ((X86Template) disassembledInstruction.template()).addressSizeAttribute() == WordWidth.BITS_64 && ((X86Template) disassembledInstruction.template()).rmCase() == X86TemplateContext.RMCase.SDWORD) { return true; } return false; } @Override Address literalAddress(DisassembledInstruction disassembledInstruction) { final ImmediateArgument immediateArgument = (ImmediateArgument) disassembledInstruction.arguments().get(1); return Address.fromLong(disassembledInstruction.addressForRelativeAddressing().plus(immediateArgument).asLong()); } } private static LoadLiteralParser createLiteralParser(final Platform platform, Disassembler disassembler, Address codeStart, byte [] code) { switch (platform.isa) { case AMD64: { return new AMD64LoadLiteralParser(disassembler, codeStart); } case ARM: case PPC: case IA32: case SPARC: { throw TeleError.unimplemented(); } } TeleError.unknownCase(); return null; } private static List<TargetCodeInstruction> create( Address codeStart, byte[] code, Disassembler disassembler, LoadLiteralParser literalParser) { List<DisassembledObject> disassembledObjects; try { final Class<List<DisassembledObject>> type = null; disassembledObjects = Utils.cast(type, disassembler.scan(new BufferedInputStream(new ByteArrayInputStream(code)))); } catch (Throwable throwable) { TeleWarning.message("Could not completely disassemble given code stream - trying partial disassembly instead", throwable); final BufferedInputStream bufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(code)); final List<DisassembledObject> objects = new ArrayList<DisassembledObject>(); try { while (bufferedInputStream.available() > 0) { objects.add(disassembler.scanOne(bufferedInputStream).get(0)); } } catch (Throwable t) { TeleWarning.message("Only partially disassembled given code stream", t); } disassembledObjects = objects; } final List<TargetCodeInstruction> targetCodeInstructions = new ArrayList<TargetCodeInstruction>(disassembledObjects.size()); for (DisassembledObject disassembledObject : disassembledObjects) { final DisassembledLabel label = disassembler.addressMapper().labelAt(disassembledObject); final TargetCodeInstruction targetCodeInstruction; if (disassembledObject instanceof DisassembledInstruction) { final DisassembledInstruction disassembledInstruction = (DisassembledInstruction) disassembledObject; final String operandsText = disassembledInstruction.operandsToString(disassembler.addressMapper()); final Address targetAddress; final Address literalSourceAddress; if (disassembledInstruction.arguments().size() == 1 && Utils.first(disassembledInstruction.arguments()) instanceof ImmediateArgument && (operandsText.contains("+") || operandsText.contains("-"))) { targetAddress = Address.fromLong(disassembledInstruction.targetAddress().asLong()); literalSourceAddress = null; } else if (literalParser.loadsLiteralData(disassembledInstruction)) { literalSourceAddress = literalParser.literalAddress(disassembledInstruction); targetAddress = null; } else { targetAddress = null; literalSourceAddress = null; } targetCodeInstruction = new TargetCodeInstruction( disassembledInstruction.mnemonic(), codeStart.plus(disassembledInstruction.startPosition()), disassembledInstruction.startPosition(), label == null ? null : label.name(), disassembledInstruction.bytes(), operandsText, targetAddress, literalSourceAddress); } else { final DisassembledData disassembledData = (DisassembledData) disassembledObject; final String operandsText = disassembledData.operandsToString(disassembler.addressMapper()); final ImmediateArgument dataTargetAddress = disassembledData.targetAddress(); final Address targetAddress; if (dataTargetAddress != null) { targetAddress = Address.fromLong(dataTargetAddress.asLong()); } else { targetAddress = null; } targetCodeInstruction = new TargetCodeInstruction(disassembledData.mnemonic(), codeStart.plus(disassembledObject.startPosition()), disassembledObject.startPosition(), label == null ? null : label.name(), disassembledObject.bytes(), operandsText, targetAddress, null); } targetCodeInstructions.add(targetCodeInstruction); } return targetCodeInstructions; } }