Continuing with Ancient Microsoft C/Linkers

Microsoft OS/2 Software Development Kit Pre-Release 2

I don’t know why, but Microsoft OS/2 2.00 beta’s are beyond rare. At one point I had a documentation set but not disks. However disk images circulated around, so at one point I did have printed documents (that basically didn’t show much interesting other than True Type fonts for OS/2), and the SDK/ToolKit. However there to date has been no operating system images surfacing.

Since yesterday’s look at the 1991 Windows NT Pre-release which turned out to be using the OS/2 compiler, I went back and checked the Microsoft OS/2 SDK, and it turns out that the compiler is a ‘bound’ executable, meaning that it’ll run under MS-DOS!… And for us that means the MS-DOS Player can make native Win32/Win64 executables out of the compiler/assembler.

Microsoft (R) Microsoft 386 C Compiler. Version 1.00.075
Copyright (c) Microsoft Corp 1984-1989. All rights reserved.

As always the devil is in the details, and this time it’s the linker. I now have OS/2 2.00 v123 (February of 1991), Citrix Multiuser 2.0 (March 1992), and OS/2 2.00 GA (July 1993). And not surprisingly despite them all either including a system link386.exe or the SDK link386 they have massive final incompatibilities.

For fun, I’m using a super simple C program, and compiling it with the Microsoft Pre-Release 2 SDK. After that I’m just using various Linkers & OS’s and the same Pre-Release libraries to see the same object relinked and running.

void main(void){
write(0,"hi\n",3);
}

It’s a very simple program that really doesn’t assume or need much other than write.

So looking at OS/2 build 123

Version 1.01.015

This is the SDK Linker 1.01.015. Dated November 28th, 1990. Which sounds a bit late in the year, but don’t worry as the OS includes Version 1.01.018, dated August 15th 1990!

Version 1.01.018

I don’t understand it either.

Citrix Multiuser 2.0

Version 2.00.000

Citrix Multiuser is using the 177H base also known as the OS/2 2.00 LE. It includes a version 2.00.000 linker, which works fine for it. However the SDK and 123 linked files do not run. While the SDK runs fine, I don’t know how did they link the tools as they work fine**.

OS/2 2.00 GA

Version 2.01.005

And finally we have OS/2 2.00 GA using it’s linker, and yeah it runs fine. Also the GA can run the LA linked files. Naturally 123 can’t run either LA or GA EXE’s.

Obviously the tool group was separate from the OS teams, and there was that brief window when everything 32bit was OMF, and LX was going to be the grand behind the doors unification thing providing 32bit exe’s for Windows 3.x VXD’s, OS/2 2.x and Windows NT. But as was obvious in the 1991 Pre-Release the tool to convert OMF to COFF wasn’t going to be a tool much longer and it was going to be integrated directly.

I’ve tried using the link386 from the Windows 3.1 DDK but I can’t get it to link properly. Just as I haven’t tried other MASM386’s or even 16bit MASM 5+ which apparently support 32bit OMF?

Again it’s interesting to me, but is it useful? Not really. Also the last interesting bit is that the Microsoft C from the 73g build of the Windows 95 SDK can produce assembly that the Pre-Release more or less understands:

D:\OneDrive\proj\link386>4.00.73g\BIN\cl /c /Famt.asm mt.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 8.00.3200
Copyright (c) Microsoft Corp 1984-1993. All rights reserved.

mt.c

D:\OneDrive\proj\link386>

And then assembling and linking on OS/2

Useful? Not very. I don’t think anything complicated will run, although I have only tried one thing and it had a tonne of 16bit/32bit collisions. I don’t know if Microsoft C/C++ 8.0 for OS/2 is all that desirable but I’d imagine if it were, smarter people than I would have made it a thing.

**Edit from the future

Well it turns out the SDK tools are 16bit Family mode apps. It’s obvious once Nathan pointed it out that there is no 32bit ‘bound’ for MS-DOS, however there was Family Mode*** for realmode/protected mode 16bit code.

DOS/4G error (2300): can’t find DOSCALLS.282 – referenced from MT
DOS/4G fatal error (1313): can’t resolve external references

PharLap famously made their early 286 extender based around OS/2, however for the 32bit stuff it was apparently home grown for the early stuff (I have version 4) but there is DLL’s to emulate MS-DOS like the 286 extender. Interestingly enough DOS/4G supports DLL’s but again there is no DOSCALLS.DLL I can find, but the loader loads it.

***Family Mode/Family API

From the book INSIDE OS/2 (ISBN 1-55615-117-9):

2.2.4.1 Family API
To provide downward compatibility for applications, OS/2 designers
integrated a Family Applications Program Interface (Family API) into the
OS/2 project. The Family API provides a standard execution environment
under MS-DOS version 3.x and OS/2. Using the Family API, a programmer can
create an application that uses a subset of OS/2 functions (but a superset
of MS-DOS version 3.x functions) and that runs in a binary compatible
fashion under MS-DOS version 3.x and OS/2. In effect, some OS/2 functions
can be retrofitted into an MS-DOS version 3.x environment by means of the
Family API.

23 thoughts on “Continuing with Ancient Microsoft C/Linkers

  1. Firstly, I really enjoyed these last two posts, so please don’t conflate lack of comments with lack of interest.

    In addition to using old compilers through the OS/2 linker, the other possibility this raises is to just invert the NT tool and convert objects coming out of a new Microsoft compiler back into an OMF format that link386 expects. There’s a few COFF-to-OMF converters out there, mainly because Borland stuck with OMF long after Microsoft moved to COFF. I don’t know if it’s that simple or if there’s more work required, but the vast bulk of the output of the compiler is just going to be x86 machine code which will be interchangeable across both, so this hypothetical tool seems buildable.

    As you’ve written about before, the compiler from Visual C++ for NT (ie., the 1993 one, C/C++ 8.0) is also bound so it can generate Win32 executables from DOS, or in this case, generate 32 bit output from a DPMI-enabled OS/2 host which can be fed into link386 without a reboot. I really wish this had been preserved into newer compilers, but as far as I know, this is the only one.

    • I didn’t think about Borland! The other one I was playing with was Watcom, as wlink understands so much. I can wlink fine for the GA, obviously none of the beta stuff works. Which I guess isn’t such a big deal. Although I do like the ability to access Citrix remotely. Obviously Citrix multiuser doesn’t have these issues being 16bit OS/2.

      I need to comb through more Windows stuff, and MASM looking for other assemblers and linkers to see what they can do.. and maybe as a bonus figure out how the SDK was linked as it runs on all OS/2 2 versions. Maybe it’s all in the header/stub or they know how to build definition files properly as they created it all.

      I found the GCC/2 patches which have some alignment fixes to build GCC with Microsoft C. I guess they really weren’t mainlined until 2.6 with the NT port.

      Seems kind of weird how the big platforms were never rolled into the big releases but I guess that is GCC showing it’s bias to RISC Unix.

  2. Wait…am I misunderstanding (specifically this bit: “While the SDK runs fine, I don’t know how did they link the tools as they work fine”), or are you saying that the cl386.exe from the SDK runs as a 32-bit app under both = 6.167? Or is it purely a 16-bit binary and/or are you running cl386 under a DOS VM?

    I thought all of the post-WPS releases of 2.0 could not run 32-bit LE format binaries?

    Finally, your supplied date of July 1993 for OS/2 2.00 GA is off. Maybe the final digit in the year was just an off-by-one typo. 🙂 2.1 was already out by like May of ’93 IIRC. You might have been running a build of 2.0 that was released in 1993 for this experiment (ServicePak/FixPak 2 a.k.a. XR_06100 perhaps? which I believe backported a lot of the 2.1 improvements to 2.0, like the 32-bit graphics subsystem and various kernel fixes), but not the GA.

  3. (hah, my previous comment was intended to read as “[does] cl386.exe … run as a 32-bit app under both less-than 6.167 and greater-than-or-equal-to 6.167?”, but looks like the comment parser mistook everything between < and > as a HTML tag or something)

    • yeah sometimes it get’s confused like me! Just like .AT. will get confused with being an email address not an old style compressed file. I don’t know how the SQL people handle .AT..AT.

      I mean to add a link to msos2-wlink.7z, naturally just deal with the 404page redirect. I’m trying to go through more stuff to find other versions, and try to use Phar Lap 286 that I bought to MS-DOS’ify them. I forget but one of the DOS Extenders can run LE/LX/LS?

      Oh and right I installed all the fixpacks, xr06100 and some unofficial one, I think it brings 2.00 up to 2.11 levels as PCI works.

  4. Oh, okay. I see. I was unfamiliar with “MS-DOS Player”, but now that I’ve Googled it, I understand where that PE executable in your 7z came from.

    As far as the unmodified binaries in “orig”, they just look like textbook 16-bit “family mode” (or as you said, “bound”) executables to me, using a technique similar to Win16 NE binaries except that the DOS part isn’t just a stub that prints a one-line error message. Do you have reason to believe that the compiler is actually running as a 32-bit process when spawned from CMD.EXE? I’m guessing it’s not…

    I’ve managed to build and link 16-bit family mode apps with Microsoft C 6.0 in the past. Now I’m trying to remember whether link386.exe would even be capable of that…I’m thinking/guessing not & that you’d have to use the linker that shipped with MS C. “Family mode” was interesting as you could code to (a specific subset of) DOSCALLS APIs and then I believe the runtime would dispatch the calls as appropriate depending on whether it was running under DOS or OS/2.

    I’m not personally aware of any bound EXEs that shipped which contain both *usable* DOS code (just, again, the very basic stub at the beginning that tells the user to try again under the appropriate environment) plus 32-bit OS/2 code, though it’s a big world out there… sure seems like it’d be theoretically possible to maybe accomplish this by replacing the normal DOS stub with a DOS extender that understands LE or LX (like DOS/4G), but I’d be shocked if Microsoft C 386 / SDK was doing anything remotely like that. Even if you successfully did that, the 32-bit side of a given “bound” binary like that would only ever work under either LE-aware 32-bit OS/2 or LX-aware 32-bit OS/2, not both (right? given the similarities, probably not possible to have both LE and LX entry points within a single binary?).

    So the most likely answer is that cl386 is just a 16-bit app that happens to produce 32-bit code…an 8086 to 80386 cross-compiler if you will? 🙂

    • I just checked with Phar Lap 286, and yeah it’s a 16bit to 32bit cross compiler!… Although MS-DOS player doesn’t like Phar Lap 286 very much. I should be using 2ine.

  5. …oh, also, regarding your question about 16-bit MASM, yes, and here’s an interesting old (archived) MS KB article on the subject with sample code attached. I’ve successfully built this with MASM 6 (also a 16-bit “bound” DOS+OS/2 app) and it does indeed work (output linked fine using link386, binary executes fine, etc.):

    https://jeffpar.github.io/kbarchive/kb/094/Q94577/

  6. Okay, mystery mostly solved, or at least I think so…

    I repeated your experiments, and re-linked your simple mt.c program several times using the native link386 included with several versions of 32-bit OS/2:

    6.123 (linker 1.01.018 Aug 1990)
    6.167 (linker 2.00.000 Oct 1991)
    Citrix 2 (6.177) (linker 2.00.000 Mar 1992)
    2.0 GA (6.307) (linker 2.00.000 Feb 1992)
    2.0 FP2 (6.543) (linker 2.01.005 Mar 1993)
    4.52 MCP2 (14.089) (linker 4.00.001 Oct 2001)

    For the last 4 versions in the list (everything from 6.177 up through Warp 4.5), every single linker produced an executable that was the exact same size: 6,656 bytes. When comparing each of these to each other byte-by-byte, only a handful of bytes ever differ (I haven’t checked yet to see which ones specifically / what function they serve).

    6.167’s linker in contrast produced an executable that is exactly 16KiB (but which still appears to be a normal 32-bit LX binary).

    However, with only ONE exception, I had exactly zero issues running ANY binary linked by ANY version of the linker on ANY other version of the OS that I tested. I could take the one linked by 4.5 and run it on 6.167, and vice-versa. I could take the one linked by Citrix MU2 and run it under OS/2 2.0 w/ FixPak 2 (and also vice-versa). They all worked perfectly fine.

    …all of them, except for 6.123. I could not run the binary linked by 6.123 on any other version of OS/2 that I tested, and I could not run any of the other OS/2 version’s binaries on 6.123.

    But this is to be expected, since 6.167 was the release where IBM switched not only from Desktop Manager to Workplace Shell, but also to using LX format binaries instead of LE! So from 6.167 to the present, all binaries are compatible with all other versions that use LX (just simply at the executable format level, of course…not talking about APIs here!). 6.123 uses LE, so it can neither run the binaries linked by the other versions, nor can its binaries be executed by the other versions.

    I can’t explain why (from your later post) 2ine was unable to run any of the binaries you linked using LX versions of link386, but could run the one linked by wlink. I especially can’t explain the different errors you got for the different binaries, given how few actual differences I saw between most of the binaries I made. Perhaps when I have some time I’ll download and build 2ine and try to have it run my executables. That, and/or I can send you copies of mine for you to look at and test.

    • I also need to figure out where native 2ine went wrong.

      But yeah the 2.00 landscape is a wasteland!

      In retrospect I’m surprised anything worked, that wasn’t 16bit or MS-DOS/Windows. No doubt the graveyard of LE and LX is a much bigger tale that is forgotten and buried

      • “But yeah the 2.00 landscape is a wasteland!”

        Eh. I guess I don’t see it that way. LE was only ever restricted to non-supported beta (alpha, really) versions…no retail release of the OS ever shipped with LE loader support. Making a change of this nature before the first shippable version is fair game as far as I’m concerned. And my experiments show that any version with LX support (spanning all the way from 2.0 pre-releases to the final release Warp) are all forwards- and backwards-compatible with each other.

        • Agree that it’s not a wasteland, and apologies if this is a stupid question, but what reason did developers have to write a 32-bit LX application as opposed to a 16 bit NE one? Both had access to plenty of memory and the OS/2 API; but due to the divorce the 16 bit tools were more plentiful/mature.

          Also, thanks for the earlier comment about family mode. I bought a copy of Microsoft C 5.1 from eBay years ago, and it turns out to have everything needed to build family mode apps, including using the C runtime library. It just doesn’t contain libs and documentation for PM, but I’m sure building PM apps with it would be straightforward.

          • The bind command will make it family mode at the end. There should be some papers with what is allowed in common space somewhere.

            For presentation manager you need the “toolkit”. I have a legit box it’s so tiny, sadly diskettes we’re not in the box though.

          • “Need” is an interesting term. Somewhat philosophically, in the digital realm, I don’t think we need anything in that it can be recreated – the only question is how difficult that recreation will be.

            In Win32 import libraries can be recreated from DLLs, since those need to indicate their exports in order to perform run-time linking. The headers need to be created, but API documentation online is easy to come by.

          • I found something called “IBM OS2 1.2 SDK (3.5”)” which does have a pm.h so I think that is the tool kit. The box I got that had no disks is the OS/2 Presentation Manager Softset, which I think might just be a subset of the SDK?

            Also Watcom 10 should have 16bit PM stuff? I think the toolkit is there on the 10.00 for 2.1 for sure!

          • I want to say even OpenWatcom can do 16-bit PM, but don’t quote me. It can certainly do 16-bit command-line OS/2 though.

          • 16-bit protected mode still enforced a maximum segment size of 64KiB I believe, so even if the amount of addressable memory is greater than 16-bit real mode, it’d still be more of a pain to utilize.

            I’m not a PM guru, but I’m pretty sure that though they are related, there are separate 16-bit PM APIs and 32-bit PM APIs. I’m not sure if you could necessarily take a 16-bit PM app and merely recompile…there might be API call tweaks you’d have to make even irrespective of any memory model changes. And so it also wouldn’t surprise me if there were more features available to be taken advantage of in 32-bit PM vs. 16-bit (don’t ask me what though)…I’m sure any new APIs introduced to 32-bit PM were never backported (if 16-bit OS/2 was no longer being marketed, sold, or developed further, what would be the point!)

          • Agree about the segment size limit, although with decent data structures and/or a good compiler, that never seemed like a big problem. (Do you really need a flat buffer bigger than 64Kb?)

            For the 16/32 bit API differences, I know nothing so tried to find things out. Superficially it seems very different to Windows where the 16 bit OS had very limited capabilities, so moving to NT meant a pile of important OS enhancements, and moving to 95 meant a pile of important UI enhancements. OS/2 already had a better OS, and I didn’t see a whole lot of visible UI changes for applications in 2.0.

            This is the best resource I’ve found (sorry for the huge link):
            https://books.google.com/books?id=u7WbsmbttwYC&lpg=PT368&ots=vSZr9H-R1Q&dq=power%20programming&pg=PT368#v=onepage&q&f=false

            It emphasizes things like the calling convention change, changing the names of kernel exports, etc, but is remarkably short on examples of things that are inexpressible in 16 bit. It includes “The 32 bit Presentation Manager API is essentially identical to the 16 bit PM API” and goes on to talk about the rest of the system.

            The most interesting detail I hadn’t considered is that a lot of capabilities were added in 1.2 along with HPFS to support long file names. Those won’t be used by C 5.1, and creating a “good” OS/2 application still wants to target at least 1.2.

          • Good find with that article, though I’ll point out that being published in June 1990 means it was well before the IBM/MS split. So not only was this early on in the development cycle of 32-bit OS/2, but there was more than enough time in the following 1.5 years for IBM to decide to change things (even things that may not have been on the table to be changed while MS was still involved…I’m honestly not even convinced that WPS would have shipped with 2.0 if it had remained a joint project!). Just like with the LE to LX executable format change, if they were going to make any changes to APIs, this was THE time to do it, since nobody had yet shipped any production 32-bit OS/2 software!

            It’s very possible that any analogs to 16-bit PM APIs in 32-bit PM largely remained unchanged, but I’m also thinking of later additions. I find it hard to believe that Warp 3.0 and 4.0 did nothing to expand the scope and feature set of PM, especially as even basic 2D graphics capabilities of PCs themselves continued to advance (32-bit color, etc.; when 2.0 was released in 1992, you were lucky to have 8-bit color at a decent resolution! and OS/2 — to my great shock — ended up attracting a surprising number of photo editing titles in the 32-bit era, all of which I’m sure turned out to be much easier to implement as
            “32-bit clean” top to bottom than they would have been: Compart ImpOS/2, SPG ColorWorks, TrueSpectra Photo>Graphics).

            So it probably depends on when in time you’re asking. Right around the time that 2.0 shipped, maybe the cost-benefit of building a 32-bit version of your existing 16-bit app wouldn’t have made much sense, but (maybe depending on your app and what it does) that calculus surely changed as the years went on. If you wanted to take advantage of conveniences afforded by new APIs in newer versions of the OS but these new APIs were only offered in 32-bit versions, well… (also, if you wanted to write classes for WPS, WPS itself was a 32-bit PM app, and so your class code running in the same process space as WPS also needs to be 32-bit)

          • …re: OS/2 1.2 improvements, I wonder if Microsoft C 6.0 supports most or all of them. MSC6 definitely still supports building for 16-bit OS/2.

            Also, one example that just occurred to me of a set of applications that absolutely won’t run on anything older than Warp 3 due to taking advantage of PM APIs that didn’t exist before would be those that use Open32, which was kind of a layer atop PM that IBM released which mimicked Win32. These were definitely utilized by Lotus (by that point “an IBM company”) to get SmartSuite 96 onto OS/2. The Open32 stuff wasn’t just a DLL shim or portable runtime that could be shipped alongside your code…it was part of the OS. You had to actually install a FixPak to Warp 3 or 4 that included the Open32 frameworks in order to be able to run SmartSuite/2 on your system.

          • I’m sure MSC6 has better support; it just wasn’t the version I ended up with. 5.1 works but is quite rough; the binaries aren’t marked as windowcompat, for example, so it’s not much fun to use.

            The reason I was asking about 16 bit as a viable app target is because the divorce left OS/2 without a good 32 bit toolchain in 1992. By the time things like Open32 came along, that problem was solved – or perhaps, solving it was a prerequisite for Open32 to make any sense.

            Fwiw, I have a copy of SmartSuite 1.1 which is before the IBM/Lotus acquisition, and I’ve never opened it or inspected what it does. But for something of that era, it sounds like we’re agreeing that it would have been practical to have a 16 bit port of a 16 bit Windows program, and Open32 was really about evolving as that changed.

        • On the topic of Open32 – does anyone know what development tools exist for it?

          I know about Odin, which seems widely available. Open32’s headers/libs/docs seem to have only been published through the Warp 4 toolkit, given to Developer Connection subscribers. But I don’t see any indication that these toolkits were ever downloadable or freely redistributable, and by 1996 there weren’t third parties investing in the area either (eg. no Borland or Watcom support.)

          Since the DLL is part of the OS and has to contain at least function names, and the Win32 function signatures are well known, it’d be easy enough to approximate; but since it’s only trying to be source compatible it could easily diverge in ways that aren’t obvious.

Leave a Reply