NE
Executable Formats |
---|
Microsoft |
*nix |
Apple |
NE
Introduction
The WIN-NE executable format, designed for Windows 3.x, was the "NE", or "New Executable" format. Again, a 16bit format, it alleviated the maximum size restrictions that the MZ format had.
Support
Operating Systems that use it: Windows 1.01 through 3.xx.
The format can be run on later 32 bit windows systems (in a VDM), but is not the native format.
Because of 64 bit lack of v8086, support is discarded on 64 bit windows.
Inside the NE file
Overview
DOS Stub
The DOS stub is a valid MZ exe. This enables the developer to package both an MS-DOS and Win16 version of the program, but normally just prints "This Program requires Microsoft Windows". The e_lfanew field (offset 0x3C) points to the NE header.
NE Header
The NE header is a relatively large structure with multiple characteristics. Because of the age of the format some items are unclear in meaning. The structure is as below (some comments may be wrong. If so please change):
struct NE_header {
char sig[2]; // {'N', 'E'}
uint8_t MajLinkerVersion; //The major linker version
uint8_t MinLinkerVersion; //The minor linker version
uint16_t EntryTableOffset; //Offset of entry table, see below
uint16_t EntryTableLength; //Length of entry table in bytes
uint32_t FileLoadCRC; //32-bit CRC of entire contents of file
uint8_t FlagWord; // Uses the FlagWord enum
uint16_t AutoDataSegIndex; //The automatic data segment index
uint16_t InitHeapSize; //The initial local heap size
uint16_t InitStackSize; //The initial stack size
uint32_t EntryPoint; //CS:IP entry point, CS is index into segment table
uint32_t InitStack; //SS:SP initial stack pointer, SS is index into segment table
uint16_t SegCount; //Number of segments in segment table
uint16_t ModRefs; //Number of module references (DLLs)
uint16_t NoResNamesTabSiz; //Size of non-resident names table, in bytes (Please clarify non-resident names table)
uint16_t SegTableOffset; //Offset of Segment table
uint16_t ResTableOffset; //Offset of resources table
uint16_t ResidNamTable; //Offset of resident names table
uint16_t ModRefTable; //Offset of module reference table
uint16_t ImportNameTable; //Offset of imported names table (array of counted strings, terminated with string of length 00h)
uint32_t OffStartNonResTab; //Offset from start of file to non-resident names table
uint16_t MovEntryCount; //Count of moveable entry point listed in entry table
uint16_t FileAlnSzShftCnt; //File alignment size shift count (0=9(default 512 byte pages))
uint16_t nResTabEntries; //Number of resource table entries
uint8_t targOS; //Target OS
//
// The rest of these are not defined in the Windows 3.0 standard and
// appear to be specific to OS/2.
uint8_t OS2EXEFlags; //Other OS/2 flags
uint16_t retThunkOffset; //Offset to return thunks or start of gangload area - what is gangload?
uint16_t segrefthunksoff; //Offset to segment reference thunks or size of gangload area
uint16_t mincodeswap; //Minimum code swap area size
uint8_t expctwinver[2]; //Expected windows version (minor first)
};
//
// In 16-bit DOS/Windows terminology, DGROUP is a segment class that referring
// to segments that are used for data.
//
// Win16 used segmentation to permit a DLL or program to have multiple
// instances along with an instance handle and manage multiple data
// segments. This allowed one NOTEPAD.EXE code segment to execute
// multiple instances of the notepad application.
//
enum FlagWord {
NOAUTODATA, //
SINGLEDATA,
MULTIPLEDATA,
LINKERROR = 0x2000, // Linker error, module cannot lode
LIBMODULE = 0x8000
// The file is a DLL/Library. NE_header.InitStack
// is therefore invalid (DLLs are libraries) so they
// do not get their own stacks. CS:IP points to
// the load procedure.
};
// The type is a 3-bit integer or'ed together with the other flags.
#define SEGFLAGS_HAS_RELOCS 0x0100
#define SEGFLAGS_DISCARD 0xF000
#define SEGFLAGS_TYPE_CODE 0
#define SEGFLAGS_TYPE_DATA 1
// Segment table entry
typedef struct {
uint16_t SectorBase; // See NE_header.FileAlnSzShftCnt
uint16_t SegBytes; // Segment bytes located in the file
uint16_t SegFlags;
uint16_t MinAlloc; // Minimum number of bytes to allocate.
}NE_SegEnt;
#define GLOBINIT 1<<2 //global initialization
#define PMODEONLY 1<<3 //Protected mode only
#define INSTRUC86 1<<4 //8086 instructions
#define INSTRU286 1<<5 //80286 instructions
#define INSTRU386 1<<6 //80386 instructions
#define INSTRUx87 1<<7 //80x87 (FPU) instructions
//Application flags
//Application type
enum apptype {
none,
fullscreeen, //fullscreen (not aware of Windows/P.M. API)
winpmcompat, //compatible with Windows/P.M. API
winpmuses //uses Windows/P.M. API
};
// #define OS2APP 1<<3 //OS/2 family application
// //bit 4 reserved?
// #define IMAGEERROR 1<<5 //errors in image/executable
// #define NONCONFORM 1<<6 //non-conforming program?
// #define DLL 1<<7
//Target Operating System
enum targetos {
unknown, //Obvious ;)
os2, //OS/2 (as if you hadn't worked that out!)
win, //Windows (Win16)
dos4, //European DOS 4.x
win386, //Windows for the 80386 (Win32s). 32 bit code.
BOSS //The boss, a.k.a Borland Operating System Services
};
//Other OS/2 flags
#define LFN 1 //OS/2 Long File Names (finally, no more 8.3 conversion :) )
#define PMODE 1<<1 //OS/2 2.x Protected Mode executable
#define PFONT 1<<2 //OS/2 2.x Proportional Fonts
#define GANGL 1<<3 //OS/2 Gangload area
If the module is a DLL, the CS:IP in the header is instead the load procedure of the DLL and SS:SP is unused (since libraries do not get their own stacks). It takes the module handle in AX, which is an opaque data type that represents an EXE or a DLL when loaded in memory. On return it outputs a AX!=0 for SUCCESS and AX==0 for failure, which is unusual but very important.