简体   繁体   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”

the java code snippet 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;
}

the bytecode outline: 字节码大纲:

// 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

it seems that whenever the operand stack is emptied by "assignment" "operation+assignment" "follow control" "method invocation" , a new label would mark a new "statement line" . 似乎每当操作数堆栈被“赋值”“操作+赋值”“跟随控制”“方法调用”清空时,新标签将标记新的“语句行”。

the "follow control" block is a collection of "statement" , and when a block(marked by "jump instructions") is end, it would also a "statement" ending hence the operand stack would be empty for the block's following code or alternating branches... “跟随控件”块是“语句”的集合,当一个块(由“跳转指令”标记)结束时,它也会以“语句”结束,因此操作数堆栈对于块的后续代码或者是空的交替分支......

so it seems to me that no case the "labeled by ' jump instruction ' code" would not be the "FRAME SAME" for the compiled java source code 所以在我看来,没有任何情况下“标记为'跳转指令'代码”不会是编译的java源代码的“FRAME SAME”

but i know it will not be the case . 但我知道情况并非如此。 otherwise the STACK MAP FRAME would not be designed into byte-code. 否则堆栈映射帧不会被设计成字节码。

please experts ,thanks very much , give me example of non-FRAME SAME branching , and explain it intuitively . 请专家,非常感谢,给我一个非FRAME SAME分支的例子,并直观地解释。

thanks very much, please help. 非常感谢,请帮忙。 help please. 请帮助。

According to the link below, you can ignore the stack map frame completely as the only use of the stack map frame is to verify that a class is secure to run; 根据下面的链接,您可以完全忽略堆栈映射框架,因为堆栈映射框架的唯一用途是验证类是否可以安全运行; the stack map frame has no relation to the underlying source code you are trying to recover with your decompiler. 堆栈映射框与您尝试使用反编译器恢复的基础源代码无关。

Is there a better explanation of stack map frames? 堆栈映射帧有更好的解释吗?

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

And, from the standard itself: 并且,从标准本身:

The StackMapTable attribute is a variable-length attribute in the attributes table of a Code (§4.7.3) attribute. StackMapTable属性是Code(§4.7.3)属性的attributes表中的可变长度属性。 This attribute is used during the process of verification by type checking (§4.10.1). 在通过类型检查(第4.10.1节)进行验证的过程中使用此属性。 A method's Code attribute may have at most one StackMapTable attribute. 方法的Code属性最多只能有一个StackMapTable属性。 https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.4 https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.4

If you only want to decompile, ie print the instruction sequence, you may simply ignore stack frames. 如果您只想反编译,即打印指令序列,您可以简单地忽略堆栈帧。 They only provide information about the types of items on the operand stack and the local variables. 它们仅提供有关操作数堆栈上的项类型和局部变量的信息。 If you don't print these types, you don't need to parse them. 如果不打印这些类型,则无需解析它们。

If you only use branch statements , there are indeed no operand stack entries on both ends of the branch, but the reason why you don't encounter local variable changes simply is that you don't have such changes in your example code. 如果您只使用分支语句 ,则分支的两端确实没有操作数堆栈条目,但您不会遇到局部变量更改的原因仅在于您的示例代码中没有此类更改。

Consider: 考虑:

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

Here, the variable i has been added to the stack frame at the loop's branch, so you are likely to encounter an F_APPEND frame there (compilers are encouraged, but not required to use the most compact form). 这里,变量i已被添加到循环分支的堆栈帧中,因此您可能在那里遇到F_APPEND帧(鼓励编译器,但不要求使用最紧凑的形式)。 Likewise, when adding another subsequent branch like in 同样,当添加另一个后续分支时,如

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

you are likely to encounter a F_CHOP frame because at the subsequent branch, i is not in scope anymore. 您可能会遇到F_CHOP帧,因为在后续分支中, i不再在范围内。

Note that there are also intra-statement branches possible, allowing to have different operand stack entries, eg 注意,还存在可能的语句内分支,允许具有不同的操作数堆栈条目,例如

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

bears two branches. 有两个分支。 For the first, a reference to a PrintStream instance is already on the stack, for the second, references to a PrintStream and a String instance are on the stack. 对于第一个,对PrintStream实例的引用已经在堆栈上,对于第二个,对PrintStreamString实例的引用在堆栈上。

Also, exception handlers form implicit branch targets having a single operand stack entry, the catched exception. 此外,异常处理程序形成具有单个操作数堆栈条目的隐式分支目标,即捕获的异常。 This situation can be encoding with the conveniently defined F_SAME1 frame type. 这种情况可以使用方便定义的F_SAME1帧类型进行编码。


If you plan to use the information contained in stack frames and are using the ASM library, you don't need to parse all the different frame types. 如果计划使用堆栈帧中包含的信息并使用ASM库,则无需解析所有不同的帧类型。 Just pass the flag ClassReader.EXPAND_FRAMES to the ClassReader upon construction and all you will ever encounter, are F_NEW frames containing the complete stack state, eliminating the need to remember previous frames. 只要传递标志ClassReader.EXPAND_FRAMESClassReader构造时,你永远不会遇到的一切,都是F_NEW包含完整的堆栈帧状态,无需记住以前的帧。

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

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