在 java class 文件中找到未指定的 JVM 字节码 (0xe2)

Found an unspecified JVM Bytecode (0xe2) in java class file

我最近正在开发一个可以分析 java class 文件的程序。在 运行 程序之后,这是它的输出:

class test_1 {

    public static String a = "Hello World";

    public static void main(String[] args) {
        int j = 0;
        for(int i = 0;i<10;i++) {
            System.out.println(a);
            j = j + j*j +j/(j+1);
        }
    }
}

我得到了一个jvm规范14中没有指定的字节码0xe2。0xe2有什么作用??

您没有考虑到多字节操作码。从the reference for goto开始,格式为:

goto
branchbyte1
branchbyte2

The unsigned bytes branchbyte1 and branchbyte2 are used to construct a signed 16-bit branchoffset, where branchoffset is (branchbyte1 << 8) | branchbyte2. Execution proceeds at that offset from the address of the opcode of this goto instruction. The target address must be that of an opcode of an instruction within the method that contains this goto instruction.

goto 是 0xa7,它后面应该跟着 2 个字节,表示分支位置,使指令宽度为 3 个字节。您的代码忽略了这一点,反汇编 1 个字节,然后将接下来的 2 个字节视为有效指令,但它们不是。

你的程序把每个字节都当做字节码指令输出,忽略了很多指令有参数的事实,所以它们是多字节指令。

例如您的程序错误地输出构造函数,如下所示:

2a: aload_0
b7: invokespecial
00: nop
01: aconst_null
b1: return

如果您 运行 javap -c test_1.class,您将看到:

0: aload_0
1: invokespecial #1   // Method java/lang/Object."<init>":()V
4: return

冒号前的数字是偏移量,不是字节码。如您所见,偏移量 2 和 3 丢失,因为 invokespecial 指令使用 2 个字节作为参数,记录在案:

Format

invokespecial
indexbyte1
indexbyte2

Description

The unsigned indexbyte1 and indexbyte2 are used to construct an index into the run-time constant pool of the current class (§2.6), where the value of the index is (indexbyte1 << 8) | indexbyte2.

2个字节为0001,索引为1,所以字节码指令如javap所示:invokespecial #1

如果您随后查看常量池输出,您会看到常量 #1 是 methodrefObject 无参数构造函数的 methodref

您的具体问题与字节码 a7 ff e2 有关,它不是 3 条指令,而是 goto 的 3 字节指令:

Format

goto
branchbyte1
branchbyte2

Description

The unsigned bytes branchbyte1 and branchbyte2 are used to construct a signed 16-bit branchoffset, where branchoffset is (branchbyte1 << 8) | branchbyte2.

意思是ff e2branchoffset = 0xffe2 = -30,意思是不是

a7: goto
ff: impdep2
e2: (null)

你的程序应该打印出如下内容:

a7 ff e2: goto -30