455 lines
17 KiB
Plaintext
455 lines
17 KiB
Plaintext
Winedump - A Wine DLL tool
|
|
--------------------------
|
|
|
|
Background
|
|
----------
|
|
|
|
Most of the functions available in Windows, and in Windows applications, are
|
|
made available to applications from DLLs. Wine implements the Win32 API by
|
|
providing replacements for the essential Windows DLLs in the form of Unix
|
|
shared library (.so) files, and provides a tool, winebuild, to allow Winelib
|
|
applications to link to functions exported from shared libraries/DLLs.
|
|
|
|
The first thing to note is that there are many DLLs that aren't yet
|
|
implemented in Wine. Mostly this doesn't present a problem because the native
|
|
Win32 versions of lots of DLLs can be used without problems, at least on
|
|
x86 platforms. However, one of Wine's goals is the eventual replacement of
|
|
every essential O/S DLL so that the whole API is implemented. This not only
|
|
means that a copy of the real O/S is not needed, but also that non-x86
|
|
platforms can run most Win32 programs after recompiling.
|
|
|
|
The second thing to note is that applications commonly use their own or 3rd
|
|
party DLLs to provide functionality. In order to call these functions with
|
|
a Winelib program, some 'glue' is needed. This 'glue' comes in the form of
|
|
a .spec file. The .spec file, along with some dummy code, is used to create
|
|
a Wine .so corresponding to the Windows DLL. The winebuild program can then
|
|
resolve calls made to DLL functions to call your dummy DLL. You then tell
|
|
Wine to only use the native Win32 version of the DLL, and at runtime your
|
|
calls will be made to the Win32 DLL. If you want to re-implement the dll,
|
|
you simply add the code for the DLL calls to your stub .so, and then tell
|
|
Wine to use the .so version instead [1].
|
|
|
|
These two factors mean that if you are:
|
|
|
|
A: Reimplementing a Win32 DLL for use within Wine, or
|
|
B: Compiling a Win32 application with Winelib that uses x86 DLLs
|
|
|
|
Then you will need to create a .spec file (amongst other things). If you
|
|
won't be doing either of the above, then you won't need winedump.
|
|
|
|
Creating a .spec file is a labour intensive task during which it is easy
|
|
to make a mistake. The idea of winedump is to automate this task and create
|
|
the majority of the support code needed for your DLL. In addition you can
|
|
have winedump create code to help you re-implement a DLL, by providing
|
|
tracing of calls to the DLL, and (in some cases) automatically determining
|
|
the parameters, calling conventions, and return values of the DLL's functions.
|
|
|
|
You can think of winedump as somewhat similar to the IMPLIB tool when
|
|
only its basic functionality is used. In addition, winedump can be used to
|
|
dump other information from PE files; See the section 'Dumping' below.
|
|
|
|
|
|
Usage
|
|
-----
|
|
Winedump is a command line tool. For the list of options and the basic usage
|
|
see the winedump(1) man page.
|
|
|
|
|
|
Spec mode: Generating stub DLLs
|
|
-------------------------------
|
|
|
|
If all you want to do is generate a stub DLL to allow you to link your
|
|
Winelib application to an x86 DLL, the above options are all you need.
|
|
|
|
As an example, lets assume the application you are porting uses functions
|
|
from a 3rd party dll called 'zipextra.dll', and the functions in the DLL
|
|
use the __stdcall calling convention. Copy zipextra.dll to an empty directory,
|
|
change to it, and run winedump as follows:
|
|
|
|
winedump spec zipextra (Note: this assumes winedump is in your path)
|
|
|
|
The output will look something like the following:
|
|
|
|
22 named symbols in DLL, 22 in total ...
|
|
Export 1 - '_OpenZipFile' ... [Ignoring]
|
|
Export 2 - '_UnZipFile' ... [Ignoring]
|
|
...
|
|
|
|
"[Ignoring]" Just tells you that winedump isn't trying to determine the
|
|
parameters or return types of the functions, it's just creating stubs.
|
|
|
|
The following files are created:
|
|
|
|
zipextra.spec
|
|
This is the .spec file. Each exported function is listed as a stub:
|
|
|
|
@ stub _OpenZipFile
|
|
@ stub _UnZipFile
|
|
...
|
|
|
|
This means that winebuild will generate dummy code for this function. That
|
|
doesn't concern us, because all we want is for winebuild to allow the symbols
|
|
to be resolved when linking. At run-time, the functions in the native DLL will
|
|
be called; this just allows us to link.
|
|
|
|
zipextra_dll.h zipextra_main.c
|
|
These are source code files containing the minimum set of code to build
|
|
a stub DLL. The C file contains one function, ZIPEXTRA_Init, which does
|
|
nothing (but must be present).
|
|
|
|
Makefile.in
|
|
This is a template for 'configure' to produce a makefile. It is designed
|
|
for a DLL that will be inserted into the Wine source tree. If your DLL
|
|
will not be part of Wine, or you don't wish to build it this way,
|
|
you should look at the Wine tool 'winemaker' to generate a DLL project.
|
|
|
|
FIXME: winemaker could run this tool automatically when generating projects
|
|
that use extra DLLs (*.lib in the "ADD LINK32" line in .dsp) ....
|
|
|
|
zipextra_install
|
|
A shell script for adding zipextra to the Wine source tree (see below).
|
|
|
|
|
|
Spec mode: Inserting a stub DLL into the Wine tree
|
|
--------------------------------------------------
|
|
|
|
To build your stub DLL as part of Wine, do the following:
|
|
|
|
chmod a+x ./zipextra_install
|
|
./zipextra_install <wine-path>
|
|
cd <wine-path>
|
|
autoconf
|
|
./configure
|
|
make depend && make
|
|
make install
|
|
|
|
Your application can now link with the DLL.
|
|
|
|
If you receive the following error when running autoconf:
|
|
|
|
autoconf: configure.in: No such file or directory
|
|
|
|
Then you need to install a newer version of autoconf. At the time of writing
|
|
version 2.53 or later is required to re-generate configure.
|
|
|
|
If you have problems with this step, you can post to the wine-devel mailing
|
|
list for help. The build process can change regularly and winebuild may lag
|
|
behind in support.
|
|
|
|
NOTE: **DO NOT** submit patches to Wine for 3rd party DLLs! Building DLLs
|
|
into your copy of the tree is just a simple way for you to link. When
|
|
you release your application you won't be distributing the Unix .so
|
|
anyway, just the Win32 DLL. As you update your version of Wine
|
|
you can simply re-run the procedure above (Since no patches are
|
|
involved, it should be pretty resilient to changes).
|
|
|
|
|
|
Spec mode: Advanced Options
|
|
---------------------------
|
|
|
|
This section discusses features of winedump that are useful to Wine Hackers
|
|
or developers looking to re-implement a Win32 DLL for Unix. Using these
|
|
features means you will need to be able to resolve compilation problems and
|
|
have a general understanding of Wine programming.
|
|
|
|
|
|
For all advanced functionality, you must give winedump a directory or file that
|
|
contains prototypes for the DLL.
|
|
|
|
Once you have created your DLL, if you generated code (see below), you can
|
|
backup the DLL header file created and use it for rebuilding the DLL (you
|
|
should remove the DLLNAME_ prefix from the prototypes to make this work). This
|
|
allows you to add names to the function arguments, for example, so that the
|
|
comments and prototype in the regenerated DLL will be clearer.
|
|
|
|
Winedump searches for prototypes using 'grep', and then retrieves each
|
|
prototype by calling 'function_grep.pl', a Perl script. When you pass the -v
|
|
option on the command line, the calls to both of these programs are logged.
|
|
This allows you to see where each function definition has come from. Should
|
|
winedump take an excessively long time to locate a prototype, you can check
|
|
that it is searching the right files; you may want to limit the number of files
|
|
searched if locating the prototype takes too long.
|
|
|
|
You can compile function_grep.pl for a slight increase in performance; see
|
|
'man perlcc' for details.
|
|
|
|
|
|
If winedump does not find a prototype, it emits code like the following:
|
|
|
|
In the .spec file:
|
|
|
|
@stub _OpenZipFile
|
|
|
|
in the header file:
|
|
|
|
/* __cdecl ZIPEXTRA__OpenZipFile() */
|
|
|
|
in the C source file:
|
|
|
|
/*********************************************************************
|
|
* _OpenZipFile (ZIPEXTRA.@)
|
|
*
|
|
*/
|
|
#if 0
|
|
__stdcall ZIPEXTRA__OpenZipFile()
|
|
{
|
|
/* '@Stubbed'ed in .spec */
|
|
}
|
|
#endif
|
|
|
|
If a prototype is found, or correctly demangled, the following is emitted:
|
|
|
|
.spec:
|
|
@ stdcall _OpenZipFile ZIPEXTRA__OpenZipFile
|
|
|
|
.h:
|
|
BOOL __stdcall ZIPEXTRA__OpenZipFile(LPCSTR pszFileName);
|
|
|
|
.c:
|
|
BOOL __stdcall ZIPEXTRA__OpenZipFile(LPCSTR pszFileName)
|
|
{
|
|
TRACE("stub\n");
|
|
return 0;
|
|
}
|
|
|
|
Note that if the prototype does not contain argument names, winedump will
|
|
add them following the convention arg0, arg1 ... argN. If the function is
|
|
demangled C++, the first argument will be called '_this' if an implicit this
|
|
pointer is passed (i.e. the function is a non-static class member function).
|
|
|
|
|
|
OPTION: -f dll Forward calls to 'dll' (implies -t)
|
|
|
|
This is the most complicated level of code generation. The same code is
|
|
generated as -t, however support is added for forwarding calls to another
|
|
DLL. The DLL to forward to is given as 'dll'. Lets suppose we built the
|
|
examples above using "-f real_zipextra". The code generated will look like
|
|
the following:
|
|
|
|
.spec
|
|
As for -c, except if a function prototype was not found:
|
|
|
|
@ forward _OpenZipFile real_zipextra._OpenZipFile
|
|
|
|
In this case the function is forwarded to the destination DLL rather
|
|
than stubbed.
|
|
|
|
.h
|
|
As for -c.
|
|
|
|
.c
|
|
|
|
A variable "hDLL" is added to hold a pointer to the DLL to forward to, and
|
|
the initialization code in ZIPEXTRA_Init is changed to load and free the
|
|
forward DLL automatically:
|
|
|
|
HMODULE hDLL = 0; /* DLL to call through to */
|
|
|
|
BOOL WINAPI ZIPEXTRA_Init(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
|
{
|
|
TRACE("(0x%08x, %ld, %p)\n", hinstDLL, fdwReason, lpvReserved);
|
|
|
|
if (fdwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
hDLL = LoadLibraryA( "real_zipextra" );
|
|
TRACE ("Forwarding DLL (real_zipextra) loaded\n" );
|
|
}
|
|
else if (fdwReason == DLL_PROCESS_DETACH)
|
|
{
|
|
FreeLibrary( hDLL );
|
|
TRACE ("Forwarding DLL (real_zipextra) freed\n" );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
The stub function is changed to call the forwarding DLL and return that value.
|
|
|
|
BOOL __stdcall ZIPEXTRA__OpenZipFile(LPCSTR pszFileName)
|
|
{
|
|
BOOL (__stdcall *pFunc)(LPCSTR) = (void*)GetProcAddress(hDLL,"_OpenZipFile");
|
|
BOOL retVal;
|
|
TRACE("((LPCSTR)%s) stub\n", pszFileName);
|
|
retVal = pFunc(pszFileName);
|
|
TRACE("returned (%ld)\n",(LONG)retVal));
|
|
return retVal;
|
|
}
|
|
|
|
This allows you to investigate the workings of a DLL without interfering in
|
|
its operation in any way (unless you want to).
|
|
|
|
In the example I have been using, we probably should have used the -o option
|
|
to change the output name of our DLL to something else, and used the -f
|
|
option to forward to the real zipextra DLL:
|
|
|
|
winedump spec zipextra -f zipextra -o myzipextra -I "~/zipextra/include/*h"
|
|
|
|
Then in the .spec file for our Winelib application, we add the line:
|
|
|
|
import myzipextra
|
|
|
|
When we build our application, winebuild resolves the calls to our Unix .so.
|
|
As our application runs we can see the values of all parameters passed to
|
|
the DLL, and any values returned, without having to write code to dump
|
|
them ourselves (see below for a better way to wrap a DLL for forwarding).
|
|
|
|
This isn't a very realistic example of the usefulness of this feature,
|
|
however, since we could print out the results anyway, because it is our
|
|
application making the calls to the DLL. Where DLL forwarding is most useful
|
|
is where an application or DLL we didn't write calls functions in the DLL.
|
|
In this case we can capture the sequence of calls made, and the values passed
|
|
around. This is an aid in reimplementing the DLL, since we can add code for a
|
|
function, print the results, and then call the real DLL and compare. Only
|
|
when our code is the same do we need to remove the function pointer and the
|
|
call to the real DLL. A similar feature in wine is +relay debugging. Using a
|
|
forwarding DLL allows more granular reporting of arguments, because you can
|
|
write code to dump out the contents of types/structures rather than just
|
|
their address in memory. A future version of winedump may generate this
|
|
code automatically for common Win32 types.
|
|
|
|
See below for more information on setting up a forwarding DLL.
|
|
|
|
|
|
Spec mode: Problems compiling a DLL containing generated code
|
|
-------------------------------------------------------------
|
|
|
|
Unless you are very lucky, you will need to do a small amount of work to
|
|
get a DLL generated with -c, -t or -f to compile. The reason for this is
|
|
that most DLLs will use custom types such as structs whose definition
|
|
is not known to the code in the DLL.
|
|
|
|
Heres an example prototype from crtdll:
|
|
|
|
double __cdecl _cabs(struct _complex arg0)
|
|
|
|
The definition for the _complex struct needs to be given. Since it is passed
|
|
by value, its size also needs to be correct in order to forward the call
|
|
correctly to a native DLL. In this case the structure is 8 bytes in size, which
|
|
means that the gcc compile flag -freg-struct-return must be given when
|
|
compiling the function in order to be compatible with the native DLL. (In
|
|
general this is not an issue, but you need to be aware of such issues if you
|
|
encounter problems with your forwarding DLL).
|
|
|
|
For third party (non C++) DLLs, the header(s) supplied with the DLL can
|
|
normally be added as an include to the generated DLL header. For other DLLs
|
|
I suggest creating a separate header in the DLL directory and adding any
|
|
needed types to that. This allows you to rebuild the DLL at whim, for example
|
|
if a new version of winedump brings increased functionality, then you
|
|
only have to overwrite the generated files and re-include the header to take
|
|
advantage of it.
|
|
|
|
Usually there isn't much work to do to get the DLL to compile if you have
|
|
headers. As an example, building a forwarded crtdll, which contains 520
|
|
functions, required 20 types to be defined before it compiled. Of these,
|
|
about half were structures, so about 35 lines of code were needed. The only
|
|
change to the generated code was one line in the header to include the type
|
|
definitions.
|
|
|
|
To save some typing in case you don't have headers for your DLL type, winedump
|
|
will dump dummy declarations for unknown classes and types it encounters,
|
|
if you use the -v option. These can be piped directly into a fix-up header
|
|
file for use in compiling your DLL. For example, if winedump encounters the
|
|
(C++ ) symbol:
|
|
|
|
??0foobar@@QAE@ABV0@@Z (Which is a constructor for a foobar object)
|
|
|
|
It will emit the following with -v set:
|
|
|
|
struct foobar { int _FIXME; };
|
|
|
|
(Classes are mapped to C structs when generating code).
|
|
|
|
The output should be piped through 'sort' and 'uniq' to remove multiple
|
|
declarations, e.g:
|
|
|
|
winedump foo -c -I "inc/*.h" -v | grep FIXME | sort | uniq > fixup.h
|
|
|
|
By adding '#include "fixup.h"' to foobar_dll.h your compile errors will be
|
|
greatly reduced.
|
|
|
|
If winedump encounters a type it doesn't know that is passed by value (as in
|
|
the _cabs example above), it also prints a FIXME message like:
|
|
|
|
/* FIXME: By value type: Assumed 'int' */ typedef int ldiv_t;
|
|
|
|
If the type is not an int, you will need to change the code and possibly
|
|
the .spec entry in order to forward correctly. Otherwise, include the typedef
|
|
in your fixup header to avoid compile errors.
|
|
|
|
|
|
Spec mode: Using a forwarding DLL
|
|
---------------------------------
|
|
|
|
To create and use a forwarding DLL to trace DLL calls, you need to first
|
|
create a DLL using the -f option as outlined above, and get it to compile.
|
|
In order to forward calls the following procedure can be used (for this
|
|
example we are going to build a forwarding msvcrt.dll for the purpose
|
|
of reimplementing it).
|
|
|
|
First we create the forwarding DLL. We will rename the real msvcrt.dll on our
|
|
system to ms_msvcrt.dll, and our msvcrt implementation will call it:
|
|
|
|
winedump spec msvcrt -C -f ms_msvcrt -I "inc/*.h"
|
|
|
|
We then install this DLL into the Wine tree and add the types we need to
|
|
make it compile. Once the DLL compiles, we create a dummy ms_msvcrt DLL so
|
|
winebuild will resolve our forward calls to it (for the cases where winedump
|
|
couldn't generate code and has placed an '@forward' line in the .spec file):
|
|
|
|
winedump spec msvcrt -C -o ms_msvcrt
|
|
|
|
Install this DLL into the wine tree (since its a stub DLL, no changes are
|
|
needed to the code).
|
|
|
|
Now uncomment the line that winedump inserted into msvcrt.spec:
|
|
|
|
#import ms_msvcrt.dll
|
|
|
|
And recompile Wine.
|
|
|
|
Finally, we must tell Wine to only use the built in msvcrt.dll and to only use
|
|
the native (Win32) ms_msvcrt.dll. Add the following two lines to ~/.wine/config
|
|
under the [DllOverrides] section:
|
|
|
|
;Use our implementation of msvcrt
|
|
"msvcrt" = "builtin, so"
|
|
;Use only the Win32 ms_msvcrt
|
|
"ms_msvcrt" = "native"
|
|
|
|
At this point, when any call is made to msvcrt.dll, Our libmsvcrt.so receives
|
|
the call. It then forwards or calls ms_msvcrt.dll, which is the native dll. We
|
|
receive a return value and pass it back to our caller, having TRACEd the
|
|
arguments on the way.
|
|
|
|
At this point you are ready to start reimplementing the calls.
|
|
|
|
|
|
|
|
Final comments
|
|
--------------
|
|
|
|
If you have any suggestions for improving this tool, please let me know.
|
|
If anyone can help answer the FIXME questions in msmangle.c or can fill me in
|
|
on any aspect of the C++ mangling scheme, I would appreciate it. In particular
|
|
I want to know what _E and _G represent.
|
|
|
|
If you encounter a C++ symbol that doesn't demangle **AND** you have the
|
|
prototype for it, please send me the symbol as reported by winedump and the
|
|
prototype. The more examples I have the easier it is to decipher the scheme,
|
|
and generating them myself is very slow.
|
|
|
|
Finally, although it is easy to generate a DLL, I _very strongly_ suggest that
|
|
you don't submit a generated DLL for inclusion into Wine unless you have
|
|
actually implemented a fairly reasonable portion of it. Even then, you should
|
|
only send the portions of the DLL you have implemented. Thousands of lines of
|
|
stub code don't help the project at all.
|
|
|
|
Please send questions and bug reports to jon_p_griffiths@yahoo.com.
|
|
|
|
|
|
References
|
|
----------
|
|
|
|
[1] See the wine and wine.conf man pages for details on how to tell Wine
|
|
whether to use native (Win32) or internal DLL's.
|