Let's take a closer look at the way Wine loads and run processes in memory.
When starting a Wine process from command line (we'll get later on to the differences between NE, PE and Winelib executables), there are a couple of things Wine need to do first. A first executable is run to check the threading model of the underlying OS (see Section 8.4 for the details) and will start the real Wine loader corresponding to the choosen threading model.
Then Wine graps a few elements from the Unix world: the environment, the program arguments. Then the ntdll.dll.so is loaded into memory using the standard shared library dynamic loader. When loaded, NTDLL will mainly first create a decent Windows environment:
create a PEB (Process Environment Block) and a TEB (Thread Environment Block).
set up the connection to the Wine server - and eventually launching the Wine server if none runs
create the process heap
Then Kernel32 is loaded (but now using the
Windows dynamic loading capabilities) and a Wine specific entry point
__wine_kernel_init. This function will
actually handle all the logic of the process loading and execution,
and will never return from it's call.
__wine_kernel_init will undergo the following
initialization of program arguments from Unix program arguments
lookup of executable in the file system
If the file is not found, then an error is printed and the Wine loader stops.
We'll cover the non-PE file type later on, so assume for now it's a PE file. The PE module is loaded in memory using the same mechanisms as for a native DLLs (mainly mapping the file data and code sections into memory, and handling relocation if needed). Note that the dependencies on the module are not resolved at this point.
A new stack is created, which size is given in the PE header, and this stack is made the one of the running thread (which is still the only one in the process). The stack used at startup will no longer be used.
Which this new stack,
ntdll.LdrInitializeThunk is called which
performs the remaining initialization parts, including resolving
all the DLL imports on the PE module, and doing the init of the
Control can now be passed to the
of the PE module, which will let the executable run.
The steps used are closely link to what is done in the previous case.
There are however a few points to look at a bit more closely. The
inner implementation creates the child process using the
calls. This means that we don't need to check again for the threading
model, we can use what the parent (or the grand-parent process...)
started from command line has found.
The Win32 process creation allows to pass a lot of information between the parent and the child. This includes object handles, windows title, console parameters, environment strings... Wine makes use of both the standard Unix inheritance mechanisms (for environment for example) and the Wine server (to pass from parent to child a chunk of data containing the relevant information).
The previously described loading mechanism will check in the Wine server if such a chunk exists, and, if so, will perform the relevant initialization.
Some further synchronization is also put in place: a parent will wait until the child has started, or has failed. The Wine server is also used to perform those tasks.
Before going into the gory details, let's first go back to what a Winelib application is. It can be either a regular Unix executable, or a more specific Wine beast. This later form in fact creates two files for a given executable (say foo.exe). The first one, named foo will be a symbolic link to the Wine loader (wine). The second one, named foo.exe.so, is the equivalent of the .dll.so files we've already described for DLLs. As in Windows, an executable is, among other things, a module with its import and export information, as any DLL, it makes sense Wine uses the same mechanisms for loading native executables and DLLs.
When starting a Winelib application from the command line (say with foo arg1 arg2), the Unix shell will execute foo as a Unix executable. Since this is in fact the Wine loader, Wine will fire up. However, will notice that it hasn't been started as wine but as foo, and hence, will try to load (using Unix shared library mechanism) the second file foo.exe.so. Wine will recognize a 32 bit module (with its descriptor) embedded in the shared library, and once the shared library loaded, it will proceed the same path as when loading a standard native PE executable.
Wine needs to implement this second form of executable in order to
maintain the order of initialization of some elements in the
executable. One particular issue is when dealing with global C++
objects. In standard Unix executable, the call of the constructor to
such objects is stored in the specific section of the executable
.init not to name it). All constructors in this
section are called before the
WinMain function is called. Creating a Wine
executable using the first form mentionned above will let those
constructors being called before Wine gets a chance to initialize
itself. So, any constructor using a Windows API will fail, because
Wine infrastructure isn't in place. The use of the second form for
Winelib executables ensures that we do the initialization using the
initialize the Wine infrastructure
load the executable into memory
handle the import sections for the executable
call the global object constructors (if any). They now can properly call the Windows APIs
call the executable entry point
The attentive reader would have noted that the resolution of imports
for the executable is done, as for a DLL, when the executable/DLL
descriptor is registered. However, this is done also by adding a
specific constructor in the
.init section. For
the above describe scheme to function properly, this constructor must
be the first constructor to be called, before all the other
constructors, generated by the executable itself. The Wine build chain
takes care of that, and also generating the executable/DLL descriptor
for the Winelib executable.