Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


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

PRELOAD El segmento de código es cargado cuando el programa invocante es cargado.
LOADONCALL Este es el valor por defecto.  El segmento es cargado cuando sea llamado por el programa.
EXECUTEONLY Significa que el segmento de código solo puede ser ejecutado.
EXECUTEREAD Significa que el segmento de código puede ser leído y ejecutado (es el valor por defecto).
FIXED El segmento permanece en una posición de memoria fija (valor por defecto).
MOVEABLE El segmento puede ser movido a otra situación por el Sistema.
DISCARDABLE El segmento puede ser descargado de memoria cuando ya no sea necesario (implica que sea MOVEABLE).
NONDISCARDABLE El segmento no puede ser eliminado de memoria (valor por defecto).
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

NONE Significa que no se creará un segmento de datos.  Esta opción solo es permitida cuando el ejecutable es una librería, y significa que solo se crea un segmento de datos que es compartido por todos los procesos (es el valor por defecto cuando se enlazan DLLs).
MULTIPLE Se creará un segmento de datos para cada proceso.  Es el valor por defecto para los ejecutables estándar (.EXE).
READONLY El segmento de datos es de solo lectura.
READWRITE Es el valor por defecto. Significa que el segmento puede ser leído y escrito.
PRELOAD Significa que el segmento de datos será cargado cuando el módulo que lo utiliza sea cargado por primera vez.
LOADONCALL Es el valor por defecto.  El segmento será cargado la primera vez que sea accedido (este valor es ignorado por las aplicaciones de 32 bits).
SHARED Todos los procesos comparten la misma copia del segmento de datos.
NONSHARED Cada proceso que necesite del segmento de código cargará su propia copia del mismo.  Es el valor por defecto para las 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]

WINDOWAPI Indica que la aplicación es un ejecutable Windows.  Equivale a la opción /aa para el compilador ILINK32 ( 1.4.0w1)
WINDOWCOMPAT Se trata de una aplicación Windows para correr en modo carácter (no gráfica).  Equivale a la opción /ap del compilador ILINK32
Si no se utiliza esta sección, el enlazador determina el tipo de ejecutable a producir en base a las opciones indicadas en la línea de comando.
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]

ExportName Especifica una cadena de caracteres ASCII que señala los símbolos declarados como exportables de la siguiente forma:

NombreEntrada [=NombreInterno]

NombreInterno es el nombre utilizado en la aplicación para referirse a dicha entrada.

NombreEntrada es el identificativo con el que aparece el recurso en la tabla de entradas ( 1.4.4b1) del ejecutable que es visible desde el exterior.

Ejemplo:

LIBmyfun = myfun

Significa que LIBMyfun es un alias del identificador myfun.

Ordinal Este valor define el número de orden de la función dentro de la tabla de entradas.  El número debe ir precedido del carácter '@' en la forma:   @numero.
Ejemplos:

myFun1 @222 NONAME
myFun2 @333

RESIDENTNAME Indica que el nombre de las funciones debe ser residente todo el tiempo.  Esto solo tiene sentido cuando las funciones son exportadas por su número de orden (Ordinal), en cuyo caso el nombre de la función es irrelevante y no necesita ser residente (lo que ocurre por defecto en estos casos).
Parameter Es un número entero, opcional, que indica el número de palabras que la función espera que le sean pasadas como parámetros.
HEAPSIZE

HEAPSIZE define el tamaño en bytes que necesita la aplicación para el montón ("Local heap" 1.3.2).

Sintaxis:       HEAPSIZE    Reservado[, Consignado]

Ejemplo:       HEAPSIZE   4096

Reservado Tamaño reservado, valor decimal o hexadecimal indicando el tamaño del montón.  El valor por defecto es 1MB.

Nota:  Con el fin de garantizar compatibilidad hacia atrás de las nuevas versiones (de 32-bit) con las de 16-bit, el enlazador reserva 1MB si se especifica un valor inferior a 64K.

Consignado Tamaño comprometido.  Valor decimal o hexadecimal opcional.  Caso de no ser especificado, el valor por defecto es 4K.  El valor mínimo es 0.  El tamaño de memoria comprometida (o consignada) debe ser siempre igual o menor que la reservada.

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.

SintaxisIMPORTS [NombreInterno=]NombreModulo.Entrada

NombreInterno Cadena ASCII con el nombre que utiliza la aplicación para invocar la función (el recurso importado).
NombreModulo Nombre del módulo.  Se trata de uno o varias mayúsculas ASCII que indican el nombre del ejecutable (librería) que contiene la función.  Este identificador debe coincidir con el del ejecutable.  Por ejemplo, nombre de módulo del fichero ejemplo.dll debe ser EJEMPLO.
Entrada Este valor puede ser indistintamente una cadena ASCII o un número. Indica el nombre u ordinal de la función que se invoca dentro del módulo (EXPORTS ).
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]

LibraryName Es una cadena de caracteres ASCII que declara el nombre de la librería.  Esta sección es opcional, y si no se declara ningún identificador el enlazador ILINK32 utiliza el nombre del fichero eliminando la terminación.
INITGLOBAL Este argumento opcional significa que la rutina de inicialización de la librería es llamada solo la primera vez que el módulo sea cargado en memoria.
INITINSTANCE Este argumento indica que la rutina de inicialización de la librería será llamada cada vez que un nuevo proceso la utilice.
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]

NombreModulo Se trata de un identificador (ASCII) opcional en mayúsculas, que identifican el nombre del ejecutable.  Por supuesto debe coincidir con el nombre del fichero.  Por ejemplo, el ejecutable Eje1.exe debe tener un nombre de módulo EJE1.

Si este identificador falta, el enlazador supone que el nombre de módulo coincide con el del ejecutable.  Por ejemplo, si el ejecutable es Aplic.exe, se supone que el nombre del módulo es APLIC.

WINDOWSAPI Este argumento opcional significa que se trata de un ejecutable Windows.  Equivale a la opción /aa en la invocación del enlazador ILINK32.
WINDOWCOMPAT Este argumento indica que es una aplicación Windows para correr en modo carácter (no gráfica).  Equivale a la opción /ap del compilador.

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.

NotaSEGMENTS 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).

SintaxisSEGMENTS

             SegmentName [CLASS 'ClassName']
             [MinAlloc]
             [SHARED | NONSHARED]
             [PRELOAD | LOADONCALL]

SegmentName Cadena de caracteres que identifica a cada segmento.  Puede ser cualquier nombre incluyendo los nombres estándar _TEXT y _DATA, que representan respectivamente los segmentos de código y datos.
ClassName Este identificador es opcional, es el nombre de clase del segmento correspondiente.  Si no se especifica nada, el enlazador utiliza el nombre de clase CODE.
MinAlloc Este dato es opcional, un número entero que indica el tamaño mínimo de memoria que se asignará al segmento, aunque el enlazador ILINK32 no lo toma en cuenta.
SHARED Una copia del segmento es compartida entre todos los procesos.
NONSHARED Es el valor por defecto para .EXEs y .DLLs.  Significa que se carga una copia del segmento de datos para cada proceso que necesite utilizarlo.
PRELOAD Significa que el segmento es cargado inmediatamente.
LOADONCALL Indica que el segmento es cargado cuando es accedido o invocado (esto es ignorado por el enlazador ILINK32), pero el compilador de recursos ( 1.4.0w1) puede sobrescribir lo señalado en las opciones LOADONCALL y PRELOAD.
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

Reservado Tamaño reservado, valor decimal o hexadecimal indicando el tamaño de la pila.  El valor por defecto es 1MB.

Nota:  Con el fin de garantizar compatibilidad hacia atrás de las nuevas versiones (de 32-bit) con las de 16-bit, el enlazador reserva 1MB si se especifica un valor inferior a 64K.

Consignado Tamaño comprometido.  Valor decimal o hexadecimal opcional.  Caso de no ser especificado, el valor por defecto es 8K.  El valor mínimo es 4K.  El tamaño comprometido debe ser siempre igual o menor que el reservado.

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'

NombreFichero Es el nombre del ejecutable que se añadirá al módulo.  Debe ser un nombre en formato DOS (8+3).  Si el fichero especificado no está en el directorio actual, el enlazador busca en las direcciones indicadas en la variable de entorno PATH.
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

subsistema Este parámetro es opcional, y puede ser cualquiera de los siguientes: WINDOWS, WINDOWAPI o WINDOWCOMPAT. Si no se especifica nada el enlazador supone WINDOWS por defecto.
subsystID Este parámetro debe especificarse utilizando un formato n.n, donde n es un número decimal.  Por ejemplo, si quiere indicarse Windows 4.0, debe utilizarse alguna de las siguientes sentencias:

SUBSYSTEM  4.0

SUBSYSTEM  WINDOWS, 4.0

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).

  Inicio.


[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.