| 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. |