java - JVM 中的非法操作码

标签 java jvm bytecode opcode

我最近在开发一个对 JVM 字节码执行操作的库时遇到了一些操作码,这些操作码没有文档(我已经找到),但 JVM 引用实现可以识别这些操作码。我找到了这些列表,它们是:

BREAKPOINT = 202;
LDC_QUICK = 203;
LDC_W_QUICK = 204;
LDC2_W_QUICK = 205;
GETFIELD_QUICK = 206;
PUTFIELD_QUICK = 207;
GETFIELD2_QUICK = 208;
PUTFIELD2_QUICK = 209;
GETSTATIC_QUICK = 210;
PUTSTATIC_QUICK = 211;
GETSTATIC2_QUICK = 212;
PUTSTATIC2_QUICK = 213;
INVOKEVIRTUAL_QUICK = 214;
INVOKENONVIRTUAL_QUICK = 215;
INVOKESUPER_QUICK = 216;
INVOKESTATIC_QUICK = 217;
INVOKEINTERFACE_QUICK = 218;
INVOKEVIRTUALOBJECT_QUICK = 219;
NEW_QUICK = 221;
ANEWARRAY_QUICK = 222;
MULTIANEWARRAY_QUICK = 223;
CHECKCAST_QUICK = 224;
INSTANCEOF_QUICK = 225;
INVOKEVIRTUAL_QUICK_W = 226;
GETFIELD_QUICK_W = 227;
PUTFIELD_QUICK_W = 228;
IMPDEP1 = 254;
IMPDEP2 = 255;

它们似乎是其他实现的替代品,但具有不同的操作码。在 Google 翻了很长一段时间后,我发现 this document 中提到了 LDC*_QUICK 操作码。 .

LDC_QUICK 操作码上引用它:

Operation Push item from constant pool

Forms ldc_quick = 203 (0xcb)

Stack ... ..., item

Description The index is an unsigned byte that must be a valid index into the constant pool of the current class (§3.6). The constant pool item at index must have already been resolved and must be one word wide. The item is fetched from the constant pool and pushed onto the operand stack.

Notes The opcode of this instruction was originally ldc. The operand of the ldc instruction is not modified.

好的。看起来很有趣,所以我决定尝试一下。 LDC_QUICK 似乎与 LDC 具有相同的格式,因此我着手将 LDC 操作码更改为 LDC_QUICK 操作码.这导致了失败,尽管 JVM 显然识别了它。尝试运行修改后的文件后,JVM 崩溃并显示以下输出:

Exception in thread "main" java.lang.VerifyError: Bad instruction: cc
Exception Details:
  Location:
    Test.main([Ljava/lang/String;)V @9: fast_bgetfield
  Reason:
    Error exists in the bytecode
  Bytecode:
    0000000: bb00 0559 b700 064c 2bcc 07b6 0008 572b
    0000010: b200 09b6 000a 5710 0ab8 000b 08b8 000c
    0000020: 8860 aa00 0000 0032 0000 0001 0000 0003
    0000030: 0000 001a 0000 0022 0000 002a b200 0d12
    0000040: 0eb6 000f b200 0d12 10b6 000f b200 0d12
    0000050: 11b6 000f bb00 1259 2bb6 0013 b700 14b8
    0000060: 0015 a700 104d 2cb6 0016 b200 0d12 17b6
    0000070: 000f b1
  Exception Handler Table:
    bci [84, 98] => handler: 101
  Stackmap Table:
    append_frame(@60,Object[#41])
    same_frame(@68)
    same_frame(@76)
    same_frame(@84)
    same_locals_1_stack_item_frame(@101,Object[#42])
    same_frame(@114)

        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
        at java.lang.Class.getMethod0(Unknown Source)
        at java.lang.Class.getMethod(Unknown Source)
        at sun.launcher.LauncherHelper.validateMainClass(Unknown Source)
        at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)

上述错误给出了混合信息。显然,类文件验证失败:java.lang.VerifyError: Bad instruction: cc。同时,JVM识别操作码:@9:fast_bgetfield。此外,它似乎认为这是一条不同的指令,因为 fast_bgetfield 并不意味着不断插入......

可以说我很困惑。这些非法操作码是什么? JVM 运行它们吗?为什么我会收到 VerifyError?弃用?与记录在案的同行相比,它们是否有优势?

如有任何见解,我们将不胜感激。

最佳答案

The first edition of the Java virtual machine specification described a technique used by one of Sun's early implementations of the Java virtual machine to speed up the interpretation of bytecodes. In this scheme, opcodes that refer to constant pool entries are replaced by a "_quick" opcode when the constant pool entry is resolved. When the virtual machine encounters a _quick instruction, it knows the constant pool entry is already resolved and can therefore execute the instruction faster.

The core instruction set of the Java virtual machine consists of 200 single-byte opcodes. These 200 opcodes are the only opcodes you will ever see in class files. Virtual machine implementations that use the "_quick" technique use another 25 single-byte opcodes internally, the "_quick" opcodes.

For example, when a virtual machine that uses the _quick technique resolves a constant pool entry referred to by an ldc instruction (opcode value 0x12), it replaces the ldc opcode byte in the bytecode stream with an ldc_quick instruction (opcode value 0xcb). This technique is part of the process of replacing a symbolic reference with a direct reference in Sun's early virtual machine.

For some instructions, in addition to overwriting the normal opcode with a _quick opcode, a virtual machine that uses the _quick technique overwrites the operands of the instruction with data that represents the direct reference. For example, in addition to replacing an invokevirtual opcode with an invokevirtual_quick, the virtual machine also puts the method table offset and the number of arguments into the two operand bytes that follow every invokevirtual instruction. Placing the method table offset in the bytecode stream following the invokevirtual_quick opcode saves the virtual machine the time it would take to look up the offset in the resolved constant pool entry.

Chapter 8 of Inside the Java Virtual Machine

基本上,您不能只将操作码放在类文件中。只有 JVM 可以在解析操作数后执行此操作。

关于java - JVM 中的非法操作码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14822557/

相关文章:

java - 创建同一个类的实例时,出现类的 noClassDefFoundError 错误。为什么会这样?

java - Hadoop jvm 进程挂起,没有任何错误消息,

java - 是否可以读取正在运行的 java 应用程序的内存?

jvm - javac 也内联吗?

java - 如何在碧 Jade 报告中显示图像?

java - 将旧的 J++ 项目迁移到 Eclipse?

Java:使用 BCEL 添加调试调用到每个方法

java - 在 Android 中抓取 Html

java - 找不到元素 'resources'的声明

Java:JFrame 对话框中的文本未换行