在 java 类文件中发现未指定的 JVM 字节码 (0xe2)

问题描述

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

program output 1

program output 2

program output 3

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);
        }
    }
}

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

解决方法

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

goto
branchbyte1
branchbyte2

无符号字节 branchbyte1 和 branchbyte2 用于构造有符号的 16 位 branchoffset,其中 branchoffset 为 (branchbyte1

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 个字节作为参数,记录如下:

格式

invokespecial
indexbyte1
indexbyte2

说明

无符号的indexbyte1indexbyte2用于构造当前类(§2.6)的运行时常量池的索引,其中索引的值为{{ 1}}。

2个字节为(indexbyte1 << 8) | indexbyte200,index为1,所以字节码指令如01所示:javap

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

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

格式

a7 ff e2

说明

无符号字节 goto branchbyte1 branchbyte2 branchbyte1 用于构造有符号的 16 位分支偏移,其中分支偏移为 branchbyte2

表示(branchbyte1 << 8) | branchbyte2ff e2,表示代替

branchoffset = 0xffe2 = -30

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

a7: goto
ff: impdep2
e2: (null)