Introducción al lenguaje ensamblador IL de .NET [Parte 1]
15 diciembre 2011 1 comentario
Introducción a los Tutoriales
Lo que pretendo con esto es realizar una serie de tutoriales consecutivos sobre este tema, por lo que los próximos tutoriales se basaran en los anteriores. No pretendo que lleguéis a ser maestros escribiendo código ILAsm, más bien que obtengáis la base necesaria para que podáis desenvolveros sin mucha dificultad con el y sigáis aprendiendo por vuestra cuenta.
Con estos tutoriales aprenderéis la base del lenguaje ensamblador IL (tambien llamado CIL, o Common Intermediate Language) para que podáis debuguear código en bajo nivel .NET ya compilado (desde cualquier lenguaje de alto nivel, como VB .NET, C# y otros) y así corregir fallos, añadir, quitar o cambiar características y más. Es decir, podrás editar libremente cualquier programa o librería una vez compilada de .NET. Además, usando estos conocimientos, tendrás la base para poder empezar a escribir tu propio compilador para crear un nuevo lenguaje .NET (aunque no vamos a llegar tan lejos).
Siempre que compilas tu código en .NET, independientemente del lenguaje que estés usando, será convertido al Lenguaje Intermedio (IL, Intermediate Language) el cual es también conocido como Lenguaje Intermedio de Microsoft (MSIL) o Lenguaje Intermedio Común (CIL). Puedes pensar que IL es como el Byte Code generado por el lenguaje Java. Si estás interesado en entender cómo .NET funciona con sus tipos de datos y cómo el código que escribes es convertido a código IL, entonces estos tutoriales sobre ILAsm te van a resultar muy útiles dándote grandes ventajas.
Entre estas ventajas encontramos entender qué es lo que devuelve el compilador .NET cuando compila, por lo tanto, podrás examinar el codigo traducido por el compilador y realizar los cambios que sean necesarios (aunque no es necesario en la mayoría de los casos). Además, puedes cambiar el código IL para hacer cambios que tu lenguaje de alto nivel no permitiría para aumentar el rendimiento de tu código, y también te ayudará a debuggear tu código en bajo nivel. Y como dije antes, si estás planeando escribir un compilador para .NET, entonces necesitarás entender el código ILAsm.
IL es el formato binario el cual el compilador devuelve una vez termina su tarea, es decir, no puede ser leido por humanos, pero al igual que otros binarios (ejecutables) tienen su lenguaje ensamblador, IL tiene tambien el suyo propio conocido como lenguaje ensamblador IL (ILAsm). El lenguaje ensamblador IL (ILAsm a partir de ahora) tiene sus propias instrucciones de la misma manera que cualquier otro lenguaje ensamblador nativo pueda tener. Por ejemplo, para sumar dos números, tienes la instrucción ‘add’, para restar dos números, tienes la instrucción ‘sub’, etc. Es obvio que el compilador en tiempo de ejecución de .NET (el JIT) no puede ejecutar código ILAsm directamente. Si tu tienes escrito código en ILAsm entonces tendrás primero que compilarlo a código IL y entonces el JIT podrá ejecutar el código.
NOTA: Por favor ten en cuenta que IL y ILAsm son cosas distintas. Siempre que hablaremos sobre IL, nos referiremos al código binario devuelto por el compilador de .NET mientras que cuando referenciemos a ILAsm, nos referiremos al lenguaje ensamblador de este código el cual no está en su forma binaria.
NOTA2: Ten en cuenta que espero que estés bastante familiarizado con .NET (incluyendo algún lenguaje de alto nivel como C# o VB.NET). En estos tutoriales no profundizaremos en todos los detalles, sino únicamente en aquellos que realmente necesitan ser explicados. Si algo te confunde, por favor házmelo saber para aclarártelo.
Introducción al Lenguaje Ensamblador IL
Bien, empecemos con el propósito de estos tutoriales, la introducción al lenguaje ensamblador IL. Ten en cuenta que puedes escribir código en ILAsm en cualquier editor de texto (como notepad) y luego usar la linea de comandos (cmd) para compilar el código. El compilador en este caso es el ILAsm.exe, el cual está distribuido junto con el SDK de .NET Framework. ILAsm es un lenguaje algo duro de aprender para aquellos que siempre han programado en lenguajes de alto nivel, pero para aquellos que han programado en C o en C++ no les debería resultar demasiado complicado. En ILAsm necesitamos hacer todas las cosas manualmente, como añadir un valor al stack, administrar la memoria, etc. Piensa en ILAsm de la misma manera que cualquier otro lenguaje ensamblador con la diferencia de que en otro lenguaje estarás tratando con ejecutables nativos para Windows y con este lenguaje (ILAsm) estarás tratando con ejecutables .NET y además, este lenguaje es un poco más fácil (que otros lenguajes ensamblador) y además está orientado a objetos.
Empecemos con este lenguaje con nuestro primer programa de ejemplo que escribirá una frase en pantalla (en la consola). Es una tradición que en el comienzo de cada lenguaje se use un “hola mundo” así que vamos a hacer lo mismo:
//Ejemplo.IL //Un simple programa que imprime una cadena de texto por consola .assembly extern mscorlib {} .assembly Ejemplo { .ver 1:0:1:0 } .module ejemplo.exe .method static void main() cil managed { .maxstack 1 .entrypoint ldstr "Hola mundo! Also, The Game !!!" call void [mscorlib]System.Console::WriteLine (string) ret }
Figura 1.1 – Un programa de ejemplo en ILAsm
Una vez escrito el codigo de arriba (o figura 1.1) en un simple editor de texto como notepad, guárdalo como “Ejemplo.il“. Ahora realizaremos nuestra primera compilación y ejecutaremos nuestro codigo. Una vez hecho esto entraremos en detalles y explicaré qué es lo que hace cada instrucción. Para compilar el código, escribe lo siguiente en una ventana de comandos (cmd):
Figura 1.2 – Salida del programa de ejemplo. Aqui puedes ver el comando usado para compilar el código
ILAsm.exe es una herramienta utilizada por linea de comando la cual está incluida con el Framework SDK de .NET y puede ser encontrada en la carpeta “<carpeta_windows>\Microsoft.NET\Framework\<version>“. Si quieres puedes incluir esta ruta a la variable de entorno Path de tu SO (por ello no he tenido que escribir la ruta completa en la figura 1.2 como podéis ver). Cuando has terminado de compilar tu archivo .IL, tendrás listo tu ejecutable (.exe) con el mismo nombre que tu archivo .IL. Puedes especificar el nombre de salida utilizando “/OutPut=<filename>” (sin comillas). Para ejecutar tu recien compilado programa, simplemente escribe el nombre de tu progama y presiona Enter. La salida del programa aparecerá ante ti. Bien, ahora nos tomaremos un tiempo para entender que es lo que hemos escrito en el código. Ten en mente que me estoy refiriendo al código de la figura 1.1
- Las dos primeras lineas (que empiezan por //) son comentarios. En ILAsm puedes escribir comentarios de la misma manera que en C# o C++. Para comentar múltiples líneas o un trozo de línea, puedes usar /* … */
- Lo siguiente que hemos hecho a sido indicar al compilador que importe la librería llamada mscorlib (.assembly extern mscorlib {}). En ILAsm, cada linea comenzada por un punto (.) indica que la línea es una instrucción especial o una directiva. Entonces la directiva .assembly que tenemos aquí dice que vamos a usar una librería externa (la cual no está escrita por nosotros en este código, está pre-compilada).
- La siguiente directiva .assembly define la información de nuestro archivo (.assembly Ejemplo ….). En nuestro caso, hemos incluido “Ejemplo” como el nombre del ensamblado y entre llaves hemos incluido informacion sobre el ensamblado de salida (nuestro .exe), en este caso hemos incluido la información de versión de nuestro programa. Podemos incluir más información sobre el ensamblado en este bloque, como la clave pública, etc.
- La siguiente directiva indica el nombre del módulo de nuestro ensamblado (.module Ejemplo.exe). Como ya sabemos debe haber al menos un módulo en cada ensamblado.
- Si seguimos hacia adelante, nos encontramos con (.method static void main() cil managed), la directiva .method indica que estamos definiendo un método, el cual es estático (usando la misma definición que en C#) y no retorna nada (void). Además, el nombre del método es ‘main’ y no toma ningún parámetro (ya que no hay nada dentro de los paréntesis). La instrucción final ‘cil managed‘ indica al compilador que el código que vamos a compilar estará gestionado por nosotros (nuestro código).
- Moviéndonos dentro del método, la primera directiva es .maxstack (.maxstack 1). Esta directiva es importante la cual anuncia el número máximo de objetos que podremos cargar en memoria (en el stack de evaluación en realidad). Discutiremos sobre esto extensamente en el próximo capítulo, ignóralo por el momento.
- La directiva .entrypoint le indica al compilador que este método será el punto de entrada para nuestra aplicación, es decir, la primera función que será llamada una vez el programa se ejecute (que suele ser el método main).
- La siguiente línea (ldstr “Hola mundo! Also, The Game !!!”) contiene la instrucción ldstr. La instrucción ldstr es usada para cargar una cadena en la memoria (o stack de evaluación). Es necesario escribir valores en el stack de evaluación antes de que puedan ser utilizados. Como he dicho antes, discutiremos sobre ello extensamente en el siguiente capítulo.
- La siguiente instrucción (call void [mscorlib]System.Console::WriteLine (string)) llama (invoca) a un método que reside en la librería mscorlib. Toma nota que hemos indicado la ruta completa de este método incluyendo el tipo de retorno, los tipos de parámetros y en qué librería reside. Con esto hemos pasado la cadena de texto como parámetro, la cual no es una variable, es un tipo de dato. La instrucción anterior (ldstr “Hola m….”) cargó la cadena al stack y este método la está usando para imprimirla.
- La instrucción final (ret), aunque no debería ser necesario explicarla, implica retornar (terminar) el método.
Leyendo las lineas de arriba, ahora deberías tener una idea de como escribir código en ILAsm, además, deberías darte cuenta de que ILAsm no es como un lenguaje .NET de alto nivel (como C#, VB, etc). De todas formas, todo el codigo que tu escribas, lo harás siguiendo una estructura de este estilo (o con pequeños cambios, cuando trabajemos con clases).
Para la siguiente parte del tutorial hablaremos sobre el stack de evaluación y los tipos de datos más comunes que nos encontraremos.
[ Introducción al lenguaje ensamblador IL de .NET [Parte 2] –> ]

Pingback: Introducción al lenguaje ensamblador IL de .NET [Parte 2] « El Blog de JuDelCo