/* * Copyright (c) 2005, 2010, 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. */ /* * An extension of BinaryCode that allows code to be printed. * Includes printing of disassembled byte codes, exception info, * local variable and line number info. * */ package ilib; import java.io.ByteArrayInputStream; import java.io.CharArrayWriter; import java.io.DataInputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.PrintStream; import java.util.Map; import java.util.HashMap; class InjectBytecodes implements RuntimeConstants { private final ClassReaderWriter c; private final PrintStream output; private final int length; private final int[] map; private final byte[] widening; private final Injector[] before = new Injector[256]; private final Injector[] after = new Injector[256]; private final String className; private final String methodName; private final Map<Integer,byte[]> snippets = new HashMap<>(); private int pos; private int newPos; private class Span { final int delta; final int target; final int newDelta; final int newTarget; Span(int delta) { this.delta = delta; this.target = pos + delta; this.newTarget = map[target]; this.newDelta = newTarget - newPos; } } /** * Constructor */ InjectBytecodes(ClassReaderWriter c, int length, String className, String methodName) { this.c = c; this.output = System.out; this.length = length; this.map = new int[length + 1]; this.widening = new byte[length + 1]; this.className = className; this.methodName = methodName; c.markLocalPositionStart(); for (int i = 0; i <= length; ++i) { map[i] = i; } } public void inject(int at, byte[] newCode) { snippets.put(new Integer(at), newCode); trace("external "); inject(at, newCode.length); } private void inject(int at, int len) { if (Inject.verbose) { traceln("Injecting " + len + " at " + at); } for (int i = at; i <= length; ++i) { map[i] += len; } } private void widen(int at, int len) { int delta = len - widening[at]; if (Inject.verbose) { traceln(); traceln("Widening to " + len + " at " + at); } inject(c.localPosition(), delta); // inject at end of instruction widening[at] = (byte)len; // mark at beginning of instruction } public void injectBefore(int code, Injector inj) { before[code] = inj; } public void injectAfter(int code, Injector inj) { after[code] = inj; } private void trace(String str) { if (Inject.verbose) { output.print(str); } } private void traceln(String str) { if (Inject.verbose) { output.println(str); } } private void traceln() { if (Inject.verbose) { output.println(); } } private void trace(int i) { if (Inject.verbose) { output.print(i); } } /** * Print an integer so that it takes 'length' characters in * the output. Temporary until formatting code is stable. */ private void traceFixedWidthInt(int x, int length) { if (Inject.verbose) { CharArrayWriter baStream = new CharArrayWriter(); PrintWriter pStream = new PrintWriter(baStream); pStream.print(x); String str = baStream.toString(); for (int cnt = length - str.length(); cnt > 0; --cnt) trace(" "); trace(str); } } void adjustOffsets() throws IOException { if (Inject.verbose) { traceln(); traceln("Method " + methodName); traceln(); } c.rewind(); while (c.localPosition() < length) { insertAtInstruction(); } trace("Searching for adjustments..."); c.rewind(); while (c.localPosition() < length) { if (!adjustInstruction()) { c.rewind(); traceln(); traceln("Restarting adjustments after change..."); } } // write the new bytecodes traceln(); traceln(); trace("Writing new code..."); c.rewind(); while (c.localPosition() < length) { writeInstruction(); } if (!snippets.isEmpty()) { throw new Error("not all snippets written"); } } /** * Walk one instruction inserting instrumentation at specified instructions */ private void insertAtInstruction() throws IOException { pos = c.localPosition(); int opcode = c.readU1(); if (opcode == opc_wide) { // no support for instrumenting wide instructions int wopcode = c.readU1(); int lvIndex = c.readU2(); switch (wopcode) { case opc_aload: case opc_astore: case opc_fload: case opc_fstore: case opc_iload: case opc_istore: case opc_lload: case opc_lstore: case opc_dload: case opc_dstore: case opc_ret: break; case opc_iinc: c.readS2(); break; default: throw new Error("Invalid wide opcode: " + wopcode); } } else { Injector inj; inj = before[opcode]; if (inj != null) { inject(pos, inj.bytecodes(className, methodName, pos)); } switch (opcode) { case opc_tableswitch:{ int header = (pos+1+3) & (~3); // 4byte boundry c.skip(header - (pos+1)); // skip old padding c.readU4(); int low = c.readU4(); int high = c.readU4(); c.skip((high+1-low) * 4); break; } case opc_lookupswitch:{ int header = (pos+1+3) & (~3); // 4byte boundry c.skip(header - (pos+1)); // skip padding c.readU4(); int npairs = c.readU4(); c.skip(npairs * 8); break; } default: { int instrLen = opcLengths[opcode]; c.skip(instrLen-1); } } inj = after[opcode]; if (inj != null) { pos = c.localPosition(); inject(pos, inj.bytecodes(className, methodName, pos)); } } } /** * Walk one instruction adjusting for insertions */ private boolean adjustInstruction() throws IOException { pos = c.localPosition(); newPos = map[pos]; int opcode = c.readU1(); if (Inject.verbose) { traceln(); traceFixedWidthInt(pos, 4); traceFixedWidthInt(newPos, 4); trace(" "); } if (opcode == opc_wide) { int wopcode = c.readU1(); int lvIndex = c.readU2(); if (Inject.verbose) { trace(opcNames[wopcode] + "_w "); } switch (wopcode) { case opc_aload: case opc_astore: case opc_fload: case opc_fstore: case opc_iload: case opc_istore: case opc_lload: case opc_lstore: case opc_dload: case opc_dstore: case opc_ret: trace(lvIndex); break; case opc_iinc: int constVal = c.readS2(); if (Inject.verbose) { trace(lvIndex + " " + constVal); } break; default: throw new Error("Invalid wide opcode: " + wopcode); } } else { if (Inject.verbose) { trace(opcNames[opcode]); } switch (opcode) { case opc_tableswitch:{ int widened = widening[pos]; int header = (pos+1+3) & (~3); // 4byte boundry int newHeader = (newPos+1+3) & (~3); // 4byte boundry c.skip(header - (pos+1)); // skip old padding Span defaultSkip = new Span(c.readU4()); int low = c.readU4(); int high = c.readU4(); if (Inject.verbose) { trace(" " + low + " to " + high); trace(": default= [was] " + defaultSkip.target); trace(" [now] " + defaultSkip.newTarget); for (int i = low; i <= high; ++i) { Span jump = new Span(c.readU4()); traceln(""); trace('\t'); traceFixedWidthInt(i, 5); trace(": " + jump.newTarget); } } else { c.skip((high+1-low) * 4); } int newPadding = newHeader - newPos; int oldPadding = header - pos; int deltaPadding = newPadding - oldPadding; if (widened != deltaPadding) { widen(pos, deltaPadding); return false; // cause restart } break; } case opc_lookupswitch:{ int widened = widening[pos]; int header = (pos+1+3) & (~3); // 4byte boundry int newHeader = (newPos+1+3) & (~3); // 4byte boundry c.skip(header - (pos+1)); // skip old padding Span defaultSkip = new Span(c.readU4()); int npairs = c.readU4(); if (Inject.verbose) { trace(" npairs: " + npairs); trace(": default= [was] " + defaultSkip.target); trace(" [now] " + defaultSkip.newTarget); for (int i = 0; i< npairs; ++i) { int match = c.readU4(); Span jump = new Span(c.readU4()); traceln(""); trace('\t'); traceFixedWidthInt(match, 5); trace(": " + jump.newTarget); } } else { c.skip(npairs * 8); } int newPadding = newHeader - newPos; int oldPadding = header - pos; int deltaPadding = newPadding - oldPadding; if (widened != deltaPadding) { widen(pos, deltaPadding); return false; // cause restart } break; } case opc_jsr: case opc_goto: case opc_ifeq: case opc_ifge: case opc_ifgt: case opc_ifle: case opc_iflt: case opc_ifne: case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge: case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt: case opc_if_acmpeq: case opc_if_acmpne: case opc_ifnull: case opc_ifnonnull: { int widened = widening[pos]; Span jump = new Span(c.readS2()); if (widened == 0) { // not yet widened int newDelta = jump.newDelta; if ((newDelta < -32768) || (newDelta > 32767)) { switch (opcode) { case opc_jsr: case opc_goto: widen(pos, 2); // will convert to wide break; default: widen(pos, 5); // will inject goto_w break; } return false; // cause restart } } if (Inject.verbose) { trace(" [was] " + jump.target + " ==> " + " [now] " + jump.newTarget); } break; } case opc_jsr_w: case opc_goto_w: { Span jump = new Span(c.readU4()); if (Inject.verbose) { trace(" [was] " + jump.target + " [now] " + jump.newTarget); } break; } default: { int instrLen = opcLengths[opcode]; c.skip(instrLen-1); break; } } } return true; // successful return } /** * Walk one instruction writing the transformed instruction. */ private void writeInstruction() throws IOException { pos = c.localPosition(); newPos = map[pos]; byte[] newCode = snippets.remove(new Integer(pos)); if (newCode != null) { traceln(); traceFixedWidthInt(pos, 4); trace(" ... -- Inserting new code"); c.writeBytes(newCode); } int opcode = c.readU1(); if (Inject.verbose) { traceln(); traceFixedWidthInt(pos, 4); traceFixedWidthInt(newPos, 4); trace(" "); } if (opcode == opc_wide) { int wopcode = c.readU1(); int lvIndex = c.readU2(); if (Inject.verbose) { trace(opcNames[wopcode] + "_w "); } c.writeU1(opcode); c.writeU1(wopcode); c.writeU2(lvIndex); switch (wopcode) { case opc_aload: case opc_astore: case opc_fload: case opc_fstore: case opc_iload: case opc_istore: case opc_lload: case opc_lstore: case opc_dload: case opc_dstore: case opc_ret: trace(lvIndex); break; case opc_iinc: int constVal = c.readS2(); c.writeU2(constVal); // ??? U vs S if (Inject.verbose) { trace(lvIndex + " " + constVal); } break; default: throw new Error("Invalid wide opcode: " + wopcode); } } else { if (Inject.verbose) { trace(opcNames[opcode]); } switch (opcode) { case opc_tableswitch:{ int header = (pos+1+3) & (~3); // 4byte boundry int newHeader = (newPos+1+3) & (~3); // 4byte boundry c.skip(header - (pos+1)); // skip old padding Span defaultSkip = new Span(c.readU4()); int low = c.readU4(); int high = c.readU4(); c.writeU1(opcode); // copy instruction for (int i = newPos+1; i < newHeader; ++i) { c.writeU1(0); // write new padding } c.writeU4(defaultSkip.newDelta); c.writeU4(low); c.writeU4(high); if (Inject.verbose) { trace(" " + low + " to " + high); trace(": default= [was] " + defaultSkip.target); trace(" [now] " + defaultSkip.newTarget); } for (int i = low; i <= high; ++i) { Span jump = new Span(c.readU4()); c.writeU4(jump.newDelta); if (Inject.verbose) { traceln(""); trace('\t'); traceFixedWidthInt(i, 5); trace(": " + jump.newTarget); } } break; } case opc_lookupswitch:{ int header = (pos+1+3) & (~3); // 4byte boundry int newHeader = (newPos+1+3) & (~3); // 4byte boundry c.skip(header - (pos+1)); // skip old padding Span defaultSkip = new Span(c.readU4()); int npairs = c.readU4(); if (Inject.verbose) { trace(" npairs: " + npairs); trace(": default= [was] " + defaultSkip.target); trace(" [now] " + defaultSkip.newTarget); } c.writeU1(opcode); // copy instruction for (int i = newPos+1; i < newHeader; ++i) { c.writeU1(0); // write new padding } c.writeU4(defaultSkip.newDelta); c.writeU4(npairs); for (int i = 0; i< npairs; ++i) { int match = c.readU4(); Span jump = new Span(c.readU4()); c.writeU4(match); c.writeU4(jump.newDelta); if (Inject.verbose) { traceln(""); trace('\t'); traceFixedWidthInt(match, 5); trace(": " + jump.newTarget); } } break; } case opc_jsr: case opc_goto: case opc_ifeq: case opc_ifge: case opc_ifgt: case opc_ifle: case opc_iflt: case opc_ifne: case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge: case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt: case opc_if_acmpeq: case opc_if_acmpne: case opc_ifnull: case opc_ifnonnull: { int widened = widening[pos]; Span jump = new Span(c.readS2()); int newOpcode = opcode; // default to unchanged if (widened == 0) { // not widened c.writeU1(opcode); // rewrite instruction c.writeU2(jump.newDelta); } else if (widened == 2) { // wide form switch (opcode) { case opc_jsr: newOpcode = opc_jsr_w; break; case opc_goto: newOpcode = opc_jsr_w; break; default: throw new Error("unexpected opcode: " + opcode); } c.writeU1(newOpcode); // write wide instruction c.writeU4(jump.newDelta); // write new and wide delta } else if (widened == 5) { // insert goto_w switch (opcode) { case opc_ifeq: newOpcode = opc_ifne; break; case opc_ifge: newOpcode = opc_iflt; break; case opc_ifgt: newOpcode = opc_ifle; break; case opc_ifle: newOpcode = opc_ifgt; break; case opc_iflt: newOpcode = opc_ifge; break; case opc_ifne: newOpcode = opc_ifeq; break; case opc_if_icmpeq: newOpcode = opc_if_icmpne; break; case opc_if_icmpne: newOpcode = opc_if_icmpeq; break; case opc_if_icmpge: newOpcode = opc_if_icmplt; break; case opc_if_icmpgt: newOpcode = opc_if_icmple; break; case opc_if_icmple: newOpcode = opc_if_icmpgt; break; case opc_if_icmplt: newOpcode = opc_if_icmpge; break; case opc_if_acmpeq: newOpcode = opc_if_acmpne; break; case opc_if_acmpne: newOpcode = opc_if_acmpeq; break; case opc_ifnull: newOpcode = opc_ifnonnull; break; case opc_ifnonnull: newOpcode = opc_ifnull; break; default: throw new Error("unexpected opcode: " + opcode); } c.writeU1(newOpcode); // write inverse branch c.writeU2(3 + 5); // beyond if and goto_w c.writeU1(opc_goto_w);// add a goto_w c.writeU4(jump.newDelta); // write new and wide delta } else { throw new Error("unexpected widening"); } if (Inject.verbose) { trace(" [was] " + jump.target + " ==> " + opcNames[newOpcode] + " [now] " + jump.newTarget); } break; } case opc_jsr_w: case opc_goto_w: { Span jump = new Span(c.readU4()); c.writeU1(opcode); // instruction itself c.writeU4(jump.newDelta); if (Inject.verbose) { trace(" [was] " + jump.target + " [now] " + jump.newTarget); } break; } default: { int instrLen = opcLengths[opcode]; c.writeU1(opcode); // instruction itself c.copy(instrLen-1); } } } } /** * Copy the exception table for this method code */ void copyExceptionTable() throws IOException { int tableLength = c.copyU2(); // exception table len if (tableLength > 0) { traceln(); traceln("Exception table:"); traceln(" from:old/new to:old/new target:old/new type"); for (int tcnt = tableLength; tcnt > 0; --tcnt) { int startPC = c.readU2(); int newStartPC = map[startPC]; c.writeU2(newStartPC); int endPC = c.readU2(); int newEndPC = map[endPC]; c.writeU2(newEndPC); int handlerPC = c.readU2(); int newHandlerPC = map[handlerPC]; c.writeU2(newHandlerPC); int catchType = c.copyU2(); if (Inject.verbose) { traceFixedWidthInt(startPC, 6); traceFixedWidthInt(newStartPC, 6); traceFixedWidthInt(endPC, 6); traceFixedWidthInt(newEndPC, 6); traceFixedWidthInt(handlerPC, 6); traceFixedWidthInt(newHandlerPC, 6); trace(" "); if (catchType == 0) traceln("any"); else { traceln("" + catchType); } } } } } /** * Copy the line number table for this method code */ void copyLineNumberAttr() throws IOException { // name index already read c.copy(4); // attr len int tableLength = c.copyU2(); // line table len if (tableLength > 0) { if (Inject.verbose) { traceln(); traceln("Line numbers for method " + methodName); } for (int tcnt = tableLength; tcnt > 0; --tcnt) { int startPC = c.readU2(); int newStartPC = map[startPC]; c.writeU2(newStartPC); int lineNumber = c.copyU2(); if (Inject.verbose) { traceln(" line " + lineNumber + ": [was] " + startPC + " [now] " + newStartPC); } } } } /** * Copy the local variable table for this method code */ void copyLocalVarAttr() throws IOException { // name index already read c.copy(4); // attr len int tableLength = c.copyU2(); // local var table len if (tableLength > 0) { if (Inject.verbose) { traceln(); traceln("Local variables for method " + methodName); } for (int tcnt = tableLength; tcnt > 0; --tcnt) { int startPC = c.readU2(); int newStartPC = map[startPC]; c.writeU2(newStartPC); int length = c.readU2(); int endPC = startPC + length; int newEndPC = map[endPC]; int newLength = newEndPC - newStartPC; c.writeU2(newLength); int nameIndex = c.copyU2(); int descriptorIndex = c.copyU2(); int index = c.copyU2(); if (Inject.verbose) { trace(" "); trace(descriptorIndex); trace(" "); trace(nameIndex); traceln(" pc= [was] " + startPC + " [now] " + newStartPC + ", length= [was] " + length + " [now] " + newLength + ", slot=" + index); } } } } }