static_initializers.cc 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <Windows.h>
  5. #include <dbghelp.h>
  6. #include <dia2.h>
  7. #include <stdio.h>
  8. #include <string>
  9. static const size_t kMaxSymbolLength = 4096;
  10. // Create an IDiaData source and open a PDB file.
  11. static bool LoadDataFromPdb(const wchar_t* filename,
  12. IDiaDataSource** source,
  13. IDiaSession** session,
  14. IDiaSymbol** global,
  15. DWORD* machine_type) {
  16. // Alternate path to search for debug data.
  17. const wchar_t search_path[] = L"SRV**\\\\symbols\\symbols";
  18. DWORD mach_type = 0;
  19. HRESULT hr = CoInitialize(NULL);
  20. // Obtain access to the provider.
  21. hr = CoCreateInstance(__uuidof(DiaSource),
  22. NULL,
  23. CLSCTX_INPROC_SERVER,
  24. __uuidof(IDiaDataSource),
  25. (void**)source);
  26. if (FAILED(hr)) {
  27. printf("CoCreateInstance failed - HRESULT = %08lX\n", hr);
  28. return false;
  29. }
  30. wchar_t ext[MAX_PATH];
  31. _wsplitpath_s(filename, NULL, 0, NULL, 0, NULL, 0, ext, MAX_PATH);
  32. if (wcsicmp(ext, L".pdb") == 0) {
  33. // Open and prepare the debug data specified.
  34. hr = (*source)->loadDataFromPdb(filename);
  35. if (FAILED(hr)) {
  36. printf("loadDataFromPdb failed - HRESULT = %08lX\n", hr);
  37. return false;
  38. }
  39. } else {
  40. // Open and prepare the debug data associated with the executable.
  41. hr = (*source)->loadDataForExe(filename, search_path, NULL);
  42. if (FAILED(hr)) {
  43. printf("loadDataForExe failed - HRESULT = %08lX\n", hr);
  44. printf(
  45. "Try copying the .pdb beside the PE file or passing the .pdb path "
  46. "to this tool directly.");
  47. return false;
  48. }
  49. }
  50. // Open a session for querying symbols.
  51. hr = (*source)->openSession(session);
  52. if (FAILED(hr)) {
  53. printf("openSession failed - HRESULT = %08lX\n", hr);
  54. return false;
  55. }
  56. // Retrieve a reference to the global scope.
  57. hr = (*session)->get_globalScope(global);
  58. if (FAILED(hr)) {
  59. printf("get_globalScope failed\n");
  60. return false;
  61. }
  62. // Set machine type for getting correct register names.
  63. if (SUCCEEDED((*global)->get_machineType(&mach_type))) {
  64. switch (mach_type) {
  65. case IMAGE_FILE_MACHINE_I386:
  66. *machine_type = CV_CFL_80386;
  67. break;
  68. case IMAGE_FILE_MACHINE_IA64:
  69. *machine_type = CV_CFL_IA64;
  70. break;
  71. case IMAGE_FILE_MACHINE_AMD64:
  72. *machine_type = CV_CFL_AMD64;
  73. break;
  74. default:
  75. printf("unexpected machine type\n");
  76. return false;
  77. }
  78. }
  79. return true;
  80. }
  81. // Release DIA objects and CoUninitialize.
  82. static void Cleanup(IDiaSymbol* global_symbol, IDiaSession* dia_session) {
  83. if (global_symbol)
  84. global_symbol->Release();
  85. if (dia_session)
  86. dia_session->Release();
  87. CoUninitialize();
  88. }
  89. static void PrintIfDynamicInitializer(const std::wstring& module,
  90. IDiaSymbol* symbol) {
  91. DWORD symtag;
  92. if (FAILED(symbol->get_symTag(&symtag)))
  93. return;
  94. if (symtag != SymTagFunction && symtag != SymTagBlock)
  95. return;
  96. BSTR bstr_name;
  97. if (SUCCEEDED(symbol->get_name(&bstr_name))) {
  98. if (wcsstr(bstr_name, L"`dynamic initializer for '") ||
  99. wcsstr(bstr_name, L"`dynamic atexit destructor for '")) {
  100. wprintf(L"%s: %s\n", module.c_str(), bstr_name);
  101. }
  102. // If there are multiple dynamic initializers in one translation unit then
  103. // a shared function is created and the individual initializers may be
  104. // inlined into it. These functions start with a characteristic name that
  105. // includes the source file. Finding the actual objects can be done through
  106. // source inspection or by setting a breakpoint on the printed name. The
  107. // "dynamic initializer" string is printed for consistent grepping.
  108. if (wcsstr(bstr_name, L"_GLOBAL__sub_I")) {
  109. wprintf(L"%s: %s (dynamic initializer)\n", module.c_str(), bstr_name);
  110. }
  111. // As of this writing, Clang does not undecorate the symbol names for
  112. // dynamic initializers, so the debug info contains the decorated name,
  113. // which starts with "??__E" or "??__F" for atexit destructors. Check for
  114. // that, and print the undecorated name if it matches.
  115. if (wcsncmp(bstr_name, L"??__E", 5) == 0 ||
  116. wcsncmp(bstr_name, L"??__F", 5) == 0) {
  117. wchar_t undecorated[kMaxSymbolLength];
  118. if (UnDecorateSymbolNameW(bstr_name, undecorated, kMaxSymbolLength,
  119. UNDNAME_NAME_ONLY) == 0) {
  120. printf("UnDecorateSymbolNameW failed, %d\n", GetLastError());
  121. return;
  122. }
  123. wprintf(L"%s: %s\n", module.c_str(), undecorated);
  124. }
  125. SysFreeString(bstr_name);
  126. }
  127. }
  128. static bool DumpStaticInitializers(IDiaSymbol* global_symbol) {
  129. // Retrieve the compilands first.
  130. IDiaEnumSymbols* enum_symbols;
  131. if (FAILED(global_symbol->findChildren(
  132. SymTagCompiland, NULL, nsNone, &enum_symbols))) {
  133. return false;
  134. }
  135. IDiaSymbol* compiland;
  136. ULONG element_count = 0;
  137. std::wstring current_module;
  138. while (SUCCEEDED(enum_symbols->Next(1, &compiland, &element_count)) &&
  139. (element_count == 1)) {
  140. BSTR bstr_name;
  141. if (FAILED(compiland->get_name(&bstr_name))) {
  142. current_module = L"<unknown>";
  143. } else {
  144. current_module = bstr_name;
  145. SysFreeString(bstr_name);
  146. }
  147. // Find all the symbols defined in this compiland, and print them if they
  148. // have the name corresponding to an initializer.
  149. IDiaEnumSymbols* enum_children;
  150. if (SUCCEEDED(compiland->findChildren(
  151. SymTagNull, NULL, nsNone, &enum_children))) {
  152. IDiaSymbol* symbol;
  153. ULONG children = 0;
  154. while (SUCCEEDED(enum_children->Next(1, &symbol, &children)) &&
  155. children == 1) { // Enumerate until we don't get any more symbols.
  156. PrintIfDynamicInitializer(current_module, symbol);
  157. symbol->Release();
  158. }
  159. enum_children->Release();
  160. }
  161. compiland->Release();
  162. }
  163. enum_symbols->Release();
  164. return true;
  165. }
  166. int wmain(int argc, wchar_t* argv[]) {
  167. if (argc != 2) {
  168. wprintf(L"usage: %ls binary_name\n", argv[0]);
  169. return 1;
  170. }
  171. IDiaDataSource* dia_data_source;
  172. IDiaSession* dia_session;
  173. IDiaSymbol* global_symbol;
  174. DWORD machine_type = CV_CFL_80386;
  175. if (!LoadDataFromPdb(argv[1],
  176. &dia_data_source,
  177. &dia_session,
  178. &global_symbol,
  179. &machine_type)) {
  180. wprintf(L"Couldn't load data from pdb.\n");
  181. return 1;
  182. }
  183. wprintf(L"Static initializers in %s:\n", argv[1]);
  184. if (!DumpStaticInitializers(global_symbol))
  185. return 1;
  186. Cleanup(global_symbol, dia_session);
  187. return 0;
  188. }