The Oldest OS/2 Executable In the Wild

While researching the history of Microsoft’s segmented-executable linker originally called LINK4.EXE, I came across an OS/2 executable that was publicly released almost a year before the first OS/2 SDK was shipped, and many months before OS/2 was even announced. In fact the executable was likely released before the name “OS/2” even existed.

The file is EXEHDR.EXE, dated June 11, 1986 (size 32,896 bytes). It was shipped in the Microsoft Windows 1.03 SDK and then again unchanged in the Windows 1.04 SDK. For reference, OS/2 was announced on April 2, 1987 and the first OS/2 SDK was shipped around May 1987.

The mid-1986 EXEHDR.EXE is a classic dual-mode executable, with a DOS-compatible “stub” followed by an OS/2 NE format module—essentially two separate executables in a single file (it is not a bound executable). The OS/2 portion imports several routines from the DOSCALLS module, such as DOSOPEN, DOSREAD, DOSALLOCSEG, DOSGETVERSION, or DOSEXIT. It is notable that this old EXEHDR.EXE imports from DOSCALLS by name, while actual OS/2 applications imported by ordinal number.

This EXEHDR.EXE from June 1986 is currently the earliest known OS/2 executable released outside Microsoft/IBM. There is a slight chance that others might have been published with earlier Windows SDKs or perhaps some other Microsoft tools. It is likely that the release of this dual-mode EXEHDR.EXE was an omission, as the OS/2 portion effectively wasted about 16KB of disk space.

The June 1986 EXEHDR utility does not run on any known version of OS/2 (either early betas from mid-1987 or later 32-bit versions). On the other hand, the utility—unsurprisingly—understands its own format and produces the following output when run against itself in a DOS environment:

Magic number:             5a4dH
Bytes on last page:       128
Pages in file:            65
Relocations:              3
Paragraphs in header:     32
Extra paragraphs needed:  0
Extra paragraphs wanted:  65535
Initial stack location:   0455:0800
Word checksum:            ca7fH
Entry point:              0000:1206
Relocation table address: 0040H
Reserved words:
 0000 0000 0000 0000 0000 0000 0000 0000
 0000 0000 0000 0000 0000 0000 0000 0000
New .EXE header address:  4400H
Memory needed:            32K

Module:                   EXEHDR
Description:              EXEHDR.EXE
Linker version:           5.00
32-bit Checksum:          f7de06d1
Data:                     INSTANCE
Segment Table:            00004440 length 16
Resource table:           00004450 length 0
Resident Names Table:     00004450 length 10
Imported Names Table:     0000445c length 157
Entry Table:              000044f9 length 10
Non-resident Names Table: 00004503 length 14
Movable entry points:     1
Segment sector size:      512
Entry point:              seg   1 offset 0fd7
Stack:                    seg   2 offset 16d0
DGROUP:                   2

no. type address  file  mem   flags
  1 CODE 00004600 02ee1 02ee1 relocs, movable, preload
  2 DATA 00007600 00a80 016d0 movable, preload

ord seg offset name

  1 type   offset target
    PTR     2ca2  imp DOSCALLS.DOSWRITE
    PTR     1cad  imp DOSCALLS.DOSSETVEC
    PTR     1729  imp DOSCALLS.DOSEXIT
    PTR     0ff5  imp DOSCALLS.DOSGETVERSION
    PTR     2a5c  imp DOSCALLS.DOSALLOCSEG
    PTR     2dba  imp DOSCALLS.DOSREALLOCSEG
    PTR     2c8e  imp DOSCALLS.DOSCHGFILEPTR
    PTR     2d4a  imp DOSCALLS.DOSCLOSE
    PTR     2e1a  imp DOSCALLS.DOSOPEN
    PTR     2e38  imp DOSCALLS.DOSQFILEMODE
    PTR     1c87  imp DOSCALLS.DOSQHANDTYPE
    PTR     2c73  imp DOSCALLS.DOSREAD
    PTR     2d96  imp DOSCALLS.DOSSETFILEMODE
    BASE    1b6f  seg   2 offset 0000

Both the DOS and OS/2 executables included in EXEHDR.EXE had been built with Microsoft C, presumably version 4.0 with unreleased OS/2 run-time libraries.

It is currently not known whether anyone discovered this back in 1986, or if they understood the implications. The fact that Microsoft was working on “Advanced DOS” was understood at the time, although not many details were known.

Worth noting is also the fact that the LINK4.EXE segmented-executable linker (dated July 18, 1986) shipped with the Windows 1.03 SDK also clearly included some OS/2 support. The list of keywords includes for example PROTMODE and IOPL, neither of which was used with Windows 1.x/2.x or with the multitasking DOS 4.

This entry was posted in DOS, Microsoft, OS/2. Bookmark the permalink.

16 Responses to The Oldest OS/2 Executable In the Wild

  1. Richard Wells says:

    How did you determine that this version of exehdr.exe was designed for a proto-OS/2 not Windows, MT-DOS 4, or one of the fabled but never shipped attempts at porting DOS itself to protected mode or some other product started and canceled before the OS/2 agreement was finalized? The NE structure got used a lot. Someone at MS liked the basic design of overlapping structures appended to a previous structure; a bunch of API and file formats used the system.

  2. it is like all the parts were there in 1986 for something chicago-ish before we eventually got there in 1995. CP-DOS must have been able to actually run things, and I’m guessing via the tripple fault on the 286 there was at least some DOS compatibility. Windows 2.0 was still a year away, but in that time MS would already have pushed out Windows/386 the DOS hypervisor.

    And even assuming MS could pull it all together before 1990, would there be room in MS’s world for MS-DOS+Windows and CP-DOS/Windows? I guess Windows 3.0 wouldn’t have needed to happen. But then again MS was still far too timid to sell EuroDOS 4.0 on its own, just as CP-DOS was at some runnable state, but again wasn’t for larger sale.

  3. michaln says:

    Now that I’ve seen actual multitasking DOS 4, it’s clear that its API is entirely different from OS/2, because it is primarily DOS INT 21h based. On the other hand, the EXEHDR executable in question (the NE part of it) does not use INT 21h at all but rather only imports from DOSCALLS (the imports are OS/2 API functions, see listing). Note that multitasking DOS 4 also had a DOSCALLS module, but exported a completely different set of functions.

    On the other hand, looking at what this EXEHDR understands, there’s the “protected mode only” flag which would not apply to either DOS or Windows 1.x/2.x.

  4. michaln says:

    You mean Microsoft was too timid to sell multitasking DOS 4.0 in retail? Because they did try selling it to OEMs and the OEMs overwhelmingly said “no thanks”.

    What you might also find interesting is that this EXEHDR can recognize executables which use 386 instructions and contain 32-bit segments with 32-bit offsets and 48-bit far pointers. Clearly Microsoft was working on the technology but the world had to wait a while. This is no doubt related to the fact that Microsoft at that time had a 32-bit XENIX compiler and was developing a 386 port of XENIX.

  5. Yuhong Bao says:

    Thinking about it, I wonder if part of the reason why “CP-DOS” targeted the 286 is to make the project go faster. Writing a 286 protected mode OS was already not a trivial task.

  6. Yuhong Bao says:

    Compare with WinWord 1.0 which took years.

  7. Michal Necasek says:

    And OS/2 was basically done by the time Intel had working 32-bit chips (actually working, not just theoretically working).

  8. Yuhong Bao says:

    What I meant is that writing a 386 one with all the paging code would have been also been much harder.

  9. mZ says:

    IMO, writing code for flat 32-bit address space with paging is way simpler than for 16-bit segmented memory not only on the applications side, but also for operating system authors.

    First of all, managing memory in small fixed blocks is more straightforward than juggling variable-length blocks that are often larger. It applies to allocation of blocks and paging out to disk.

    Second, Intel’s segmented model is over complicated and requires managing lots of in-memory structures to function properly. Any mistake filling these structures leads to hard to debug failures.

    Compilers and linkers can be simplified when they do not need to deal with near/far pointers and functions, structures spanning several segments and juggling segments in general.

    In addition to that, 386 gives V86 mode and a (relatively) easy way to switch back to real mode.

    So I really think that given a choice no one would bother writing an OS for 286. OS/2 developers simply had no choice.

  10. Michal Necasek says:

    If you ever spent any debugging a problem caused by TLB mismanagement, you wouldn’t be saying that. TLB bugs are absolute hell to debug and can hide very well. No segmentation related problem is nearly as evil.

    Add to that the fact that early 386s could barely run 386 code at all, and that 32-bit code is bigger and slower, and you’re looking for a repeat of Windows NT where yes, sure, it did exist in 1993, but it took years before NT moved to the mainstream.

  11. Yuhong Bao says:

    It is not just TLB management either. Writing 386 paging code requires a lot of decisions to be made, not to mention a lot of additional code.

  12. MiaM says:

    ” Windows NT where yes, sure, it did exist in 1993, but it took years before NT moved to the mainstream.”

    Side track: Has it been thoroughly discussed why NT had to have such high hardware requirements? (This for sure was one of the biggest reason for it not becoming mainstream sooner) Thinking about that Windows 3.x were able to manage some form of protected with “only” 1M of RAM, and that for example AmigaOS ran fine with 256k in ROM and 256-512k RAM at the time. (We can’t really compare for example Macintosh OS or TOS for the Atari ST as those had no attempts at multi tasking).

  13. Michal Necasek says:

    32-bit C code and 16-bit assembler are quite different in size. Windows 3.x was optimized for size, NT was 32-bit from top to bottom, written entirely in C, and was significantly more complex and capable.

  14. MiaM says:

    I know that it’s generally said that x86 32-bit code becomes about 50% larger than the equivalent 68k code. But also I wounder if the compilers were way worse at that time than they became later on?

    As the x86 was rather different to more or less everything else it probably needed dedicated compilers while I would think that a compiler for for example VAX could likely to a large extent be reused for producing 68k code.

    (TL;DR: VAX has three operand instructions, 68k has two operand instructions. Both have 16 32-bit registers but VAX uses up more of them to dedicated uses (iirc the program counteris one of them) than the 68k (that only uses one of them as the stack pointer) but the 68k has a split where 8 are mostly usable for addressing purposes while the other 8 are mostly useable for computation purposes)).

    Especially if compilers for 32-bit x86 code were based on compilers for 16-bit x86 code then they might had contained constraints that produced inefficient code.

    Or maybe Dave Cutler & co just targeted the future with NT and ignored lower end systems at the time, i.e. having larger / more elaborate data structures and whatnot that would increase memory usage?

  15. Michal Necasek says:

    NT both targeted future systems and didn’t target x86 specifically. The NT design is vastly more elaborate than most other systems, and it’s perhaps fair to say that it’s overengineered. There’s none of the simplicity and elegance of UNIX — in fact NT is the OS by and for the UNIX haters.

    Old compilers definitely weren’t as efficient, but I can’t say how much difference that made. I don’t think the Microsoft compilers used to build NT were at all bad. The 32-bit 386 code was still definitely more compact than 32-bit RISC architectures.

  16. Yuhong Bao says:

    Don’t forget the additional RAM overhead for the paging tables as well. CP-DOS already have the overhead of the GDT/LDT/IDT, which is probably part of why MS gave on trying to fit it into 640K.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.