Portable Executable

El formato Portable Executable (PE) es un formato de archivo para archivos ejecutables, de código objeto, bibliotecas de enlace dinámico (DLL), archivos de fuentes FON,[1] y otros usados en versiones de 32 bit y 64 bit del sistema operativo Microsoft Windows. El término «portable» refiere a la versatilidad del formato en numerosos ambientes de arquitecturas de software de sistema operativo.[2] El formato PE es una estructura de datos que encapsula la información necesaria para el cargador de Windows para administrar el código ejecutable que le envuelve. Esto incluye las referencias hacia las bibliotecas de enlace dinámico para el enlazado, la importación y exportación de las tablas de la API, gestión de los datos de los recursos y los datos de almacenamiento local de subprocesos (datos de TLS). En sistemas operativos basados en Windows NT, el formato PE es usado para EXE, DLL, SYS (controladores de dispositivo), y otros tipos de archivo. La especificación Extensible Firmware Interface (EFI) indica que PE es el formato estándar para ejecutables en entornos EFI.

Portable Executable
Desarrollador
Microsoft
Información general
Extensión de archivo .cpl, .exe, .dll, .ocx, .sys, .scr, .drv
Tipo de MIME application/vnd.microsoft.portable-executable, application/x-msdownload y application/efi
Uniform Type Identifier com.microsoft.windows-executable
Número mágico MZ
Tipo de formato Binario, ejecutable, código objeto, bibliotecas compartidas
Extendido de DOS MZ executable,
COFF
Estándar(es) Microsoft PE and COFF Specification (ver. 8)
Formato abierto ?

PE es una versión modificada del formato COFF de Unix. Además, PE/COFF es un nombre alternativo en el desarrollo de Windows.

En sistemas operativos Windows NT, actualmente PE soporta los conjuntos de instrucciones (ISA) de IA-32, IA-64, y x86-64 (AMD/Intel64). Previo a Windows 2000, Windows NT (y por tanto PE) soportaban los conjuntos de instrucciones MIPS, DEC Alpha y PowerPC. Ya que PE es usado en Windows CE, este continua soportando diversas variantes de las arquitecturas MIPS, ARM (incluyendo a Thumb), y SuperH.

Los principales competidores de PE son ELF (usado en Linux y muchos otros sistemas tipo Unix) y Mach-O (usado en Mac OS X).

Historia

Microsoft migró al formato PE con la introducción del sistema operativo Windows NT 3.1. Todas las versiones posteriores de Windows, incluyendo Windows 95, 98, y ME, soportan la estructura de archivo PE. El formato conservó un soporte limitado heredado para cerrar la brecha entre los sistemas basados en DOS y sistemas basados en NT. Por ejemplo, las cabeceras de PE/COFF todavía incluyen un programa ejecutable MS-DOS, que por defecto es un trozo que muestra el sencillo mensaje «This program cannot be run in DOS mode» (o similar).[2] También PE sigue atendiendo a la cambiante plataforma Windows. Entre las extensiones se encuentra el formato PE .NET (véase más abajo), una versión para binarios de 64 bits llamada PE32+ (o a veces PE+), y una especificación para Windows CE.

Detalles técnicos

Diseño

Un archivo PE consiste de una serie de cabeceras y secciones que indican al enlazador dinámico cómo asignar el archivo en memoria. Una imagen ejecutable consta de varias zonas diferentes, cada una de las cuales requieren diferente protección de memoria, por lo que el inicio de cada sección debe estar alineado a un límite de página. Por ejemplo, generalmente la sección .text (que contiene código de programa) es mapeado como «ejecutar/sólo lectura», y la sección .data (que contiene las variables globales) es mapeado como «no-ejecutar/lectura-escritura». Sin embargo, para evitar el desperdicio de espacio, las diferentes secciones no son alineadas en página en disco. Parte del trabajo del enlazador dinámico es mapear cada sección en memoria de manera individual y asignar los permisos correctos para las regiones resultantes, de acuerdo con las instrucciones que se encuentran en las cabeceras.

Tabla de secciones

La tabla de secciones se encuentra justo detrás de la cabecera PE. Esta es una tabla que contiene varias estructuras IMAGE_SECTION_HEADER. Estas estructuras contienen información sobre las secciones de los binarios para ser cargados en la memoria.

El campo NumberOfSections de la estructura IMAGE_FILE_HEADER indica cuantas entradas hay en la tabla. El máximo soportado por Windows es de 96 secciones.

Un prototipo de una tabla de secciones se ve de la siguiente manera:

typedef struct _IMAGE_SECTION_HEADER {
  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];
  union {
      DWORD PhysicalAddress;
      DWORD VirtualSize;
  } Misc;
  DWORD VirtualAddress;
  DWORD SizeOfRawData;
  DWORD PointerToRawData;
  DWORD PointerToRelocations;
  DWORD PointerToLinenumbers;
  WORD  NumberOfRelocations;
  WORD  NumberOfLinenumbers;
  DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

Cada sección tiene un nombre de 8 caracteres; este nombre no es significativo, pero normalmente se puede encontrar lo siguiente:

NombreDescripción
.text El código (instrucciones) del programa
.bss Las variables no inicializadas
.reloc La tabla de relocalizaciones
.data Las variables inicializadas
.rsrc Los recursos del archivo (cursores, sonidos, menús...)
.rdata Los datos de solo lectura
.idata La tabla de importación
.upx Firma de una compresión UPX, propio de software UPX
.aspack Firma de un paquete ASPACK, propio de software ASPACK
.adata Firma de un paquete ASPACK, propio de software ASPACK

Tabla IMPORT

Una sección a mencionar es la tabla de importación de direcciones (en inglés import address table o IAT), que se usa como una tabla de búsqueda cuando la aplicación llama una función en un módulo diferente. Esto puede darse en forma de importación por ordinal o importación por nombre. Debido a que un programa compilado no puede conocer la ubicación en memoria de las bibliotecas de las que depende directamente, cada vez que se hace una llamada a la API es necesario un salto indirecto. Como el enlazador dinámico carga los módulos y los une, este escribe la dirección actuales en las ranuras IAT, de manera que apuntan a las ubicaciones de memoria correspondientes de la biblioteca de funciones. Aunque esto agrega un salto adicional en el costo de una llamada intra-módulo que resulta en una reducción del rendimiento, ofrece una ventaja clave: se reduce al mínimo el número de páginas de memoria que necesitan ser cambiados copy-on-write por el cargador, ahorrando memoria y tiempo de I/O de disco. Si el compilador conoce de antemano que será una llamada inter-módulo (mediante el atributo dllimport) puede producir código más optimizado que simplemente se traduce en una llamada indirecta opcode.

Relocalizaciones

Los archivos PE no contienen código independiente de la posición. En su lugar son compilados a una dirección base preferida, y todas las direcciones emitidas por el compilador/enlazador se fijan previamente. Si un archivo PE no puede ser cargado en su dirección preferida (porque ya fue tomada por algo más), el sistema operativo lo reajustará. Esto implica recalcular cada dirección absoluta y modificar el código para utilizar los nuevos valores. El cargador lo realiza comparando las direcciones de carga actual y preferida, y calculando un valor delta. Esto se añade a continuación a la dirección preferida para llegar a la nueva dirección de la localización de memoria. Las relocalizaciones base son almacenadas en una lista y añadidas, cuando sea necesario, a una posición de memoria existente. El código resultante ahora es privado al proceso y no es más compartido, por lo que muchos de los beneficios de ahorro de memoria de las DLLs se pierden en este escenario. También retrasa la carga del módulo de manera significativa. Por esta razón, el reajuste es evitado siempre que sea posible, y las DLL proporcionadas por Microsoft tienen direcciones base pre-calculadas de manera que no se superpongan. Por lo tanto, en el caso de no reajuste, PE ofrece la ventaja de un código muy eficiente, pero en presencia de reajuste, el éxito en el uso de memoria puede ser costoso. Esto contrasta con ELF, que utiliza código totalmente independiente de la posición y una tabla de desplazamiento global, que abandona tiempo en ejecución contra el uso de memoria en favor de estos últimos.

.NET, metadatos y el formato PE

.NET Framework de Microsoft ha ampliado el formato PE con características que soportan el Common Language Runtime (CLR). Entre las características añadidas se encuentra las secciones de cabecera CLR y datos CLR. Al cargar un binario, el cargador del sistema operativo cede la ejecución a CLR mediante una referencia en la tabla IMPORT de PE/COFF. Entonces CLR carga las secciones CLR cabecera y datos.

La sección de datos de CLR contiene dos segmentos importantes, código de metadatos y código de Intermediate Language (IL):

  • Los metadatos contienen información relevante para el ensamblado, incluyendo el manifiesto de ensamblado. Un manifiesto describe el ensamblado en detalle, incluyendo la identificación única (mediante un hash, número de versión, etc.), datos en componentes exportados, información de tipo extensivo (soportado por el Common Type System (CTS)), referencias externas, y una lista de archivos dentro del ensamblado. El entorno CLR hace un uso amplio de los metadatos.[3]
  • El código IL es un código abstracto e independiente del lenguaje que satisface los requisitos del Common Intermediate Languge (CIL) del CLR. El término «intermediate» (en español «intermedio») se refiere a la naturaleza compatible entre lenguajes y entre plataformas del código IL. Este lenguaje intermedio, similar a Java bytecode, permite a las plataformas y a los lenguajes soportar el CLR común. IL soporta la programación orientada a objetos (polimorfismo, herencia, tipos abstractos, etc.), excepciones, eventos, y varias estructuras de datos.

Uso en otros sistemas operativos

El formato PE también es usado por ReactOS, ya que este sistema operativo tiene la intención de ser compatible a nivel binario con Windows. El formato PE también ha sido utilizado históricamente por unos cuantos sistemas operativos, entre ellos SkyOS y BeOS R3. Sin embargo tanto SkyOS como BeOS se trasladaron al formato ELF.

Como la plataforma de desarrollo Mono está diseñado para ser compatible a nivel binario con Microsoft .NET, usa el mismo formato PE de la implementación de Microsoft.

En sistemas operativos similares a Unix de 32 bits, algunos binarios de Windows (en formato PE) pueden ser ejecutados con Wine. HX DOS Extender también usa el formato PE para los binarios nativos de 32 bit de DOS, además de que se puede, hasta cierto grado, ejecutar binarios de Windows existentes en DOS, así actuando como si fuera un Wine para DOS.

Mac OS X 10.5 tiene la capacidad de cargar y analizar archivos PE, pero no es compatible a nivel binario con Windows.[4]

Referencias

  1. Tatham, Simon. «mkwinfont» (en inglés). Consultado el 31 de octubre de 2012.
  2. Microsoft, ed. (1º de noviembre de 2006). «Formato Common Object File (Format COFF)». Microsoft Support. Consultado el 5 de abril de 2013.
  3. Microsoft (ed.). «Metadatos y la estructura del archivo PE». MSDN Library. Consultado el 5 de abril de 2013.
  4. Chartier, David (30 de noviembre de 2007). «Uncovered: Evidence that Mac OS X could run Windows apps soon». Ars Technica. Consultado el 3 de diciembre de 2007. «... Steven Edwards describes the discovery that Leopard apparently contains an undocumented loader for Portable Executables, a type of file used in 32-bit and 64-bit versions of Windows. More poking around revealed that Leopard's own loader tries to find Windows DLL files when attempting to load a Windows binary. »

Véase también

Enlaces externos

Este artículo ha sido escrito por Wikipedia. El texto está disponible bajo la licencia Creative Commons - Atribución - CompartirIgual. Pueden aplicarse cláusulas adicionales a los archivos multimedia.