<div dir="auto">Very nice sum up of the whole situation. Sounds perfect for the wiki.<div dir="auto"><br></div><div dir="auto">Bernhard </div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Eric Pouech <<a href="mailto:eric.pouech@orange.fr">eric.pouech@orange.fr</a>> schrieb am Do., 14. Apr. 2022, 17:26:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div>
<p><font face="Helvetica, Arial, sans-serif">I tried to write down
what I grasped for the current modules' evolution</font></p>
<p><font face="Helvetica, Arial, sans-serif">since there's always
questions floating around this subject, might be worthwhile to
document it a bit</font></p>
<p><font face="Helvetica, Arial, sans-serif">it's likely full of
errors, shortcuts, misconceptions... so don't hesitate to
comment/amend</font></p>
<p><font face="Helvetica, Arial, sans-serif"><br>
</font></p>
<p><font face="monospace">Wine "old" module model:<br>
-----------------------<br>
Wine used to generate a module (DLL or EXE) as:<br>
- an .so (ELF) module, compiled with a native compiler<br>
- this .so module embeds also PE module information, so that
Wine's loader<br>
exposes to Windows application the PE module only.<br>
<br>
+--------------------------+<br>
| <a href="http://FOOBAR.dll.so" target="_blank" rel="noreferrer">FOOBAR.dll.so</a> (ELF) |<br>
| +----------------------+ |<br>
| | PE information | |<br>
| +----------------------+ |<br>
+--------------------------+<br>
<br>
Wine loader is also able to load "pure" PE modules from
.dll/.exe files.<br>
<br>
A built-in module (<a href="http://FOOBAR.dll.so" target="_blank" rel="noreferrer">FOOBAR.dll.so</a>) can use standard ELF dynamic
linking to load<br>
also ELF libraries (eg winepulse needs to loads libpulse.so).
This is needed to<br>
implement in Wine most of the integration into the Unix world;
e.g: graphical<br>
(X11 or image coding/decoding jpeg...), audio/video (pulse,
gstreamer), and<br>
other utilities (xml/xslt, database connectivity...) to name a
few.<br>
<br>
+--------------------------+ +-----------+<br>
| <a href="http://FOOBAR.dll.so" target="_blank" rel="noreferrer">FOOBAR.dll.so</a> (ELF) | -------------> | helper.so |<br>
| +----------------------+ | ELF dyn link +-----------+<br>
| | PE information | |<br>
| +----------------------+ |<br>
+--------------------------+<br>
<br>
But, this had some limitations: some applications (version
checking,<br>
anti-crack...) were trying to open directly the FOOBAR.dll file
(as it exists on<br>
Windows) to read some elements inside. Wine implemented "fake"
files to mimic<br>
that PE file content.<br>
<br>
+--------------------------+ +-----------+<br>
| <a href="http://FOOBAR.dll.so" target="_blank" rel="noreferrer">FOOBAR.dll.so</a> (ELF) | -------------> | helper.so |<br>
| +----------------------+ | ELF dyn link +-----------+<br>
| | PE information | |<br>
| +----------------------+ |<br>
+--------------------------+<br>
<br>
+--------------------------+<br>
| FOOBAR.dll (Fake PE) |<br>
+--------------------------+<br>
<br>
But this doesn't take into account 32 vs 64 bit applications
(letting aside the<br>
16bit part). So in fact, the global picture is closer to this:<br>
<br>
+------------------------------+ +-----------+<br>
| <a href="http://FOOBAR.dll.so" target="_blank" rel="noreferrer">FOOBAR.dll.so</a> (ELF 64bit) | -------------> | helper.so
|<br>
| +------------------------+ | ELF dyn link | (64 bit) |<br>
| | PE information (64bit) | | +-----------+<br>
| +------------------------+ |<br>
+------------------------------+<br>
<br>
+------------------------------+<br>
| FOOBAR.dll (Fake 64 bit PE) |<br>
+------------------------------+<br>
<br>
+------------------------------+ +-----------+<br>
| <a href="http://FOOBAR.dll.so" target="_blank" rel="noreferrer">FOOBAR.dll.so</a> (ELF 32bit) | -------------> | helper.so
|<br>
| +------------------------+ | ELF dyn link | (32 bit) |<br>
| | PE information (32bit) | | +-----------+<br>
| +------------------------+ |<br>
+------------------------------+<br>
<br>
+------------------------------+<br>
| FOOBAR.dll (Fake 32 bit PE) |<br>
+------------------------------+<br>
<br>
This requires that both 32 and 64bit .so libraries to be
installed on the host.<br>
<br>
But again, this started to show some limitations:<br>
- some Linux distros and Apple are willing to drop the 32bit
support from their<br>
packages, so the 32 bit helper.so libraries might not be
available in the<br>
future<br>
- Apple evolution to support M1 family chip also adds another
dimension to the<br>
above picture (as a an application can either be compiled for
ARM/M1 or Intel<br>
chip, so there's also a need to support invocation from both
processors (the<br>
ABI is different, to start with).<br>
<br>
Wine "new" PE model: (PE migration...)<br>
-------------------<br>
In this model, Wine requires a cross-compiler to generate
directly a PE file for<br>
each module.<br>
<br>
+-----------------+<br>
| FOOBAR.dll (PE) |<br>
+-----------------+<br>
<br>
One of interesting benefit of this choice, is that the PE module
can now simply<br>
link with Window's libc implementation (either msvcrt or ucrt).
In the "old"<br>
model, the default was to use the unix libc libraries, yet
providing some other<br>
potential differences.<br>
<br>
For modules not requiring system .so files, this migration is
rather<br>
simple. There are still some elements to look at [1]. But most
of the hard job<br>
is done by the compiler and the Wine infrastructure.<br>
<br>
For modules requiring linking to .so files, the scheme has to be
extended by<br>
generating an additional ELF library, tied to the PE module.<br>
+-----------------+
+-----------------+ +-----------+<br>
| FOOBAR.dll (PE) | | FOOBAR.so (ELF) |
-------------> | helper.so |<br>
| | --------> | (unixlib) | ELF dyn
link +-----------+<br>
| | | |<br>
+-----------------+ +-----------------+<br>
<br>
Addind this extra module in the middle allows to:<br>
- do the translation from a entities defined in PE world into
entities<br>
defined in the ELF world (ABI are different, structures layout
are different)<br>
- this provides an abstraction of the library for the PE world.<br>
- decide what to migrate from the existing code into the PE part
(remember<br>
existing library in old model lives in the ELF world)<br>
<br>
One can see the FOOBAR.so like a Wine-"system driver" for
providing the required<br>
support from the host.<br>
<br>
But, it brings constraints too. The ELF part cannot call back
into the PE part<br>
of the module, nor into other PE modules.<br>
If this ELF parts requires some Wine "kernel features", the it
can (using ELF<br>
dynamic linking) use the APIs provided by ntdll.so ELF library.<br>
<br>
+-----------------+
+-----------------+ +-----------+<br>
| FOOBAR.dll (PE) | | FOOBAR.so (ELF) |
-------------> | ntdll.so |<br>
| | --------> | (unixlib) | ELF dyn
link +-----------+<br>
| | | |
-------------> | helper.so |<br>
+-----------------+
+-----------------+ +-----------+<br>
<br>
Note: likely some other modules .so should be available too
(like win32u) (to be<br>
confirmed)<br>
<br>
Currently, the 32 <> 64 bit is still duplicated:<br>
<br>
+-----------------+
+------------------------+ +--------------------+<br>
| FOOBAR.dll (PE) | | FOOBAR.so (ELF 64 bit) |
-------------> | ntdll.so (64 bit) |<br>
| 64 bit | --------> | (unixlib 64 bit) |
ELF dyn link +--------------------+<br>
| | | |
-------------> | helper.so (64 bit) |<br>
+-----------------+
+------------------------+ +--------------------+<br>
<br>
+-----------------+
+------------------------+ +--------------------+<br>
| FOOBAR.dll (PE) | | FOOBAR.so (ELF 32 bit) |
-------------> | ntdll.so (32 bit) |<br>
| 32 bit | --------> | (unixlib 32 bit) |
ELF dyn link +--------------------+<br>
| | | |
-------------> | helper.so (32 bit) |<br>
+-----------------+
+------------------------+ +--------------------+<br>
<br>
When all modules are converted to the new scheme, this shall
look like:<br>
<br>
+-----------------+
+------------------------+ +--------------------+<br>
| FOOBAR.dll (PE) | | FOOBAR.so (ELF 64 bit) |
-------------> | ntdll.so (64 bit) |<br>
| 64 bit | --------> | (unixlib ) |
ELF dyn link +--------------------+<br>
| | +----> | (64 + 32 bit) |
-------------> | helper.so (64 bit) |<br>
+-----------------+ |
+------------------------+ +--------------------+<br>
|<br>
+-----------------+ |<br>
| FOOBAR.dll (PE) | |<br>
| 32 bit | ---+<br>
| |<br>
+-----------------+<br>
<br>
This will require that FOOBAR.so correctly handles the "syscall"
for both 32 bit<br>
and 64 bit unixlib calls.<br>
When this is in place, no support for 32-bit .so files will be
necessary (both<br>
the packages that provide the .so files, but also for the
development packages.<br>
<br>
Unixlib in a nutshell<br>
---------------------<br>
A unixlib interface is just a table of entry points. Both ends
(calling PE<br>
side, and callee on ELF side) share:<br>
- the semantic attached to an entry point a given index in the
table<br>
(usually defined as a value inside an enum)<br>
- the signature of each entry point is always the same:<br>
NTSTATUS the_func(void *args);<br>
- args is a pointer to a structure containing input and output<br>
parameters to this very entry point.<br>
There's a specific structure for each function (in some rare
cases, several<br>
functions can use the same structure).<br>
<br>
Invoking on PE side looks like:<br>
| struct xxx_yyy arg;<br>
| /* fill in arg */<br>
| NTSTATUS status = __wine_unixlib_callinto(foobar.so, /*
pseudo code <g> */<br>
| 23 /* index of
entry */,<br>
| &arg);<br>
<br>
An on ELF side:<br>
| static NTSTATUS my_func_23(void* parg)<br>
| {<br>
| struct xxx_yyy *arg = parg;<br>
| /* do the hard work */<br>
| }<br>
|<br>
| /* other functions */<br>
|<br>
| entry_point_table[] = {<br>
| /* ... */<br>
| my_func_23, /* index 23 */<br>
| /* ... */<br>
| };<br>
|<br>
| /* a bit of black magic to register the entry_point_table */<br>
<br>
Keep in mind:<br>
- the same structure can have different sizes for fields, and
different layouts<br>
when compiled for PE or ELF, 32 or 64 bit, or different CPUs.<br>
So the ELF part must handle those discrepancies if any.<br>
- pointers will be of different sizes...<br>
- allocation of memory can be harder to implement on ELF side
(as one must<br>
ensure that allocated memory can be accessed from 32 bit PE
part)<br>
- exception handling?<br>
<br>
Actually, there will be two distinct tables:<br>
- one used for the invocation from 64 bit PE side,<br>
- the second one used for the invocation from 32 bit PE side.<br>
<br>
Upon invocation, their argument will be mapped to the 32 bit or
the 64 bit<br>
layout of the argument structure.<br>
This allows either to adapt the implementation to any difference
between the 32<br>
and 64 bit worlds.<br>
<br>
So the interface between the PE module and its .so counterpart
has to be<br>
designed with care:<br>
+ as already stated, no callback from ELF part into PE part is
available,<br>
+ some objects can be defined differently from both sides:<br>
- integers (int) doesn't have the same size<br>
- pointers are either 32 bit or 64 bit entities... so if
memory has to be<br>
returned to PE side, it must be in lower part of memory (at
least for 32 bit<br>
"syscalls")<br>
- ...<br>
<br>
So the migration of a module from the "old" model to this PE
model requires to:<br>
- define the API between the PE and the ELF parts<br>
- ensure that code in the ELF doesn't call back into PE APIs<br>
- call through the Unixlib mechanism between the PE and ELF
part.<br>
- the Unixlib call mechanism is available even if the module is
generated in the<br>
old model, hence allowing to put the unixlib API in place
still using the old<br>
model. When all is migrated to the Unixlib interface, module
can be generated<br>
using the new module.<br>
(it may be that after reading this you need either/or a) a
break, b) a deep<br>
breath, c) check your aspirin stock and d) restart from the
beginning)<br>
<br>
TODO:<br>
- debugging<br>
- sketch the ARM/Intel on Darwin<br>
<br>
Additional bits:<br>
---------------<br>
From the .so files that Wine used to link to, some of them are
not tied to the<br>
physical system, and can be available directly as PE modules
(eg. JPEG decoder,<br>
XML reader/writer...). The most important of those libraries are
cross-compiled,<br>
and their source code inserted into Wine tree (see libs/).<br>
It's also possible to load the system-wide equivalent PE modules
when installed.<br>
<br>
It's still not clear to me how the new module wow64 will be
activated.<br>
<br>
[1]: on 64 bit, Windows chose the LLP64 integer model, while
Unix chose the LP64<br>
one (sur. The main difference is that long is 32 bit on Windows
and 64 bit on<br>
Unix.<br>
In the old model, as compilation was native, the Windows types
were redefined to<br>
fit the LP64 model (like DWORD was an unsigned int to get the
right size).<br>
With the migration to the new model, as compilation of PE
modules is done on<br>
LLP64 models, DWORD (and friends) are now defined as on Windows
as an unsigned<br>
long.<br>
This is called the migration to long types (90% of modules
migrated) and takes<br>
place along side the PE migration.</font></p>
<br>
</div>
</blockquote></div>