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.
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.