New Executable Format DOS 2.0 Windows will define a new executable format for .EXE files that is a superset of the current DOS 2.0 .EXE format. The purpose of this new format is to provide the additional information needed to support the new dynamic linking and segmentation facilities provided by DOS 2.0 Windows and DOS 4.0. In order for Windows and DOS 4.0 to recognize the new executable format, the existing .EXE format will be used with a slight modification: - the word at offset 18h in the existing .EXE file contains the relative byte offset to the relocation table. If this offset is 40h, then that identifies this as a new format .EXE file and The word at offset 24h is the relative byte offset from the beginning of the file to the beginning of the new format executable header. The remainder of the old format header will describe a small program that will either print an error message or bring in a loader that can handle the job. See the picture below for the actual file layout. - this format will only be used for .EXE files that use the new memory model supported by DOS 2.0 Windows and DOS 4.0. Old .EXE files will continue with the DOS 2.0 file format, as modified by the DOS 4.0 group. In that format, the DOS 4.0 behavior bits (2 bytes) are stored at offset 20h. The format of the new .EXE file format looks like: 00h ------------------- | | | Old EXE Header | | | ------------------- 20h | DOS 4.0 bbits | 22h | unused bbits | 3eh | offset to new | ---- | EXE header | | 40h ------------------- | | DOS 2.0 Stub | | | Program & | | | Reloc. Table | | xxh ------------------- <--- | | | New EXE Header |\ | | \ ------------------- \ | | | | Segment Table | | | | | ------------------- | | Resource | | DOS4 and Windows keep this | Table | |-----------part as part of their ------------------- | MODULE table, currently | Resident | | | Name | | | Table | | ------------------- / | Module Ref | / | Table |/ ------------------- | Imported | | Names | | Table | ------------------- * | Entry | * | Table | ------------------- | Non-Resident | | Name | | Table | ------------------- | Seg #1 Data | | Seg #1 Info | ------------------- . . . ------------------- | Seg #n Data | | Seg #n Info | ------------------- 20h - DW DOS 4.0 behavior bits * 22h - 3ch - reserved for more behavior info * 3eh - DW offset to new executable header Program that DOS 2.0 header points to, that will either print out an error message or load in a new-EXE loader. xxh - Beginning of new executable header DW signature word - can never be too careful "N" is low order byte "E" is high order byte DB version# of LINK that produced this executable DB revision# of LINK that produced this executable DW Entry Table file offset relative to beginning of new EXE header DW #bytes in Entry Table DD CRC-32 of entire contents of file (with these words taken as 00 during the calculation) DW flag word 0000h = NOAUTODATA 0001h = SINGLEDATA (SOLO) 0002h = MULTIPLEDATA (INSTANCE) 0004h = runs in real mode 0008h = runs in protected mode (if 0Ch set, runs in either) 4000h = non-conforming program (a valid stack is not maintained) 8000h = Library module (SS:SP info is invalid, CS:IP points to initialization procedure that is called with AX = the module handle. The procedure must execute a far return to the caller, with AX != 0 to indicate success and AX = 0 to indicate failure to initialize. DS = the library's data segment if the SINGLEDATA flag is set and the caller's DS otherwise. A program can only contain dynamic links to executables that have this flag set. If this flag is set, the MULTIPLEDATA flag must be reset. The SINGLEDATA flag may be set or reset. DW segment# of automatic data segment (index into segment table) set to zero if SINGLEDATA and MULTIPLEDATA flag bits are reset DW initial size (bytes) of dynamic heap added to data segment (0 if no local alloc) DW initial size (bytes) of stack added to data segment (0 if SS!=DS) DD segment#:offset of CS:IP DD segment#:offset of SS:SP Segment# is an index into the module's segment table. The first entry in the segment table is segment number 1. If SS = automatic data segment and SP = 0, the stack pointer is set to the top of the automatic data segment just below the additional heap area. +-------------------------+ | additional dynamic heap | +-------------------------+ <- SP | additional stack | +-------------------------+ | loaded data segment | +-------------------------+ <- DS, SS DW #of entries in Segment Table DW #of entries in Module Ref Table DW #bytes in Non-Resident Name Table DW Segment Table file offset relative to beginning of new EXE header DW Resource Table file offset relative to beginning of new EXE header DW Resident Name Table file offset relative to beginning of new EXE header DW Module Ref Table file offset relative to beginning of new EXE header DW Imported Names Table file offset relative to beginning of new EXE header DD Non-Resident Name Table offset relative to beginning of file * DW #moveable entry points * DW alignment shift count for segment data. Value of zero means * use the default value of 9 for 512 byte alignment. * DB 12 DUP (?) - room for growth here Segment Table ============= "N" segment table entries: The first entry in the segment table is segment number 1. * DW logical sector offset to contents of the segment data * relative to beginning of file (zero means no file data) * The alignment field in the header determines the units * of this offset. DW length of segment in file (bytes) (zero means 64k bytes) DW flag word TYPE_MASK = 0007h ; segment type field CODE = 0000h ; code segment type DATA = 0001h ; data segment type ITERATED = 0008h ; segment data is iterated MOVABLE = 0010h ; segment is not fixed PURE = 0020h ; segment can be shared PRELOAD = 0040h ; segment is not demand loaded ERONLY = 0080h ; execute only if code segment ; read only if data segment RELOCINFO = 0100h ; set if segment has reloc records DEBUGINFO = 0200h ; set if segment has debug info SEGDPL = 0C00h ; reserved for 286 DPL bits DISCARDABLE = F000h ; static discard priority level DW minimum allocation size (bytes) Total size of the segment (0 means 65536) Resource Table ============== DW alignment shift count for resource data "N" iterations of record: | DW type ID - integer type if high order bit is set (8000h) | otherwise offset to type string, relative to | beginning of the resource table | = 0 marks end of resource records | | DW #resources for this type | DD reserved for runtime use | | | | "#resources" copies of Resource Entry (8 bytes) | | | | DW file offset to contents of the resource data relative | | to beginning of file. Offset is in terms of alignment | | units specified at beginning of resource table. | | DW length of resource in file (bytes) | | DW flag word | | MOVEABLE = 0010h ; resource is not fixed | | PURE = 0020h ; resource can be shared | | PRELOAD = 0040h ; resource is not demand loaded | | DW resource ID - integer type if high order bit is set (8000h) | | otherwise offset to resource string, relative to beginning | | of the resource table | | DD reserved for runtime use \ \ Resource type and name strings stored at end of resource table Note that these strings are NOT null terminated DB length of type or name ; = 0 if end of resource table DB ASCII text of type or name ; Case sensitive Module Reference Table ====================== "N" entries of the form: (1-based) DW offset within Imported Names Table to module name string Entry Table (1 based) =========== "N" bundles of entry definitions. The ordinal value of an entry | point is its ordinal within the entry table, counting the | first entry as ordinal #1. The loader must scan over the | bundles until it finds the bundle containing the entry point; | the loader can then multiply by entry size to index the | proper entry. | | The linker forms bundles in the densest manner it can, given | the restriction that it cannot reorder entry points to improve | bundling because other EXE files may refer to entry points within | this one by their ordinal in this table. | | DB #entries in this bundle. All records in one bundle are | either movabe or refer to the same fixed segment. | =0 if no more bundles in Entry Table | | DB segment indicator for this bundle | | 000 - Unused | | 0FF - Movable segment, # is in entry | | otherwise is segment # of fixed segment | | | | If fixed segment, entries are 3 bytes: | | DB flags | | 0000 0001 - set if entry is exported | | 0000 0010 - set if entry uses global (shared) data segment | | "mov ax,#ds-value" must be the 1st | | instruction in the prolog of this | | entry. This flag may only be set | | for SINGLEDATA library modules. | | nnnn n--- - # of parameter words | | DW offset | | Else movable segment, entries are 6 bytes: | | DB flags | | 0000 0001 - set if entry is exported | | 0000 0010 - set if segment uses global (shared) data segment | | nnnn n--- - # of parameter words | | int 3Fh | | DB segment# \ \ DW offset Resident or Non-resident Name Table Entry (3 + n bytes) ========================================= The strings are CASE SENSITIVE and NOT NULL TERMINATED. If the * .EXE was produced with the /IGNORECASE switch, then all strings * will be UPPERCASE DB Length of string ; =0 if no more strings in table DB ASCII text of string DW ordinal# (index into entry table) First string in resident name table is the module name. First string in non-resident name table is the module description. Imported Names Table Entry (1 + n bytes) ========================== The strings are CASE SENSITIVE and NOT NULL TERMINATED. If the * .EXE was produced with the /IGNORECASE switch, then all strings * will be UPPERCASE DB Length of name ; =0 if no more strings in Table DB ASCII text of name Per segment data: ================ If ITERATED DW #iterations DW #bytes of data DB data bytes else DB data bytes If RELOCINFO DW #relocation items | | Relocation Item: (8 bytes) | | DB source type (32 bit address, 16 bit segment, 16 bit offset) | NRSTYP = 07h ; source type mask | NRSBYTE = 00h | NRSSEG = 02h ; 16-bit segment | NRSPTR = 03h ; 32-bit pointer | NRSOFF = 05h ; 16-bit offset | DB flags | TARGET_MASK = 03h | INTERNALREF = 00h | IMPORTORDINAL = 01h | IMPORTNAME = 02h | ADDITIVE = 04h | DW offset within this segment of source chain | If ADDITIVE flag set, then add target value to source contents, | instead of replacing source and following the chain. | The source chain is a 0xFFFF terminated linked list within | this segment of all references to the target. | Target | INTERNALREF | DB segment# for fixed segment or FF if movable | DB 0 | DW if moveable segment * | ordinal# (index into entry table of this module) | if fixed segment | offset into segment if fixed | | IMPORTNAME | DW index into module ref table | DW offset within Imported Names Table to proc. name string | | IMPORTORDINAL | DW index into module ref table | DW procedure ordinal# | | OSFIXUP | DW Operating system fixup type | | floating-point fixups | 0001h = FIARQQ, FJARQQ | 0002h = FISRQQ, FJSRQQ | 0003h = FICRQQ, FJCRQQ | 0004h = FIERQQ | 0005h = FIDRQQ | 0006h = FIWRQQ | applied by adding the fixup to the coprocessor instr. | for 1-3, apply the first fixup at the offset, and | the second fixup at the offset+1. for 4-6, just add | the fixup at the offset. | | NOTE: the linker marks these relocations as NRSOFF, but | they must be applied differently. in ldreloc.asm, we | use a flag to discern this case. | \ DW 0 If DEBUGINFO DW # of bytes of debug info NOTE: this document does not totally agree with version 1.7, dated 5/05/87.