1.4.4a Fichero de definición
Nota: Este capítulo trata de características no estándar del lenguaje C++. No obstante, aunque se trata de peculiaridades de los entornos de programación C++ de Borland y Microsoft para Win32, otros entornos, por ejemplo GNU/Linux utilizan opciones equivalentes.
§1 Sinopsis
Un fichero de definición de módulo ("Module definition file") o simplemente fichero de definición, es un fichero de texto ASCII que proporciona al enlazador información relativa al contenido y requerimientos de sistema de una aplicación. Generalmente esta información se refiere a la utilización de librerías de enlazado dinámico (DLLs), e indican qué funciones de la librería son exportables y cuales importables. Abreviadamente nos referiremos a ellos como fichero DEF o simplemente .DEF, en alusión a su terminación.
Estos ficheros pueden ser construidos a mano con cualquier editor de texto ASCII, pero los entornos de desarrollo suelen disponer de utilidades que permiten crearlos cómodamente. Por ejemplo, BC++ dispone de una utilidad específica para crearlos; el ejecutable IMPDEF ( 1.4.4b2c). Además permite utilizar los ficheros DEF en el makefile ( 1.4.0a) del proyecto. Por su parte, las binutils de GNU disponen de la utilidad dlltool, que permite construir ficheros de definición con la opción -z.
Nota: en realidad, Impdef.exe obtiene un fichero .DEF que solo incluye la siguiente información (extraída de la sección de cabecera del ejecutable analizado): NAME; DESCRIPTION e IMPORTS. Por su parte, la opción -z de dlltool construye el fichero de definición explorando los ficheros objeto que se le pasan en la línea de comando, e incluyendo entradas en el fichero .def para aquellas funciones que han sido declaradas exportables.
§2 Elementos de un fichero DEF
El fichero de definición de una aplicación (.EXE o .DLL) contiene información que debe ser conocida por el enlazador para construirla [2]. Esta información contiene entre otros, los siguientes datos: tipo de aplicación; lista de funciones importables y exportables; atributos de los segmentos de código y de datos; tamaño de la pila; del montón, y datos complementarios que se describen a continuación.
La información del fichero está clasificada en secciones que son las siguientes [1]: CODE ; DATA ; DESCRIPTION ; EXETYPE ; EXPORTS ; HEAPSIZE ; IMPORTS ; LIBRARY ; NAME ; SECTIONS ; SEGMENTS ; STACKSIZE ; STUB SUBSYSTEM .
Sección | Comentario | ||||||||||||||||
CODE | Esta variable
define los atributos por defecto de los segmentos
de código (
1.3.2). Los segmentos de código
pueden tener cualquier nombre, pero deben terminar en CODE, por
ejemplo, CODE o MICODE.
Sintaxis: CODE [PRELOAD | LOADONCALL] [EXECUTEONLY | EXECUTEREAD] Ejemplo: CODE PRELOAD MOVEABLE DISCARDABLE
|
||||||||||||||||
DATA |
Esta sección define ciertos atributos del segmento
de datos (
1.3.2).
Sintaxis: DATA [NONE | SINGLE | MULTIPLE] [READONLY | READWRITE] [PRELOAD | LOADONCALL] [SHARED | NONSHARED] Ejemplo: DATA PRELOAD MOVEABLE MULTIPLE ; para ejecutable estándar DATA PRELOAD MOVEABLE SINGLE ; para DLLs
|
||||||||||||||||
DESCRIPTION |
Permite insertar un texto arbitrario en el fichero de la
aplicación. Generalmente se utiliza para datos de autor, fecha,
e información de derechos de autor (copyright).
Sintaxis: DESCRIPTION 'Texto' El texto debe ser una cadena ASCII delimitada por simples comillas. Esta sección es opcional. |
||||||||||||||||
EXETYPE |
En los antiguos ejecutables esta sección indicaba el tipo de
ejecutable, y puede ser utilizada para compatibilidad con aplicaciones
existentes, pero si se quiere cambiar el tipo de aplicación, debe
utilizarse la sección NAME .
Sintaxis: EXETYPE [WINDOWAPI] | [WINDOWCOMPAT]
|
||||||||||||||||
EXPORTS |
Esta sección define el nombre y atributos de las funciones que serán
exportables (
1.4.4b2a).
La palabra EXPORTS señala el comienzo de la lista de tales funciones,
pudiendo ser seguida de una serie de nombres, cada uno en una línea.
Nota: Esta sección puede ser suprimida si se utilizan en el código fuente los especificadores __declspec(dllexport) o _export (preferiblemente el primero), en la definición de las funciones. Observe sin embargo, que si se utiliza esta fórmula, la función es exportada por nombre en vez de por número de orden. Sintaxis: EXPORTS ExportName [Ordinal [NONAME]] [RESIDENTNAME] [Parameter]
|
||||||||||||||||
HEAPSIZE |
HEAPSIZE define el tamaño en
bytes que necesita la aplicación para el montón
("Local heap"
1.3.2). Ejemplo: HEAPSIZE 4096
El tamaño reservado se refiere al máximo de memoria de cualquier tipo que será autorizada para el montón. Por supuesto, es el Sistema Operativo el encargado de reservar y poner a disposición del ejecutable la memoria necesaria. El significado de la memoria comprometida ("committed") varia según el Sistema. En Windows NT se refiere al total de memoria física asignada al montón en el momento de inicio de la aplicación. Esta memoria puede estar situada en memoria física (RAM) y/o en memoria paginada. Cuanto mayor sea esta tamaño inicial del montón, más tiempo se ahorra cada vez que la aplicación necesite reservar más memoria (hasta el límite del total reservado), pero desde luego incrementa las necesidades mínimas de la aplicación y el tiempo de inicialización ("Startup time"). El tamaño consignado en el fichero de definición puede ser sobrecontrolado por los comandos /H o /Hc en la invocación del enlazador. Además, /H permite especificar un tamaño reservado incluso menor que el mínimo de 64 K admitido en el fichero DEF. |
||||||||||||||||
IMPORTS |
Esta sección declara los nombres y atributos de las funciones que
serán importadas desde DLLs (
1.4.4b2b).
La palabra IMPORTS señala el comienzo de la lista de las funciones a
importar (ver sintaxis ),
cada una en una línea.
Nota: Una posible alternativa a utilizar esta sección para relacionar las funciones a importar desde DLLs, sería sustituirla por una librería de importación ( 1.4.4b2c) para las DLLs. Esta librería sería especificada directamente en el comando de invocación del enlazador ILINK32. Para que una aplicación pueda utilizar recursos (clases, funciones o datos) situados en librerías dinámicas, deben ser declarados previamente como "importables", lo que puede hacerse mediante los declaradores __declspec(dllimport) o __import, aunque este último es a extinguir y se recomienda el primero. Sintaxis: IMPORTS [NombreInterno=]NombreModulo.Entrada |
||||||||||||||||
LIBRARY |
LIBRARY declara el nombre del ejecutable cuando se trata de una
librería dinámica. Un fichero de definición puede contener
una sección LIBRARY para indicar una DLL o una sección NAME
para indicar que es un .EXE (pero no ambas secciones).
Si el fichero de definición no incluye sección LIBRARY ni NAME, el enlazador asume una sección NAME sin ningún parámetro asociado. El nombre consignado en esta sección debe coincidir con el del ejecutable. Por ejemplo, la librería Mylib.DLL debe tener MYLIB en esta sección. Sintaxis: LIBRARY LibraryName [INITGLOBAL | INITINSTANCE]
|
||||||||||||||||
NAME |
NAME contiene el nombre del ejecutable que constituye la aplicación,
e identifica al ejecutable cuando se exportan funciones.
Esta sección debe preceder a EXETYPE
en caso que NAME y EXETYPE no especifiquen el mismo tipo, ya que el
enlazador utiliza el tipo indicado en esta sección.
Sintaxis: NAME NombreModulo [WINDOWSAPI] | [WINDOWCOMPAT]
Recuerde que según lo indicado para la sección LIBRARY , si el fichero de definición no incluye sección LIBRARY ni NAME, el enlazador asume una sección NAME sin ningún parámetro asociado, y que ambas secciones no pueden coexistir juntas. Se utiliza NAME para ejecutables normales y LIBRARY para librerías dinámicas. |
||||||||||||||||
SECTIONS |
Esta sección permite establecer atributos para una o varias secciones
del fichero imagen. Puede utilizarse para sobreescribir los
atributos por defecto para cada tipo de sección.
La palabra SECTIONS marca el comienzo de una lista con las definiciones de diversas secciones, cada una de las cuales debe ser incluida en una línea independiente, aunque la primera definición puede estar en la misma linea que SECTIONS. Un mismo fichero de definición puede tener varias de estas SECTIONS. Sintaxis: SECTIONS <nombre_de_seccion> [CLASS 'nombreclase'] atributos nombre_de_seccion distingue mayúsculas de minúsculas. La palabrfa CLASS se acepta por cuestiones de compatibilidad, pero es ignorada. atributos puede ser una o más de las palabras siguientes: EXECUTE, READ, SHARED y WRITE. Nota: SEGMENTS puede ser tomada como sinónimo de SECTIONS |
||||||||||||||||
SEGMENTS |
Esta sección define los atributos de segmentos adicionales de código
y datos (
1.3.2).
Sintaxis: SEGMENTS SegmentName [CLASS
'ClassName']
|
||||||||||||||||
STACKSIZE |
Esta sección define el tamaño en bytes que se necesita en la pila de
la aplicación (
1.3.2). Es
similar a HEAPSIZE ,
aunque en este caso se refiere a la pila en vez del montón.
Sintaxis: STACKSIZE Reservado[, Consignado] Ejemplo: STACKSIZE 1048576
El tamaño reservado se refiere al máximo de memoria de cualquier tipo que será autorizada para la pila. Por supuesto, es el Sistema Operativo el encargado de reservar y poner a disposición del ejecutable la memoria necesaria. El significado de la memoria comprometida ("committed") varia según el Sistema. En Windows NT se refiere al total de memoria física asignada a la pila en el momento de inicio de la aplicación. La memoria comprometida puede estar situado en memoria física (RAM) y/o en memoria paginada. Cuanto mayor sea este valor, menos tiempo se necesita cuando la aplicación necesita más espacio en la pila (hasta el límite del total reservado), pero incrementa las necesidades mínimas de la aplicación y el momento de la inicialización. El tamaño consignado en el fichero de definición puede ser sobrecontrolado por los comandos /S o /Sc en la invocación del enlazador. Además, /S permite especificar un tamaño reservado incluso menor que el mínimo de 64 K admitido en el fichero DEF, pero tenga en cuenta que 8192 bytes es el mínimo recomendado, y que debe elegir un valor elevado si su programa utiliza funciones recursivas y/o variables automáticas grandes. Nota: Esta sección no debe ser utilizada en la compilación librerías dinámicas (DLLs). |
||||||||||||||||
STUB |
Esta sección puede añadir un pequeño fichero ejecutable al
principio del módulo. Este ejecutable es encargado de mostrar
un mensaje de advertencia si el usuario intenta ejecutar la
aplicación en un entorno incorrecto. Por ejemplo, ejecutar una
aplicación Windows en un entorno DOS.
Nota: Borland C++ dispone de un pequeño "Stub" que es colocado por defecto al principio de cada aplicación Windows si en esta sección no se indica otra cosa. No debe utilizarse esta sentencia para añadir el ejecutable WINSTUB.EXE, porque el enlazador lo hace automáticamente. Sintaxis: STUB 'NombreFichero'
|
||||||||||||||||
SUBSYSTEM |
Esta sección permite especificar el sistema Windows y número de
versión para el que se ha construido la aplicación.
Sintaxis: SUBSYSTEM [subsistema,]subsystID
El valor indicado en esta sección del fichero DEF, puede sobrescribirse utilizando las opciones /a y /V en la invocación del enlazador. |
§2.1 Cuando no se especifica
ningún fichero de definición, el enlazador supone los siguientes valores
por defecto:
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE
MULTIPLE; (para ejecutables estándar)
PRELOAD MOVEABLE SINGLE;
(para librerías dinámicas, DLLs)
HEAPSIZE 4096
STACKSIZE 1048576
Nota: La sintaxis utilizada en este fichero permite que lo que siga a un punto y coma (;) sea considerado un comentario.
Para cambiar estos valores es necesario utilizar un fichero .DEF explícito.
§3 Ejemplo de fichero DEF (
1.4.4a1).
[1] Mantenemos el original inglés para los nombres de las secciones, dado que son los que aparecen en el fichero.
[2] Toda esta información formará parte de la sección de cabecera del ejecutable. El denominado nuevo formato de fichero ejecutable NEF ("New Executable file format"). También denominado formato PE ("Portable Executable") porque se supone que es portable a todos los Sistemas Microsoft de 32 bits. Se trata de una especificación de MS para las aplicaciones que deban correr bajo sus Sistemas Windows, que establece instrucciones precisas de como debe encontrarse esta información www.windowsitlibrary.com. En el caso de Linux la especificación es conocida como ELF ("Executable and Linking Format") www.cs.ucdavis.edu. En ambos casos, durante la fase inicial de la ejecución, un programa especial se encarga de leer esta porción de la cabecera, y en base a la información encontrada, determina que librerías se necesitan y las carga en memoria. A continuación ejecuta un enlazado para fijar los valores de los punteros que permitan al ejecutable alcanzar las zonas adecuadas de las librerías cargadas. De esta forma no queden indefiniciones y el ejecutable pueda funcionar.