Microsoft Segmented Executable Format ------------------------------------- 5/05/87 This document defines the Segmented Executable format, which is a superset of the MS-DOS 2.x and 3.x .EXE format (hereafter called DOS 3.x). The purpose of the segmented-executable format is to provide the information needed to support the dynamic linking and segmentation capabilities provided by OS/2, MS-DOS 4.0, and MS Windows. Also, information needed for protected mode is provided. The DOS 3.x .EXE format has been extended as follows: - 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 the double word at offset 3Ch is assumed to be the relative byte offset from the beginning of the file to the beginning of the new format executable header. A new format .EXE file is identified if the new executable header contains a valid signature. If the signature is not valid the file is assumed to be an old format .EXE file. The remainder of the old format header will describe a DOS 3.x program, the stub. The stub may be any valid program but will typically be a program which displays an error message, emulates the new-format program, or brings 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 OS/2, MS-DOS 4.0 and MS Windows. Old .EXE files will continue with the DOS 3.x file format. Note ==== Throughout this specification, all unused fields and flag bits are reserved for future use and are expected to contain 0. The new .EXE file has the following format: 00h ------------------- | | | Old EXE Header | | | 20h ------------------- | reserved | ------------------- | | 3Ch | offset to new | ---+ | EXE header | | 40h ------------------- | | DOS 3.x Stub | | | Program & | | | Reloc. Table | | ------------------- | | | | xxh ------------------- <--- | | | New EXE Header | | | ------------------- | | | Segment Table | | | ------------------- | Resource | | Table | ------------------- | 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 | ------------------- | Debug | | Information | ------------------- New Executable Header ===================== 00h DW signature word "N" is low order byte "E" is high order byte 02h DB version# 03h DB revision# 04h DW Entry Table file offset relative to beginning of new EXE header 06h DW #bytes in Entry Table 08h DD 32-bit checksum of entire contents of file (with these words taken as 00 during the calculation) 0Ch DW flag word 0000h = NOAUTODATA 0001h = Shared automatic data segment (SINGLEDATA) 0002h = Instance automatic data segment (MULTIPLEDATA) 0004h = Per-process library initialization (INITINSTANCE) 0008h = Runs in protected mode only (PROTMODE) 0008h = runs in protected mode only 0010h = 8086 instructions present 0020h = 286 instructions present 0080h = floating point instructions present 2000h = errors detected at link time 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. 0Eh DW segment# of automatic data segment (index into segment table) set to zero if SINGLEDATA and MULTIPLEDATA flag bits are reset 10h DW initial size of dynamic heap added to data segment in bytes (0 if no local alloc) 12h DW initial size of stack added to data segment in bytes (0 if SS!=DS) 14h DD segment#:offset of CS:IP 18h 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 1Ch DW #of entries in Segment Table 1Eh DW #of entries in Module Ref Table 20h DW #bytes in Non-Resident Name Table 22h DW Segment Table file offset relative to beginning of new EXE header 24h DW Resource Table file offset relative to beginning of new EXE header 26h DW Resident Name Table file offset relative to beginning of new EXE header 28h DW Module Ref Table file offset relative to beginning of new EXE header 2Ah DW Imported Names Table file offset relative to beginning of new EXE header 2Ch DD Non-Resident Name Table offset relative to beginning of file 30h DW #of movable entries in Entry Table 32h DW logical sector alignment shift count, log(base 2) of segment sector size (default 9) 34h DW #of resource entries 36h - 3Eh DW reserved, currently 0's Segment Table ============= "N" segment table entries: The first entry in the segment table is segment number 1. DW n-byte logical sector offset to contents of the segment data relative to beginning of file (zero means no file data) DW length of segment in file, in bytes (zero means 64K) DW flag word 0007h = TYPE_MASK ; segment type field 0000h = CODE ; code segment type 0001h = DATA ; data segment type 0008h = ITERATED ; segment data is iterated 0010h = MOVABLE ; segment is movable (OS/2 ignores) 0020h = SHARED ; segment can be shared 0040h = PRELOAD ; segment is preloaded 0080h = ERONLY ; execute only if code segment ; read only if data segment 0100h = RELOCINFO ; set if segment has reloc records 0200h = CONFORM ; segment is conforming 0C00h = SEGDPL ; I/O privilege level 1000h = DISCARD ; discardable segment (OS/2 ; ignores) 4000h = HUGE ; huge segment: length of segment ; and minimum allocation sizes are ; in units of segment sector size DW minimum allocation size in bytes Total size of the segment 0 means 64K 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 | | | | "#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 | | 0010h = MOVEABLE ; resource is not fixed | | 0020h = PURE ; resource can be shared | | 0040h = PRELOAD ; 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 | | DD Reserved \ \ of the resource table 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 Resident or Non-resident Name Table Entry (3 + n bytes) ========================================= The strings are CASE SENSITIVE and NOT NULL TERMINATED 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. Module Reference Table ====================== "N" entries of the form: (1-based) DW offset within Imported Names Table to module name string Imported Names Table (1 + n bytes) ==================== The strings are CASE SENSITIVE and NOT NULL TERMINATED DB 0 ; to ensure non-zero first entry ; offset "N" entries of the form: DB Length of name DB ASCII text of name 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 movable or refer to the same fixed segment. | Equal to 0 if no more bundles in Entry Table. | | DB segment indicator for this bundle | | 00h - Unused, bundle not present, next bundle count | | follows | | FFh - Movable segment, # is in entry | | otherwise is segment # of fixed segment | | | | If fixed segment, entries are 3 bytes: | | DB flags | | 01h = set if entry is exported | | 02h = 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. | | F8h = # of parameter words | | DW offset | | Else movable segment, entries are 6 bytes: | | DB flags | | 01h = set if entry is exported | | 02h = set if entry uses global (shared) data | | segment | | F8h = # of parameter words | | int 3Fh | | DB segment# \ \ DW offset 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 | 0Fh = SOURCE_MASK | 00h = LOBYTE | 02h = SEGMENT | 03h = FAR_ADDR (32-bit pointer) | 05h = OFFSET (16-bit offset) | | DB flags | 03h = TARGET_MASK | 00h = INTERNALREF | 01h = IMPORTORDINAL | 02h = IMPORTNAME | 03h = OSFIXUP | 04h = ADDITIVE | | 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 FFFFh terminated linked list within | this segment of all references to the target. | | Target | INTERNALREF | DB segment# for fixed segment or FFh if movable | DB 0 | DW offset into segment if fixed | index into Entry Table iff movable | | 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 \ DW 0 Debug Information ================= Any debug information will be at the end of the executable. No information about it will appear in the header, so executable files may be longer than the file length indicated in the header.