1 ////////////////////////////////////////////////////////////////////////////////
2 // $Id: utility.cpp,v 1.24 2006/11/18 03:12:35 dmouldin Exp $
4 // Visual Leak Detector - Various Utility Functions
5 // Copyright (c) 2005-2006 Dan Moulding
7 // This library is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU Lesser General Public
9 // License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
12 // This library is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // Lesser General Public License for more details.
17 // You should have received a copy of the GNU Lesser General Public
18 // License along with this library; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 // See COPYING.txt for the full terms of the GNU Lesser General Public License.
23 ////////////////////////////////////////////////////////////////////////////////
28 #define __out_xcount(x) // Workaround for the specstrings.h bug in the Platform SDK.
29 #define DBGHELP_TRANSLATE_TCHAR
30 #include <dbghelp.h> // Provides portable executable (PE) image access functions.
31 #define VLDBUILD // Declares that we are building Visual Leak Detector.
32 #include "utility.h" // Provides various utility functions and macros.
33 #include "vldheap.h" // Provides internal new and delete operators.
35 // Imported Global Variables
36 extern CRITICAL_SECTION imagelock;
39 static BOOL reportdelay = FALSE; // If TRUE, we sleep for a bit after calling OutputDebugString to give the debugger time to catch up.
40 static FILE *reportfile = NULL; // Pointer to the file, if any, to send the memory leak report to.
41 static BOOL reporttodebugger = TRUE; // If TRUE, a copy of the memory leak report will be sent to the debugger for display.
42 static encoding_e reportencoding = ascii; // Output encoding of the memory leak report.
44 // dumpmemorya - Dumps a nicely formatted rendition of a region of memory.
45 // Includes both the hex value of each byte and its ASCII equivalent (if
48 // - address (IN): Pointer to the beginning of the memory region to dump.
50 // - size (IN): The size, in bytes, of the region to dump.
56 VOID dumpmemorya (LPCVOID address, SIZE_T size)
58 WCHAR ascdump [18] = {0};
64 WCHAR formatbuf [BYTEFORMATBUFFERLENGTH];
65 WCHAR hexdump [HEXDUMPLINELENGTH] = {0};
68 // Each line of output is 16 bytes.
69 if ((size % 16) == 0) {
74 // We'll need to pad the last line out to 16 bytes.
75 dumplen = size + (16 - (size % 16));
78 // For each byte of data, get both the ASCII equivalent (if it is a
79 // printable character) and the hex representation.
81 for (byteindex = 0; byteindex < dumplen; byteindex++) {
82 hexindex = 3 * ((byteindex % 16) + ((byteindex % 16) / 4)); // 3 characters per byte, plus a 3-character space after every 4 bytes.
83 ascindex = (byteindex % 16) + (byteindex % 16) / 8; // 1 character per byte, plus a 1-character space after every 8 bytes.
84 if (byteindex < size) {
85 byte = ((PBYTE)address)[byteindex];
86 _snwprintf_s(formatbuf, BYTEFORMATBUFFERLENGTH, _TRUNCATE, L"%.2X ", byte);
88 wcsncpy_s(hexdump + hexindex, HEXDUMPLINELENGTH - hexindex, formatbuf, 4);
90 ascdump[ascindex] = (WCHAR)byte;
93 ascdump[ascindex] = L'.';
97 // Add padding to fill out the last line to 16 bytes.
98 wcsncpy_s(hexdump + hexindex, HEXDUMPLINELENGTH - hexindex, L" ", 4);
99 ascdump[ascindex] = L'.';
102 if ((bytesdone % 16) == 0) {
103 // Print one line of data for every 16 bytes. Include the
104 // ASCII dump and the hex dump side-by-side.
105 report(L" %s %s\n", hexdump, ascdump);
108 if ((bytesdone % 8) == 0) {
109 // Add a spacer in the ASCII dump after every 8 bytes.
110 ascdump[ascindex + 1] = L' ';
112 if ((bytesdone % 4) == 0) {
113 // Add a spacer in the hex dump after every 4 bytes.
114 wcsncpy_s(hexdump + hexindex + 3, HEXDUMPLINELENGTH - hexindex - 3, L" ", 4);
120 // dumpmemoryw - Dumps a nicely formatted rendition of a region of memory.
121 // Includes both the hex value of each byte and its Unicode equivalent.
123 // - address (IN): Pointer to the beginning of the memory region to dump.
125 // - size (IN): The size, in bytes, of the region to dump.
131 VOID dumpmemoryw (LPCVOID address, SIZE_T size)
137 WCHAR formatbuf [BYTEFORMATBUFFERLENGTH];
138 WCHAR hexdump [HEXDUMPLINELENGTH] = {0};
141 WCHAR unidump [18] = {0};
144 // Each line of output is 16 bytes.
145 if ((size % 16) == 0) {
146 // No padding needed.
150 // We'll need to pad the last line out to 16 bytes.
151 dumplen = size + (16 - (size % 16));
154 // For each word of data, get both the Unicode equivalent and the hex
157 for (byteindex = 0; byteindex < dumplen; byteindex++) {
158 hexindex = 3 * ((byteindex % 16) + ((byteindex % 16) / 4)); // 3 characters per byte, plus a 3-character space after every 4 bytes.
159 uniindex = ((byteindex / 2) % 8) + ((byteindex / 2) % 8) / 8; // 1 character every other byte, plus a 1-character space after every 8 bytes.
160 if (byteindex < size) {
161 byte = ((PBYTE)address)[byteindex];
162 _snwprintf_s(formatbuf, BYTEFORMATBUFFERLENGTH, _TRUNCATE, L"%.2X ", byte);
163 formatbuf[BYTEFORMATBUFFERLENGTH - 1] = '\0';
164 wcsncpy_s(hexdump + hexindex, HEXDUMPLINELENGTH - hexindex, formatbuf, 4);
165 if (((byteindex % 2) == 0) && ((byteindex + 1) < dumplen)) {
166 // On every even byte, print one character.
167 word = ((PWORD)address)[byteindex / 2];
168 if ((word == 0x0000) || (word == 0x0020)) {
169 unidump[uniindex] = L'.';
172 unidump[uniindex] = word;
177 // Add padding to fill out the last line to 16 bytes.
178 wcsncpy_s(hexdump + hexindex, HEXDUMPLINELENGTH - hexindex, L" ", 4);
179 unidump[uniindex] = L'.';
182 if ((bytesdone % 16) == 0) {
183 // Print one line of data for every 16 bytes. Include the
184 // ASCII dump and the hex dump side-by-side.
185 report(L" %s %s\n", hexdump, unidump);
188 if ((bytesdone % 8) == 0) {
189 // Add a spacer in the ASCII dump after every 8 bytes.
190 unidump[uniindex + 1] = L' ';
192 if ((bytesdone % 4) == 0) {
193 // Add a spacer in the hex dump after every 4 bytes.
194 wcsncpy_s(hexdump + hexindex + 3, HEXDUMPLINELENGTH - hexindex - 3, L" ", 4);
200 // findimport - Determines if the specified module imports the named import
201 // from the named exporting module.
203 // - importmodule (IN): Handle (base address) of the module to be searched to
204 // see if it imports the specified import.
206 // - exportmodule (IN): Handle (base address) of the module that exports the
207 // import to be searched for.
209 // - exportmodulename (IN): ANSI string containing the name of the module that
210 // exports the import to be searched for.
212 // - importname (IN): ANSI string containing the name of the import to search
213 // for. May be an integer cast to a string if the import is exported by
218 // Returns TRUE if the module imports to the specified import. Otherwise
221 BOOL findimport (HMODULE importmodule, HMODULE exportmodule, LPCSTR exportmodulename, LPCSTR importname)
223 IMAGE_THUNK_DATA *iate;
224 IMAGE_IMPORT_DESCRIPTOR *idte;
226 IMAGE_SECTION_HEADER *section;
229 // Locate the importing module's Import Directory Table (IDT) entry for the
230 // exporting module. The importing module actually can have several IATs --
231 // one for each export module that it imports something from. The IDT entry
232 // gives us the offset of the IAT for the module we are interested in.
233 EnterCriticalSection(&imagelock);
234 idte = (IMAGE_IMPORT_DESCRIPTOR*)ImageDirectoryEntryToDataEx((PVOID)importmodule, TRUE,
235 IMAGE_DIRECTORY_ENTRY_IMPORT, &size, §ion);
236 LeaveCriticalSection(&imagelock);
238 // This module has no IDT (i.e. it imports nothing).
241 while (idte->OriginalFirstThunk != 0x0) {
242 if (_stricmp((PCHAR)R2VA(importmodule, idte->Name), exportmodulename) == 0) {
243 // Found the IDT entry for the exporting module.
248 if (idte->OriginalFirstThunk == 0x0) {
249 // The importing module does not import anything from the exporting
254 // Get the *real* address of the import. If we find this address in the IAT,
255 // then we've found that the module does import the named import.
256 import = GetProcAddress(exportmodule, importname);
257 assert(import != NULL); // Perhaps the named export module does not actually export the named import?
259 // Locate the import's IAT entry.
260 iate = (IMAGE_THUNK_DATA*)R2VA(importmodule, idte->FirstThunk);
261 while (iate->u1.Function != 0x0) {
262 if (iate->u1.Function == (DWORD_PTR)import) {
263 // Found the IAT entry. The module imports the named import.
269 // The module does not import the named import.
273 // findpatch - Determines if the specified module has been patched to use the
274 // specified replacement.
276 // - importmodule (IN): Handle (base address) of the module to be searched to
277 // see if it imports the specified replacement export.
279 // - exportmodulename (IN): ANSI string containing the name of the module that
280 // normally exports that import that would have been patched to use the
281 // replacement export.
283 // - replacement (IN): Address of the replacement, or destination, function or
284 // variable to search for.
288 // Returns TRUE if the module has been patched to use the specified
289 // replacement export.
291 BOOL findpatch (HMODULE importmodule, LPCSTR exportmodulename, LPCVOID replacement)
293 IMAGE_THUNK_DATA *iate;
294 IMAGE_IMPORT_DESCRIPTOR *idte;
295 IMAGE_SECTION_HEADER *section;
298 // Locate the importing module's Import Directory Table (IDT) entry for the
299 // exporting module. The importing module actually can have several IATs --
300 // one for each export module that it imports something from. The IDT entry
301 // gives us the offset of the IAT for the module we are interested in.
302 EnterCriticalSection(&imagelock);
303 idte = (IMAGE_IMPORT_DESCRIPTOR*)ImageDirectoryEntryToDataEx((PVOID)importmodule, TRUE,
304 IMAGE_DIRECTORY_ENTRY_IMPORT, &size, §ion);
305 LeaveCriticalSection(&imagelock);
307 // This module has no IDT (i.e. it imports nothing).
310 while (idte->OriginalFirstThunk != 0x0) {
311 if (_stricmp((PCHAR)R2VA(importmodule, idte->Name), exportmodulename) == 0) {
312 // Found the IDT entry for the exporting module.
317 if (idte->OriginalFirstThunk == 0x0) {
318 // The importing module does not import anything from the exporting
323 // Locate the replacement's IAT entry.
324 iate = (IMAGE_THUNK_DATA*)R2VA(importmodule, idte->FirstThunk);
325 while (iate->u1.Function != 0x0) {
326 if (iate->u1.Function == (DWORD_PTR)replacement) {
327 // Found the IAT entry for the replacement. This patch has been
334 // The module does not import the replacement. The patch has not been
339 // insertreportdelay - Sets the report function to sleep for a bit after each
340 // call to OutputDebugString, in order to allow the debugger to catch up.
346 VOID insertreportdelay ()
351 // moduleispatched - Checks to see if any of the imports listed in the specified
352 // patch table have been patched into the specified importmodule.
354 // - importmodule (IN): Handle (base address) of the module to be queried to
355 // determine if it has been patched.
357 // - patchtable (IN): An array of patchentry_t structures specifying all of the
358 // import patches to search for.
360 // - tablesize (IN): Size, in entries, of the specified patch table.
364 // Returns TRUE if at least one of the patches listed in the patch table is
365 // installed in the importmodule. Otherwise returns FALSE.
367 BOOL moduleispatched (HMODULE importmodule, patchentry_t patchtable [], UINT tablesize)
373 // Loop through the import patch table, individually checking each patch
374 // entry to see if it is installed in the import module. If any patch entry
375 // is installed in the import module, then the module has been patched.
376 for (index = 0; index < tablesize; index++) {
377 entry = &patchtable[index];
378 found = findpatch(importmodule, entry->exportmodulename, entry->replacement);
380 // Found one of the listed patches installed in the import module.
385 // No patches listed in the patch table were found in the import module.
389 // patchimport - Patches all future calls to an imported function, or references
390 // to an imported variable, through to a replacement function or variable.
391 // Patching is done by replacing the import's address in the specified target
392 // module's Import Address Table (IAT) with the address of the replacement
393 // function or variable.
395 // - importmodule (IN): Handle (base address) of the target module for which
396 // calls or references to the import should be patched.
398 // - exportmodule (IN): Handle (base address) of the module that exports the
399 // the function or variable to be patched.
401 // - exportmodulename (IN): ANSI string containing the name of the module that
402 // exports the function or variable to be patched.
404 // - importname (IN): ANSI string containing the name of the imported function
405 // or variable to be patched. May be an integer cast to a string if the
406 // import is exported by ordinal.
408 // - replacement (IN): Address of the function or variable to which future
409 // calls or references should be patched through to. This function or
410 // variable can be thought of as effectively replacing the original import
411 // from the point of view of the module specified by "importmodule".
415 // Returns TRUE if the patch was installed into the import module. If the
416 // import module does not import the specified export, so nothing changed,
417 // then FALSE will be returned.
419 BOOL patchimport (HMODULE importmodule, HMODULE exportmodule, LPCSTR exportmodulename, LPCSTR importname,
422 IMAGE_THUNK_DATA *iate;
423 IMAGE_IMPORT_DESCRIPTOR *idte;
426 IMAGE_SECTION_HEADER *section;
429 // Locate the importing module's Import Directory Table (IDT) entry for the
430 // exporting module. The importing module actually can have several IATs --
431 // one for each export module that it imports something from. The IDT entry
432 // gives us the offset of the IAT for the module we are interested in.
433 EnterCriticalSection(&imagelock);
434 idte = (IMAGE_IMPORT_DESCRIPTOR*)ImageDirectoryEntryToDataEx((PVOID)importmodule, TRUE,
435 IMAGE_DIRECTORY_ENTRY_IMPORT, &size, §ion);
436 LeaveCriticalSection(&imagelock);
438 // This module has no IDT (i.e. it imports nothing).
441 while (idte->OriginalFirstThunk != 0x0) {
442 if (_stricmp((PCHAR)R2VA(importmodule, idte->Name), exportmodulename) == 0) {
443 // Found the IDT entry for the exporting module.
448 if (idte->OriginalFirstThunk == 0x0) {
449 // The importing module does not import anything from the exporting
454 // Get the *real* address of the import. If we find this address in the IAT,
455 // then we've found the entry that needs to be patched.
456 import = GetProcAddress(exportmodule, importname);
457 assert(import != NULL); // Perhaps the named export module does not actually export the named import?
459 // Locate the import's IAT entry.
460 iate = (IMAGE_THUNK_DATA*)R2VA(importmodule, idte->FirstThunk);
461 while (iate->u1.Function != 0x0) {
462 if (iate->u1.Function == (DWORD_PTR)import) {
463 // Found the IAT entry. Overwrite the address stored in the IAT
464 // entry with the address of the replacement. Note that the IAT
465 // entry may be write-protected, so we must first ensure that it is
467 VirtualProtect(&iate->u1.Function, sizeof(iate->u1.Function), PAGE_READWRITE, &protect);
468 iate->u1.Function = (DWORD_PTR)replacement;
469 VirtualProtect(&iate->u1.Function, sizeof(iate->u1.Function), protect, &protect);
471 // The patch has been installed in the import module.
477 // The import's IAT entry was not found. The importing module does not
478 // import the specified import.
482 // patchmodule - Patches all imports listed in the supplied patch table, and
483 // which are imported by the specified module, through to their respective
484 // replacement functions.
486 // Note: If the specified module does not import any of the functions listed
487 // in the patch table, then nothing is changed for the specified module.
489 // - importmodule (IN): Handle (base address) of the target module which is to
490 // have its imports patched.
492 // - patchtable (IN): An array of patchentry_t structures specifying all of the
493 // imports to patch for the specified module.
495 // - tablesize (IN): Size, in entries, of the specified patch table.
499 // Returns TRUE if at least one of the patches listed in the patch table was
500 // installed in the importmodule. Otherwise returns FALSE.
502 BOOL patchmodule (HMODULE importmodule, patchentry_t patchtable [], UINT tablesize)
506 BOOL patched = FALSE;
508 // Loop through the import patch table, individually patching each import
509 // listed in the table.
510 for (index = 0; index < tablesize; index++) {
511 entry = &patchtable[index];
512 if (patchimport(importmodule, (HMODULE)entry->modulebase, entry->exportmodulename, entry->importname,
513 entry->replacement) == TRUE) {
521 // report - Sends a printf-style formatted message to the debugger for display
524 // Note: A message longer than MAXREPORTLENGTH characters will be truncated
525 // to MAXREPORTLENGTH.
527 // - format (IN): Specifies a printf-compliant format string containing the
528 // message to be sent to the debugger.
530 // - ... (IN): Arguments to be formatted using the specified format string.
536 VOID report (LPCWSTR format, ...)
540 CHAR messagea [MAXREPORTLENGTH + 1];
541 WCHAR messagew [MAXREPORTLENGTH + 1];
543 va_start(args, format);
544 _vsnwprintf_s(messagew, MAXREPORTLENGTH + 1, _TRUNCATE, format, args);
546 messagew[MAXREPORTLENGTH] = L'\0';
548 if (reportencoding == unicode) {
549 if (reportfile != NULL) {
550 // Send the report to the previously specified file.
551 fwrite(messagew, sizeof(WCHAR), wcslen(messagew), reportfile);
553 if (reporttodebugger) {
554 OutputDebugStringW(messagew);
558 if (wcstombs_s(&count, messagea, MAXREPORTLENGTH + 1, messagew, _TRUNCATE) == -1) {
559 // Failed to convert the Unicode message to ASCII.
563 messagea[MAXREPORTLENGTH] = '\0';
564 if (reportfile != NULL) {
565 // Send the report to the previously specified file.
566 fwrite(messagea, sizeof(CHAR), strlen(messagea), reportfile);
568 if (reporttodebugger) {
569 OutputDebugStringA(messagea);
573 if (reporttodebugger && (reportdelay == TRUE)) {
574 Sleep(10); // Workaround the Visual Studio 6 bug where debug strings are sometimes lost if they're sent too fast.
578 // restoreimport - Restores the IAT entry for an import previously patched via
579 // a call to "patchimport" to the original address of the import.
581 // - importmodule (IN): Handle (base address) of the target module for which
582 // calls or references to the import should be restored.
584 // - exportmodule (IN): Handle (base address) of the module that exports the
585 // function or variable to be patched.
587 // - exportmodulename (IN): ANSI string containing the name of the module that
588 // exports the function or variable to be patched.
590 // - importname (IN): ANSI string containing the name of the imported function
591 // or variable to be restored. May be an integer cast to a string if the
592 // import is exported by ordinal.
594 // - replacement (IN): Address of the function or variable which the import was
595 // previously patched through to via a call to "patchimport".
601 VOID restoreimport (HMODULE importmodule, HMODULE exportmodule, LPCSTR exportmodulename, LPCSTR importname,
604 IMAGE_THUNK_DATA *iate;
605 IMAGE_IMPORT_DESCRIPTOR *idte;
608 IMAGE_SECTION_HEADER *section;
611 // Locate the importing module's Import Directory Table (IDT) entry for the
612 // exporting module. The importing module actually can have several IATs --
613 // one for each export module that it imports something from. The IDT entry
614 // gives us the offset of the IAT for the module we are interested in.
615 EnterCriticalSection(&imagelock);
616 idte = (IMAGE_IMPORT_DESCRIPTOR*)ImageDirectoryEntryToDataEx((PVOID)importmodule, TRUE,
617 IMAGE_DIRECTORY_ENTRY_IMPORT, &size, §ion);
618 LeaveCriticalSection(&imagelock);
620 // This module has no IDT (i.e. it imports nothing).
623 while (idte->OriginalFirstThunk != 0x0) {
624 if (_stricmp((PCHAR)R2VA(importmodule, idte->Name), exportmodulename) == 0) {
625 // Found the IDT entry for the exporting module.
630 if (idte->OriginalFirstThunk == 0x0) {
631 // The importing module does not import anything from the exporting
636 // Get the *real* address of the import.
637 import = GetProcAddress(exportmodule, importname);
638 assert(import != NULL); // Perhaps the named export module does not actually export the named import?
640 // Locate the import's original IAT entry (it currently has the replacement
642 iate = (IMAGE_THUNK_DATA*)R2VA(importmodule, idte->FirstThunk);
643 while (iate->u1.Function != 0x0) {
644 if (iate->u1.Function == (DWORD_PTR)replacement) {
645 // Found the IAT entry. Overwrite the address stored in the IAT
646 // entry with the import's real address. Note that the IAT entry may
647 // be write-protected, so we must first ensure that it is writable.
648 VirtualProtect(&iate->u1.Function, sizeof(iate->u1.Function), PAGE_READWRITE, &protect);
649 iate->u1.Function = (DWORD_PTR)import;
650 VirtualProtect(&iate->u1.Function, sizeof(iate->u1.Function), protect, &protect);
657 // restoremodule - Restores all imports listed in the supplied patch table, and
658 // which are imported by the specified module, to their original functions.
660 // Note: If the specified module does not import any of the functions listed
661 // in the patch table, then nothing is changed for the specified module.
663 // - importmodule (IN): Handle (base address) of the target module which is to
664 // have its imports restored.
666 // - patchtable (IN): Array of patchentry_t structures specifying all of the
667 // imports to restore for the specified module.
669 // - tablesize (IN): Size, in entries, of the specified patch table.
675 VOID restoremodule (HMODULE importmodule, patchentry_t patchtable [], UINT tablesize)
680 // Loop through the import patch table, individually restoring each import
681 // listed in the table.
682 for (index = 0; index < tablesize; index++) {
683 entry = &patchtable[index];
684 restoreimport(importmodule, (HMODULE)entry->modulebase, entry->exportmodulename, entry->importname,
689 // setreportencoding - Sets the output encoding of report messages to either
690 // ASCII (the default) or Unicode.
692 // - encoding (IN): Specifies either "ascii" or "unicode".
698 VOID setreportencoding (encoding_e encoding)
703 reportencoding = encoding;
711 // setreportfile - Sets a destination file to which all report messages should
712 // be sent. If this function is not called to set a destination file, then
713 // report messages will be sent to the debugger instead of to a file.
715 // - file (IN): Pointer to an open file, to which future report messages should
718 // - copydebugger (IN): If true, in addition to sending report messages to
719 // the specified file, a copy of each message will also be sent to the
726 VOID setreportfile (FILE *file, BOOL copydebugger)
729 reporttodebugger = copydebugger;
732 // strapp - Appends the specified source string to the specified destination
733 // string. Allocates additional space so that the destination string "grows"
734 // as new strings are appended to it. This function is fairly infrequently
735 // used so efficiency is not a major concern.
737 // - dest (IN/OUT): Address of the destination string. Receives the resulting
738 // combined string after the append operation.
740 // - source (IN): Source string to be appended to the destination string.
746 VOID strapp (LPWSTR *dest, LPCWSTR source)
752 length = wcslen(*dest) + wcslen(source);
753 *dest = new WCHAR [length + 1];
754 wcsncpy_s(*dest, length + 1, temp, _TRUNCATE);
755 wcsncat_s(*dest, length + 1, source, _TRUNCATE);
759 // strtobool - Converts string values (e.g. "yes", "no", "on", "off") to boolean
762 // - s (IN): String value to convert.
766 // Returns TRUE if the string is recognized as a "true" string. Otherwise
769 BOOL strtobool (LPCWSTR s) {
772 if ((_wcsicmp(s, L"true") == 0) ||
773 (_wcsicmp(s, L"yes") == 0) ||
774 (_wcsicmp(s, L"on") == 0) ||
775 (wcstol(s, &end, 10) == 1)) {