| Winedump - A Wine DLL tool | |
| -------------------------- | |
| Background | |
| ---------- | |
| Most of the functions available in Windows, and in Windows applications, are | |
| made available to applications from DLL's. Wine implements the Win32 API by | |
| providing replacement's for the essential Windows DLL's 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/DLL's. | |
| The first thing to note is that there are many DLL's that aren't yet | |
| implemented in Wine. Mostly this doesn't present a problem because the native | |
| Win32 versions of lots of DLL's 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 DLL's 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 DLL's | |
| 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 the winedump(1) man page. | |
| Spec mode: Generating stub DLL's | |
| ------------------------------- | |
| 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, its 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 DLL's (*.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 DLL's! Building DLL's | |
| 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 DLL's 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++) DLL's, the header(s) supplied with the DLL can | |
| normally be added as an include to the generated DLL header. For other DLL's | |
| 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. |