From b9c8671907fd1dd2fdf77cdb0e1423af0147bc67 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Tue, 15 Jun 2004 18:26:52 +0000 Subject: [PATCH] Add documentation on the Wine initialization process. --- documentation/architecture.sgml | 143 ++++++++++++++++++++++++++++++++++++++++ documentation/threading.sgml | 6 +- documentation/wine-devel.sgml | 6 +- 3 files changed, 148 insertions(+), 7 deletions(-) diff --git a/documentation/architecture.sgml b/documentation/architecture.sgml index a42f93840ac..66afd02199c 100644 --- a/documentation/architecture.sgml +++ b/documentation/architecture.sgml @@ -1947,6 +1947,149 @@ WSOCK32.DLL: 32-bit sockets APIs + + The Wine initialization process + + + Wine has a rather complex startup procedure, so unlike many programs the best place to begin + exploring the code-base is not in fact at the main() 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. + + + + First Steps + + + 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 + loader/glibc.c. The result of this check is an exec of either + wine-pthread or wine-kthread, potentially (on Linux) via + the preloader. 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. + + + + The Wine preloader is found in loader/preloader.c, 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 __libc_start_main which + initializes glibc before passing control to the main function as defined by the programmer. + + + + 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). + + + + 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. + + + + 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. + + + + + + Starting the emulator + + + 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. + + + + Both the wine-pthread and wine-kthread binaries share a common main + function, defined in loader/main.c, 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 libs/wine/loader.c. This is where the emulation really starts: + wine_init can, with the correct preparation, + be called from programs other than the wine loader itself. + + + + wine_init 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 __wine_process_init function defined + in dlls/ntdll/loader.c + + + + 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. + + + + Finally, it loads and jumps to __wine_kernel_init in kernel32.dll: this + is defined in dlls/kernel32/process.c. 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 CreateProcess specifying various things like how the first + window should be displayed: this is sent to the new process via the wineserver. + + + + 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 + __wine_kernel_init. This function ends with the new process stack being + initialized, and start_process being called on the new stack. Nearly there! + + + + The final element of initializing Wine is starting the newly loaded program + itself. start_process sets up the SEH backstop handler, calls + LdrInitializeThunk 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: + + + + ExitProcess( entry( peb ) ); + + + + ... 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 ExitProcess API will be used to initialize a graceful shutdown. + + + + + - + -- 2.11.4.GIT