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 <dia2.h>
6#include <stdio.h>
7
8#include <string>
9
10// Create an IDiaData source and open a PDB file.
11static 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
21  // Obtain access to the provider.
22  hr = CoCreateInstance(__uuidof(DiaSource),
23                        NULL,
24                        CLSCTX_INPROC_SERVER,
25                        __uuidof(IDiaDataSource),
26                        (void**)source);
27
28  if (FAILED(hr)) {
29    printf("CoCreateInstance failed - HRESULT = %08lX\n", hr);
30    return false;
31  }
32
33  wchar_t ext[MAX_PATH];
34  _wsplitpath_s(filename, NULL, 0, NULL, 0, NULL, 0, ext, MAX_PATH);
35
36  // Open and prepare the debug data associated with the executable.
37  hr = (*source)->loadDataForExe(filename, search_path, NULL);
38  if (FAILED(hr)) {
39    printf("loadDataForExe failed - HRESULT = %08lX\n", hr);
40    return false;
41  }
42
43  // Open a session for querying symbols.
44  hr = (*source)->openSession(session);
45
46  if (FAILED(hr)) {
47    printf("openSession failed - HRESULT = %08lX\n", hr);
48    return false;
49  }
50
51  // Retrieve a reference to the global scope.
52  hr = (*session)->get_globalScope(global);
53
54  if (FAILED(hr)) {
55    printf("get_globalScope failed\n");
56    return false;
57  }
58
59  // Set machine type for getting correct register names.
60  if (SUCCEEDED((*global)->get_machineType(&mach_type))) {
61    switch (mach_type) {
62      case IMAGE_FILE_MACHINE_I386:
63        *machine_type = CV_CFL_80386;
64        break;
65      case IMAGE_FILE_MACHINE_IA64:
66        *machine_type = CV_CFL_IA64;
67        break;
68      case IMAGE_FILE_MACHINE_AMD64:
69        *machine_type = CV_CFL_AMD64;
70        break;
71      default:
72        printf("unexpected machine type\n");
73        return false;
74    }
75  }
76
77  return true;
78}
79
80// Release DIA objects and CoUninitialize.
81static void Cleanup(IDiaSymbol* global_symbol, IDiaSession* dia_session) {
82  if (global_symbol)
83    global_symbol->Release();
84  if (dia_session)
85    dia_session->Release();
86  CoUninitialize();
87}
88
89static void PrintIfDynamicInitializer(const std::wstring& module,
90                                      IDiaSymbol* symbol) {
91  DWORD symtag;
92
93  if (FAILED(symbol->get_symTag(&symtag)))
94    return;
95
96  if (symtag != SymTagFunction && symtag != SymTagBlock)
97    return;
98
99  BSTR bstr_name;
100  if (SUCCEEDED(symbol->get_name(&bstr_name))) {
101    if (wcsstr(bstr_name, L"`dynamic initializer for '")) {
102      wprintf(L"%s: %s\n", module.c_str(), bstr_name);
103      SysFreeString(bstr_name);
104    }
105  }
106}
107
108static bool DumpStaticInitializers(IDiaSymbol* global_symbol) {
109  // Retrieve the compilands first.
110  IDiaEnumSymbols* enum_symbols;
111  if (FAILED(global_symbol->findChildren(
112          SymTagCompiland, NULL, nsNone, &enum_symbols))) {
113    return false;
114  }
115
116  IDiaSymbol* compiland;
117  ULONG element_count = 0;
118
119  std::wstring current_module;
120  while (SUCCEEDED(enum_symbols->Next(1, &compiland, &element_count)) &&
121         (element_count == 1)) {
122    BSTR bstr_name;
123    if (FAILED(compiland->get_name(&bstr_name))) {
124      current_module = L"<unknown>";
125    } else {
126      current_module = bstr_name;
127      SysFreeString(bstr_name);
128    }
129
130    // Find all the symbols defined in this compiland, and print them if they
131    // have the name corresponding to an initializer.
132    IDiaEnumSymbols* enum_children;
133    if (SUCCEEDED(compiland->findChildren(
134            SymTagNull, NULL, nsNone, &enum_children))) {
135      IDiaSymbol* symbol;
136      ULONG children = 0;
137      while (SUCCEEDED(enum_children->Next(1, &symbol, &children)) &&
138             children == 1) {  // Enumerate until we don't get any more symbols.
139        PrintIfDynamicInitializer(current_module, symbol);
140        symbol->Release();
141      }
142      enum_children->Release();
143    }
144    compiland->Release();
145  }
146
147  enum_symbols->Release();
148  return true;
149}
150
151int wmain(int argc, wchar_t* argv[]) {
152  if (argc != 2) {
153    wprintf(L"usage: %ls binary_name\n", argv[0]);
154    return 1;
155  }
156
157  IDiaDataSource* dia_data_source;
158  IDiaSession* dia_session;
159  IDiaSymbol* global_symbol;
160  DWORD machine_type = CV_CFL_80386;
161  if (!LoadDataFromPdb(argv[1],
162                       &dia_data_source,
163                       &dia_session,
164                       &global_symbol,
165                       &machine_type)) {
166    wprintf(L"Couldn't load data from pdb.\n");
167    return 1;
168  }
169
170  wprintf(L"Static initializers in %s:\n", argv[1]);
171
172  if (!DumpStaticInitializers(global_symbol))
173    return 1;
174
175  Cleanup(global_symbol, dia_session);
176
177  return 0;
178}
179