2013-02-11 15 views
11

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.

Odpowiedz

13

Pierwsze wydanie specyfikacji maszyny wirtualnej Java opisuje technikę stosowaną przez jedną z wczesnych implementacji wirtualnej maszyny języka Java w celu przyspieszenia interpretacji bajtów. W tym schemacie opkody odnoszące się do stałych wpisów puli są zastępowane przez "_szybki" kod operacji, gdy pozycja stałej puli zostanie rozstrzygnięta. Gdy wirtualna maszyna napotka na _szybką instrukcję, wie, że wpis stałej puli został już rozwiązany i może w ten sposób wykonać instrukcję szybciej.

Podstawowy zestaw instrukcji wirtualnej maszyny Java składa się z 200 jednobajtowych kodów opcyjnych. Te 200 kodów jest jedynymi opkodami widocznymi w plikach klas. Implementacje maszyn wirtualnych korzystające z techniki "_quick" wykorzystują wewnętrznie 25 bajtów jednobajtowych, kody "_quick".

Na przykład, gdy maszyna wirtualna korzystająca z techniki _quick rozwiązuje stały wpis puli, do którego odwołuje się instrukcja ldc (wartość kodu 0x12), zastępuje bajtowy kod polecenia ldc w strumieniu bajtów za pomocą instrukcji ldc_quick (wartość kodu operacji 0xcb). Technika ta jest częścią procesu zastępowania odniesienia symbolicznego bezpośrednim odniesieniem we wczesnej maszynie wirtualnej Sun.

W przypadku niektórych instrukcji, oprócz nadpisania zwykłego opcode z _szybkim opcode, maszyna wirtualna, która używa techniki _quick, zastępuje operandy instrukcji danymi reprezentującymi bezpośrednie odniesienie. Na przykład, oprócz zamiany inwirtualnego opcode'a na invokevirtual_quick, maszyna wirtualna umieszcza również tablicę offset metody i liczbę argumentów w dwóch bajtach operandów, które następują po każdej instrukcji wirtualnej. Umieszczenie przesunięcia tablicy metody w strumieniu bajtów po kodzie wywołania invokevirtual_quick powoduje zapisanie maszyny wirtualnej o czasie potrzebnym do wyszukania przesunięcia w rozwiązaniu stałej puli.

Chapter 8 of Inside the Java Virtual Machine

Zasadniczo nie można po prostu umieścić kod operacji w pliku klasy. Tylko JVM może to zrobić po rozwiązaniu operandów.

4

nie wiem o wszystkie rozkazy zostały wymienione, ale trzy z nich — przerwania, impdep1 i impdep2 — są zarezerwowane opcodes udokumentowane w Section 6.2 of the Java Virtual Machine Specification.Mówi ona, w części:

Two zastrzeżonego rozkazy, numery 254 (0xfe) i 255 (0xFF), mają mnemoniki impdep1 i impdep2 odpowiednio. Te instrukcje mają na celu zapewnienie "tylnych drzwi" lub pułapek dla funkcji specyficznych dla implementacji zaimplementowanych odpowiednio w oprogramowaniu i sprzęcie. Trzeci zarezerwowany kod operacji, numer 202 (0xca), ma mnemoniczny punkt przerwania i jest przeznaczony do użycia przez debuggery do implementacji punktów przerwania.

Mimo że te kody operacyjne zostały zarezerwowane, mogą być używane tylko w implementacji maszyny wirtualnej Java. Nie mogą pojawić się w prawidłowych plikach klas. . . .

Podejrzewam (z ich nazw), że pozostałe pozostałe kody są częścią mechanizmu JIT, a także nie mogą pojawić się w prawidłowym pliku klasy.

3

Te kody operacyjne są zarezerwowane i nie mogą pojawić się w prawidłowym pliku klasy, stąd błąd VerifyError. Jednak JVM używa ich wewnętrznie. Dlatego w reprezentacji pamięci niektórych bajtów może zawierać te kody opcodes po modyfikacji przez VM. Jest to jednak wyłącznie detal wdrożeniowy.