【问题标题】:ASM - java.lang.VerifyError: Operand stack overflow ExceptionASM - java.lang.VerifyError: 操作数堆栈溢出异常
【发布时间】:2015-12-28 14:35:24
【问题描述】:

我在 Tomcat 8 和 JDK 8 中使用 ASM 5.0.3 字节码库。 我也在使用

ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES);

classReader.accept(myClassVisitor, ClassReader.SKIP_FRAMES);

ASM 抛出以下异常

  Caused by: java.lang.VerifyError: Operand stack overflow
Exception Details:
  Location:      org/apache/tomcat/websocket/server/WsServerContainer.addEndpoint(Ljavax/websocket/server/ServerEndpointConfig;)V @0: aload_0
   Reason:
    Exceeded max stack size.
  Current Frame:
    bci: @0
    flags: { }
    locals: { 'org/apache/tomcat/websocket/server/WsServerContainer', 'javax/websocket/server/ServerEndpointConfig' }
    stack: { }
  Bytecode:
    0x0000000: 2ab4 000a 9900 1a2a b400 0b9a 0013 bb01
    0x0000010: 3d59 b200 4412 45b6 0046 b700 47bf 2ab4
    0x0000020: 000e c700 13bb 0043 59b2 0044 1248 b600
    0x0000030: 46b7 0047 bf2b b900 4901 004d bb00 4a59
    0x0000040: 2bb9 004b 0100 2bb9 004c 0100 2cb7 004d
    0x0000050: 4e2d b600 4ec7 0018 2db6 004f c700 112d
    0x0000060: b600 50c7 000a 2db6 0051 9900 122b b900
    0x0000070: 5201 0012 532d b900 5403 0057 bb00 5559
    0x0000080: 2cb7 0056 3a04 1904 b600 5799 0087 1904
    0x0000090: b600 58b8 0059 3a05 2ab4 0008 1905 b600
    0x00000a0: 5ac0 005b 3a06 1906 c700 29bb 005c 59b8
    0x00000b0: 005d b700 5e3a 062a b400 0819 0519 06b6
    0x00000c0: 005f 572a b400 0819 05b6 005a c000 5b3a
    0x00000d0: 0619 06bb 0060 592b 1904 b700 61b9 0062
    0x00000e0: 0200 9a00 2dbb 0043 59b2 0044 1263 06bd
    0x00000f0: 0064 5903 2c53 5904 2bb9 004b 0100 5359
    0x0000100: 052b b900 4b01 0053 b600 65b7 0047 bfa7
    0x0000110: 0043 2ab4 0007 2c2b b900 5403 00c0 0066
    0x0000120: 3a05 1905 c600 2ebb 0043 59b2 0044 1263
    0x0000130: 06bd 0064 5903 2c53 5904 1905 b900 4b01
    0x0000140: 0053 5905 2bb9 004b 0100 53b6 0065 b700
    0x0000150: 47bf 2a04 b500 0db1                    

    at org.apache.tomcat.websocket.server.WsSci.init(WsSci.java:131)
    at org.apache.tomcat.websocket.server.WsSci.onStartup(WsSci.java:47)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5244)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)

如何在不使用 -noverify 选项的情况下解决此问题?

编辑 #1:我也尝试过简单地使用 COMPUTE_FRAMES,如下所示:

ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES);

我省略了下面的 SKIP_FRAMES 语句:

//classReader.accept(myClassVisitor, ClassReader.SKIP_FRAMES);

我继续收到相同的 "Caused by: java.lang.VerifyError: Operand stack overflow" 错误。

编辑#2:我已经尝试了以下所有组合并且得到了相同的错误:java.lang.VerifyError: Operand stack overflow Reason:Exceeded max stack size.

  1. ClassWriter.COMPUTE_FRAMESClassReader.EXPAND_FRAMESmv.visitMaxs(maxStack, maxLocals);
  2. ClassWriter.COMPUTE_FRAMESClassReader.EXPAND_FRAMESmv.visitMaxs(-1, -1);
  3. ClassWriter.COMPUTE_FRAMESClassReader.SKIP_FRAMESmv.visitMaxs(maxStack, maxLocals);
  4. ClassWriter.COMPUTE_FRAMESClassReader.SKIP_FRAMESmv.visitMaxs(-1, -1);

编辑#3。这是我的建议适配器代码

public class PojoMethodAdviceAdapter extends AdviceAdapter  {
private String methodName;
private String className;
private String description;
private boolean excludeCheckFlag = false;
private int okFlag =  newLocal(Type.getType("Z")); //newLocal(Type.BOOLEAN_TYPE); 
private int classFileVersion;

Label startFinally = new Label();

private static Hashtable excludeMethods = new  Hashtable();
private static final String yesString = "Yes";
private boolean isValid;

static{
    excludeMethods.put("<clinit>", yesString);
    excludeMethods.put("<init>", yesString);
}

public PojoMethodAdviceAdapter(int access , MethodVisitor mv , String methodName, String description, String className, int classFileVersion){
    super(Opcodes.ASM5 , mv, access, methodName, description);
    this.className = className;
    this.methodName = methodName;
    this.description = description;
    this.excludeCheckFlag = true;
    this.isValid = true;
    this.classFileVersion = classFileVersion;

    String yesStr = (String)excludeMethods.get(this.methodName);
    if(yesStr!=null && yesStr.equals(yesString))
        isValid = false;
    if(this.methodName.indexOf("$") > 0)
        isValid = false;
    if(isValid){
        System.out.println(" [POJO MethodAdviceAdapter] :"+className+" \t "+methodName +" \t  "+description);
    }
}

public void visitCode() {
    super.visitCode(); 
    mv.visitLabel(startFinally);
}

protected void onMethodEnter(){
    if(isValid) {
        mv.visitInsn(Opcodes.ICONST_0);
        mv.visitVarInsn(ISTORE, okFlag);

        mv.visitLdcInsn(className);
        mv.visitLdcInsn(methodName);
        mv.visitLdcInsn(description);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/mini/agent/trace/RootTracer", "pojoMethodBegin", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z", false);
        mv.visitVarInsn(ISTORE, okFlag);
    }
}

protected void onMethodExit(int opcode){
    if(opcode!=ATHROW) {
        onFinally(opcode);
    }
}

public void visitMaxs(int maxStack, int maxLocals){
    Label endFinally = new Label();
    mv.visitTryCatchBlock(startFinally, endFinally, endFinally, null);
    mv.visitLabel(endFinally);
    onFinally(ATHROW);
    mv.visitInsn(ATHROW);
    if(classFileVersion <= 50){
        mv.visitMaxs(maxStack, maxLocals);
    }
    else{
        mv.visitMaxs(0, 0);
    }
}

private void onFinally(int opcode){
    if(isValid){
        if(opcode == ATHROW){
            mv.visitInsn(Opcodes.DUP);
            mv.visitLdcInsn(className);
            mv.visitLdcInsn(methodName);
            mv.visitLdcInsn(description);
            mv.visitVarInsn(ILOAD, okFlag);
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/mini/agent/trace/RootTracer", "recordPOJOException", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", false);
        }

        mv.visitLdcInsn(className);
        mv.visitLdcInsn(methodName);
        mv.visitLdcInsn(description);
        mv.visitVarInsn(ILOAD, okFlag);
        mv.visitLdcInsn(opcode);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/mini/agent/trace/RootTracer", "pojoMethodEnd", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZI)V", false);
    }
}

}

【问题讨论】:

  • 只看ClassWriterClassReader标志的文档,你应该使用COMPUTE_FRAMES作为作者,SKIP_FRAMES作为读者,然后visitMaxs将被忽略.异常显示堆栈为空(stack: { }),所以我真的不知道为什么它会超过任何最大堆栈大小......

标签: java-8 java-bytecode-asm


【解决方案1】:

尝试使用 TraceClassVisitor 查看它在哪里搞砸了。以我的经验,当标签写入错误时会发生这种情况,迫使它编写 jsr/ret 指令(在这种情况下,JVM 应该简单地拒绝该类,尽管在计算帧时 asm 会崩溃),并且最常见的是忘记调用 visitMaxs调用 visitEnd 之前的 methodVisitor。在我的代码中,我正在使用

visitMaxs(mv.getMaxLocals(),mv.getMaxLocals());

我不记得我这样做的原因是什么,但我的 ClassWriter 在我这样做时可以工作,但我希望这会有所帮助。

【讨论】:

  • 嗨,James,谢谢您的回复。在我的情况下,我使用的是建议适配器,因为在调用 visitEnd 之前我会先调用 visitMaxs。将分享我的建议适配器..它可能有助于解决问题
  • 我很乐意发布我的适配器,但我需要先获得许可,所以请留意。
  • 除此之外,我发现您的 try-catch-finally 标签可能存在问题。我将查看我的代码以查看我访问它们的顺序。在我的访问者中,我通过计算每条指令的字节长度来解决所有指令和标签偏移量,然后按访问优先级对每个标签进行排序,并按偏移量进行二次排序并按该顺序访问它们。我会看看是否可以为您的问题找到更简单的解决方案。我 99% 肯定这就是您的问题的来源,因为我疯狂地试图弄清楚为什么我的代码会从尝试捕获中中断。
  • 嗨詹姆斯,基于教程wiki.jvmlangsummit.com/pdf/23_Kuleshov_asm.pdf - 我使用 try-catch-finally 标签..它对我有用,没有任何问题
  • 嘿 Ramesh,你能发布你生成的代码的 javap 输出吗?根据我对输出的了解:它在第一条指令上失败,即 aload_0 下一条指令是 getField 在索引 0 的某个地方,当我们返回或抛出时,东西并没有从堆栈中弹出。 javap 将帮助查看它添加到字节码的 maxstack 值。
猜你喜欢
  • 1970-01-01
  • 2016-11-23
  • 2010-11-27
  • 1970-01-01
  • 1970-01-01
  • 2016-02-19
  • 2012-05-28
相关资源
最近更新 更多