Overview
Brief overview of Wine's architecture...
Wine Overview
With the fundamental architecture of Wine stabilizing, and
people starting to think that we might soon be ready to
actually release this thing, it may be time to take a look at
how Wine actually works and operates.
Foreword
Wine is often used as a recursive acronym, standing for
"Wine Is Not an Emulator". Sometimes it is also known to be
used for "Windows Emulator". In a way, both meanings are
correct, only seen from different perspectives. The first
meaning says that Wine is not a virtual machine, it does not
emulate a CPU, and you are not supposed to install
Windows nor any Windows device drivers on top of it; rather,
Wine is an implementation of the Windows API, and can be
used as a library to port Windows applications to Unix. The
second meaning, obviously, is that to Windows binaries
(.exe files), Wine does look like
Windows, and emulates its behaviour and quirks rather
closely.
"Emulator"
The "Emulator" perspective should not be thought of as if
Wine is a typical inefficient emulation layer that means
Wine can't be anything but slow - the faithfulness to the
badly designed Windows API may of course impose a minor
overhead in some cases, but this is both balanced out by
the higher efficiency of the Unix platforms Wine runs on,
and that other possible abstraction libraries (like Motif,
GTK+, CORBA, etc) has a runtime overhead typically
comparable to Wine's.
Executables
Wine's main task is to run Windows executables under non
Windows operating systems. It supports different types of
executables:
DOS executable. Those are even older programs, using
the DOS format (either .com or
.exe (the later being also called
MZ)).
Windows NE executable, also called 16 bit. They were
the native processes run by Windows 2.x and 3.x. NE
stands for New Executable <g>.
Windows PE executable. These are programs were
introduced in Windows 95 (and became the native
formats for all later Windows version), even if 16 bit
applications were still supported. PE stands for
Portable Executable, in a sense where the format of
the executable (as a file) is independent of the CPU
(even if the content of the file - the code - is CPU
dependent).
Winelib executable. These are applications, written
using the Windows API, but compiled as a Unix
executable. Wine provides the tools to create such
executables.
Let's quickly review the main differences for the supported
executables:
Wine executables
DOS (.COM or .EXE)
Win16 (NE)
Win32 (PE)
Winelib
Multitasking
Only one application at a time (except for TSR)
Cooperative
Preemptive
Preemptive
Address space
One MB of memory, where each application is loaded
and unloaded.
All 16 bit applications share a single address
space, protected mode.
Each application has it's own address
space. Requires MMU support from CPU.
Each application has it's own address
space. Requires MMU support from CPU.
Windows API
No Windows API but the DOS API (like Int
21h traps).
Will call the 16 bit Windows API.
Will call the 32 bit Windows API.
Will call the 32 bit Windows API, and possibly
also the Unix APIs.
Code (CPU level)
Only available on x86 in real mode. Code and data
are in segmented forms, with 16 bit
offsets. Processor is in real mode.
Only available on IA-32 architectures, code and
data are in segmented forms, with 16 bit offsets
(hence the 16 bit name). Processor is in protected
mode.
Available (with NT) on several CPUs, including
IA-32. On this CPU, uses a flat memory model with
32 bit offsets (hence the 32 bit name).
Flat model, with 32 bit addresses.
Multi-threading
Not available.
Not available.
Available.
Available, but must use the Win32 APIs for
threading and synchronization, not the Unix ones.
Wine deals with this issue by launching a separate Wine process (which
is in fact a Unix process) for each Win32 process, but not for Win16
tasks. Win16 tasks are run as different intersynchronized Unix-threads
in the same dedicated Wine process; this Wine process is commonly
known as a WOW process (Windows on Windows),
referring to a similar mechanism used by Windows NT.
Synchronization between the Win16 tasks running in the WOW
process is normally done through the Win16 mutex - whenever
one of them is running, it holds the Win16 mutex, keeping
the others from running. When the task wishes to let the
other tasks run, the thread releases the Win16 mutex, and
one of the waiting threads will then acquire it and let its
task run.
winevdm is the Wine process dedicated to running the
Win16 processes. Note that several instances of this process could
exist, has Windows has support for different VDM (Virtual Dos
Machines) in order to have Win16 processes running in different
address spaces. Wine also uses the same architecture to run DOS
programs (in this case, the DOS emulation is provided by a Wine only
DLL called winedos.
Standard Windows Architectures
Windows 9x architecture
The windows architecture (Win 9x way) looks like this:
+---------------------+ \
| Windows EXE | } application
+---------------------+ /
+---------+ +---------+ \
| Windows | | Windows | \ application & system DLLs
| DLL | | DLL | /
+---------+ +---------+ /
+---------+ +---------+ \
| GDI32 | | USER32 | \
| DLL | | DLL | \
+---------+ +---------+ } core system DLLs
+---------------------+ /
| Kernel32 DLL | /
+---------------------+ /
+---------------------+ \
| Win9x kernel | } kernel space
+---------------------+ /
+---------------------+ \
| Windows low-level | \ drivers (kernel space)
| drivers | /
+---------------------+ /
Windows NT architecture
The windows architecture (Windows NT way) looks like the
following drawing. Note the new DLL
(NTDLL) which allows implementing
different subsystems (as win32);
kernel32 in NT architecture
implements the Win32 subsystem on top of
NTDLL.
+---------------------+ \
| Windows EXE | } application
+---------------------+ /
+---------+ +---------+ \
| Windows | | Windows | \ application & system DLLs
| DLL | | DLL | /
+---------+ +---------+ /
+---------+ +---------+ +-----------+ \
| GDI32 | | USER32 | | | \
| DLL | | DLL | | | \
+---------+ +---------+ | | \ core system DLLs
+---------------------+ | | / (on the left side)
| Kernel32 DLL | | Subsystem | /
| (Win32 subsystem) | |Posix, OS/2| /
+---------------------+ +-----------+ /
+---------------------------------------+
| NTDLL.DLL |
+---------------------------------------+
+---------------------------------------+ \
| NT kernel | } NT kernel (kernel space)
+---------------------------------------+ /
+---------------------------------------+ \
| Windows low-level drivers | } drivers (kernel space)
+---------------------------------------+ /
Note also (not depicted in schema above) that the 16 bit
applications are supported in a specific subsystem.
Some basic differences between the Win9x and the NT
architectures include:
Several subsystems (Win32, Posix...) can be run on NT,
while not on Win 9x
Win 9x roots its architecture in 16 bit systems, while
NT is truly a 32 bit system.
The drivers model and interfaces in Win 9x and NT are
different (even if Microsoft tried to bridge the gap
with some support of WDM drivers in Win 98 and above).
Wine architecture
Global picture
Wine implementation is closer to the Windows NT
architecture, even if several subsystems are not implemented
yet (remind also that 16bit support is implemented in a 32-bit
Windows EXE, not as a subsystem). Here's the overall picture:
+---------------------+ \
| Windows EXE | } application
+---------------------+ /
+---------+ +---------+ \
| Windows | | Windows | \ application & system DLLs
| DLL | | DLL | /
+---------+ +---------+ /
+---------+ +---------+ +-----------+ +--------+ \
| GDI32 | | USER32 | | | | | \
| DLL | | DLL | | | | Wine | \
+---------+ +---------+ | | | Server | \ core system DLLs
+---------------------+ | | | | / (on the left side)
| Kernel32 DLL | | Subsystem | | NT-like| /
| (Win32 subsystem) | |Posix, OS/2| | Kernel | /
+---------------------+ +-----------+ | | /
| |
+---------------------------------------+ | |
| NTDLL | | |
+---------------------------------------+ +--------+
+---------------------------------------+ \
| Wine executable (wine-?thread) | } unix executable
+---------------------------------------+ /
+---------------------------------------------------+ \
| Wine drivers | } Wine specific DLLs
+---------------------------------------------------+ /
+------------+ +------------+ +--------------+ \
| libc | | libX11 | | other libs | } unix shared libraries
+------------+ +------------+ +--------------+ / (user space)
+---------------------------------------------------+ \
| Unix kernel (Linux,*BSD,Solaris,OS/X) | } (Unix) kernel space
+---------------------------------------------------+ /
+---------------------------------------------------+ \
| Unix device drivers | } Unix drivers (kernel space)
+---------------------------------------------------+ /
Wine must at least completely replace the "Big Three" DLLs
(KERNEL/KERNEL32,
GDI/GDI32, and
USER/USER32),
which all other DLLs are layered on top of. But since Wine
is (for various reasons) leaning towards the NT way of
implementing things, the NTDLL is
another core DLL to be implemented in Wine, and many
KERNEL32 and
ADVAPI32 features will be
implemented through the NTDLL.
As of today, no real subsystem (apart the Win32 one) has
been implemented in Wine.
The Wine server provides the backbone for the implementation
of the core DLLs. It mainly implementents inter-process
synchronization and object sharing. It can be seen, from a
functional point of view, as a NT kernel (even if the APIs
and protocols used between Wine's DLL and the Wine server
are Wine specific).
Wine uses the Unix drivers to access the various hardware
pieces on the box. However, in some cases, Wine will
provide a driver (in Windows sense) to a physical hardware
device. This driver will be a proxy to the Unix driver
(this is the case, for example, for the graphical part
with X11 or SDL drivers, audio with OSS or ALSA drivers...).
All DLLs provided by Wine try to stick as much as possible
to the exported APIs from the Windows platforms. There are
rare cases where this is not the case, and have been
propertly documented (Wine DLLs export some Wine specific
APIs). Usually, those are prefixed with
__wine.
Let's now review in greater details all of those components.
The Wine server
The Wine server is among the most confusing concepts in
Wine. What is its function in Wine? Well, to be brief, it
provides Inter-Process Communication (IPC),
synchronization, and process/thread management. When the
Wine server launches, it creates a Unix socket for the
current host based on (see below) your home directory's
.wine subdirectory (or wherever the
WINEPREFIX environment variable points to)
- all Wine processes launched later connects to the Wine
server using this socket. If a Wine server was not
already running, the first Wine process will start up the
Wine server in auto-terminate mode (i.e. the Wine server
will then terminate itself once the last Wine process has
terminated).
In earlier versions of Wine the master socket mentioned
above was actually created in the configuration directory;
either your home directory's /wine
subdirectory or wherever the
WINEPREFIX environment variable points>.
Since that might not be possible the socket is actually
created within the /tmp directory
with a name that reflects the configuration directory.
This means that there can actually be several separate
copies of the Wine server running; one per combination of
user and configuration directory. Note that you should
not have several users using the same configuration
directory at the same time; they will have different
copies of the Wine server running and this could well
lead to problems with the registry information that
they are sharing.
Every thread in each Wine process has its own request
buffer, which is shared with the Wine server. When a
thread needs to synchronize or communicate with any other
thread or process, it fills out its request buffer, then
writes a command code through the socket. The Wine server
handles the command as appropriate, while the client
thread waits for a reply. In some cases, like with the
various WaitFor??? synchronization
primitives, the server handles it by marking the client
thread as waiting and does not send it a reply before the
wait condition has been satisfied.
The Wine server itself is a single and separate Unix
process and does not have its own threading - instead, it
is built on top of a large poll()
loop that alerts the Wine server whenever anything
happens, such as a client having sent a command, or a wait
condition having been satisfied. There is thus no danger
of race conditions inside the Wine server itself - it is
often called upon to do operations that look completely
atomic to its clients.
Because the Wine server needs to manage processes,
threads, shared handles, synchronization, and any related
issues, all the clients' Win32 objects are also managed by
the Wine server, and the clients must send requests to the
Wine server whenever they need to know any Win32 object
handle's associated Unix file descriptor (in which case
the Wine server duplicates the file descriptor, transmits
it back to the client, and leaves it to the client to
close the duplicate when the client has finished with
it).
Wine builtin DLLs: about Relays, Thunks, and DLL
descriptors
This section mainly applies to builtin DLLs (DLLs provided
by Wine). See section for the
details on native vs. builtin DLL handling.
Loading a Windows binary into memory isn't that hard by
itself, the hard part is all those various DLLs and entry
points it imports and expects to be there and function as
expected; this is, obviously, what the entire Wine
implementation is all about. Wine contains a range of DLL
implementations. You can find the DLLs implementation in the
dlls/ directory.
Each DLL (at least, the 32 bit version, see below) is
implemented in a Unix shared library. The file name of this
shared library is the module name of the DLL with a
.dll.so suffix (or
.drv.so or any other relevant extension
depending on the DLL type). This shared library contains the
code itself for the DLL, as well as some more information,
as the DLL resources and a Wine specific DLL descriptor.
The DLL descriptor, when the DLL is instanciated, is used to
create an in-memory PE header, which will provide access to
various information about the DLL, including but not limited
to its entry point, its resources, its sections, its debug
information...
The DLL descriptor and entry point table is generated by
the winebuild tool (previously just
named build), taking DLL specification
files with the extension .spec as
input. Resources (after compilation by
wrc) or message tables (after
compilation by wmc) are also added to
the descriptor by winebuild.
Once an application module wants to import a DLL, Wine
will look at:
through its list of registered DLLs (in fact, both
the already loaded DLLs, and the already loaded
shared libraries which has registered a DLL
descriptor). Since, the DLL descriptor is
automatically registered when the shared library is
loaded - remember, registration call is put inside a
shared library constructor - using the
PRELOAD environment variable when
running a Wine process can force the registration of
some DLL descriptors.
If it's not registered, Wine will look for it on
disk, building the shared library name from the DLL
module name. Directory searched for are specified by
the WINEDLLPATH environment variable.
Failing that, it will look for a real Windows
.DLL file to use, and look
through its imports, etc) and use the loading of
native DLLs.
After the DLL has been identified (assuming it's still a
native one), it's mapped into memory using a
dlopen() call. Note, that Wine doesn't
use the shared library mechanisms for resolving and/or
importing functions between two shared libraries (for two
DLLs). The shared library is only used for providing a way
to load a piece of code on demand. This piece of code,
thanks the DLL descriptor, will provide the same type of
information a native DLL would. Wine can then use the same
code for native and builtin DLL to handle imports/exports.
Wine also relies on the dynamic loading features of the Unix
shared libraries to relocate the DLLs if needed (the same
DLL can be loaded at different address in two different
processes, and even in two consecutive run of the same
executable if the order of loading the DLLs differ).
The DLL descriptor is registered in the Wine realm using
some tricks. The winebuild tool, while
creating the code for DLL descriptor, also creates a
constructor, that will be called when the shared library is
loaded into memory. This constructor will actually register
the descriptor to the Wine DLL loader. Hence, before the
dlopen call returns, the DLL descriptor
will be known and registered. This also helps to deal with
the cases where there's still dependencies (at the ELF
shared lib level, not at the embedded DLL level) between
different shared libraries: the embedded DLLs will be
properly registered, and even loaded (from a Windows point
of view).
Since Wine is 32-bit code itself, and if the compiler
supports Windows' calling convention, stdcall
(gcc does), Wine can resolve imports
into Win32 code by substituting the addresses of the Wine
handlers directly without any thunking layer in
between. This eliminates the overhead most people
associate with "emulation", and is what the applications
expect anyway.
However, if the user specified WINEDEBUG=+relay
, a thunk layer is inserted between the
application imports and the Wine handlers (actually the
export table of the DLL is modified, and a thunk is
inserted in the table); this layer is known as "relay"
because all it does is print out the arguments/return
values (by using the argument lists in the DLL
descriptor's entry point table), then pass the call on,
but it's invaluable for debugging misbehaving calls into
Wine code. A similar mechanism also exists between Windows
DLLs - Wine can optionally insert thunk layers between
them, by using WINEDEBUG=+snoop,
but since no DLL descriptor information exists for
non-Wine DLLs, this is less reliable and may lead to
crashes.
For Win16 code, there is no way around thunking - Wine
needs to relay between 16-bit and 32-bit code. These
thunks switch between the app's 16-bit stack and Wine's
32-bit stack, copies and converts arguments as appropriate
(an int is 16 bit 16-bit and 32 bits in 32-bit, pointers
are segmented in 16 bit (and also near or far) but are 32
bit linear values in 32 bit), and handles the Win16
mutex. Some finer control can be obtained on the
conversion, see winebuild reference
manual for the details. Suffice to say that the kind of
intricate stack content juggling this results in, is not
exactly suitable study material for beginners.
A DLL descriptor is also created for every 16 bit
DLL. However, this DLL normally paired with a 32 bit
DLL. Either, it's the 16 bit counterpart of the 16 bit DLL
(KRNL386.EXE for
KERNEL32, USER
for USER32...), or a 16
bit DLL directly linked to a 32 bit DLL (like
SYSTEM for KERNEL32,
or DDEML for
USER32). In those cases, the 16 bit
descriptor(s) is (are) inserted in the same shared library
as the the corresponding 32 bit DLL. Wine will also create
symbolic links between kernel32.dll.so and system.dll.so
so that loading of either
KERNEL32.DLL or
SYSTEM.DLL will end up on the same
shared library.
Wine/Windows DLLs
This document mainly deals with the status of current DLL
support by Wine. The Wine ini file currently supports
settings to change the load order of DLLs. The load order
depends on several issues, which results in different settings
for various DLLs.
Pros of Native DLLs
Native DLLs of course guarantee 100% compatibility for
routines they implement. For example, using the native
USER DLL would maintain a virtually
perfect and Windows 95-like look for window borders,
dialog controls, and so on. Using the built-in Wine
version of this library, on the other hand, would produce
a display that does not precisely mimic that of Windows
95. Such subtle differences can be engendered in other
important DLLs, such as the common controls library
COMMCTRL or the common dialogs library
COMMDLG, when built-in Wine DLLs
outrank other types in load order.
More significant, less aesthetically-oriented problems can
result if the built-in Wine version of the
SHELL DLL is loaded before the native
version of this library. SHELL
contains routines such as those used by installer utilities
to create desktop shortcuts. Some installers might fail when
using Wine's built-in SHELL.
Cons of Native DLLs
Not every application performs better under native DLLs. If
a library tries to access features of the rest of the system
that are not fully implemented in Wine, the native DLL might
work much worse than the corresponding built-in one, if at
all. For example, the native Windows GDI
library must be paired with a Windows display driver, which
of course is not present under Intel Unix and Wine.
Finally, occasionally built-in Wine DLLs implement more
features than the corresponding native Windows DLLs.
Probably the most important example of such behavior is the
integration of Wine with X provided by Wine's built-in
USER DLL. Should the native Windows
USER library take load-order
precedence, such features as the ability to use the
clipboard or drag-and-drop between Wine windows and X
windows will be lost.
Deciding Between Native and Built-In DLLs
Clearly, there is no one rule-of-thumb regarding which
load-order to use. So, you must become familiar with
what specific DLLs do and which other DLLs or features
a given library interacts with, and use this information
to make a case-by-case decision.
Load Order for DLLs
Using the DLL sections from the wine configuration file, the
load order can be tweaked to a high degree. In general it is
advised not to change the settings of the configuration
file. The default configuration specifies the right load
order for the most important DLLs.
The default load order follows this algorithm: for all DLLs
which have a fully-functional Wine implementation, or where
the native DLL is known not to work, the built-in library
will be loaded first. In all other cases, the native DLL
takes load-order precedence.
The DefaultLoadOrder from the
[DllDefaults] section specifies for all DLLs which version
to try first. See manpage for explanation of the arguments.
The [DllOverrides] section deals with DLLs, which need a
different-from-default treatment.
The [DllPairs] section is for DLLs, which must be loaded in
pairs. In general, these are DLLs for either 16-bit or
32-bit applications. In most cases in Windows, the 32-bit
version cannot be used without its 16-bit counterpart. For
Wine, it is customary that the 16-bit implementations rely
on the 32-bit implementations and cast the results back to
16-bit arguments. Changing anything in this section is bound
to result in errors.
For the future, the Wine implementation of Windows DLL seems
to head towards unifying the 16 and 32 bit DLLs wherever
possible, resulting in larger DLLs. They are stored in the
dlls/ subdirectory using the 32-bit
name.
Memory management
Every Win32 process in Wine has its own dedicated native
process on the host system, and therefore its own address
space. This section explores the layout of the Windows
address space and how it is emulated.
Firstly, a quick recap of how virtual memory works. Physical
memory in RAM chips is split into
frames, and the memory that each
process sees is split into pages. Each
process has its own 4 gigabytes of address space (4gig being
the maximum space addressable with a 32 bit pointer). Pages
can be mapped or unmapped: attempts to access an unmapped
page cause an
EXCEPTION_ACCESS_VIOLATION which has
the easily recognizable code of
0xC0000005. Any page can be mapped to
any frame, therefore you can have multiple addresses which
actually "contain" the same memory. Pages can also be mapped
to things like files or swap space, in which case accessing
that page will cause a disk access to read the contents into
a free frame.
Initial layout (in Windows)
When a Win32 process starts, it does not have a clear
address space to use as it pleases. Many pages are already
mapped by the operating system. In particular, the EXE
file itself and any DLLs it needs are mapped into memory,
and space has been reserved for the stack and a couple of
heaps (zones used to allocate memory to the app
from). Some of these things need to be at a fixed address,
and others can be placed anywhere.
The EXE file itself is usually mapped at address
0x400000 and up: indeed, most EXEs have
their relocation records stripped which means they must be
loaded at their base address and cannot be loaded at any
other address.
DLLs are internally much the same as EXE files but they
have relocation records, which means that they can be
mapped at any address in the address space. Remember we
are not dealing with physical memory here, but rather
virtual memory which is different for each
process. Therefore OLEAUT32.DLL may
be loaded at one address in one process, and a totally
different one in another. Ensuring all the functions
loaded into memory can find each other is the job of the
Windows dynamic linker, which is a part of
NTDLL.
So, we have the EXE and its DLLs mapped into memory. Two
other very important regions also exist: the stack and the
process heap. The process heap is simply the equivalent of
the libc malloc arena on UNIX: it's a
region of memory managed by the OS which
malloc/HeapAlloc
partitions and hands out to the application. Windows
applications can create several heaps but the process heap
always exists.
Windows 9x also implements another kind of heap: the
shared heap. The shared heap is unusual in that
anything allocated from it will be visible in every other
process.
Comparison
So far we've assumed the entire 4 gigs of address space is
available for the application. In fact that's not so: only
the lower 2 gigs are available, the upper 2 gigs are on
Windows NT used by the operating system and hold the
kernel (from 0x80000000). Why is the
kernel mapped into every address space? Mostly for
performance: while it's possible to give the kernel its own
address space too - this is what Ingo Molnars 4G/4G VM
split patch does for Linux - it requires that every system
call into the kernel switches address space. As that is a
fairly expensive operation (requires flushing the
translation lookaside buffers etc) and syscalls are made
frequently it's best avoided by keeping the kernel mapped
at a constant position in every processes address space.
Basically, the comparison of memory mappings looks as
follows:
Memory layout (Windows and Wine)
Address
Windows 9x
Windows NT
Linux
00000000-7fffffff
User
User
User
80000000-bfffffff
Shared
User
User
c0000000-ffffffff
Kernel
Kernel
Kernel
On Windows 9x, in fact only the upper gigabyte
(0xC0000000 and up) is used by the
kernel, the region from 2 to 3 gigs is a shared area used
for loading system DLLs and for file mappings. The bottom
2 gigs on both NT and 9x are available for the programs
memory allocation and stack.
Wine drivers
Wine will not allow running native Windows drivers under
Unix. This comes mainly because (look at the generic
architecture schemas) Wine doesn't implement the kernel
features of Windows (kernel here really means the kernel,
not the KERNEL32 DLL), but rather
sets up a proxy layer on top of the Unix kernel to provide
the NTDLL and
KERNEL32 features. This means that
Wine doesn't provide the inner infrastructure to run
native drivers, either from the Win9x family or from the
NT family.
In other words, Wine will only be able to provide access to
a specific device, if and only if, 1/ this device is
supported in Unix (there is Unix-driver to talk to it), 2/
Wine has implemented the proxy code to make the glue between
the API of a Windows driver, and the Unix interface of the
Unix driver.
Wine, however, tries to implement in the various DLLs
needing to access devices to do it through the standard
Windows APIs for device drivers in user space. This is for
example the case for the multimedia drivers, where Wine
loads Wine builtin DLLs to talk to the OSS interface, or the
ALSA interface. Those DLLs implement the same interface as
any user space audio driver in Windows.