Lenguaje Intermedio Común

AjustarCompartirImprimirCitar

Lenguaje intermedio común (CIL), anteriormente llamado Lenguaje intermedio de Microsoft (MSIL) o Idioma intermedio (IL), es el conjunto de instrucciones binarias de lenguaje intermedio definido dentro de la especificación de infraestructura de lenguaje común (CLI). Las instrucciones CIL se ejecutan mediante un entorno de tiempo de ejecución compatible con CLI, como Common Language Runtime. Idiomas que apuntan a la compilación CLI a CIL. CIL es un código de bytes basado en pilas y orientado a objetos. Los tiempos de ejecución suelen compilar instrucciones CIL justo a tiempo en código nativo.

CIL se conocía originalmente como Microsoft Intermediate Language (MSIL) durante las versiones beta de los lenguajes.NET. Debido a la estandarización de C# y CLI, el código de bytes ahora se conoce oficialmente como CIL. Las definiciones de virus de Windows Defender siguen haciendo referencia a los archivos binarios compilados con él como MSIL.

Información general

Durante la compilación de los lenguajes de programación CLI, el código fuente se traduce a código CIL en lugar de a código objeto específico de plataforma o procesador. CIL es un conjunto de instrucciones independiente de la CPU y la plataforma que se puede ejecutar en cualquier entorno compatible con Common Language Infrastructure, como el tiempo de ejecución.NET en Windows o el tiempo de ejecución Mono multiplataforma. En teoría, esto elimina la necesidad de distribuir diferentes archivos ejecutables para diferentes plataformas y tipos de CPU. La seguridad del código CIL se verifica durante el tiempo de ejecución, lo que brinda mayor seguridad y confiabilidad que los archivos ejecutables compilados de forma nativa.

El proceso de ejecución se ve así:

  1. El código fuente se convierte en código de bytecode CIL y se crea un montaje CLI.
  2. Al ejecutar una asamblea CIL, su código se transmite a través del compilador JIT del tiempo 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 archivo ejecutable.
  3. El procesador del ordenador ejecuta el código nativo.

Instrucciones

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

  • Carga y tienda
  • Arithmetic
  • Conversión de tipo
  • Creación y manipulación de objetos
  • Gestión de pilas de Operand (push / pop)
  • Transferencia de control (marcación)
  • Método de invocación y retorno
  • Extrayendo excepciones
  • Compromiso basado en monitores
  • Manipulación de datos y punteros de función necesaria para C++/CLI y código C# inseguro

Modelo computacional

El lenguaje intermedio común está orientado a objetos y basado en pilas, lo que significa que los parámetros de instrucción y los resultados se mantienen 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 agrega 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 // empuje la variable local 0 en la pilaldloc.1 // empuje la variable local 1 en la pilaañadir // pop y añadir los dos elementos de la pila superior luego empujar el resultado en la pilastloc.0 // pop y almacenar el elemento de la pila superior a la variable local 0

En el último ejemplo, los valores de los dos registros, eax y edx, primero se colocan en la pila. Cuando se llama a la instrucción de adición, los operandos se "extraen" o se recuperan, y el resultado se "empuja" o se almacena en la pila. 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 usar otros tipos de miembros, como campos.

Todos los métodos necesitan (con algunas excepciones) residir en una clase. También lo hace este método estático:

.clase público Foo {} .método público estática int32 Añadir()int32, int32) cil gestionado {} .maxstack 2 Idarg.0 // cargar el primer argumento; Idarg.1 // cargar el segundo argumento; añadir // añadirlos; 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 así en C#:

int r = Foo.Añadir()2, 3); // 5

En CIL se vería así:

ldc.i4.2ldc.i4.3llamada int32 Foo::Añadir()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úblico Car {} .método público nombre especial nombre especial ejemplo vacío .ctor()int32, int32) cil gestionado {} * Constructor */ } .método público vacío Muévanse.()int32) cil gestionado {} Omitiendo la aplicación */ } .método público vacío TurnRight() cil gestionado {} Omitiendo la aplicación */ } .método público vacío TurnLeft() cil gestionado {} Omitiendo la aplicación */ } .método público vacío Brake() cil gestionado {} Omitiendo la aplicación */ }}

Creación de objetos

En la clase C#, las instancias se crean así:

Car myCar = nuevo Car()1, 4); Car tu Car = nuevo Car()1, 3);

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

ldc.i4.1ldc.i4.4newobj ejemplo vacío Car::.ctor()int, int)stloc.0 // myCar = nuevo coche(1, 4);ldc.i4.1ldc.i4.3newobj ejemplo vacío Car::.ctor()int, int)stloc.1 // su coche = nuevo coche(1, 3);

Métodos de instancia de invocación

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

myCar.Muévanse.()3);

Como se invoca en CIL:

ldloc.0 // Carga el objeto "miCar" en la pilaldc.i4.3llamada ejemplo vacío Car::Muévanse.()int32)

Metadatos

Common Language Infrastructure (CLI) registra información sobre 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 clase Attribute. Esta es una característica poderosa. Le permite al creador de la clase la capacidad de adornarla con información adicional que los consumidores de la clase pueden usar de varias maneras significativas, según el dominio de la aplicación.

Ejemplo

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

.montaje Hola. {}.montaje externa mscorlib {}.método estática vacío Main(){} .punto de entrada .maxstack 1 ldstr "¡Hola, mundo!" llamada vacío [mscorlib]Sistema.Consola::WriteLine()cuerda) Ret}

El siguiente código es más complejo en 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 el código de bytes de Java.

estática vacío Main()cuerda[] args){} para ()int i = 2; i . 1000; i++) {} para ()int j = 2; j . i; j++) {} si ()i % j == 0) Goto exterior; } Consola.WriteLine()i); exterior: }}

En la sintaxis del ensamblador CIL se ve así:

.método privado hidebysig estática vacío Main()cuerda[] args) cil gestionado{} .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 añadir stloc. 1 IL_0011: ldloc. 1 ldloc.0 blt.s IL_0008 ldloc.0 llamada vacío [mscorlib]Sistema.Consola::WriteLine()int32) IL_001b: ldloc.0 ldc.i4. 1 añadir 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 compilan, 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 compilador o una utilidad llamada IL Assembler (ILAsm) que se envía con el entorno de ejecución generan un ensamblado CIL y las instrucciones.

El CIL ensamblado también se puede desensamblar en código nuevamente usando el Desensamblador de IL (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. Este rasgo se comparte con el bytecode de Java. Sin embargo, existen herramientas que pueden ofuscar el código y hacerlo de modo que el código no se pueda leer fácilmente pero aún se pueda ejecutar.

Ejecución

Compilación justo a tiempo

La compilación justo a tiempo (JIT) consiste en convertir el código de bytes en código inmediatamente ejecutable por la CPU. La conversión se realiza gradualmente durante la ejecución del programa. La compilación JIT proporciona optimización específica del entorno, seguridad de tipo de tiempo de ejecución y verificación de ensamblaje. Para lograr esto, el compilador JIT examina los metadatos del ensamblaje en busca de accesos ilegales y maneja las infracciones de manera adecuada.

Compilació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 eliminando el proceso JIT en tiempo de ejecución.

En.NET Framework hay una herramienta especial llamada Native Image Generator (NGEN) que realiza el AOT. Un enfoque diferente para AOT es CoreRT que permite la compilación de código.Net Core en un único ejecutable sin dependencia de un tiempo de ejecución. En Mono también hay una opción para hacer un AOT.

Instrucciones de puntero - C++/CLI

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

clase A {} público: virtual vacío __stdcall meth() {}};vacío test_pointer_operations()int param) {}int k = 0;int * ptr = "k;*ptr = 1;ptr = "param;*ptr = 2;A a;A * ptra = "a;ptra-meth();}

El código correspondiente en CIL se puede representar así:

.método montaje estática vacío modopt[mscorlib]Sistema.Hora de correr.CompilerServices.CallConvCdecl)  test_pointer_operations()int32 param) cil gestionado{} .vtentry 1 : 1 // Tamaño del código 44 (0x2c) .maxstack 2 .locales [0] int32* ptr, [1] valor A* V_1, [2] valor A* a, [3] int32 k)// k = 0; IL_0000: ldc.i4.0  IL_0001: stloc.3// ptr = &k; IL_0002: ldloca.s k // Cargar instrucciones de dirección local IL_0004: stloc.0// *ptr = 1; IL_0005: ldloc.0 IL_0006: ldc.i4. 1 IL_0007: Destino.i4 // Instrucción indirecta// ptr = ¶m IL_0008: ldarga.s param // instrucciones 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: Destino.i4// a = nuevo A; IL_000e: ldloca.s a IL_0010: llamada valor A* modopt[mscorlib]Sistema.Hora de correr.CompilerServices.CallConvThiscall) 'A. {}ctor}'()valor A* modopt[mscorlib]Sistema.Hora de correr.CompilerServices.IsConst) modopt[mscorlib]Sistema.Hora de correr.CompilerServices.IsConst) IL_0015: pop// ptra = a); IL_0016: ldloca.s a IL_0018: stloc. 1// ptra- títulometh(); IL_0019: ldloc. 1 IL_001a:  IL_001b: Idind.i4 // leer el VMT para llamadas virtuales IL_001c: Idind.i4 IL_001d: Calli no gestionados stdcall vacío modopt[mscorlib]Sistema.Hora de correr.CompilerServices.CallConvStdcall)nativo int) IL_0022: Ret} // final del método 'Global Functions'::test_pointer_operations

Contenido relacionado

OSGi

Adicción a la computadora

La adicción a la computadora es una forma de adicción conductual que puede describirse como el uso excesivo o compulsivo de la computadora, que persiste a...

Neuromante

Más resultados...