123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 |
- 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(const char *filename);
- .c:
- BOOL __stdcall ZIPEXTRA__OpenZipFile(const char *filename)
- {
- 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 dll, DWORD reason, void *reserved)
- {
- TRACE("(0x%08x, %u, %p)\n", dll, reason, reserved);
- if (reason == DLL_PROCESS_ATTACH)
- {
- hDLL = LoadLibraryA( "real_zipextra" );
- TRACE ("Forwarding DLL (real_zipextra) loaded\n" );
- }
- else if (reason == 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(const char *filename)
- {
- BOOL (__stdcall *pFunc)(const char *) = (void*)GetProcAddress(hDLL,"_OpenZipFile");
- BOOL retVal;
- TRACE("((const char *)%s) stub\n", filename);
- retVal = pFunc(filename);
- 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 it's 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 man page for details on how to tell Wine
- whether to use native (Win32) or internal DLL's.
|