Sweden-Number/documentation/winedev-kernel.sgml

965 lines
38 KiB
Plaintext
Raw Normal View History

<chapter>
<title>Kernel modules</title>
<para>
This section cover the kernel modules. As already stated, Wine
implements the NT architecture, hence provides NTDLL for the
core kernel functions, and KERNEL32, which is the
implementation of the basis of the Win32 subsystem, on top of
NTDLL.
</para>
<sect1 id="ntdll">
<title>NTDLL</title>
<para>
NTDLL provides most of the services you'd expect from a
kernel.
</para>
<para>
Process and thread management are part of them (even if
process management is still mainly done in KERNEL32, unlike
NT). A Windows process runs as a Unix process, and a Windows
thread runs as a Unix thread.
</para>
<para>
Wine also provide fibers (which is the Windows name of
co-routines).
</para>
<para>
Most of the Windows memory handling (Heap, Global and Local
functions, virtual memory...) are easily mapped upon their
Unix equivalents. Note the NTDLL doesn't know about 16 bit
memory, which is only handled in KERNEL32/KRNL386.EXE (and
also the DOS routines).
</para>
<sect2>
<title>File management</title>
<para>
Wine uses some configuration in order to map Windows
filenames (either defined with drive letters, or as UNC
names) to the unix filenames. Wine also uses some
incantation so that most of file related APIs can also
take full unix names. This is handy when passing filenames
on the command line.
</para>
<para>
File handles can be waitable objects, as Windows define
them.
</para>
<para>
Asynchronous I/O is implemented on file handles by
queueing pseudo APC. They are not real APC in the sense
that they have the same priority as the threads in the
considered process (while APCs on NT have normally a
higher priority). These APCs get called when invoking
Wine server (which should lead to correct behavior when the
program ends up waiting on some object - waiting always
implies calling Wine server).
</para>
<para>
FIXME: this should be enhanced and updated to latest work
on FS.
</para>
</sect2>
<sect2>
<title>Synchronization</title>
<para>
Most of the synchronization (between threads or processes)
is done in Wine server, which handles both the waiting
operation (on a single object or a set of objects) and the
signaling of objects.
</para>
</sect2>
<sect2>
<title>Module (DLL) loading</title>
<para>
Wine is able to load any NE and PE module. In all cases,
the module's binary code is directly executed by the
processor.
</para>
</sect2>
<sect2>
<title>Device management</title>
<para>
Wine allows usage a wide variety of devices:
<itemizedlist>
<listitem>
<para>
Communication ports are mapped to Unix
communication ports (if they have sufficient
permissions).
</para>
</listitem>
<listitem>
<para>
Parallel ports are mapped to Unix parallel ports (if
they have sufficient permissions).
</para>
</listitem>
<listitem>
<para>
CDROM: the Windows device I/O control calls are
mapped onto Unix <function>ioctl()</function>.
</para>
</listitem>
<listitem>
<para>
Some Win9x VxDs are supported, by rewriting some of
their internal behavior. But this support is
limited. Portable programs to Windows NT shouldn't
need them.
</para>
<para>
Wine will not support native VxD.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
<sect2 id="threading">
<title>Multi-threading in Wine</title>
<para>
This section will assume you understand the basics of
multithreading. If not there are plenty of good tutorials
available on the net to get you started.
</para>
<para>
Threading in Wine is somewhat complex due to several
factors. The first is the advanced level of multithreading
support provided by Windows - there are far more threading
related constructs available in Win32 than the Linux
equivalent (pthreads). The second is the need to be able to
map Win32 threads to native Linux threads which provides us
with benefits like having the kernel schedule them without
our intervention. While it's possible to implement threading
entirely without kernel support, doing so is not desirable
on most platforms that Wine runs on.
</para>
<sect3>
<title> Threading support in Win32 </title>
<para>
Win32 is an unusually thread friendly API. Not only is it
entirely thread safe, but it provides many different
facilities for working with threads. These range from the
basics such as starting and stopping threads, to the
extremely complex such as injecting threads into other
processes and COM inter-thread marshalling.
</para>
<para>
One of the primary challenges of writing Wine code
therefore is ensuring that all our DLLs are thread safe,
free of race conditions and so on. This isn't simple -
don't be afraid to ask if you aren't sure whether a piece
of code is thread safe or not!
</para>
<para>
Win32 provides many different ways you can make your code
thread safe however the most common are <emphasis>critical
section</emphasis> and the <emphasis>interlocked
functions</emphasis>. Critical sections are a type of
mutex designed to protect a geographic area of code. If
you don't want multiple threads running in a piece of code
at once, you can protect them with calls to
<function>EnterCriticalSection</function> and
<function>LeaveCriticalSection</function>. The first call
to <function>EnterCriticalSection</function> by a thread
will lock the section and continue without stopping. If
another thread calls it then it will block until the
original thread calls
<function>LeaveCriticalSection</function> again.
</para>
<para>
It is therefore vitally important that if you use critical
sections to make some code thread-safe, that you check
every possible codepath out of the code to ensure that any
held sections are left. Code like this:
</para>
<programlisting>
if (res != ERROR_SUCCESS) return res;
</programlisting>
<para>
is extremely suspect in a function that also contains a
call to <function>EnterCriticalSection</function>. Be
careful.
</para>
<para>
If a thread blocks while waiting for another thread to
leave a critical section, you will see an error from the
<function>RtlpWaitForCriticalSection</function> function,
along with a note of which thread is holding the
lock. This only appears after a certain timeout, normally
a few seconds. It's possible the thread holding the lock
is just being really slow which is why Wine won't
terminate the app like a non-checked build of Windows
would, but the most common cause is that for some reason a
thread forgot to call
<function>LeaveCriticalSection</function>, or died while
holding the lock (perhaps because it was in turn waiting
for another lock). This doesn't just happen in Wine code:
a deadlock while waiting for a critical section could be
due to a bug in the app triggered by a slight difference
in the emulation.
</para>
<para>
Another popular mechanism available is the use of
functions like <function>InterlockedIncrement</function>
and <function>InterlockedExchange</function>. These make
use of native CPU abilities to execute a single
instruction while ensuring any other processors on the
system cannot access memory, and allow you to do common
operations like add/remove/check a variable in thread-safe
code without holding a mutex. These are useful for
reference counting especially in free-threaded (thread
safe) COM objects.
</para>
<para>
Finally, the usage of TLS slots are also popular. TLS
stands for thread-local storage, and is a set of slots
scoped local to a thread which you can store pointers
in. Look on MSDN for the <function>TlsAlloc</function>
function to learn more about the Win32 implementation of
this. Essentially, the contents of a given slot will be
different in each thread, so you can use this to store
data that is only meaningful in the context of a single
thread. On recent versions of Linux the __thread keyword
provides a convenient interface to this functionality - a
more portable API is exposed in the pthread
library. However, these facilities is not used by Wine,
rather, we implement Win32 TLS entirely ourselves.
</para>
</sect3>
<sect3>
<title> SysLevels </title>
<para>
SysLevels are an undocumented Windows-internal
thread-safety system. They are basically critical sections
which must be taken in a particular order. The mechanism
is generic but there are always three syslevels: level 1
is the Win16 mutex, level 2 is the USER mutex and level 3
is the GDI mutex.
</para>
<para>
When entering a syslevel, the code (in
<filename>dlls/kernel/syslevel.c</filename>) will check
that a higher syslevel is not already held and produce an
error if so. This is because it's not legal to enter level
2 while holding level 3 - first, you must leave level 3.
</para>
<para>
Throughout the code you may see calls to
<function>_ConfirmSysLevel()</function> and
<function>_CheckNotSysLevel()</function>. These functions
are essentially assertions about the syslevel states and
can be used to check that the rules have not been
accidentally violated. In particular,
<function>_CheckNotSysLevel()</function> will break
(probably into the debugger) if the check fails. If this
happens the solution is to get a backtrace and find out,
by reading the source of the wine functions called along
the way, how Wine got into the invalid state.
</para>
</sect3>
<sect3>
<title> POSIX threading vs kernel threading </title>
<para>
Wine runs in one of two modes: either pthreads (posix
threading) or kthreads (kernel threading). This section
explains the differences between them. The one that is
used is automatically selected on startup by a small test
program which then execs the correct binary, either
wine-kthread or wine-pthread. On NPTL-enabled systems
pthreads will be used, and on older non-NPTL systems
kthreads is selected.
</para>
<para>
Let's start with a bit of history. Back in the dark ages
when Wines threading support was first implemented a
problem was faced - Windows had much more capable
threading APIs than Linux did. This presented a problem -
Wine works either by reimplementing an API entirely or by
mapping it onto the underlying systems equivalent. How
could Win32 threading be implemented using a library which
did not have all the neeed features? The answer, of
course, was that it couldn't be.
</para>
<para>
On Linux the pthreads interface is used to start, stop and
control threads. The pthreads library in turn is based on
top of so-called "kernel threads" which are created using
the <function>clone(2)</function> syscall. Pthreads
provides a nicer (more portable) interface to this
functionality and also provides APIs for controlling
mutexes. There is a <ulink
url="http://www.llnl.gov/computing/tutorials/pthreads/">
good tutorial on pthreads </ulink> available if you want
to learn more.
</para>
<para>
As pthreads did not provide the necessary semantics to
implement Win32 threading, the decision was made to
implement Win32 threading on top of the underlying kernel
threads by using syscalls like <function>clone</function>
directly. This provided maximum flexibility and allowed a
correct implementation but caused some bad side
effects. Most notably, all the userland Linux APIs assumed
that the user was utilising the pthreads library. Some
only enabled thread safety when they detected that
pthreads was in use - this is true of glibc, for
instance. Worse, pthreads and pure kernel threads had
strange interactions when run in the same process yet some
libraries used by Wine used pthreads internally. Throw in
source code porting using WineLib - where you have both
UNIX and Win32 code in the same process - and chaos was
the result.
</para>
<para>
The solution was simple yet ingenius: Wine would provide
its own implementation of the pthread library
<emphasis>inside</emphasis> its own binary. Due to the
semantics of ELF symbol scoping, this would cause Wines
own implementations to override any implementation loaded
later on (like the real libpthread.so). Therefore, any
calls to the pthread APIs in external libraries would be
linked to Wines instead of the systems pthreads library,
and Wine implemented pthreads by using the standard
Windows threading APIs it in turn implemented itself.
</para>
<para>
As a result, libraries that only became thread-safe in the
presence of a loaded pthreads implementation would now do
so, and any external code that used pthreads would
actually end up creating Win32 threads that Wine was aware
of and controlled. This worked quite nicely for a long
time, even though it required doing some extremely
un-kosher things like overriding internal libc structures
and functions. That is, it worked until NPTL was developed
at which point the underlying thread implementation on
Linux changed dramatically.
</para>
<para>
The fake pthread implementation can be found in
<filename>loader/kthread.c</filename>, which is used to
produce to wine-kthread binary. In contrast,
loader/pthread.c produces the wine-pthread binary which is
used on newer NPTL systems.
</para>
<para>
NPTL is a new threading subsystem for Linux that hugely
improves its performance and flexibility. By allowing
threads to become much more scalable and adding new
pthread APIs, NPTL made Linux competitive with Windows in
the multi-threaded world. Unfortunately it also broke many
assumptions made by Wine (as well as other applications
such as the Sun JVM and RealPlayer) in the process.
</para>
<para>
There was, however, some good news. NPTL made Linux
threading powerful enough that Win32 threads could now be
implemented on top of pthreads like any other normal
application. There would no longer be problems with mixing
win32-kthreads and pthreads created by external libraries,
and no need to override glibc internals. As you can see
from the relative sizes of the
<filename>loader/kthread.c</filename> and
<filename>loader/pthread.c</filename> files, the
difference in code complexity is considerable. NPTL also
made several other semantic changes to things such as
signal delivery so changes were required in many different
places in Wine.
</para>
<para>
On non-Linux systems the threading interface is typically
not powerful enough to replicate the semantics Win32
applications expect and so kthreads with the pthread
overrides are used.
</para>
</sect3>
<sect3>
<title> The Win32 thread environment </title>
<para>
All Win32 code, whether from a native EXE/DLL or in Wine
itself, expects certain constructs to be present in its
environment. This section explores what those constructs
are and how Wine sets them up. The lack of this
environment is one thing that makes it hard to use Wine
code directly from standard Linux applications - in order
to interact with Win32 code a thread must first be
"adopted" by Wine.
</para>
<para>
The first thing Win32 code requires is the
<emphasis>TEB</emphasis> or "Thread Environment
Block". This is an internal (undocumented) Windows
structure associated with every thread which stores a
variety of things such as TLS slots, a pointer to the
threads message queue, the last error code and so on. You
can see the definition of the TEB in
<filename>include/thread.h</filename>, or at least what we
know of it so far. Being internal and subject to change,
the layout of the TEB has had to be reverse engineered
from scratch.
</para>
<para>
A pointer to the TEB is stored in the %fs register and can
be accessed using <function>NtCurrentTeb()</function> from
within Wine code. %fs actually stores a selector, and
setting it therefore requires modifying the processes
local descriptor table (LDT) - the code to do this is in
<filename>lib/wine/ldt.c</filename>.
</para>
<para>
The TEB is required by nearly all Win32 code run in the
Wine environment, as any wineserver RPC will use it, which
in turn implies that any code which could possibly block
(for instance by using a critical section) needs it. The
TEB also holds the SEH exception handler chain as the
first element, so if when disassembling you see code like
this:
</para>
<programlisting> movl %esp, %fs:0 </programlisting>
<para>
... then you are seeing the program set up an SEH handler
frame. All threads must have at least one SEH entry, which
normally points to the backstop handler which is
ultimately responsible for popping up the all-too-familiar
"This program has performed an illegal operation and will
be terminated" message. On Wine we just drop straight into
the debugger. A full description of SEH is out of the
scope of this section, however there are some good
articles in MSJ if you are interested.
</para>
<para>
All Win32-aware threads must have a wineserver
connection. Many different APIs require the ability to
communicate with the wineserver. In turn, the wineserver
must be aware of Win32 threads in order to be able to
accurately report information to other parts of the program
and do things like route inter-thread messages, dispatch
APCs (asynchronous procedure calls) and so on. Therefore a
part of thread initialization is initializing the thread
serverside. The result is not only correct information in
the server, but a set of file descriptors the thread can use
to communicate with the server - the request fd, reply fd
and wait fd (used for blocking).
</para>
</sect3>
</sect2>
</sect1>
<sect1>
<title>KERNEL Module</title>
<para>
FIXME: Needs some content...
</para>
<sect2 id="consoles">
<title>Consoles in Wine</title>
<para>
As described in the Wine User Guide's CUI section, Wine
manipulates three kinds of "consoles" in order to support
properly the Win32 CUI API.
</para>
<para>
The following table describes the main implementation
differences between the three approaches.
<table>
<title>Function consoles implementation comparison</title>
<tgroup cols="4" align="left">
<thead>
<row>
<entry>Function</entry>
<entry>Bare streams</entry>
<entry>Wineconsole &amp; user backend</entry>
<entry>Wineconsole &amp; curses backend</entry>
</row>
</thead>
<tbody>
<row>
<entry>
Console as a Win32 Object (and associated
handles)
</entry>
<entry>
No specific Win32 object is used in this
case. The handles manipulated for the standard
Win32 streams are in fact "bare handles" to
their corresponding Unix streams. The mode
manipulation functions
(<function>GetConsoleMode</function> /
<function>SetConsoleMode</function>) are not
supported.
</entry>
<entry>
Implemented in server, and a specific Winelib
program (wineconsole) is in charge of the
rendering and user input. The mode manipulation
functions behave as expected.
</entry>
<entry>
Implemented in server, and a specific Winelib
program (wineconsole) is in charge of the
rendering and user input. The mode manipulation
functions behave as expected.
</entry>
</row>
<row>
<entry>
Inheritance (including handling in
<function>CreateProcess</function> of
<constant>CREATE_DETACHED</constant>,
<constant>CREATE_NEW_CONSOLE</constant> flags).
</entry>
<entry>
Not supported. Every process child of a process
will inherit the Unix streams, so will also
inherit the Win32 standard streams.
</entry>
<entry>
Fully supported (each new console creation will
be handled by the creation of a new USER32
window)
</entry>
<entry>
Fully supported, except for the creation of a
new console, which will be rendered on the same
Unix terminal as the previous one, leading to
unpredictable results.
</entry>
</row>
<row>
<entry>
<function>ReadFile</function> /
<function>WriteFile</function>
operations
</entry>
<entry>Fully supported</entry>
<entry>Fully supported</entry>
<entry>Fully supported</entry>
</row>
<row>
<entry>
Screen-buffer manipulation (creation, deletion,
resizing...)
</entry>
<entry>Not supported</entry>
<entry>Fully supported</entry>
<entry>
Partly supported (this won't work too well as we
don't control (so far) the size of underlying
Unix terminal
</entry>
</row>
<row>
<entry>
APIs for reading/writing screen-buffer content,
cursor position
</entry>
<entry>Not supported</entry>
<entry>Fully supported</entry>
<entry>Fully supported</entry>
</row>
<row>
<entry>
APIs for manipulating the rendering window size
</entry>
<entry>Not supported</entry>
<entry>Fully supported</entry>
<entry>
Partly supported (this won't work too well as we
don't control (so far) the size of underlying
Unix terminal
</entry>
</row>
<row>
<entry>
Signaling (in particular, Ctrl-C handling)
</entry>
<entry>
Nothing is done, which means that Ctrl-C will
generate (as usual) a
<constant>SIGINT</constant> which will terminate
the program.
</entry>
<entry>
Partly supported (Ctrl-C behaves as expected,
however the other Win32 CUI signaling isn't
properly implemented).
</entry>
<entry>
Partly supported (Ctrl-C behaves as expected,
however the other Win32 CUI signaling isn't
properly implemented).
</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
The Win32 objects behind a console can be created in
several occasions:
<itemizedlist>
<listitem>
<para>
When the program is started from wineconsole, a new
console object is created and will be used
(inherited) by the process launched from
wineconsole.
</para>
</listitem>
<listitem>
<para>
When a program, which isn't attached to a console,
calls <function>AllocConsole</function>, Wine then
launches wineconsole, and attaches the current
program to this console. In this mode, the USER32
mode is always selected as Wine cannot tell the
current state of the Unix console.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Please also note, that starting a child process with the
<constant>CREATE_NEW_CONSOLE</constant> flag, will end-up
calling <function>AllocConsole</function> in the child
process, hence creating a wineconsole with the USER32
backend.
</para>
</sect2>
</sect1>
<sect1 id="initialization">
<title> The Wine initialization process </title>
<para>
Wine has a rather complex startup procedure, so unlike many
programs the best place to begin exploring the code-base is
<emphasis>not</emphasis> in fact at the
<function>main()</function> function but instead at some of the
more straightforward DLLs that exist on the periphery such as
MSI, the widget library (in USER and COMCTL32) etc. The purpose
of this section is to document and explain how Wine starts up
from the moment the user runs "wine myprogram.exe" to the point
at which myprogram gets control.
</para>
<sect2>
<title> First Steps </title>
<para>
The actual wine binary that the user runs does not do very much, in fact it is only
responsible for checking the threading model in use (NPTL vs LinuxThreads) and then invoking
a new binary which performs the next stage in the startup sequence. See the threading chapter
for more information on this check and why it's necessary. You can find this code in
<filename>loader/glibc.c</filename>. The result of this check is an exec of either
wine-pthread or wine-kthread, potentially (on Linux) via
the <emphasis>preloader</emphasis>. We need to use separate binaries here because overriding
the native pthreads library requires us to exploit a property of ELF symbol fixup semantics:
it's not possible to do this without starting a new process.
</para>
<para>
The Wine preloader is found in <filename>loader/preloader.c</filename>, and is required in
order to impose a Win32 style address space layout upon the newly created Win32 process. The
details of what this does is covered in the address space layout chapter. The preloader is a
statically linked ELF binary which is passed the name of the actual Wine binary to run (either
wine-kthread or wine-pthread) along with the arguments the user passed in from the command
line. The preloader is an unusual program: it does not have a main() function. In standard ELF
applications, the entry point is actually at a symbol named _start: this is provided by the
standard gcc infrastructure and normally jumps to <function>__libc_start_main</function> which
initializes glibc before passing control to the main function as defined by the programmer.
</para>
<para>
The preloader takes control direct from the entry point for a few reasons. Firstly, it is
required that glibc is not initialized twice: the result of such behaviour is undefined and
subject to change without notice. Secondly, it's possible that as part of initializing glibc,
the address space layout could be changed - for instance, any call to malloc will initialize a
heap arena which modifies the VM mappings. Finally, glibc does not return to _start at any
point, so by reusing it we avoid the need to recreate the ELF bootstrap stack (env, argv,
auxiliary array etc).
</para>
<para>
The preloader is responsible for two things: protecting important regions of the address
space so the dynamic linker does not map shared libraries into them, and once that is done
loading the real Wine binary off disk, linking it and starting it up. Normally all this is
done automatically by glibc and the kernel but as we intercepted this process by using a
static binary it's up to us to restart the process. The bulk of the code in the preloader is
about loading wine-[pk]thread and ld-linux.so.2 off disk, linking them together, then
starting the dynamic linking process.
</para>
<para>
One of the last things the preloader does before jumping into the dynamic linker is scan the
symbol table of the loaded Wine binary and set the value of a global variable directly: this
is a more efficient way of passing information to the main Wine program than flattening the
data structures into an environment variable or command line parameter then unpacking it on
the other side, but it achieves pretty much the same thing. The global variable set points to
the preload descriptor table, which contains the VMA regions protected by the preloader. This
allows Wine to unmap them once the dynamic linker has been run, so leaving gaps we can
initialize properly later on.
</para>
</sect2>
<sect2>
<title> Starting the emulator </title>
<para>
The process of starting up the emulator itself is mostly one of chaining through various
initializer functions defined in the core libraries and DLLs: libwine, then NTDLL, then kernel32.
</para>
<para>
Both the wine-pthread and wine-kthread binaries share a common <function>main</function>
function, defined in <filename>loader/main.c</filename>, so no matter which binary is selected
after the preloader has run we start here. This passes the information provided by the
preloader into libwine and then calls wine_init, defined
in <filename>libs/wine/loader.c</filename>. This is where the emulation really starts:
<function>wine_init</function> can, with the correct preparation,
be called from programs other than the wine loader itself.
</para>
<para>
<function>wine_init</function> does some very basic setup tasks such as initializing the
debugging infrastructure, yet more address space manipulation (see the information on the
4G/4G VM split in the address space chapter), before loading NTDLL - the core of both Wine and
the Windows NT series - and jumping to the <function>__wine_process_init</function> function defined
in <filename>dlls/ntdll/loader.c</filename>
</para>
<para>
This function is responsible for initializing the primary Win32 environment. In thread_init(),
it sets up the TEB, the wineserver connection for the main thread and the process heap. See
the threading chapter for more information on this.
</para>
<para>
Finally, it loads and jumps to <function>__wine_kernel_init</function> in kernel32.dll: this
is defined in <filename>dlls/kernel32/process.c</filename>. This is where the bulk of the work
is done. The kernel32 initialization code retrieves the startup info for the process from the
server, initializes the registry, sets up the drive mapping system and locale data, then
begins loading the requested application itself. Each process has a STARTUPINFO block that can
be passed into <function>CreateProcess</function> specifying various things like how the first
window should be displayed: this is sent to the new process via the wineserver.
</para>
<para>
After determining the type of file given to Wine by the user (a Win32 EXE file, a Win16 EXE, a
Winelib app etc), the program is loaded into memory (which may involve loading and
initializing other DLLs, the bulk of Wines startup code), before control reaches the end of
<function>__wine_kernel_init</function>. This function ends with the new process stack being
initialized, and start_process being called on the new stack. Nearly there!
</para>
<para>
The final element of initializing Wine is starting the newly loaded program
itself. <function>start_process</function> sets up the SEH backstop handler, calls
<function>LdrInitializeThunk</function> which performs the last part of the process
initialization (such as performing relocations and calling the DllMains with PROCESS_ATTACH),
grabs the entry point of the executable and then on this line:
</para>
<programlisting>
ExitProcess( entry( peb ) );
</programlisting>
<para>
... jumps to the entry point of the program. At this point the users program is running and
the API provided by Wine is ready to be used. When entry returns,
the <function>ExitProcess</function> API will be used to initialize a graceful shutdown.
</para>
</sect2>
</sect1>
2004-11-04 22:06:14 +01:00
<sect1 id="seh">
<title>Structured Exception Handling</title>
<para>
Structured Exception Handling (or SEH) is an implementation of
exceptions inside the Windows core. It allows code written in
different languages to throw exceptions across DLL boundaries, and
Windows reports various errors like access violations by throwing
them. This section looks at how it works, and how it's implemented
in Wine.
</para>
<sect2>
<title> How SEH works </title>
<para>
SEH is based on embedding EXCEPTION_REGISTRATION_RECORD
structures in the stack. Together they form a linked list rooted
at offset zero in the TEB (see the threading section if you
don't know what this is). A registration record points to a
handler function, and when an exception is thrown the handlers
are executed in turn. Each handler returns a code, and they can
elect to either continue through the handler chain or it can
handle the exception and then restart the program. This is
referred to as unwinding the stack. After each handler is called
it's popped off the chain.
</para>
<para>
Before the system begins unwinding the stack, it runs vectored
handlers. This is an extension to SEH available in Windows XP,
and allows registered functions to get a first chance to watch
or deal with any exceptions thrown in the entire program, from
any thread.
</para>
<para>
A thrown exception is represented by an EXCEPTION_RECORD
structure. It consists of a code, flags, an address and an
arbitrary number of DWORD parameters. Language runtimes can use
these parameters to associate language-specific information with
the exception.
</para>
<para>
Exceptions can be triggered by many things. They can be thrown
explicitly by using the RaiseException API, or they can be
triggered by a crash (ie, translated from a signal). They may be
used internally by a language runtime to implement
language-specific exceptions. They can also be thrown across
DCOM connections.
</para>
<para>
Visual C++ has various extensions to SEH which it uses to
implement, eg, object destruction on stack unwind as well as the
ability to throw arbitrary types. The code for this is in dlls/msvcrt/except.c
</para>
</sect2>
<sect2>
<title> Translating signals to exceptions </title>
<para>
In Windows, compilers are expected to use the system exception
interface, and the kernel itself uses the same interface to
dynamically insert exceptions into a running program. By contrast
on Linux the exception ABI is implemented at the compiler level
(inside GCC and the linker) and the kernel tells a thread of
exceptional events by sending <emphasis>signals</emphasis>. The
language runtime may or may not translate these signals into
native exceptions, but whatever happens the kernel does not care.
</para>
<para>
You may think that if an app crashes, it's game over and it
really shouldn't matter how Wine handles this. It's what you might
intuitively guess, but you'd be wrong. In fact some Windows
programs expect to be able to crash themselves and recover later
without the user noticing, some contain buggy binary-only
components from third parties and use SEH to swallow crashes, and
still others execute priviledged (kernel-level) instructions and
expect it to work. In fact, at least one set of APIs (the
IsBad*Ptr series) can only be implemented by performing an
operation that may crash and returning TRUE if it does, and FALSE
if it doesn't! So, Wine needs to not only implement the SEH
infrastructure but also translate Unix signals into SEH
exceptions.
</para>
<para>
The code to translate signals into exceptions is a part of NTDLL,
and can be found in dlls/ntdll/signal_i386.c. This file sets up
handlers for various signals during Wine startup, and for the ones
that indicate exceptional conditions translates them into
exceptions. Some signals are used by Wine internally and have
nothing to do with SEH.
</para>
<para>
Signal handlers in Wine run on their own stack. Each thread has
its own signal stack which resides 4k after the TEB. This is
important for a couple of reasons. Firstly, because there's no
guarantee that the app thread which triggered the signal has
enough stack space for the Wine signal handling code. In
Windows, if a thread hits the limits of its stack it triggers a
fault on the stack guard page. The language runtime can use this
to grow the stack if it wants to.
<!-- fixme: is it really the language runtime that does this? i
can't find any code in Wine to reallocate the stack on
STATUS_GUARD_PAGE_VIOLATION -->
However, because a guard page violation is just a regular
segfault to the kernel, that would lead to a nested signal
handler and that gets messy really quick so we disallow that in
Wine. Secondly, setting up the exception to throw requires
modifying the stack of the thread which triggered it, which is
quite hard to do when you're still running on it.
</para>
<para>
Windows exceptions typically contain more information than the
Unix standard APIs provide. For instance, a
STATUS_ACCESS_VIOLATION exception (0xC0000005) structure
contains the faulting address, whereas a standard Unix SIGSEGV
just tells the app that it crashed. Usually this information is
passed as an extra parameter to the signal handler, however its
location and contents vary between kernels (BSD, Solaris,
etc). This data is provided in a SIGCONTEXT structure, and on
entry to the signal handler it contains the register state of
the CPU before the signal was sent. Modifying it will cause the
kernel to adjust the context before restarting the thread.
</para>
</sect2>
</sect1>
</chapter>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-parent-document:("wine-devel.sgml" "set" "book" "part" "chapter" "")
End:
-->