Hello World de Bytecode pour JVM

Nous compilons un programme simple qui affiche "Hello World" et parcourons sa structure


Je ne pense pas que l'article sera suffisamment informatif pour ceux qui ne savent pas superficiellement Ă  quoi ressemble le bytecode et comment la JVM fonctionne avec lui (par exemple, au moins les instructions les plus simples (connaissance de leur existence)).


En fait, ce n'est pas si difficile. Il suffit d'utiliser l'outil javap du JDK et de considérer le code désassemblé.


Et nous allons commencer Ă  analyser la structure du bytecode pour la JVM


Un livre trÚs utile pour cela était la spécification JVM officielle - La spécification de la machine virtuelle Java sur oracle


Pour commencer, créez un programme simple:


  public class Main { public static void main(String ... args) { System.out.println("Hello World"); } } 

Compilez-le avec l'équipe javac Main.java et effectuez le démontage


  javap -c -v Main 

Main.class


 Classfile /C:/Users/Arthur/playground/java/jvm/Main.class Last modified 26.10.2019; size 413 bytes MD5 checksum 6449121a3bb611fee394e4f322401ee1 Compiled from "Main.java" public class Main minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#15 // java/lang/Object."<init>":()V #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #18 // Hello World #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #21 // Main #6 = Class #22 // java/lang/Object #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 main #12 = Utf8 ([Ljava/lang/String;)V #13 = Utf8 SourceFile #14 = Utf8 Main.java #15 = NameAndType #7:#8 // "<init>":()V #16 = Class #23 // java/lang/System #17 = NameAndType #24:#25 // out:Ljava/io/PrintStream; #18 = Utf8 Hello World #19 = Class #26 // java/io/PrintStream #20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V #21 = Utf8 Main #22 = Utf8 java/lang/Object #23 = Utf8 java/lang/System #24 = Utf8 out #25 = Utf8 Ljava/io/PrintStream; #26 = Utf8 java/io/PrintStream #27 = Utf8 println #28 = Utf8 (Ljava/lang/String;)V { public Main(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 public static void main(java.lang.String...); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello World 5: invokevirtual #4// Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 4: 0 line 5: 8 } SourceFile: "Main.java" 

Ceci est juste une représentation de bytecode qui est plus facile à voir pour une personne que le bytecode original, mais cela a l'air différent:


  cafe babe 0000 0034 001d 0a00 0600 0f09 0010 0011 0800 120a 0013 0014 0700 1507 0016 0100 063c 696e 6974 3e01 0003 2829 5601 0004 436f 6465 0100 0f4c 696e 654e 756d 6265 7254 6162 6c65 0100 046d 6169 6e01 0016 285b 4c6a 6176 612f 6c61 6e67 2f53 7472 696e 673b 2956 0100 0a53 6f75 7263 6546 696c 6501 0009 4d61 696e 2e6a 6176 610c 0007 0008 0700 170c 0018 0019 0100 0b48 656c 6c6f 2057 6f72 6c64 0700 1a0c 001b 001c 0100 044d 6169 6e01 0010 6a61 7661 2f6c 616e 672f 4f62 6a65 6374 0100 106a 6176 612f 6c61 6e67 2f53 7973 7465 6d01 0003 6f75 7401 0015 4c6a 6176 612f 696f 2f50 7269 6e74 5374 7265 616d 3b01 0013 6a61 7661 2f69 6f2f 5072 696e 7453 7472 6561 6d01 0007 7072 696e 746c 6e01 0015 284c 6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b29 5600 2100 0500 0600 0000 0000 0200 0100 0700 0800 0100 0900 0000 1d00 0100 0100 0000 052a b700 01b1 0000 0001 000a 0000 0006 0001 0000 0001 0089 000b 000c 0001 0009 0000 0025 0002 0001 0000 0009 b200 0212 03b6 0004 b100 0000 0100 0a00 0000 0a00 0200 0000 0400 0800 0500 0100 0d00 0000 0200 0e 

(Vous pouvez ouvrir votre fichier .class via Sublime Text indiquant File-> Save with Encoding -> Hexademical)


Nous travaillerons avec ce code.


Mais d'abord, nous devons le formater afin de ne pas se tromper oĂč il se trouve, et le bytecode, en fait, a une structure trĂšs rigide:


  ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; } 

Vous pouvez le trouver dans la spécification JVM Chapitre 4.1 La structure ClassFile


Ici, tout est simple - la dimension en octets est indiquée à gauche et la description à droite.


Nous analyserons le bytecode en hexadĂ©cimal, oĂč chaque chiffre prend 4 bits, et donc, pour deux octets - 4 chiffres et pour quatre octets - 8 chiffres.


la magie


la magie est une valeur qui identifie le format de notre classe. Il est égal à 0xCAFEBABE , qui a sa propre histoire de création .


minor_version, major_version


Ce sont des versions de votre fichier de class . Si nous appelons major_version M et minor_version m, nous obtenons la version de notre fichier de class comme Mm


Maintenant, je vais immédiatement donner des exemples de notre programme "Hello World" pour voir comment ils sont utilisés:


  cafe babe -- magic 0000 -- minor_version 0034 -- major_version 

Nous pouvons le voir dans le code démonté, mais déjà dans le systÚme de nombre décimal:


  ... public class Main minor version: 0 major version: 52 flags: ACC_PUBLIC, ... 

constant_pool_count


Le nombre de variables dans le pool constant est indiquĂ© ici. En mĂȘme temps, si vous dĂ©cidez d'Ă©crire du code en bytecode pur, alors vous devez certainement surveiller sa valeur, car si vous spĂ©cifiez une valeur incorrecte, tout le programme ira en enfer (vĂ©rifiĂ©!).


N'oubliez pas non plus que vous devez y inscrire le ___ + 1 de_variables_ dans le ___ + 1


Total nous obtenons:


  cafe babe -- magic 0000 0034 -- version 001d -- constant_pool_count 

constant_pool []


Chaque type de variable dans le pool constant a sa propre structure:


  cp_info { u1 tag; u1 info[]; } 

Tout doit ĂȘtre fait ici dans l'ordre. Nous lisons d'abord la tag pour dĂ©couvrir le type de la variable et par le type de cette variable nous regardons quelle structure sa valeur suivante a info[]


Un tableau avec des balises se trouve dans le Tableau 4.3 Spécification des balises de pool constant .


En fait, voici la tablette:


Type constantValeur
CONSTANT_Class7
CONSTANT_Fieldref9
CONSTANT_Methodref10
CONSTANT_InterfaceMethodref11
CONSTANT_String8
CONSTANT_Integer3
CONSTANT_Float4
CONSTANT_Long5
CONSTANT_Double6
CONSTANT_NameAndType12
CONSTANT_Utf81
CONSTANT_MethodHandle15
CONSTANT_MethodType16
CONSTANT_InvokeDynamic18

Comme mentionné précédemment, chaque type de constante a sa propre structure.


Voici, par exemple, la structure CONSTANT_Class :


  CONSTANT_Class_info { u1 tag; u2 name_index; } 

Structure du champ et de la méthode:


  CONSTANT_Fieldref_info { u1 tag; u2 class_index; u2 name_and_type_index; } CONSTANT_Methodref_info { u1 tag; u2 class_index; u2 name_and_type_index; } 

Il est important de noter ici que différentes structures peuvent avoir des longueurs différentes.


Considérez une partie de notre code:


  cafe babe 0000 0034 001d -- constant_pool_count 0a00 0600 0f09 0010 0011 0800 12 ... 

Donc, nous regardons la structure de la constante et découvrons que le premier octet est réservé au type de constante. Ici, nous voyons 0a (10) - et donc, c'est CONSTANT_Methodref


Nous regardons sa structure:


  CONSTANT_Methodref_info { u1 tag; u2 class_index; u2 name_and_type_index; } 

AprÚs un octet pour la balise, nous avons besoin de 4 octets supplémentaires pour class_index et name_and_type_index


  cafe babe 0000 0034 001d -- constant_pool_count 0a 0006 000f -- CONSTANT_Methodref 0900 1000 1108 0012 ... 

Eh bien, nous avons trouvé l'une des valeurs du pool constant. Allez-y. Nous regardons, 09 - signifie le type CONSTANT_Fieldref


Nous obtenons:


  cafe babe 0000 0034 001d -- constant_pool_count 0a 0006 000f -- CONSTANT_Methodref 09 0010 0011 -- CONSTANT_Fieldref 08 0012 ... 

Vous pourriez penser que la plupart des types ont la mĂȘme forme, mais ce n'est pas le cas.
Par exemple, une structure du type suivant ressemble Ă  CONSTANT_String :


  CONSTANT_String_info { u1 tag; u2 string_index; } 

Toutes ces structures se trouvent au chapitre 4.4 Le bassin constant.


Voyons maintenant ce que signifient les types d' info internes.


Les méthodes qui relÚvent du modÚle *_index contiennent généralement l'adresse de la table de pool constante. Par exemple, class_index pour une valeur de type CONSTANT_Class_info et string_index pour une string_index CONSTANT_Utf8_info


Nous pouvons voir cela dans le code démonté:


  #1 = Methodref #6.#15 // java/lang/Object."<init>":()V #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #18 

  0a 0006 000f -- CONSTANT_Methodref 09 0010 0011 -- CONSTANT_Fieldref 08 0012 -- CONSTANT_String 

Vous pouvez également mettre en évidence la représentation des nombres et des chaßnes.


Vous pouvez lire sur la représentation des nombres à partir du chapitre 4.4.4 , mais pour l'instant nous n'analyserons que les lignes, car les nombres ne sont pas encore inclus dans le programme Hello World


En fait, voici comment la ligne apparaĂźt:


  CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; } 

Par exemple, notre Hello World:


  01 -- tag 000b -- length 48 65 6c 6c 6f 20 57 6f 72 6c 64 -- bytes[length] // H ello W orld 

En analysant l'ensemble du pool de constantes de bytecode, nous obtenons:


L'ensemble du pool de constantes
  -- [Constant Pool] -- methodref 0a 0006 000f -- fieldref 09 0010 0011 -- string 08 0012 -- methodref 0a 0013 0014 -- Class 07 0015 -- Class 07 0016 -- Utf8 01 0006 3c 69 6e 69 74 3e -- Utf8 01 0003 28 29 56 -- Utf8 01 0004 43 6f 64 65 -- Utf8 01 000f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 -- Utf8 01 0004 6d 61 69 6e -- Utf8 01 0016 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 -- Utf8 01 000a 53 6f 75 72 63 65 46 69 6c 65 -- Utf8 01 0009 4d 61 69 6e 2e 6a 61 76 61 -- NameAndType 0c 0007 0008 -- Class 07 0017 -- NameAndType 0c 0018 0019 -- Utf8 01 000b 48 65 6c 6c 6f 20 57 6f 72 6c 64 -- Class 07 001a -- NameAndType 0c 001b 001c -- Utf8 01 0004 4d 61 69 6e -- Utf8 01 0010 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 -- Utf8 01 0010 6a 61 76 61 2f 6c 61 6e 67 2f 53 79 73 74 65 6d -- Utf8 01 0003 6f 75 74 -- Utf8 01 0015 4c 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d 3b -- Utf8 01 0013 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d -- Utf8 01 0007 70 72 69 6e 74 6c 6e -- Utf8 01 0015 28 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 -- [Constant Pool END] 

Aussi, nous pouvons le comparer avec du code démonté:


  Constant pool: #1 = Methodref #6.#15 // java/lang/Object."<init>":()V #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #18 // Hello World #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #21 // Main #6 = Class #22 // java/lang/Object #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 main #12 = Utf8 ([Ljava/lang/String;)V #13 = Utf8 SourceFile #14 = Utf8 Main.java #15 = NameAndType #7:#8 // "<init>":()V #16 = Class #23 // java/lang/System #17 = NameAndType #24:#25 // out:Ljava/io/PrintStream; #18 = Utf8 Hello World #19 = Class #26 // java/io/PrintStream #20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V #21 = Utf8 Main #22 = Utf8 java/lang/Object #23 = Utf8 java/lang/System #24 = Utf8 out #25 = Utf8 Ljava/io/PrintStream; #26 = Utf8 java/io/PrintStream #27 = Utf8 println #28 = Utf8 (Ljava/lang/String;)V 

Vérifier ainsi que tout correspond, car en fait javap traite simplement ce bytecode et nous le montre sous une forme formatée.


Le pool constant est nécessaire pour les instructions. Par exemple:


  public Main(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 //    1    4: return 

Pour plus d'informations sur tous les types du pool constant, voir le chapitre 4.4 Le pool constant.


Aller plus loin dans la structure ClassFile


access_flags


Il s'agit d'un masque de bits pour les propriétés des modificateurs.


Nom du drapeauValeurInterprétation
ACC_PUBLIC0x0001DĂ©clarĂ© public ; peut ĂȘtre consultĂ© de l'extĂ©rieur de son emballage.
ACC_FINAL0x0010Déclarée final ; aucune sous-classe n'est autorisée.
ACC_SUPER0x0020Traitez les méthodes de superclasse spécialement lorsqu'elles sont invoquées par l'instruction invokespecial .
ACC_INTERFACE0x0200Est une interface, pas une classe.
ACC_ABSTRACT0x0400abstract dĂ©clarĂ©; ne doit pas ĂȘtre instanciĂ©.
ACC_SYNTHETIC0x1000Synthétique déclaré; non présent dans le code source.
ACC_ANNOTATION0x2000Déclaré comme type d'annotation.
ACC_ENUM0x4000Déclaré comme type enum .

cette_classe


Doit contenir une adresse sur this classe. Dans notre cas, il se situe Ă  l'adresse 5:


  Constant pool: #1 = Methodref #6.#15 // java/lang/Object."<init>":()V #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #18 // Hello World #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #21 // Main #6 = Class #22 // java/lang/Object ... 

Il est Ă  noter que la structure de cette variable doit ĂȘtre conforme Ă  CONSTANT_Class_info


super_class


Adresse de l'ancĂȘtre de la classe. Dans notre cas, la valeur est Ă  l'adresse #6 . Eh bien, la structure de valeurs CONSTANT_Class_info est Ă©galement requise


Les noms de ces classes sont définis dans la structure de la constante CONSTANT_Utf8_info . Si nous regardons les cellules #21 et #22 , nous verrons:


  ... #21 = Utf8 Main #22 = Utf8 java/lang/Object ... 

Autrement dit, dans ces cellules, le name_index de la structure est indiqué:


  CONSTANT_Class_info { u1 tag; u2 name_index; } 

interfaces_count, fields_count


Ils ne sont pas dans notre programme, donc leurs valeurs seront égales à 0000, et il n'y aura tout simplement pas de valeurs ultérieures des fields[] , interfaces[] .


En savoir plus 4.1 La structure ClassFile


méthodes_comptes


Nombre de méthodes. Bien que dans le code, nous voyons une méthode dans la classe, il y en a en fait deux. En plus de la méthode main , il existe également un constructeur par défaut. Par conséquent, leur nombre est de deux, dans notre cas.


méthodes []


Chaque Ă©lĂ©ment doit ĂȘtre conforme Ă  la structure method_info dĂ©crite au chapitre 4.6 MĂ©thodes


  method_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; } 

Dans notre bytecode (formaté, avec commentaires), cela ressemble à ceci:


  -- [methods] -- public Main(); 0001 --access_flags 0007 -- name_index 0008 -- descriptor_index 0001 -- attributes_count -- attribute_info 0009 -- attribute_name_index (Code) 0000 001d - attribute_length 0001 -- max_stack 0001 -- max_locals 0000 0005 -- code_length 2a b7 00 01 b1 -- code[] 0000 -- exception_table_length 0001 -- attributes_count; 000a -- attribute_name_index 0000 0006 -- attribute_length 00 01 00 00 00 01 -- public static void main(java.lang.String...); 0089 --access_flags 000b -- name_index 000c -- descriptor_index 0001 -- attributes_count -- attribute_info 0009 -- attribute_name_index (Code) 0000 0025 -- attribute_length 0002 -- max_stack 0001 -- max_locals 0000 0009 -- code_length b2 00 02 12 03 b6 00 04 b1 -- code[] 0000 -- exception_table_length 0001 -- attributes_count 000a -- attribute_name_index 0000 000a -- attribute_length 00 02 00 00 00 04 00 08 00 05 -- [methods END] 

Analysons plus en détail la structure des méthodes:


access_flags


Masque modificateur. Tableau 4.5 Indicateurs d'accÚs aux méthodes et de propriété


Nom du drapeauValeurInterprétation
ACC_PUBLIC0x0001DĂ©clarĂ© public ; peut ĂȘtre consultĂ© de l'extĂ©rieur de son emballage.
ACC_PRIVATE0x0002Déclaré private ; accessible uniquement dans la classe de définition.
ACC_PROTECTED0x0004Déclaré protected ; sont accessibles dans les sous-classes.
ACC_STATIC0x0008Déclaré static .
ACC_FINAL0x0010DĂ©clarĂ©e final ; ne doit pas ĂȘtre outrepassĂ© ( §5.4.5 ).
ACC_SYNCHRONIZED0x0020Déclaré synchronized ; l'invocation est encapsulée par une utilisation de moniteur.
ACC_BRIDGE0x0040Une méthode de pont, générée par le compilateur.
ACC_VARARGS0x0080Déclaré avec un nombre variable d'arguments.
ACC_NATIVE0x0100native déclaré; implémenté dans un langage autre que Java.
ACC_ABSTRACT0x0400abstract déclaré; aucune implémentation n'est fournie.
ACC_STRICT0x0800Déclaré strictfp ; le mode virgule flottante est FP-strict.
ACC_SYNTHETIC0x1000Synthétique déclaré; non présent dans le code source.

Comme nous pouvons le voir dans le bytecode, dans la méthode public Main(); (constructeur) est le masque 0001 , ce qui signifie ACC_PUBLIC .


Essayons maintenant d'assembler la mĂ©thode main nous-mĂȘmes. Voici ce qu'il a:


  • public - ACC_PUBLIC - 0x0001
  • statique - ACC_STATIC - 0x0008
  • ChaĂźne ... args - ACC_VARARGS - 0x0080

Nous collectons le masque: 0x0001 + 0x0008 + 0x0080 = 0x0089 . Nous avons donc obtenu access_flag


Soit dit en passant, ACC_VARARGS est facultatif ici, dans le sens oĂč si nous
utilisé String [] args au lieu de String ... args, alors cet indicateur ne serait pas

nom_index


Adresse du nom de la méthode ( CONSTANT_Utf8_info ) dans le pool constant. Il est important de noter ici que le nom du constructeur n'est pas Main, mais <init> , situé dans la cellule # 7.


En savoir plus sur <init> et <clinit> au chapitre 2.9 Méthodes spéciales


descriptor_index


En gros, il s'agit d'une adresse pointant vers un descripteur de méthode. Ce descripteur contient le type de la valeur de retour et le type de sa signature.


De plus, la JVM utilise des abréviations interprétées:


CaractÚre BaseTypeTapezInterprétation
Bbyteoctet signé
CcharPoint de code de caractÚre Unicode dans le plan multilingue de base, codé avec UTF-16
Ddoublevaleur à virgule flottante double précision
Ffloatvaleur à virgule flottante simple précision
Iintentier
Jlongentier long
L ClassName ;referenceune instance de classe ClassName
Sshortsigné court
Zbooleantrue ou false
[referenceune dimension de tableau

En général, cela ressemble à ceci:


  ( ParameterDescriptor* ) ReturnDescriptor 

Par exemple, la méthode suivante:


  Object method(int i, double d, Thread t) {..} 

Peut ĂȘtre reprĂ©sentĂ© comme


  (IDLjava/lang/Thread;)Ljava/lang/Object 

En fait, I est int , D est double et Ljava/lang/Thread; classe Thread de la bibliothĂšque java.lang standard.


Ensuite, il existe des attributs qui ont Ă©galement leur propre structure.


Mais d'abord, comme toujours, son nombre d' attributes_count


Ensuite, les attributs eux-mĂȘmes avec la structure dĂ©crite au chapitre 4.7 Attributs


  attribute_info { u2 attribute_name_index; u4 attribute_length; u1 info[attribute_length]; } 

nom_attribut_index


SpĂ©cifie un nom d'attribut. Dans notre cas, les deux mĂ©thodes ont un Code . Les attributs sont un grand sujet distinct dans lequel vous pouvez mĂȘme crĂ©er vos propres attributs par spĂ©cification. Mais pour l'instant, nous devons savoir que attribute_name_index pointe simplement vers l'adresse dans le pool constant avec la structure CONSTANT_Utf8_info


longueur_attribut


Contient la longueur de l'attribut, sans compter l' attribute_name_index et l' attribute_length


info


Ensuite, nous utiliserons la structure Code , car dans la valeur de attribute_name_index nous avons pointé la valeur dans le pool de constantes Code .


En savoir plus: Chapitre 4.7.3 L'attribut de code


Voici sa structure:


  Code_attribute { u2 attribute_name_index; u4 attribute_length; u2 max_stack; u2 max_locals; u4 code_length; u1 code[code_length]; u2 exception_table_length; { u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_length]; u2 attributes_count; attribute_info attributes[attributes_count]; } 

max_stack


Il me semble que le nom de cet attribut peut ĂȘtre trompeur en raison du prĂ©fixe max. En fait, il s'agit de la taille de pile minimale requise pour terminer l'opĂ©ration. Eh bien, ce nom prend une logique, c'est-Ă -dire la taille de pile maximale qui sera atteinte pendant l'opĂ©ration.


Autrement dit, la JVM allouera de l'espace pour la pile d'opérandes. Là, vous pouvez spécifier une valeur plus grande que nécessaire, mais définir une valeur dans cet attribut moins que nécessaire entraßnera une erreur.


Sur le sujet de la pile, vous pouvez lire " Sur la pile et le tas dans le contexte du monde Java " ou dans " JVM Internals "


max_locals


Taille maximale des variables locales


Vous pouvez vous familiariser avec les variables locales soit dans Mastering Java Bytecode au cƓur de la JVM ou dans les mĂȘmes internes JVM


code_length


La taille du code qui sera exécuté à l'intérieur de la méthode


code []


Chaque code pointe vers une instruction. Le tableau de corrĂ©lation de l' optcode et des commandes avec les mnĂ©moniques se trouve sur Wikipedia - Liste d'instructions du bytecode Java ou dans la spĂ©cification elle-mĂȘme Ă  la fin du livre


Par exemple, prenez notre constructeur:


  -- public Main(); 0001 --access_flags 0007 -- name_index 0008 -- descriptor_index 0001 -- attributes_count -- attribute_info 0009 -- attribute_name_index (Code) 0000 001d - attribute_length 00 01 -- max_stack 00 01 -- max_locals 00 00 00 05 -- code_length 2a b7 00 01 b1 -- code[] 0000 -- exception_table_length 0001 -- attributes_count; 00 0a -- attribute_name_index 0000 0006 -- attribute_length 00 01 00 00 00 01 

Ici, nous pouvons trouver notre code:


  2a b7 00 01 b1 

Nous recherchons des commandes dans le tableau et comparons:


  2a - aload_0 b7 0001 - invokespecial #1 b1 - return 

Vous trouverez également des descriptions de ces commandes ici: Chapitre 4.10.1.9. Instructions de vérification de type


exception_table_length


Spécifie le nombre d'éléments dans la table d'exceptions. Nous n'avons pas de hooks d'exception donc nous ne le analyserons pas. Mais vous pouvez également lire le chapitre 4.7.3 L'attribut de code


table_exceptions []


Il a la structure suivante:


  { u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } 

Pour simplifier, vous devez spécifier le début, la fin ( start_pc , end_pc ) du code que handler_pc gérera et le catch_type exception catch_type


attributs_count


Nombre d'attributs dans le Code


attributs []


Les attributs sont souvent utilisés par les analyseurs ou les débogueurs.




Outils de bytecode


Ce n'est pas le sujet qui se rapporte à cet article, mais il y est toujours indirectement lié.


Il existe de nombreux outils pour travailler avec le bytecode. Ici, je voudrais examiner la bibliothÚque d'ingénierie de code d'octets (BCEL) d'Apache Commons.


Pour commencer, en l'utilisant, nous pouvons obtenir des attributs de bytecode:


  // read file from resources/complied/ClassA.class InputStream inputStream = ClassParserExample.class.getResourceAsStream("/compiled/ClassA.class"); if (inputStream == null) throw new FileNotFoundException(); ClassParser parser = new ClassParser(inputStream, "ClassA.class"); JavaClass clazz = parser.parse(); final String HEX_BYTECODE = getHex(clazz.getBytes()); System.out.println("Hex bytecode: "); System.out.println(HEX_BYTECODE); System.out.println(); final String MINOR_VER = getHex(clazz.getMinor()); final String MAJOR_VER = getHex(clazz.getMajor()); final String CONSTANT_POOL = getHex(clazz.getConstantPool().getConstantPool()); final String ACCESS_FLAGS = getHex(clazz.getAccessFlags()); final String THIS_CLASS = getHex(clazz.getClassName().getBytes()); final String SUPER_CLASS = getHex(clazz.getSuperClass().getBytes()); final String INTERFACES = getHex(clazz.getInterfaces()); final String FIELDS = getHex(clazz.getFields()); final String METHODS = getHex(clazz.getMethods()); final String ATTRIBUTES = getHex(clazz.getAttributes()); 

Liste complĂšte des codes
 import org.apache.bcel.classfile.*; import org.apache.commons.codec.binary.Hex; import java.io.*; public class ClassParserExample { public static void main(String... args) throws IOException, ClassNotFoundException { // read file from resources/complied/ClassA.class InputStream inputStream = ClassParserExample.class.getResourceAsStream("/compiled/ClassA.class"); if (inputStream == null) throw new FileNotFoundException(); ClassParser parser = new ClassParser(inputStream, "ClassA.class"); JavaClass clazz = parser.parse(); final String HEX_BYTECODE = getHex(clazz.getBytes()); System.out.println("Hex bytecode: "); System.out.println(HEX_BYTECODE); System.out.println(); final String MINOR_VER = getHex(clazz.getMinor()); final String MAJOR_VER = getHex(clazz.getMajor()); final String CONSTANT_POOL = getHex(clazz.getConstantPool().getConstantPool()); final String ACCESS_FLAGS = getHex(clazz.getAccessFlags()); final String THIS_CLASS = getHex(clazz.getClassName().getBytes()); final String SUPER_CLASS = getHex(clazz.getSuperClass().getBytes()); final String INTERFACES = getHex(clazz.getInterfaces()); final String FIELDS = getHex(clazz.getFields()); final String METHODS = getHex(clazz.getMethods()); final String ATTRIBUTES = getHex(clazz.getAttributes()); System.out.println( "minor: " + MINOR_VER ); // 0 System.out.println( "major: " + MAJOR_VER ); // 34 System.out.println( "constant pool: " + CONSTANT_POOL); // not correctly System.out.println( "access flags: " + ACCESS_FLAGS ); // 21 System.out.println( "this class: " + THIS_CLASS ); System.out.println( "super class: " + SUPER_CLASS ); // Object System.out.println( "interfaces: " + INTERFACES ); // <empty> System.out.println( "fields: " + FIELDS ); // <empty> System.out.println( "methods: " + METHODS ); // one method: psvm hello world System.out.println( "attributes: " + ATTRIBUTES ); // 536f7572636546696c65 - I think it's instructions for Java tools } private static String getHex(byte[] bytes){ return Hex.encodeHexString(bytes); } private static String getHex(int intNum){ return Integer.toHexString(intNum); } private static String getHex(Constant[] constants){ if (constants == null) return null; StringBuilder sb = new StringBuilder(); for (Constant c : constants){ if (c == null) continue; sb.append(getHex(c.getTag())).append(" "); } return sb.toString(); } private static String getHex(JavaClass[] clazzes){ if (clazzes == null) return null; StringBuilder sb = new StringBuilder(); for (JavaClass c : clazzes){ sb.append(getHex(c.getClassName().getBytes())).append(" "); } return sb.toString(); } private static String getHex(Field[] fields){ if (fields == null) return null; StringBuilder sb = new StringBuilder(); for (Field c : fields){ sb.append(getHex(c.getName().getBytes())).append(" "); } return sb.toString(); } private static String getHex(Method[] methods){ if (methods == null) return null; StringBuilder sb = new StringBuilder(); for (Method c : methods){ sb.append(getHex(c.getName().getBytes())).append(" "); } return sb.toString(); } private static String getHex(Attribute[] attributes){ if (attributes == null) return null; StringBuilder sb = new StringBuilder(); for (Attribute c : attributes){ sb.append(getHex(c.getName().getBytes())).append(" "); } return sb.toString(); } } /* Class A: public class ClassA { public static void main(String[] args) { System.out.println("Hello world"); } } */ /* Class A bytecode: cafe babe 0000 0034 0022 0a00 0600 1409 0015 0016 0800 170a 0018 0019 0700 1a07 001b 0100 063c 696e 6974 3e01 0003 2829 5601 0004 436f 6465 0100 0f4c 696e 654e 756d 6265 7254 6162 6c65 0100 124c 6f63 616c 5661 7269 6162 6c65 5461 626c 6501 0004 7468 6973 0100 1d4c 636f 6d2f 6170 706c 6f69 6478 7878 2f70 6172 7365 2f43 6c61 7373 413b 0100 046d 6169 6e01 0016 285b 4c6a 6176 612f 6c61 6e67 2f53 7472 696e 673b 2956 0100 0461 7267 7301 0013 5b4c 6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b01 000a 536f 7572 6365 4669 6c65 0100 0b43 6c61 7373 412e 6a61 7661 0c00 0700 0807 001c 0c00 1d00 1e01 000b 4865 6c6c 6f20 776f 726c 6407 001f 0c00 2000 2101 001b 636f 6d2f 6170 706c 6f69 6478 7878 2f70 6172 7365 2f43 6c61 7373 4101 0010 6a61 7661 2f6c 616e 672f 4f62 6a65 6374 0100 106a 6176 612f 6c61 6e67 2f53 7973 7465 6d01 0003 6f75 7401 0015 4c6a 6176 612f 696f 2f50 7269 6e74 5374 7265 616d 3b01 0013 6a61 7661 2f69 6f2f 5072 696e 7453 7472 6561 6d01 0007 7072 696e 746c 6e01 0015 284c 6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b29 5600 2100 0500 0600 0000 0000 0200 0100 0700 0800 0100 0900 0000 2f00 0100 0100 0000 052a b700 01b1 0000 0002 000a 0000 0006 0001 0000 0006 000b 0000 000c 0001 0000 0005 000c 000d 0000 0009 000e 000f 0001 0009 0000 0037 0002 0001 0000 0009 b200 0212 03b6 0004 b100 0000 0200 0a00 0000 0a00 0200 0000 0800 0800 0900 0b00 0000 0c00 0100 0000 0900 1000 1100 0000 0100 1200 0000 0200 13 */ /* Assembled code: Classfile /C:/java/BCEL/src/main/resources/compiled/ClassA.class Last modified 08.12.2019; size 563 bytes MD5 checksum bcd0198f6764a1dc2f3967fef701452e Compiled from "ClassA.java" public class com.apploidxxx.parse.ClassA minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#20 // java/lang/Object."<init>":()V #2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #23 // Hello world #4 = Methodref #24.#25 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #26 // com/apploidxxx/parse/ClassA #6 = Class #27 // java/lang/Object #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 Lcom/apploidxxx/parse/ClassA; #14 = Utf8 main #15 = Utf8 ([Ljava/lang/String;)V #16 = Utf8 args #17 = Utf8 [Ljava/lang/String; #18 = Utf8 SourceFile #19 = Utf8 ClassA.java #20 = NameAndType #7:#8 // "<init>":()V #21 = Class #28 // java/lang/System #22 = NameAndType #29:#30 // out:Ljava/io/PrintStream; #23 = Utf8 Hello world #24 = Class #31 // java/io/PrintStream #25 = NameAndType #32:#33 // println:(Ljava/lang/String;)V #26 = Utf8 com/apploidxxx/parse/ClassA #27 = Utf8 java/lang/Object #28 = Utf8 java/lang/System #29 = Utf8 out #30 = Utf8 Ljava/io/PrintStream; #31 = Utf8 java/io/PrintStream #32 = Utf8 println #33 = Utf8 (Ljava/lang/String;)V { public com.apploidxxx.parse.ClassA(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 6: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/apploidxxx/parse/ClassA; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello world 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 8: 0 line 9: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String; } SourceFile: "ClassA.java */ 

, (, Jasmin) -.



, Jasmin . , , , JVM -.


:


Hello World
 .bytecode 52.0 .source Main.j .class public Main .super java/lang/Object .method public static main([Ljava/lang/String;)V .limit stack 2 .limit locals 2 getstatic java/lang/System/out Ljava/io/PrintStream; ldc "Hello world!" invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V return .end method 

 ; ClassCreating.j .bytecode 52.0 .source ClassCreating.java .class public ClassCreating .super java/lang/Object .method public <init>()V .limit stack 1 .limit locals 1 .line 1 0: aload_0 1: invokespecial java/lang/Object/<init>()V 4: return .end method .method public static main([Ljava/lang/String;)V ; Flag ACC_VARARGS set, see JVM spec .limit stack 2 .limit locals 3 .line 3 0: new java/lang/String 3: dup 4: invokespecial java/lang/String/<init>()V 7: astore_1 .line 4 8: new ClassCreating 11: dup 12: invokespecial ClassCreating/<init>()V 15: astore_2 .line 5 16: aload_2 17: invokevirtual ClassCreating/sayHello()V .line 6 20: return .end method .method public sayHello()V .limit stack 2 .limit locals 1 .line 9 0: getstatic java/lang/System/out Ljava/io/PrintStream; 3: ldc "Hello, User!" 5: invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V .line 10 8: return .end method 



Hello World


- : gist.github


.


Littérature utilisée


  • The Java Virtual Machine Specification — docs.oracle

Source: https://habr.com/ru/post/fr480550/


All Articles