我正在尝试bcel通过在特定指令之前插入invoke来修改方法。
看来,我的检测将导致不同的stackmap表,而bcel包本身无法自动生成该表。
因此,我检测到的类文件包含旧的stackmap表,这将导致jvm错误。
我曾尝试过使用removeCodeAttributes(MethodGen的方法)来删除所有代码属性。例如,它可以在简单的情况下工作,例如包装函数。对于我而言,它现在无法正常工作。

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();
    }
}

最佳答案

删除StackMapTable并不是解决错误的StackMapTable的正确解决方案。重要的引用是:


  4.7.4. The StackMapTable Attribute
  
  在版本号为50.0或更高版本的class文件中,如果方法的Code属性没有StackMapTable属性,则它具有隐式堆栈映射属性(§4.10.1)。此隐式堆栈映射属性等效于StackMapTable等于零的number_of_entries属性。


由于StackMapTable必须为每个分支目标都具有显式条目,因此此类隐式StackMapTable仅适用于无分支方法。但是在这些情况下,该方法通常通常都没有明确的StackMapTable,因此您就不会遇到这个问题(除非该方法具有您的仪器删除的分支)。

另一个结论是,如果将类文件版本号修补为小于StackMapTable的值,则可以删除50。当然,仅当您不需要版本50或更高版本中引入的任何类文件功能时,这才是解决方案……

有一段宽限期,在该宽限期内,JVM仅对像您这样的情况下的工具支持不是最新的情况,支持具有StackMapTable损坏的类文件的后备模式。 (请参阅-XX:+FailoverToOldVerifier-XX:-UseSplitVerifier)但是宽限期现在已经结束,并且支持被拒绝了,即Java 8 JVM不再支持回退模式。

如果要跟上Java开发并使用可能使用这些新版本功能的较新的类文件,请选择两种选择:


手动计算正确的StackMapTable
使用支持计算正确的StackMapTable属性的工具,例如ASM(请参阅java-bytecode-asm)确实支持它

关于java - 使用bcel时构造方法的堆栈图,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26072210/

10-09 15:51