问题
I am trying bcel to modify a method by inserting invoke before specific instructions. It seems that my instrumentation would result in a different stackmap table, which can not be auto-generated by the bcel package itself. So, my instrumented class file contains the old stackmap table, which would cause error with jvm. I haved tried with removeCodeAttributes, the method of MethodGen, that can remove all the code attributes. It can work in simple cases, a wrapped function, for example. And it can not work in my case now.
public class Insert{
public static void main(String[] args) throws ClassFormatException, IOException{
Insert isrt = new Insert();
String className = "StringBuilder.class";
JavaClass jclzz = new ClassParser(className).parse();
ClassGen cgen = new ClassGen(jclzz);
ConstantPoolGen cpgen = cgen.getConstantPool();
MethodGen mgen = new MethodGen(jclzz.getMethods()[1], className, cpgen);
InstructionFactory ifac = new InstructionFactory(cgen);
InstructionList ilist = mgen.getInstructionList();
for (InstructionHandle ihandle : ilist.getInstructionHandles()){
System.out.println(ihandle.toString());
}
InstructionFinder f = new InstructionFinder(ilist);
InstructionHandle[] insert_pos = (InstructionHandle[])(f.search("invokevirtual").next());
Instruction inserted_inst = ifac.createInvoke("java.lang.System", "currentTimeMillis", Type.LONG, Type.NO_ARGS, Constants.INVOKESTATIC);
System.out.println(inserted_inst.toString());
ilist.insert(insert_pos[0], inserted_inst);
mgen.setMaxStack();
mgen.setMaxLocals();
mgen.removeCodeAttributes();
cgen.replaceMethod(jclzz.getMethods()[1], mgen.getMethod());
ilist.dispose();
//output the file
FileOutputStream fos = new FileOutputStream(className);
cgen.getJavaClass().dump(fos);
fos.close();
}
}
回答1:
Removing a StackMapTable
is not a proper solution for fixing a wrong StackMapTable
. The important cite is:
4.7.4. The StackMapTable Attribute
In a
class
file whose version number is 50.0 or above, if a method'sCode
attribute does not have aStackMapTable
attribute, it has an implicit stack map attribute (§4.10.1). This implicit stack map attribute is equivalent to aStackMapTable
attribute withnumber_of_entries
equal to zero.
Since a StackMapTable
must have explicit entries for every branch target, such an implicit StackMapTable
will work with branch-free methods only. But in these cases, the method usually doesn’t have an explicit StackMapTable
anyway, so you wouldn’t have that problem then (unless the method had branches which your instrumentation removed).
Another conclusion is that you can get away with removing the StackMapTable
, if you patch the class file version number to a value below 50
. Of course, this is only a solution if you don’t need any class file feature introduced in version 50
or newer…
There was a grace period in which JVMs supported a fall-back mode for class files with broken StackMapTable
s just for scenarios like yours, where the tool support is not up-to-date. (See -XX:+FailoverToOldVerifier or -XX:-UseSplitVerifier) But the grace period is over now and that support has been declined, i.e. Java 8 JVMs do not support the fall-back mode anymore.
If you want to keep up with the Java development and instrument newer class files which might use features of these new versions you have only two choices:
- Calculate the correct
StackMapTable
manually - Use a tool which supports calculating the correct
StackMapTable
attributes, e.g.ASM
, (see java-bytecode-asm) does support it
来源:https://stackoverflow.com/questions/26072210/construct-the-stackmap-of-method-while-using-bcel