format - son - que significa.exe en un juego
¿Cómo se almacena el código en el formato EXE? (2)
El formato de archivo PE (y los formatos de archivo ELF / COFF en máquinas que no son Windows) define un encabezado que aparece al principio del archivo, y en este encabezado, hay un código ''Máquina''. En un archivo PE, el código ''Máquina'' es de 2 bytes, y la especificación define un conjunto de constantes para varias máquinas:
0x1d3 Matsushita AM33
0x8664 AMD x64
0x1c0 ARM little endian
0x1c4 ARMv7 (or higher) Thumb mode only
0xebc EFI byte code
0x14c Intel 386 or later processors and compatible processors
0x200 Intel Itanium processor family
0x9041 Mitsubishi M32R little endian
0x266 MIPS16
0x366 MIPS with FPU
0x466 MIPS16 with FPU
0x1f0 Power PC little endian
0x1f1 Power PC with floating point support
0x166 MIPS little endian
0x1a2 Hitachi SH3
0x1a3 Hitachi SH3 DSP
0x1a6 Hitachi SH4
0x1a8 Hitachi SH5
0x1c2 ARM or Thumb (“interworking”)
0x169 MIPS little endian WCE v2
Luego, dentro del archivo PE (o ELF) hay una o más secciones ''Código'' que contienen código máquina (binario). Ese código se carga en la memoria y la CPU lo ejecuta directamente. El OS o el enlazador / cargador dinámico (que realiza la carga real) sabe en qué máquina se está ejecutando, por lo que verifica el código ''Máquina'' en el encabezado para asegurarse de que coincida antes de intentar cargar y ejecutar el código. Si no coincide, el ejecutable será rechazado, ya que no se puede ejecutar.
Mis preguntas son las siguientes:
- ¿Cómo se relaciona el formato Portable Executable (en Windows / Unix) con el conjunto de instrucciones x86 / x64 en general?
- ¿El formato PE almacena el conjunto exacto de códigos de operación admitidos por el procesador, o es un formato más genérico que el sistema operativo convierte para que coincida con la CPU?
- ¿Cómo indica el archivo EXE las extensiones de conjunto de instrucciones necesarias (como 3DNOW! O SSE / MMX?)
- ¿Los códigos de operación son comunes en todas las plataformas como Windows, Mac y Unix?
- Los chips de CPU Intel i386 compatibles, como los de Intel y AMD, usan un conjunto de instrucciones comunes. Pero estoy seguro de que las CPU con ARM usan códigos de operación diferentes. ¿Son estos muy diferentes o los conceptos son similares? registros, int / float / double, SIMD, etc.?
En plataformas más nuevas como .NET, Java o Flash, los conjuntos de instrucciones son códigos de operación basados en la pila que un JIT convierte al formato nativo en el tiempo de ejecución. Como estoy acostumbrado a este formato, me gustaría saber cómo se ejecuta y formatea el "antiguo" formato EXE nativo. Por ejemplo, los "registros" generalmente no están disponibles en los códigos de operación de la plataforma más nueva, ya que el JIT convierte los comandos de pila a los 16/32 registros de CPU disponibles según lo considere necesario. Pero en los formatos nativos, debe consultar los registros por índice y determinar qué registros se pueden reutilizar y con qué frecuencia.
¿Los códigos de ARM son muy diferentes de los códigos de operación x86?
Sí lo son. Debe suponer que todos los conjuntos de instrucciones para diferentes familias de procesadores son completamente diferentes e incompatibles. Un conjunto de instrucciones define primero una codificación, que especifica uno o más de estos:
- el código de operación de instrucción;
- el modo de direccionamiento;
- el tamaño del operando;
- el tamaño de la dirección;
- los operandos mismos.
La codificación además depende de cuántos registros pueda tratar, si tiene que ser compatible con versiones anteriores, si debe ser decodificable rápidamente y qué tan compleja puede ser la instrucción.
Sobre la complejidad: el conjunto de instrucciones ARM requiere que todos los operandos se carguen desde la memoria para registrarlos y almacenarlos de registro a memoria usando instrucciones especializadas load / store, mientras que las instrucciones x86 pueden codificar una única dirección de memoria como uno de sus operandos y por lo tanto no tienen instrucciones separadas de carga / tienda.
Luego se establece la instrucción: los diferentes procesadores tendrán instrucciones especializadas para manejar situaciones específicas. Incluso si dos familias de procesadores tienen la misma instrucción para la misma cosa (por ejemplo, una instrucción add
), están codificadas de manera muy diferente y pueden tener una semántica ligeramente diferente.
Como puede ver, dado que cualquier diseñador de CPU puede decidir sobre todos estos factores, esto hace que las arquitecturas de conjuntos de instrucciones para diferentes familias de procesadores sean completamente diferentes e incompatibles.
¿Los registros, int / float / double y SIMD son conceptos muy diferentes en diferentes arquitecturas?
No, son muy similares. Toda arquitectura moderna se ha registrado y puede manejar enteros, y la mayoría puede manejar instrucciones de punto flotante compatibles con IEEE 754 de algún tamaño. Por ejemplo, la arquitectura x86 tiene valores de punto flotante de 80 bits que se truncan para ajustarse a los valores de punto flotante de 32 o 64 bits que conoce. La idea detrás de las instrucciones SIMD también es la misma en todas las arquitecturas que la admiten, pero muchas no la admiten y la mayoría tiene requisitos o restricciones diferentes para ellas.
¿Los códigos de operación son comunes en todas las plataformas como Windows, Mac y Unix?
Dados tres sistemas Intel x86, uno con Windows, uno con Mac OS X y uno con Unix / Linux, entonces sí, los códigos de operación son exactamente los mismos ya que se ejecutan en el mismo procesador. Sin embargo, cada sistema operativo es diferente. Muchos aspectos como la asignación de memoria, los gráficos, la interfaz del controlador del dispositivo y el enhebrado requieren un código específico del sistema operativo. Por lo tanto, generalmente no puede ejecutar un ejecutable compilado para Windows en Linux.
¿El formato PE almacena el conjunto exacto de códigos de operación admitidos por el procesador, o es un formato más genérico que el sistema operativo convierte para que coincida con la CPU?
No, el formato PE no almacena el conjunto de códigos de operación. Como se explicó anteriormente, las arquitecturas del conjunto de instrucciones de diferentes familias de procesadores son simplemente demasiado diferentes para que esto sea posible. Un archivo PE generalmente almacena código de máquina para una familia específica de procesadores y una familia de sistemas operativos, y solo se ejecutará en dichos procesadores y sistemas operativos.
Sin embargo, hay una excepción: los ensamblados .NET también son archivos PE pero contienen instrucciones genéricas que no son específicas de ningún procesador o sistema operativo. Dichos archivos PE pueden ''ejecutarse'' en otros sistemas, pero no directamente. Por ejemplo, mono en Linux puede ejecutar tales ensamblajes .NET.
¿Cómo indica el archivo EXE las extensiones de conjunto de instrucciones necesarias (como 3DNOW! O SSE / MMX?)
Si bien el ejecutable puede indicar el conjunto de instrucciones para el que fue construido ( ver la respuesta de Chris Dodd ), no creo que el ejecutable pueda indicar las extensiones que se requieren. Sin embargo, el código ejecutable, cuando se ejecuta, puede detectar tales extensiones. Por ejemplo, el conjunto de instrucciones x86 tiene una instrucción CPUID
que devuelve todas las extensiones y características compatibles con esa CPU en particular. El ejecutable simplemente probaría eso y abortaría cuando el procesador no cumpla con los requisitos.
.NET versus código nativo
Parece que sabe una o dos cosas sobre los ensamblados .NET y su conjunto de instrucciones, llamado CIL (Common Intermediate Language). Cada instrucción CIL sigue una codificación específica y usa la pila de evaluación para sus operandos. El conjunto de instrucciones CIL se mantiene muy general y de alto nivel. Cuando se ejecuta (en Windows por mscoree.dll
, en Linux por mono
) y se mscoree.dll
un método, el compilador Just-In-Time (JIT) toma las instrucciones CIL del método y las compila en código máquina. Según el sistema operativo y la familia de procesadores, el compilador debe decidir qué instrucciones de máquina usar y cómo codificarlas. El resultado compilado se almacena en algún lugar de la memoria. La próxima vez que se llame al método, el código salta directamente al código de la máquina compilada y puede ejecutarse con la misma eficacia que un ejecutable nativo.
¿Cómo se codifican las instrucciones ARM?
Nunca he trabajado con ARM, pero de un vistazo rápido a la documentación puedo decirle lo siguiente. Una instrucción ARM es siempre de 32 bits de longitud. Hay muchas codificaciones excepcionales (por ejemplo, para instrucciones de ramificación y coprocesador), pero el formato general de una instrucción ARM es así:
31 28 27 26 25 21 20 16 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-- | Condition | 0 | 0 |R/I| Opcode | S | Operand 1 | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-- 12 0 --+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... | Destination | Operand 2 | --+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Los campos significan lo siguiente:
- Condición : Una condición que, cuando es verdadera, hace que se ejecute la instrucción. Esto mira las banderas Cero, Llevar, Negativo y Desbordamiento. Cuando se establece en 1110, la instrucción siempre se ejecuta.
- R / I : cuando 0, el operando 2 es un registro. Cuando 1, el operando 2 es un valor constante.
- Opcode : el código de operación de la instrucción.
- S : Cuando 1, las banderas Cero, Llevar, Negativo y Desbordamiento se configuran de acuerdo con el resultado de la instrucción.
- Operand1 : El índice de un registro que se utiliza como primer operando.
- Destino : el índice de un registro que se usa como el operando de destino.
- Operando 2 : El segundo operando. Cuando R / I es 0, el índice de un registro. Cuando R / I es 1, un valor constante de 8 bits sin signo. Además de cualquiera de estos, algunos bits en el operando 2 indican si el valor se desplaza / gira.
Para obtener información más detallada, debe leer la documentación de la versión de ARM específica que desea conocer. Usé esta Hoja de Datos ARM7TDMI-S, Capítulo 4 para este ejemplo.
Tenga en cuenta que cada instrucción ARM, sin importar cuán simple, tome 4 bytes para codificar. Debido a la posible sobrecarga, los procesadores ARM modernos le permiten utilizar un conjunto de instrucciones alternativas de 16 bits llamado Thumb . No puede expresar todas las cosas que puede hacer el conjunto de instrucciones de 32 bits, pero también es la mitad de grande.
Por otro lado, las instrucciones x86-64 tienen una codificación de longitud variable y usan todo tipo de modificadores para ajustar el comportamiento de las instrucciones individuales. Si desea comparar las instrucciones de ARM con cómo se codifican las instrucciones x86 y x86-64, debe leer el artículo de codificación de instrucciones x86-64 que escribí en OSDev.org.
Tu pregunta original es muy amplia. Si desea saber más, debe investigar y crear una nueva pregunta con lo específico que desea saber.