392 lines
17 KiB
Plaintext
392 lines
17 KiB
Plaintext
|
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
|
||
|
<debug info - not yet defined>
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
NOTE: this document does not totally agree with version 1.7, dated 5/05/87.
|