Running Windows 95 Binaries on Linux: What wsl9x Actually Has to Solve
Source: lobsters
The name is a deliberate inversion. Microsoft ships WSL, the Windows Subsystem for Linux, to let developers run ELF binaries and bash scripts inside Windows. wsl9x flips that: a Windows 9x subsystem for Linux, letting you run old Win32 and Win16 PE executables from the Windows 95/98/ME era directly on a Linux host. The joke in the name is obvious, but what makes the project genuinely interesting is the target. Windows 9x is not just an older version of Windows NT. It is a fundamentally different operating system, and building a compatibility layer for it requires solving a different set of problems than Wine has spent two decades solving.
Why 9x Is a Separate Problem
Wine runs Windows applications on Linux by implementing the Win32 API. It handles NT-lineage executables: the kind that assume a proper microkernel-influenced architecture, structured driver model, and stable ABI that Microsoft has maintained from Windows 2000 onward. Wine is good at this. It has a working DirectX stack, registry emulation, COM infrastructure, and coverage of the vast majority of APIs an NT-era application would call.
Windows 9x predates that stability. The 9x kernel, internally called VMM32 (Virtual Machine Manager), was a 32-bit protected-mode kernel that loaded during boot but retained a live, running MS-DOS instance underneath it at all times. Applications could drop into real mode. VxD drivers (Virtual Device Drivers) ran in Ring 0 without any of the WDM (Windows Driver Model) isolation that NT introduced. User-mode code on Windows 9x could, under certain conditions, touch Ring 0 directly, which is why so many games and multimedia applications of that era worked at all without proper audio or video drivers.
The memory model was also hybrid in a way NT was not. Windows 9x used a single shared address space for all processes in the region from 0x80000000 to 0xBFFFFFFF. Every process could read and write that range. This was not a bug people exploited; it was the mechanism by which Windows 9x shared system DLLs like KERNEL32, USER32, and GDI32 across processes without per-process mappings. An NT compatibility layer can assume separation. A 9x compatibility layer cannot, at least not naively.
The Thunk Problem
Windows 9x was also a bridge OS in the literal sense. It carried Win16 support not as an afterthought but as a core feature, because the transition from Windows 3.1 was still in progress throughout the mid-1990s. The mechanism it used to let 16-bit Win16 code and 32-bit Win32 code call each other was called thunking.
Flat thunks, generic thunks, and universal thunks all served different purposes depending on the direction of the call and the calling convention involved. A 16-bit application calling a 32-bit DLL would go through a flat thunk stub that translated the 16:16 segmented pointer model into flat 32-bit addressing. A 32-bit application calling a 16-bit DLL went the other direction. These were not trivial translations; segment registers had to be managed, stack frames rebuilt, and the processor’s actual segment descriptor tables updated in some cases.
Any compatibility layer targeting 9x-era software has to decide how deeply to handle Win16 compatibility. If the goal is purely Win32 executables compiled for Windows 95 targets, you might avoid most of this. But plenty of 9x-era software shipped with 16-bit installer stubs, 16-bit helper DLLs, or used the Win16 messaging system for interop. Ignoring Win16 means a lot of software simply will not start.
What wsl9x Appears to Be Doing
From the project structure on Codeberg, wsl9x is early-stage work implementing a PE loader and Win32 API stub layer targeting Linux. The architecture resembles how Wine operates at its lowest level: load the PE executable into memory, map its import table to stub implementations of Windows functions, and handle system calls by translating them to Linux equivalents via ptrace or a seccomp-based mechanism.
The interesting question is how much of the 9x-specific behavior it will eventually need to reproduce. If the target is consumer software from 1995 to 2000, the answer is: quite a lot. Games from that era frequently assumed the shared memory region layout. Many used DirectDraw or DirectSound interfaces from DirectX 3 through 6, which had different COM interface GUIDs and method tables than the DirectX 9 and later interfaces Wine handles well. Multimedia applications used Video for Windows (VfW) and the ACM (Audio Compression Manager), both of which are present in Wine but in forms closer to their later NT implementations.
The VxD Question
The component most difficult to emulate is the VxD layer. In Windows 9x, VxDs were the hardware abstraction layer. VFAT.VXD handled the file system. NDIS.VXD handled networking. VWIN32.VXD provided thread and process management services to Win32. Applications did not usually call VxDs directly, but the system DLLs they used made VxD calls constantly through a mechanism called V86 mode interrupts and the protected-mode INT 21h handler.
For most application compatibility, you can paper over VxDs by providing clean Win32 API implementations that never let the call sink that deep. But copy protection schemes of the 9x era loved VxDs. StarForce, Safedisc, and various disc-check mechanisms used privileged Ring 0 VxD calls specifically because they were hard to intercept or emulate from user mode. DOSBox and Wine have both struggled with these for years.
Situating wsl9x in the Ecosystem
The existing landscape of 9x-era software preservation has several approaches. DOSBox emulates a complete x86 PC with DOS, which handles many early-90s games well but is not designed for Windows 9x. DOSBox-X extends this with partial Windows 3.1 and 9x support, but through full PC emulation, not binary compatibility. Wine handles 9x-era Win32 applications with varying success, but it is explicitly an NT compatibility layer and does not attempt to reproduce 9x-specific behaviors. QEMU with a Windows 98 image is the brute-force option and it works, but at the cost of running a full emulated PC.
wsl9x stakes out a different position: native code execution without emulation, targeting the 9x API surface specifically. If successful, 9x binaries would run at native speed on a Linux host, which no current option provides. The tradeoff is implementation depth: Wine has 30 years of development and thousands of contributors. A focused 9x layer starts from scratch.
Why Anyone Cares in 2026
This might seem like pure retrocomputing curiosity, but there are real preservation stakes. A substantial amount of educational software, early multimedia CD-ROMs, business applications, and games from the 1995 to 2001 window runs only on 9x. Some of it was never ported to XP or later. QEMU images of Windows 98 work but are fragile, require proprietary OS licenses to be legal, and are slow to maintain as host systems evolve.
A clean, open-source 9x compatibility layer that ran these binaries natively would be genuinely useful for archivists, for the Internet Archive which already hosts thousands of 9x-era Windows programs, and for anyone doing research on early-2000s software behavior. The Wine AppDB has thousands of entries flagging 9x-era software as broken or unsupported under Wine. That is the user base wsl9x could eventually serve.
The technical ambition is high and the project is early. But the decision to target the 9x API surface specifically, rather than extending Wine or wrapping a DOSBox instance, means the people building it understand what makes 9x distinct. That is the right starting point. Whether the thunk layer, the shared memory model, and the VxD surface eventually get implemented in full is the question that will determine whether this becomes genuinely useful or stays a proof of concept. The approach is sound. The scope is enormous.