Search code examples
javajvminstrumentationjava-bytecode-asm.class-file

"VerifyError: StackMapTable error: bad offset" at the first instruction of a method in instrumented class


I used ASM to instrument a class file, and the instrumented class throws VerifyError. The error message is like:

java.lang.VerifyError: StackMapTable error: bad offset
Exception Details:
  Location:
    org/joda/time/TestAllPackages.suite()Ljunit/framework/Test; @0: invokestatic
  Reason:
    Invalid stackmap specification.
  Current Frame:
    bci: @88
    flags: { }
    locals: { }
    stack: { }
  Bytecode:
    0x0000000: b800 5bb6 005f 3f1e 1276 b800 67bb 0002
    0x0000010: 59b7 0003 4d2c b800 04b6 0005 2cb8 0006
    0x0000020: b600 052c b800 07b6 0005 2cb8 0008 b600
    0x0000030: 052c b800 09b6 0005 2cb8 000a b600 052c
    0x0000040: b800 0bb6 0005 2c1e 1276 b800 74b0 a700
    0x0000050: 0a1e 1276 b800 74bf                    
  Exception Handler Table:
    bci [0, 78] => handler: 81
  Stackmap Table:
    same_locals_1_stack_item_extended(@81,Object[#85])
    same_frame(@88)

    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
    at java.lang.Class.getMethod0(Class.java:3018)
    at java.lang.Class.getMethod(Class.java:1784)
    at org.apache.maven.surefire.common.junit3.JUnit3Reflector.createInstanceFromSuiteMethod(JUnit3Reflector.java:153)
    at org.apache.maven.surefire.common.junit3.JUnit3Reflector.constructTestObject(JUnit3Reflector.java:124)
    at org.apache.maven.surefire.junit.JUnitTestSet.execute(JUnitTestSet.java:75)
    at org.apache.maven.surefire.junit.JUnit3Provider.executeTestSet(JUnit3Provider.java:140)
    at org.apache.maven.surefire.junit.JUnit3Provider.invoke(JUnit3Provider.java:113)
    at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:379)
    at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:340)
    at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:125)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:413)

And the instrumented class is rather simple: org.joda.time.TestAllPackages.class. I inserted those stackmap frames use visitFrames manually (didn't use COMPUTE_FRAMES which is painful) because I want to correctly write visitFrames by myself.

I really want to know what is happening, i.e., what is the problem with the class file since I did not discover anything suspicious.

Thanks!


BTW, my instrumentation code is like:

...
    private Label tryStart = new Label();
    private Label tryEnd = new Label();
    private Label catchStart = new Label();
    private Label catchEnd = new Label();

    public XxxMethodVisitor(MethodVisitor mv, String methodName, int access, String desc, String className, boolean isStatic, boolean isPublic) {
        super(Config.ASM_VERSION, mv);
        this.methodName = methodName;
        this.className = className;
        this.selfDesc = desc;
        selfReturnType = Type.getReturnType(desc);
        selfMethodId = String.format("%s%c%s%c%s", className, SEPARATOR, methodName, SEPARATOR, desc);
        this.isStatic = isStatic;
        this.isPublic = isPublic;
    }

    // start of the method
    public void visitCode() {
        mv.visitCode();
//        mv.visitFrame(F_NEW, initLocals.length, initLocals, 0, new Object[]{});
        mv.visitTryCatchBlock(tryStart, tryEnd, catchStart, "java/lang/Throwable");
        mv.visitLabel(tryStart);
        ...
    }

    public void visitEnd() {
        mv.visitLabel(tryEnd);
        mv.visitJumpInsn(GOTO, catchEnd);
        mv.visitLabel(catchStart);
        // exception caught
        mv.visitFrame(F_SAME1, 0, null, 1, new Object[] {"java/lang/Throwable"});
        ...
        mv.visitInsn(ATHROW);
        mv.visitLabel(catchEnd);
        mv.visitFrame(F_SAME, 0, null, 0, null);
        super.visitEnd();
    }

Solution

  • I don't know why the verify error is complaining about the stack map table, when there's a much more glaring problem: the goto instruction is branching to offset 84, which is just past the end of the suite method. This is what I found when checking the class that you linked to. The dump from the verify error looks different, but it references "bci: @88", which is also out of bounds.