stringtranslate.com

Lenguaje Intermedio Común

Common Intermediate Language ( CIL ), anteriormente llamado Microsoft Intermediate Language ( MSIL ) o Intermediate Language ( IL ), [1] es el conjunto de instrucciones binarias de lenguaje intermedio definido dentro de la especificación Common Language Infrastructure (CLI). [2] Las instrucciones CIL son ejecutadas por un entorno de ejecución compatible con CIL, como Common Language Runtime . Los lenguajes que tienen como destino la CLI compilan en CIL. CIL es un código de bytes basado en pila y orientado a objetos . Los entornos de ejecución normalmente compilan las instrucciones CIL en código nativo justo a tiempo .

En un principio, CIL se conocía como Lenguaje intermedio de Microsoft (MSIL) durante las versiones beta de los lenguajes .NET. Debido a la estandarización de C# y la CLI, el código de bytes ahora se conoce oficialmente como CIL. [3] Las definiciones de virus de Windows Defender siguen haciendo referencia a los binarios compilados con él como MSIL. [4]

información general

Durante la compilación de lenguajes de programación CLI , el código fuente se traduce a código CIL en lugar de a código de objeto específico de la plataforma o del procesador . CIL es un conjunto de instrucciones independiente de la CPU y de la plataforma que se puede ejecutar en cualquier entorno que admita la infraestructura de lenguaje común, como el entorno de ejecución .NET en Windows o el entorno de ejecución Mono multiplataforma . En teoría, esto elimina la necesidad de distribuir diferentes archivos ejecutables para diferentes plataformas y tipos de CPU. El código CIL se verifica para garantizar su seguridad durante el tiempo de ejecución, lo que proporciona una mayor seguridad y confiabilidad que los archivos ejecutables compilados de forma nativa. [5] [6]

El proceso de ejecución se ve así:

  1. El código fuente se convierte a código de bytes CIL y se crea un ensamblaje CLI .
  2. Al ejecutar un ensamblado CIL, su código pasa a través del compilador JIT del entorno de ejecución para generar código nativo. También se puede utilizar la compilación anticipada, que elimina este paso, pero a costa de la portabilidad de los archivos ejecutables.
  3. El procesador de la computadora ejecuta el código nativo.

Instrucciones

El código de bytes CIL tiene instrucciones para los siguientes grupos de tareas:

Modelo computacional

El lenguaje intermedio común está orientado a objetos y basado en pila , lo que significa que los parámetros de las instrucciones y los resultados se guardan en una sola pila en lugar de en varios registros u otras ubicaciones de memoria, como en la mayoría de los lenguajes de programación .

Código que suma dos números en lenguaje ensamblador x86 , donde eax y edx especifican dos registros de propósito general diferentes :

añadir eax , edx  

Código en un lenguaje intermedio (IL), donde 0 es eax y 1 es edx:

ldloc . 0 // inserta la variable local 0 en la pila ldloc . 1 // inserta la variable local 1 en la pila add // extrae y agrega los dos elementos superiores de la pila y luego inserta el resultado en la pila stloc . 0 // extrae y almacena el elemento superior de la pila en la variable local 0    

En el último ejemplo, los valores de los dos registros, eax y edx, se introducen primero en la pila. Cuando se llama a la instrucción add, se extraen los operandos y se almacena el resultado en la pila. A continuación, el valor resultante se extrae de la pila y se almacena en eax.

Conceptos orientados a objetos

CIL está diseñado para estar orientado a objetos. Puede crear objetos, llamar a métodos y utilizar otros tipos de miembros, como campos.

Todos los métodos deben (con algunas excepciones) residir en una clase. Este método estático también lo hace:

. clase pública Foo { . método público estático int32 Add ( int32 , int32 ) cil administrado { . maxstack 2 ldarg . 0 // cargar el primer argumento; ldarg . 1 // cargar el segundo argumento; add // agregarlos; ret // devolver el resultado; } }                       

El método Add no requiere que se declare ninguna instancia de Foo porque se declara como estático y luego se puede usar de esta manera en C#:

int r = Foo . Agregar ( 2 , 3 ); // 5     

En CIL se vería así:

ldc . i4 . 2 ldc . i4 . 3 llamar int32 Foo :: Add ( int32 , int32 ) stloc . 0   

Clases de instancia

Una clase de instancia contiene al menos un constructor y algunos miembros de instancia . La siguiente clase tiene un conjunto de métodos que representan acciones de un objeto Car.

. clase pública Car { . método público specialname rtspecialname instancia void . ctor ( int32 , int32 ) cil administrado { /* Constructor */ }                 . método público void Move ( int32 ) cil administrado { /* Omitiendo la implementación */ } . método público void TurnRight () cil administrado { /* Omitiendo la implementación */ } . método público void TurnLeft () cil administrado { /* Omitiendo la implementación */ } . método público void Brake () cil administrado { /* Omitiendo la implementación */ } }                                   

Creando objetos

En C# las instancias de clase se crean de la siguiente manera:

Coche miCoche = Coche nuevo ( 1 , 4 ); Coche tuCoche = Coche nuevo ( 1 , 3 );           

Y esas afirmaciones son aproximadamente las mismas que estas instrucciones en CIL:

ldc . i4 . 1 ldc . i4 . 4 nueva instancia de objeto void Automóvil ::. ctor ( int , int ) stloc . 0 // miAutomóvil = nuevo Automóvil(1, 4); ldc . i4 . 1 ldc . i4 . 3 nueva instancia de objeto void Automóvil ::. ctor ( int , int ) stloc . 1 // tuAutomóvil = nuevo Automóvil(1, 3);          

Invocación de métodos de instancia

Los métodos de instancia se invocan en C# como el que sigue:

miCoche.Mover ( 3 ) ;

Como se invoca en el CIL:

ldloc . 0 // Carga el objeto "myCar" en la pila ldc . i4 . 3 call instance void Car :: Move ( int32 )    

Metadatos

La infraestructura de lenguaje común (CLI) registra información sobre las clases compiladas como metadatos . Al igual que la biblioteca de tipos en el modelo de objetos componentes , esto permite que las aplicaciones admitan y descubran las interfaces, clases, tipos, métodos y campos en el ensamblaje. El proceso de lectura de dichos metadatos se denomina " reflexión ".

Los metadatos pueden ser datos en forma de "atributos". Los atributos se pueden personalizar ampliando la Attributeclase. Esta es una característica muy potente, ya que permite al creador de la clase adornarla con información adicional que los consumidores de la clase pueden utilizar de diversas formas significativas, según el dominio de la aplicación.

Ejemplo

A continuación se muestra un programa básico "¡Hola, mundo!" escrito en ensamblador CIL. Mostrará la cadena "¡Hola, mundo!".

. ensamblaje Hola {} . ensamblaje extern mscorlib {} . método static void Main () { . punto de entrada . maxstack 1 ldstr "¡Hola, mundo!" llamada void [ mscorlib ] System . Console :: WriteLine ( string ) ret }                 

El siguiente código es más complejo en cuanto a número de códigos de operación.

Este código también se puede comparar con el código correspondiente en el artículo sobre bytecode de Java .

void estático Main ( string [] args ) { para ( int i = 2 ; i < 1000 ; i ++ ) { para ( int j = 2 ; j < i ; j ++ ) { si ( i % j == 0 ) ir a externo ; } Console.WriteLine ( i ) ; externo :; } }                                   

En la sintaxis del ensamblador CIL se ve así:

. método privado hidebysig static void Main ( cadena [] args ) cil administrado { . punto de entrada . maxstack 2 . locales init ( int32 V_0 , int32 V_1 )                  ldc . i4 . 2 stloc . 0 br . s IL_001f IL_0004 : ldc . i4 . 2 stloc . 1 br . s IL_0011 IL_0008 : ldloc . 0 ldloc . 1 rem brfalse . s IL_001b ldloc . 1 ldc . i4 . 1 add stloc . 1 IL_0011 : ldloc . 1 ldloc . 0 blt . s IL_0008 ldloc . 0 llamada void [ mscorlib ] Sistema . Console :: WriteLine ( int32 ) IL_001b : ldloc . 0 ldc . i4 . 1 add stloc . 0 IL_001f : ldloc . 0 ldc . i4 0x3e8 blt . s IL_0004 ret }                                       

Esta es solo una representación de cómo se ve CIL cerca del nivel de máquina virtual (VM). Cuando se compila, los métodos se almacenan en tablas y las instrucciones se almacenan como bytes dentro del ensamblaje, que es un ejecutable portátil (PE).

Generación

Un ensamblaje CIL y sus instrucciones se generan mediante un compilador o una utilidad llamada Ensamblador IL ( ILAsm ) que se envía con el entorno de ejecución.

El CIL ensamblado también se puede desensamblar en código nuevamente utilizando el IL Disassembler (ILDASM). Existen otras herramientas como .NET Reflector que pueden descompilar CIL en un lenguaje de alto nivel (por ejemplo, C# o Visual Basic ). Esto hace que CIL sea un objetivo muy fácil para la ingeniería inversa. Esta característica es compartida con Java bytecode . Sin embargo, existen herramientas que pueden ofuscar el código y hacerlo de manera que el código no sea fácilmente legible pero aún así sea ejecutable.

Ejecución

Recopilación justo a tiempo

La compilación Just-in-time (JIT) implica convertir el código de bytes en código ejecutable inmediatamente por la CPU. La conversión se realiza de forma gradual durante la ejecución del programa. La compilación JIT proporciona optimización específica del entorno, seguridad de tipos en tiempo de ejecución y verificación del ensamblado. Para lograr esto, el compilador JIT examina los metadatos del ensamblado en busca de accesos ilegales y maneja las violaciones de forma adecuada.

Recopilación anticipada

Los entornos de ejecución compatibles con CLI también vienen con la opción de realizar una compilación anticipada (AOT) de un ensamblaje para que se ejecute más rápido al eliminar el proceso JIT en tiempo de ejecución.

En .NET Framework existe una herramienta especial llamada Generador de imágenes nativas (NGEN) que realiza la AOT. Un enfoque diferente para la AOT es CoreRT , que permite la compilación de código .Net Core en un único ejecutable sin dependencia de un entorno de ejecución. En Mono también existe una opción para realizar una AOT.

Instrucciones de puntero - C++/CLI

Una diferencia notable con el código de bytes de Java es que CIL viene con ldind, stind, ldlocay muchas instrucciones de llamada que son suficientes para la manipulación de punteros de datos/funciones necesarias para compilar código C/C++ en CIL.

clase A { público : virtual void __stdcall meth () {} }; void operaciones_de_puntero_de_prueba ( int parámetro ) { int k = 0 ; int * ptr = & k ; * ptr = 1 ; ptr = & parámetro ; * ptr = 2 ; A a ; A * ptra = &a a ; ptra -> met (); }                             

El código correspondiente en CIL se puede representar de la siguiente manera:

. método ensamblaje static void modopt ([ mscorlib ] System.Runtime.CompilerServices.CallConvCdecl ) test_pointer_operations ( int32 param ) cil managed { .vtentry 1 : 1 // Tamaño del código 44 (0x2c) .maxstack 2.locals ( [ 0 ] int32 * ptr , [ 1 ] valuetypeA * V_1 , [ 2 ] valuetypeA * a , [ 3 ] int32k ) // k = 0 ; IL_0000 : ldc.i4.0 IL_0001 : stloc.3 // ptr = & k ; IL_0002 : ldloca.sk // instrucción de dirección de carga local IL_0004 : stloc.0 // * ptr = 1 ; IL_0005 : ldloc .0 IL_0006 : ldc . i4 .1 IL_0007 : stind . i4 // instrucción de indirección // ptr = ¶m IL_0008 : ldarga . s param // instrucción de dirección del parámetro de carga IL_000a : stloc .0 // *ptr = 2 IL_000b : ldloc .0 IL_000c : ldc . i4 .2 IL_000d : stind . i4 // a = new A; IL_000e : ldloca . s a IL_0010 : call valuetype A * modopt ([ mscorlib ] System . Runtime . CompilerServices . CallConvThiscall ) ' A .{ ctor } ' ( valuetype A * modopt ([ mscorlib ]                                                                        Sistema . Tiempo de ejecución . Servicios del compilador . IsConst ) modopt ([ mscorlib ] Sistema . Tiempo de ejecución . CompilerServices . IsConst )) IL_0015 : pop // ptra = &a; IL_0016 : ldloca . s a IL_0018 : stloc .1 // ptra->meth(); IL_0019 : ldloc .1 IL_001a : dup IL_001b : ldind . i4 // leyendo el VMT para la llamada virtual IL_001c : ldind . i4 IL_001d : calli no administrado stdcall void modopt ([ mscorlib ] System.Runtime.CompilerServices.CallConvStdcall ) ( inte nativo ) IL_0022 : ret } // fin del método ' Funciones globales':: test_pointer_operations                           

Véase también

Referencias

  1. ^ "Lenguaje intermedio y ejecución".
  2. ^ "Infraestructura de lenguaje común (CLI) ECMA-335".
  3. ^ "Qué es el lenguaje intermedio (IL)/MSIL/CIL en .NET" . Consultado el 17 de febrero de 2011 . CIL: ... Cuando compilamos un proyecto de .NET, no se convierte directamente a código binario, sino al lenguaje intermedio. Cuando se ejecuta un proyecto, todos los lenguajes de programación de .NET se convierten a código binario en CIL. Solo una parte de CIL que se requiere en tiempo de ejecución se convierte a código binario. Las DLL y EXE de .NET también están en formato CIL.
  4. ^ "HackTool:MSIL/SkypeCracker". Microsoft . Consultado el 26 de noviembre de 2019 .
  5. ^ Troelsen, Andrew (2 de mayo de 2009). Beneficios del CIL. ISBN 9781590598849. Consultado el 17 de febrero de 2011 .
  6. ^ "Extensiones administradas y no administradas para C++, Managed y .Net Framework". www.visualcplusdotnet.com . Consultado el 7 de julio de 2020 .

Lectura adicional

Enlaces externos