diff --git a/documentation/wine-devel.sgml b/documentation/wine-devel.sgml index c8243a8762c..39befd78ae7 100644 --- a/documentation/wine-devel.sgml +++ b/documentation/wine-devel.sgml @@ -15,7 +15,6 @@ - ]> diff --git a/documentation/winedev-kernel.sgml b/documentation/winedev-kernel.sgml index 700835f97f9..9b4648ba930 100644 --- a/documentation/winedev-kernel.sgml +++ b/documentation/winedev-kernel.sgml @@ -813,6 +813,147 @@ ExitProcess( entry( peb ) ); + + + Structured Exception Handling + + + 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. + + + + How SEH works + + + 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. + + + + 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. + + + + 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. + + + + 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. + + + + 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 + + + + + + Translating signals to exceptions + + + 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 signals. The + language runtime may or may not translate these signals into + native exceptions, but whatever happens the kernel does not care. + + + + 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. + + + + 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. + + + + 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. + + + + 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. + + + + 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. + + + + + +