繁体   English   中英

尝试构建java字节码的反编译器,并且不知道在什么情况下“堆栈映射帧”不会恰好是“FRAMSAME”

[英]try to build a decompiler of java bytecode, and don't know in what case the “stack map frame” would not happen to be the “FRAMSAME”

java代码片段

    int x4(int a) {
    if(a>7){
        System.out.println("a>7");
    }


    if(a==0){
        System.out.println("a==0");
    }else if(a>77){
        System.out.println(" a>77");
    }else if(a>44){
        System.out.println(" a>44");
    }else{
        System.out.println("otherwise");
    }

    return 44;
}

字节码大纲:

// access flags 0x0
x4(I)I
// parameter  a
L0
ILOAD 1
BIPUSH 7
IF_ICMPLE L1  ---- operand stack is empty here , end of a statement
L2 ---- new statement mark
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "a>7"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
FRAME SAME ---- branching place start with empty operand stack
ILOAD 1
IFNE L3
L4
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "a==0"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L5
GOTO L6
L3
FRAME SAME ---- branching place start with empty operand stack
ILOAD 1
BIPUSH 77
IF_ICMPLE L7
L8
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC " a>77"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L9
GOTO L6
L7
FRAME SAME ---- branching place start with empty operand stack
ILOAD 1
BIPUSH 44
IF_ICMPLE L10
L11
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC " a>44"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L12
GOTO L6
L10
FRAME SAME ---- branching place start with empty operand stack
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "otherwise"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L6
FRAME SAME ---- branching place start with empty operand stack
BIPUSH 44
IRETURN
L13
// access flags 0x0
x4(I)I
// parameter  a
L0
ILOAD 1
BIPUSH 7
IF_ICMPLE L1
L2
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "a>7"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
FRAME SAME ---- branching place start with empty operand stack
ILOAD 1
IFNE L3
L4
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "a==0"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L5
GOTO L6
L3
FRAME SAME ---- branching place start with empty operand stack
ILOAD 1
BIPUSH 77
IF_ICMPLE L7
L8
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC " a>77"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L9
GOTO L6
L7
FRAME SAME ---- branching place start with empty operand stack
ILOAD 1
BIPUSH 44
IF_ICMPLE L10
L11
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC " a>44"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L12
GOTO L6
L10
FRAME SAME ---- branching place start with empty operand stack
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "otherwise"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L6
FRAME SAME ---- branching place start with empty operand stack
BIPUSH 44
IRETURN
L13

似乎每当操作数堆栈被“赋值”“操作+赋值”“跟随控制”“方法调用”清空时,新标签将标记新的“语句行”。

“跟随控件”块是“语句”的集合,当一个块(由“跳转指令”标记)结束时,它也会以“语句”结束,因此操作数堆栈对于块的后续代码或者是空的交替分支......

所以在我看来,没有任何情况下“标记为'跳转指令'代码”不会是编译的java源代码的“FRAME SAME”

但我知道情况并非如此。 否则堆栈映射帧不会被设计成字节码。

请专家,非常感谢,给我一个非FRAME SAME分支的例子,并直观地解释。

非常感谢,请帮忙。 请帮助。

根据下面的链接,您可以完全忽略堆栈映射框架,因为堆栈映射框架的唯一用途是验证类是否可以安全运行; 堆栈映射框与您尝试使用反编译器恢复的基础源代码无关。

堆栈映射帧有更好的解释吗?

另请参见: http//chrononsystems.com/blog/java-7-design-flaw-leads-to-huge-backward-step-for-the-jvm

并且,从标准本身:

StackMapTable属性是Code(§4.7.3)属性的attributes表中的可变长度属性。 在通过类型检查(第4.10.1节)进行验证的过程中使用此属性。 方法的Code属性最多只能有一个StackMapTable属性。 https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.4

如果您只想反编译,即打印指令序列,您可以简单地忽略堆栈帧。 它们仅提供有关操作数堆栈上的项类型和局部变量的信息。 如果不打印这些类型,则无需解析它们。

如果您只使用分支语句 ,则分支的两端确实没有操作数堆栈条目,但您不会遇到局部变量更改的原因仅在于您的示例代码中没有此类更改。

考虑:

for(int i=0; i<10; i++)
    System.out.println(i);

这里,变量i已被添加到循环分支的堆栈帧中,因此您可能在那里遇到F_APPEND帧(鼓励编译器,但不要求使用最紧凑的形式)。 同样,当添加另一个后续分支时,如

for(int i=0; i<10; i++)
  System.out.println(i);
if(a==0)
  System.out.println();

您可能会遇到F_CHOP帧,因为在后续分支中, i不再在范围内。

注意,还存在可能的语句内分支,允许具有不同的操作数堆栈条目,例如

System.out.println(a==0? "zero": "non-zero");

有两个分支。 对于第一个,对PrintStream实例的引用已经在堆栈上,对于第二个,对PrintStreamString实例的引用在堆栈上。

此外,异常处理程序形成具有单个操作数堆栈条目的隐式分支目标,即捕获的异常。 这种情况可以使用方便定义的F_SAME1帧类型进行编码。


如果计划使用堆栈帧中包含的信息并使用ASM库,则无需解析所有不同的帧类型。 只要传递标志ClassReader.EXPAND_FRAMESClassReader构造时,你永远不会遇到的一切,都是F_NEW包含完整的堆栈帧状态,无需记住以前的帧。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM