Hola mundo de Bytecode para JVM

Compilamos un programa simple que muestra "Hello World" y recorremos su estructura


No creo que el artículo sea lo suficientemente informativo para aquellos que no saben superficialmente cómo se ve el bytecode y cómo funciona la JVM (por ejemplo, al menos las instrucciones más simples (conocimiento sobre su existencia)).


De hecho, no es tan difícil. Es suficiente usar la herramienta javap del JDK y considerar el código desmontado.


Y comenzaremos a analizar la estructura del bytecode para la JVM


Un libro muy útil para esto fue la especificación JVM oficial: la especificación de máquina virtual Java en Oracle


Para comenzar, cree un programa simple:


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

javac Main.java con el equipo javac Main.java y de hecho desarme


  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" 

Esta es solo una representación de código de bytes que es más fácil de ver para una persona que el código de bytes original, pero se ve diferente:


  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 

(Puede abrir su archivo .class través de Sublime Text que indica File-> Save with Encoding -> Hexademical)


Trabajaremos con este código.


Pero primero, necesitamos formatearlo para no confundirnos donde está, y el bytecode, de hecho, tiene una estructura muy rígida:


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

Puede encontrarlo en la especificación JVM Capítulo 4.1 La estructura de ClassFile


Aquí todo es simple: la dimensión en bytes se indica a la izquierda y la descripción a la derecha.


Analizaremos el bytecode en hexadecimal, donde cada dígito toma 4 bits y, por lo tanto, para dos bytes - 4 dígitos y para cuatro bytes - 8 dígitos.


magia


La magia es un valor que identifica el formato de nuestra clase. Es igual a 0xCAFEBABE , que tiene su propia historia de creación .


menor_versión, mayor_versión


Estas son versiones de su archivo de class . Si llamamos major_version M y minor_version m, obtenemos la versión de nuestro archivo de class como Mm


Ahora daré ejemplos de nuestro programa "Hello World" de inmediato para ver cómo se usan:


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

Podemos verlo en el código desmontado, pero ya en el sistema de números decimales:


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

constant_pool_count


El número de variables en el grupo constante se indica aquí. Al mismo tiempo, si decide escribir código en bytecode puro, entonces definitivamente necesita monitorear su valor, porque si especifica el valor incorrecto, todo el programa se irá al infierno (¡verificado!).


Además, no olvide que debe escribir allí el ___ + 1 de_variables_ en el ___ + 1


Total que obtenemos:


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

constant_pool []


Cada tipo de variable en el grupo constante tiene su propia estructura:


  cp_info { u1 tag; u1 info[]; } 

Aquí todo debe hacerse secuencialmente. Primero leemos la tag para averiguar el tipo de la variable y por el tipo de esta variable miramos qué estructura tiene su valor posterior info[]


Se puede encontrar una tabla con etiquetas en la Tabla 4.3 Especificación de etiquetas de agrupación constante .


En realidad, aquí está la tableta:


Tipo constanteValor
CONSTANT_Class7 7
CONSTANT_Fieldref9 9
CONSTANT_Methodref10
CONSTANT_InterfaceMethodref11
CONSTANT_String8
CONSTANT_Integer3
CONSTANT_Float4 4
CONSTANT_Long5 5
CONSTANT_Double6 6
CONSTANT_NameAndType12
CONSTANT_Utf81
CONSTANT_MethodHandle15
CONSTANT_MethodType16
CONSTANT_InvokeDynamic18 años

Como se mencionó anteriormente, cada tipo de constante tiene su propia estructura.


Aquí, por ejemplo, está la estructura CONSTANT_Class :


  CONSTANT_Class_info { u1 tag; u2 name_index; } 

Estructura de campo y método:


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

Es importante señalar aquí que diferentes estructuras pueden tener diferentes longitudes.


Considere parte de nuestro código:


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

Entonces, miramos la estructura de la constante y descubrimos que el primer byte está reservado para el tipo de constante. Aquí vemos 0a (10) y, por lo tanto, es CONSTANT_Methodref


Nos fijamos en su estructura:


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

Después de un byte para la etiqueta, necesitamos 4 bytes más para class_index y name_and_type_index


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

Bueno, encontramos uno de los valores del grupo constante. Adelante Miramos, 09 - significa el tipo CONSTANT_Fieldref


Obtenemos:


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

Puede pensar que la mayoría de los tipos tienen la misma forma, pero este no es el caso.
Por ejemplo, una estructura del siguiente tipo se parece a CONSTANT_String :


  CONSTANT_String_info { u1 tag; u2 string_index; } 

Todas estas estructuras se pueden encontrar en el Capítulo 4.4 The Constant Pool.


Ahora veamos qué significan los tipos de info interna.


Los métodos que se encuentran dentro del patrón *_index generalmente contienen la dirección de la tabla de pool constante. Por ejemplo, class_index para un valor de tipo CONSTANT_Class_info y string_index para un string_index CONSTANT_Utf8_info


Podemos ver esto en el código desmontado:


  #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 

También puede resaltar la representación de números y cadenas.


Puede leer sobre la representación de números a partir del capítulo 4.4.4 , pero por ahora analizaremos solo las líneas, ya que los números aún no están incluidos en el programa Hello World


En realidad, así es como aparece la línea:


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

Por ejemplo, nuestro Hello World:


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

Analizando todo el grupo de constantes de bytecode, obtenemos:


Todo el grupo 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] 

Además, podemos compararlo con el código desmontado:


  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 

Por lo tanto, verificando que todo coincida, porque de hecho javap simplemente procesa este código de bytes y nos lo muestra en forma formateada.


El grupo constante es necesario para obtener instrucciones. Por ejemplo:


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

Para obtener más información sobre todos los tipos en el grupo constante, consulte el Capítulo 4.4 El Grupo Constante.


Yendo más allá en la estructura ClassFile


banderas de acceso


Esta es una máscara de bits para las propiedades del modificador.


Nombre de la banderaValorInterpretación
ACC_PUBLIC0x0001Declarado public ; Se puede acceder desde fuera de su paquete.
ACC_FINAL0x0010Declarado final ; No se permiten subclases.
ACC_SUPER0x0020Trate los métodos de superclase especialmente cuando sea invocado por la instrucción especial invok .
ACC_INTERFACE0x0200Es una interfaz, no una clase.
ACC_ABSTRACT0x0400abstract declarado; No debe ser instanciado.
ACC_SYNTHETIC0x1000Declarado sintético; no presente en el código fuente.
ACC_ANNOTATION0x2000Declarado como un tipo de anotación.
ACC_ENUM0x4000Declarado como un tipo de enum .

esta_clase


Debe contener una dirección en this clase. En nuestro caso, se encuentra en la dirección 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 ... 

Cabe señalar que la estructura de esta variable debe cumplir con CONSTANT_Class_info


superclase


Dirección del antepasado de la clase. En nuestro caso, el valor está en la dirección #6 . Bueno, también se requiere la estructura de valor CONSTANT_Class_info


Los nombres de estas clases se definen en la estructura de la constante CONSTANT_Utf8_info . Si miramos las celdas #21 y #22 , veremos:


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

Es decir, en estas celdas se indica name_index de la estructura:


  CONSTANT_Class_info { u1 tag; u2 name_index; } 

interfaces_count, fields_count


No están en nuestro programa, por lo que sus valores serán iguales a 0000, y simplemente no habrá valores posteriores de los fields[] , interfaces[] .


Leer más 4.1 La estructura de ClassFile


métodos_cuenta


Número de métodos Aunque en el código vemos un método en la clase, en realidad hay dos de ellos. Además del método main , también hay un constructor predeterminado. Por lo tanto, su número es dos, en nuestro caso.


métodos []


Cada elemento debe cumplir con la estructura method_info descrita en el Capítulo 4.6 Métodos


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

En nuestro código de bytes (formateado, con comentarios) se ve así:


  -- [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] 

Analicemos la estructura de los métodos con más detalle:


banderas de acceso


Modificador de máscara. Tabla 4.5 Acceso al método e indicadores de propiedad


Nombre de la banderaValorInterpretación
ACC_PUBLIC0x0001Declarado public ; Se puede acceder desde fuera de su paquete.
ACC_PRIVATE0x0002Declarado private ; accesible solo dentro de la clase definitoria.
ACC_PROTECTED0x0004Declarado protected ; Se puede acceder dentro de las subclases.
ACC_STATIC0x0008Declarado static .
ACC_FINAL0x0010Declarado final ; no debe ser anulado ( §5.4.5 ).
ACC_SYNCHRONIZED0x0020Declarado synchronized ; la invocación está envuelta por un uso de monitor.
ACC_BRIDGE0x0040Un método de puente, generado por el compilador.
ACC_VARARGS0x0080Declarado con número variable de argumentos.
ACC_NATIVE0x0100Declarado native ; implementado en un lenguaje que no sea Java.
ACC_ABSTRACT0x0400abstract declarado; no se proporciona implementación
ACC_STRICT0x0800Declarado strictfp ; el modo de punto flotante es estrictamente FP.
ACC_SYNTHETIC0x1000Declarado sintético; no presente en el código fuente.

Como podemos ver en el bytecode, en el método public Main(); (constructor) es la máscara 0001 , que significa ACC_PUBLIC .


Ahora intentemos ensamblar el método main nosotros mismos. Aquí está lo que tiene:


  • public - ACC_PUBLIC - 0x0001
  • estática - ACC_STATIC - 0x0008
  • Cadena ... args - ACC_VARARGS - 0x0080

Recopilamos la máscara: 0x0001 + 0x0008 + 0x0080 = 0x0089 . Entonces tenemos access_flag


Por cierto, ACC_VARARGS es opcional aquí, en el sentido de que si
usó String [] args en lugar de String ... args, entonces esta bandera no sería

index_nombre


Dirección del nombre del método ( CONSTANT_Utf8_info ) en el grupo constante. Es importante tener en cuenta aquí que el nombre del constructor no es Main, sino <init> , ubicado en la celda # 7.


Obtenga más información sobre <init> y <clinit> en el Capítulo 2.9 Métodos especiales


índice_descriptor


En términos generales, esta es una dirección que apunta a un identificador de método. Este descriptor contiene el tipo del valor de retorno y el tipo de su firma.


Además, la JVM usa abreviaturas interpretadas:


Carácter BaseTypeTipoInterpretación
Bbytebyte firmado
CcharPunto de código de caracteres Unicode en el plano multilingüe básico, codificado con UTF-16
Ddoublevalor de coma flotante de doble precisión
Ffloatvalor de coma flotante de precisión simple
Iintentero
Jlongentero largo
L ClassName ;referenceuna instancia de clase ClassName
Sshortcorto firmado
Zbooleantrue o false
[referenceuna dimensión de matriz

En general, se ve así:


  ( ParameterDescriptor* ) ReturnDescriptor 

Por ejemplo, el siguiente método:


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

Puede ser representado como


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

En realidad, I int , D es double y Ljava/lang/Thread; Thread clase de la biblioteca estándar java.lang .


A continuación, hay atributos que también tienen su propia estructura.


Pero primero, como siempre, su cuenta de attributes_count


Luego, los atributos mismos con la estructura descrita en el Capítulo 4.7 Atributos


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

nombre_atributo_index


Especifica un nombre de atributo. En nuestro caso, ambos métodos tienen un Code . Atributos es un gran tema separado en el que incluso puede crear sus propios atributos por especificación. Pero por ahora, deberíamos saber que attribute_name_index solo apunta a la dirección en el grupo constante con la estructura CONSTANT_Utf8_info


longitud_atributo


Contiene la longitud del atributo, sin incluir attribute_name_index y attribute_length


informacion


A continuación, usaremos la estructura de Code , porque en el valor de attribute_name_index señalamos el valor en el conjunto de constantes de Code .


Leer más: Capítulo 4.7.3 El Atributo del Código


Aquí está su estructura:


  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


Me parece que el nombre de este atributo puede ser engañoso debido al prefijo max. De hecho, este es el tamaño mínimo de pila necesario para completar la operación. Bueno, este nombre toma lógica, para decir el tamaño máximo de pila que se alcanzará durante la operación.


En pocas palabras, la JVM asignará espacio para la pila de operandos. Allí puede especificar un valor que sea más grande de lo necesario, pero definir un valor en este atributo menos de lo necesario dará como resultado un error.


Sobre el tema de la pila, puede leer " En la pila y el montón en el contexto del mundo Java " o en " Elementos internos de JVM "


max_locals


Tamaño máximo de variables locales.


Puede familiarizarse con las variables locales, ya sea en Mastering Java Bytecode en el núcleo de la JVM o en las mismas partes internas de JVM


longitud_código


El tamaño del código que se ejecutará dentro del método.


código []


Cada código apunta a alguna instrucción. La tabla de correlación de optcode y comandos con mnemónicos se puede encontrar en Wikipedia: listados de instrucciones de bytecode de Java o en la especificación al final del libro.


Por ejemplo, tome nuestro constructor:


  -- 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 

Aquí podemos encontrar nuestro código:


  2a b7 00 01 b1 

Buscamos comandos en la tabla y comparamos:


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

También se pueden encontrar descripciones de estos comandos aquí: Capítulo 4.10.1.9. Instrucciones de verificación de tipo


excepción_tabla_length


Especifica el número de elementos en la tabla_excepción. No tenemos ganchos de excepción, por lo que no lo analizaremos. Pero también puede leer el Capítulo 4.7.3 El Atributo del Código


tabla_excepción []


Tiene la siguiente estructura:


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

Para simplificar, debe especificar el principio, el final ( start_pc , end_pc ) del código que handler_pc y el catch_type excepción catch_type


cuenta_atributos


Número de atributos en el Code


atributos []


Los atributos suelen ser utilizados por analizadores o depuradores.




Herramientas de Bytecode


Este no es el tema relacionado con este artículo, pero aún está indirectamente relacionado con él.


Hay muchas herramientas para trabajar con bytecode. Aquí me gustaría revisar la Byte Code Engineering Library (BCEL) de Apache Commons.


Para empezar, usándolo podemos obtener algunos atributos 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()); 

Listado de código completo
 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


.


Literatura usada


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


All Articles