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