Inside Windows 3
18 points by calvin
18 points by calvin
Matt Pietrek has a book (Windows Internals) that explored Windows 3.1 internals based off of disassembly: http://www.bitsavers.org/pdf/microsoft/windows_3.1/Pietrek_-_Windows_Internals_1993.pdf
I have a hard copy of this book and I can read 16 bit x86 assembly but it’s still way over my head.
Win16 DLLs are quite interesting. Most platforms’ shared libraries are position-independent code, but this was hard on the 8086. The 8086 had no way of doing PC-relative addressing. To get the PC, you did a call instruction one instruction forward and then popped the spilled return address from the stack. That then consumed a register, and there weren’t many available (four main registers, four index registers including the stack pointer, and constraints for a number of instructions on which registers they could use). This meant that position-independent code was much slower than normal code (and nothing was fast on these machines).
Instead, Windows 3.0 made DLLs position dependent and statically relocated them. Each DLL had a base address where it would be loaded and the system included a constraint solver that would look at every Windows .EXE, find the DLLs it loaded, and work out a layout that allowed every DLL to be loaded at non-overlapping addresses.
As I recall, Win32 did the same thing, but had two advantages. First, they had a much bigger address space, so mapping each DLL at a unique location was much easier. Second, as I recall, Windows 95 and NT had a fallback mechanism where they could load a DLL twice and relocate it differently, so if the constraint solver failed to find a solution they could still run programs.
It’s interesting that 64-bit x86 moved towards making RIP-relative addressing cheaper, when a 64-bit (or 48-bit) address space pretty much guarantees that any system would be able to do the win16 approach there. Since then, there’s been more work on code signing and immutable images, which make install-time relocation less desirable, so in retrospect it was probably the right call.
I don’t recall any Win 3.0 or 3.1 constraint resolver, just a slightly tweaked version of the usual DOS exe fixup mechanism for loaded executables.
That was all just part of the NE handler, and a growth out of the prior DOS dynamic overlay handler, whereby bits of the NE were dynamically discardable; i.e. the same “technology” which was in Windows 1 and 2. That all largely became irrelevant with the 286 protected mode support added later, but it still had discardable segments.
But again I don’t recall anything as complex as a constraint resolver - just load it in memory, generally above 1M at the first available slot. With memory below 1M being avoided so it could be used for the DOS PSP such that calling down to DOS could still easily work.
What is your reference for your suggestion of a Windows 3 era constraint resolver?