Win3mu - Part 5 - Windows 3 Executable Files

Win3mu - Part 5 - Windows 3 Executable Files

This is Part 5 in a series of articles about building Win3mu — a 16-bit Windows 3 emulator. If you’re not sure what this is or why I’m doing it you should read from the start.

This post briefly describes the Windows 3 .exe file format — the basic structure, a little about Win3mu’s implementation and some anomalies discovered along the way.

Basic Structure

Windows 3 .exe files use the “New Executable”, aka “NE” file format. Both .dll and .exe files use this format and it’s the basis of dynamic linking — the mechanism by which Windows loads and links modules together at run-time.

NE files contain:

  • MZ Header — a DOS executable header that lets DOS load the file and run a little stub program.
  • DOS Stub — a little stub program which simply prints “You need Windows” if you try to run the file directly from DOS.
  • NE Header — information required to run the program under Windows as well as pointers to other important parts of the file.
  • Referenced Module Table — the names of any other modules used by this module.
  • Segment Table — information about each of the program’s code and data segments.
  • Entry Table — information about various entry points into the program as well as internally referenced locations.
  • Code Relocation Tables — describes how to patch code addresses once the module has been loaded into memory and the addresses of imported function are known.
  • A Resource Table — a list of resources and where they reside in the file
  • A Resident Name Table — names of functions exported from this file
  • A Non-Resident Name Table — names of functions imported from other modules
  • The Code and Data Segments
  • The Resources

Most of the details for this I found through various online references:

Code and Data Segments

The bulk of the .exe file consists of the code and data segments. Code segments contain the x86 instructions that Sharp86 will be running. The data segments contain other data referenced by the program’s code.

Each segment can be up to 64k in length. Eventually each will be mapped to a selector. Code segments will be marked as read-only+executable, data segments as read/write.

Windows supported other flags on these segments too — like moveable and discardable but Win3mu ignores these since it doesn’t have to deal with the tight memory limits that Windows 3 did.

Relocations

Code segments have a relocation table — a list of places within the code segment that need to be “fixed up” once it’s loaded into memory.

Relocations include things like updating referenced addresses in other segments and filling in the addresses of any imported functions.

(Much of the online documentation neglects to mention that most relocations point to a chain of addresses. The relocation entry holds the address of the first relocation and at that address is the address of the next fix up. The module loader needs to follow the entire chain and fix up each location).

Resources

NE files support resources. Resources are embedded assets such as icons, bitmaps, menus etc… and each resource has a type and a name.

Resources can referenced by a name or by an ordinal (basically a fancy name for id number). For simplicity Win3mu coverts all ordinals to strings eg: ordinal 101 would be renamed to the string “#101". This just makes passing the names around in C# a little easier.

NeFileReader Class

In Win3mu the NeFileReader class is responsible for loading and parsing NE files. It takes the path to a file, reads all the relevant headers and lookup tables, provides methods for loading other parts of the file as required and properties to expose key information:

// NeFileReader - opens a 16-bit "NE" file
public class NeFileReader : IDisposable
{
    // Constructor, throws exception if error, keeps file open, call IDispose to close
    public NeFileReader(string filename);

    // Name of currently open file
    public string FileName { get; }

    // Name of the module extract from name table
    public string ModuleName { get; }

    // All code and data segments contained in the file
    public IList<SegmentEntry> Segments { get; }

    // The names of all referenced modules
    public string[] ModuleReferenceTable { get; }

    // Retrieves an entry point given an ordinal
    public EntryPoint GetEntryPoint(ushort ordinal);

    // Retrieves the index of the default (aka auto) data segment
    public int DataSegmentIndex { get; }

    // Retrieves the default (auto) data segment
    public SegmentEntry DataSegment { get; }

    // Retrieves the NeHeader 
    public NeHeader Header { get; }

    // Read a code or data segment into a buffer (must be pre-allocated a big enough)
    public void ReadSegment(SegmentEntry seg, byte[] buffer);

    // Looks up the ordinal for an exported name
    public ushort GetOrdinalFromName(string functionName);

    // Retrieve the exported name for an ordinal
    public string GetNameFromOrdinal(ushort ordinal);

    // Get the proc address for a function with given ordinal
    public uint GetProcAddress(ushort ordinal);

    // Check the header flags if this is a DLL or EXE
    public bool IsDll { get; }

    // Dump everything to the log file, optionally including the relocations
    public void Dump(bool includeRelocations);

    // Find a resource type
    public ResourceTypeTable FindResourceType(string name);

    // Find a resource
    public ResourceEntry FindResource(string type, string entry);

    // Load a resource returning it in a buffer
    public byte[] LoadResource(string type, string entry);

    // Seek the file stream to position of a resource and return it
    // Doesn't really open a new stream (don't close the returned stream)
    public Stream GetResourceStream(string type, string entry);
}

NeTool

Originally I planned to write a little utility program “NeTool” that would use the NeFileReader class to read an .exe file and dump out all the internal info (ie: a more advanced version of exehdr). I thought it would be a handy tool to have — and it probably would be — but I just haven’t had much need for it.

Instead, whenever Win3mu loads a module it dumps all the important information to its log file. It also generates a map — a top to bottom listing describing the contents of each section of the file. I’ve found that this log combined with a hex dump of the .exe works well enough.

If you’re interested, here’s a typical module listing and here’s a file map.

The Mysterious Resource Type #15

In the couple of week since I originally wrote the NeFileReader class I’ve come across one interesting twist. While trying to get the game Wordzap running I noticed that its menu was missing:

AWOL Menu

Opening the .exe file in AppStudio showed the menu had a string name of “WORDZAPMENU”:

Hands up if you remember AppStudio!

But when NeFileReader listed out the resources the menu had ordinal number #1 and no string name:

After double checking everything I was left a little stumped — so I just searched the file for the string “WORDZAPMENU”:

Cross referencing these back to the file map shows that the second string (at address 0x00011A19) was in a resource called “15/#1".

Cross referencing this back to the Windows.h shows there’s no such thing as resource type 15:

After searching for a bunch of different phrases that I thought might bring up some information and still not finding anything, just on a whim and guessing that it looked like a name table tried searching for “RT_NAMETABLE”. That led me to this header file from an old Watcom compiler that mentions it:

And that’s where the trail ended. I still haven’t found any documentation on it except one other header file that mentioned it was now obsolete and no longer supported.

However, knowing that it did once exist I decided to look at its contents:

And it wasn’t too hard to figure out the format:

struct
{
    WORD lengthEntry;        // length of this entry
    WORD resourceType;       // see RT_xxx constants
    WORD resourceId;         // ordinal of resource | 0x8000
    BYTE paddingZero;        // maybe, not sure??
    CHAR szName[];           // null-terminated name
}

NeFileReader now checks for a RT_NAMETABLE resource and if found automatically applies the resource name:

and the menu now loads:

To Sum Up

So that’s a quick overview of the NE file format. If you want the nitty-gritty, field by field details see the links above. Otherwise that’s enough to be able to follow along with the rest of these articles.

From here on I’ll be describing the core of the emulator and in the next post I’ll be talking about memory management — specifically the global and local heaps.