I niedawno natknąć podczas rozwoju biblioteki, która wykonuje operacje na JVM kodu bajtowego kilka opcodes na którym nie ma dokumentacji (który znalazłem), ale które są uznawane przez wdrożenie odniesienia JVM. Znalazłem listę z nich, a są to:Nielegalne rozkazy w 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;
Wydają się być zamienniki dla swoich innych implementacjach, ale mają różne opcodes. Po długim okresie trałowania strony po stronie przez Google natrafiłem na wzmiankę o opcodes LDC*_QUICK
w this document.
Cytat z nim na LDC_QUICK
opcode:
Operacja poz push od stałej puli
Forms ldc_quick = 203 (0xcb)
Stos ... ..., poz.
Opis Indeks jest bajtem bez znaku, który musi być poprawnym indeksem w puli stałej bieżącej klasy (§3.6). Stały element puli o wartości w indeksie musi już być rozstrzygnięty i musi mieć szerokość jednego słowa. Element jest pobierany ze stałej puli i pchany na stos operandu na .
Notatki Kod operacyjny tej instrukcji był pierwotnie w formacie LDD. Operand instrukcji ldc nie jest modyfikowany.
W porządku. Wydało się to interesujące, dlatego postanowiłem to wypróbować. LDC_QUICK
wydaje się mieć taki sam format jak LDC
, więc przystąpił do zmieniania LDC
opcodu do LDC_QUICK
jeden. Spowodowało to awarię, chociaż JVM najwyraźniej ją rozpoznała. Po próbie uruchomienia zmodyfikowanego pliku maszyna JVM uległa awarii z następującym wynikiem:
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)
Powyższy błąd zawiera mieszane wiadomości. Oczywiście weryfikacja pliku klasowego nie powiodła się: java.lang.VerifyError: Bad instruction: cc
. W tym samym czasie JVM rozpoznała kod operacji: @9: fast_bgetfield
. Dodatkowo, wydaje się, że jest to inna instrukcja, ponieważ fast_bgetfield
nie implikuje ciągłego pchania ...
Myślę, że można powiedzieć, że jestem całkiem zdezorientowany. Jakie są te nielegalne kody opcyjne? Czy JVM je uruchamia? Dlaczego otrzymuję VerifyError
s? Dezaprobata? Czy mają przewagę nad ich udokumentowanymi odpowiednikami?
Każdy wgląd byłby bardzo doceniony.