¿Cuál es la diferencia entre código nativo, código de máquina y código de ensamblaje?

Estoy confundido sobre el código de máquina y el código nativo en el contexto de los lenguajes .NET.

¿Cuál es la diferencia entre ellos? ¿Son lo mismo?

Los términos son de hecho un poco confusos, porque a veces se usan de manera inconsistente.

Código de máquina: este es el más definido. Es un código que usa las instrucciones de código de bytes que su procesador (la pieza de metal física que hace el trabajo real) entiende y ejecuta directamente. El rest del código debe traducirse o transformarse en código de máquina antes de que su máquina pueda ejecutarlo.

Código nativo: este término se usa a veces en lugares donde se entiende el código de máquina (ver arriba). Sin embargo, también se usa a veces para indicar el código no administrado (ver a continuación).

Código no administrado y código administrado: el código no administrado se refiere al código escrito en un lenguaje de progtwigción como C o C ++, que se comstack directamente en el código de la máquina . Contrasta con el código administrado , que está escrito en C #, VB.NET, Java o similar, y se ejecuta en un entorno virtual (como .NET o JavaVM) que “simula” un procesador en el software. La principal diferencia es que el código administrado “administra” los recursos (principalmente la asignación de memoria) para usted mediante el empleo de recolección de basura y manteniendo las referencias a los objetos opacas. El código no administrado es el tipo de código que requiere que asignes y desasignes manualmente la memoria, lo que a veces provoca memory leaks (cuando te olvidas de desasignar) y, a veces, fallas de segmentación (cuando te desasignas demasiado pronto). Sin gestionar también suele implicar que no hay comprobaciones en tiempo de ejecución para errores comunes como desreferenciación de puntero nulo o desbordamiento de límites de matriz.

Estrictamente hablando, la mayoría de los lenguajes de tipo dynamic, como Perl, Python, PHP y Ruby, también son código administrado . Sin embargo, no se describen comúnmente como tales, lo que demuestra que el código administrado es en realidad un término bastante comercial para los entornos de progtwigción comercial realmente grandes, serios (.NET y Java).

Código de ensamblado: este término generalmente se refiere al tipo de código fuente que las personas escriben cuando realmente desean escribir un código de bytes. Un ensamblador es un progtwig que convierte este código fuente en un código de bytes real. No es un comstackdor porque la transformación es de 1 a 1. Sin embargo, el término es ambiguo en cuanto a qué tipo de código de bytes se usa: podría ser administrado o no. Si no está administrado, el código de bytes resultante es código de máquina . Si se administra, resulta en el código de bytes utilizado detrás de las escenas por un entorno virtual como .NET. El código administrado (por ejemplo, C #, Java) se comstack en este lenguaje especial de código de bytes, que en el caso de .NET se denomina Common Intermediate Language (CIL) y en Java se denomina código de bytes Java . Generalmente, el progtwigdor común tiene poca necesidad de acceder a este código o de escribir en este idioma directamente, pero cuando las personas lo hacen, a menudo se refieren a él como código de ensamblaje porque usan un ensamblador para convertirlo en código de bytes.

Lo que ve cuando usa Debug + Windows + Desassembly al depurar un progtwig C # es una buena guía para estos términos. Aquí hay una versión anotada cuando compilo un progtwig ‘hello world’ escrito en C # en la configuración Release con la optimización JIT habilitada:

static void Main(string[] args) { Console.WriteLine("Hello world"); 00000000 55 push ebp ; save stack frame pointer 00000001 8B EC mov ebp,esp ; setup current frame 00000003 E8 30 BE 03 6F call 6F03BE38 ; Console.Out property getter 00000008 8B C8 mov ecx,eax ; setup "this" 0000000a 8B 15 88 20 BD 02 mov edx,dword ptr ds:[02BD2088h] ; arg = "Hello world" 00000010 8B 01 mov eax,dword ptr [ecx] ; TextWriter reference 00000012 FF 90 D8 00 00 00 call dword ptr [eax+000000D8h] ; TextWriter.WriteLine() 00000018 5D pop ebp ; restre stack frame pointer } 00000019 C3 ret ; done, return 

Haga clic con el botón derecho en la ventana y marque “Mostrar bytes de código” para obtener una visualización similar.

La columna de la izquierda es la dirección del código de máquina. Su valor es falsificado por el depurador, el código está ubicado en otro lugar. Pero eso podría estar en cualquier lugar, dependiendo de la ubicación seleccionada por el comstackdor JIT, por lo que el depurador simplemente comienza a numerar las direcciones desde 0 al comienzo del método.

La segunda columna es el código de máquina . Los 1s y 0s reales que ejecuta la CPU. El código de máquina, como aquí, se muestra comúnmente en hexadecimal. Quizás lo más ilustrativo sea que 0x8B selecciona la instrucción MOV, los bytes adicionales están ahí para decirle a la CPU exactamente lo que se debe mover. También tenga en cuenta los dos sabores de la instrucción CALL, 0xE8 es la llamada directa, 0xFF es la instrucción de llamada indirecta.

La tercera columna es el código de ensamblaje . Assembly es un lenguaje simple, diseñado para facilitar la escritura de código de máquina. Se compara con C # comstackdo a IL. El comstackdor utilizado para traducir el código ensamblador se denomina “ensamblador”. Probablemente tenga el ensamblador de Microsoft en su máquina, su nombre ejecutable es ml.exe, ml64.exe para la versión de 64 bits. Hay dos versiones comunes de lenguajes de ensamblaje en uso. El que ves es el que usan Intel y AMD. En el mundo de fuente abierta, el ensamblado en la notación de AT & T es común. La syntax del lenguaje depende en gran medida del tipo de CPU para el que se escribió, el lenguaje ensamblador para un PowerPC es muy diferente.

De acuerdo, eso aborda dos de los términos en su pregunta. El “código nativo” es un término difuso, no se usa poco para describir el código en un lenguaje no administrado. Quizás sea instructivo ver qué tipo de código máquina es generado por un comstackdor de C. Esta es la versión de ‘hello world’ en C:

 int _tmain(int argc, _TCHAR* argv[]) { 00401010 55 push ebp 00401011 8B EC mov ebp,esp printf("Hello world"); 00401013 68 6C 6C 45 00 push offset ___xt_z+128h (456C6Ch) 00401018 E8 13 00 00 00 call printf (401030h) 0040101D 83 C4 04 add esp,4 return 0; 00401020 33 C0 xor eax,eax } 00401022 5D pop ebp 00401023 C3 ret 

No lo anoté, sobre todo porque es muy similar al código máquina generado por el progtwig C #. La llamada a la función printf () es bastante diferente de la llamada Console.WriteLine () pero todo lo demás es más o menos lo mismo. También tenga en cuenta que el depurador ahora está generando la dirección del código de máquina real y que es un poco más inteligente con respecto a los símbolos. Un efecto secundario de generar información de depuración después de generar código de máquina como comstackdores no administrados a menudo sí. También debo mencionar que desactivé algunas opciones de optimización de código de máquina para hacer que el código de la máquina se vea similar. Los comstackdores C / C ++ tienen mucho más tiempo disponible para optimizar el código, el resultado a menudo es difícil de interpretar. Y muy difícil de depurar.

El punto clave aquí es que hay muy pocas diferencias entre el código máquina generado a partir de un lenguaje administrado por el comstackdor JIT y el código máquina generado por un comstackdor de código nativo. Cuál es la razón principal por la cual el lenguaje C # puede ser competitivo con un comstackdor de código nativo. La única diferencia real entre ellos son las llamadas de función de soporte. Muchos de los cuales se implementan en el CLR. Y eso gira alrededor del recolector de basura.

El código nativo y el código máquina son lo mismo: los bytes reales que ejecuta la CPU.

El código de ensamblado tiene dos significados: uno es el código máquina traducido a una forma más legible para el ser humano (con los bytes para las instrucciones traducidas en mnemotécnicos cortos como “JMP” (que “salta” a otro lugar en el código). es el bytecode IL (bytes de instrucciones que generan los comstackdores como C # o VB, que terminarán traducidos al código de la máquina con el tiempo, pero aún no) que vive en un archivo DLL o EXE.

En .NET, los ensamblados contienen el código de MS Intermediate Language (MSIL, a veces CIL).
Es como un código de máquina de “alto nivel”.

Cuando se carga, MSIL se comstack mediante el comstackdor JIT en código nativo (código de máquina Intel x86 o x64).