1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/pe/dia_util.h"
16 :
17 : #include <diacreate.h>
18 :
19 : #include "base/logging.h"
20 : #include "base/win/scoped_bstr.h"
21 : #include "base/win/scoped_comptr.h"
22 : #include "sawbuck/common/com_utils.h"
23 :
24 : namespace pe {
25 :
26 : using base::win::ScopedBstr;
27 : using base::win::ScopedComPtr;
28 :
29 : const wchar_t kDiaDllName[] = L"msdia100.dll";
30 : const wchar_t kFixupDiaDebugStreamName[] = L"FIXUP";
31 : const wchar_t kOmapToDiaDebugStreamName[] = L"OMAPTO";
32 : const wchar_t kOmapFromDiaDebugStreamName[] = L"OMAPFROM";
33 :
34 E : bool CreateDiaSource(IDiaDataSource** created_source) {
35 E : DCHECK(created_source != NULL);
36 :
37 E : *created_source = NULL;
38 :
39 E : ScopedComPtr<IDiaDataSource> dia_source;
40 E : HRESULT hr1 = dia_source.CreateInstance(CLSID_DiaSource);
41 E : if (SUCCEEDED(hr1)) {
42 E : *created_source = dia_source.Detach();
43 E : return true;
44 : }
45 :
46 : HRESULT hr2 = NoRegCoCreate(kDiaDllName,
47 : CLSID_DiaSource,
48 : IID_IDiaDataSource,
49 E : reinterpret_cast<void**>(&dia_source));
50 E : if (SUCCEEDED(hr2)) {
51 E : *created_source = dia_source.Detach();
52 E : return true;
53 : }
54 :
55 i : LOG(ERROR) << "Failed to create DiaDataSource.";
56 i : LOG(ERROR) << " CreateInstance failed with: " << com::LogHr(hr1);
57 i : LOG(ERROR) << " NoRegCoCreate failed with: " << com::LogHr(hr2);
58 :
59 i : return false;
60 E : }
61 :
62 : bool CreateDiaSession(const base::FilePath& file,
63 : IDiaDataSource* dia_source,
64 E : IDiaSession** dia_session) {
65 E : DCHECK(dia_source != NULL);
66 E : DCHECK(dia_session != NULL);
67 :
68 E : *dia_session = NULL;
69 :
70 E : HRESULT hr = E_FAIL;
71 :
72 E : if (file.Extension() == L".pdb") {
73 E : hr = dia_source->loadDataFromPdb(file.value().c_str());
74 E : } else {
75 E : hr = dia_source->loadDataForExe(file.value().c_str(), NULL, NULL);
76 : }
77 :
78 E : if (FAILED(hr)) {
79 i : LOG(ERROR) << "Failed to load DIA data for \"" << file.value() << "\": "
80 : << com::LogHr(hr) << ".";
81 i : return false;
82 : }
83 :
84 E : ScopedComPtr<IDiaSession> session;
85 E : hr = dia_source->openSession(session.Receive());
86 E : if (FAILED(hr)) {
87 i : LOG(ERROR) << "Failed to open DIA session for \"" << file.value() << "\" : "
88 : << com::LogHr(hr) << ".";
89 i : return false;
90 : }
91 :
92 E : *dia_session = session.Detach();
93 :
94 E : return true;
95 E : }
96 :
97 : SearchResult FindDiaTable(const IID& iid,
98 : IDiaSession* dia_session,
99 E : void** out_table) {
100 E : DCHECK(dia_session != NULL);
101 E : DCHECK(out_table != NULL);
102 :
103 E : *out_table = NULL;
104 :
105 : // Get the table enumerator.
106 E : base::win::ScopedComPtr<IDiaEnumTables> enum_tables;
107 E : HRESULT hr = dia_session->getEnumTables(enum_tables.Receive());
108 E : if (FAILED(hr)) {
109 i : LOG(ERROR) << "Failed to get DIA table enumerator: "
110 : << com::LogHr(hr) << ".";
111 i : return kSearchErrored;
112 : }
113 :
114 : // Iterate through the tables.
115 E : while (true) {
116 E : base::win::ScopedComPtr<IDiaTable> table;
117 E : ULONG fetched = 0;
118 E : hr = enum_tables->Next(1, table.Receive(), &fetched);
119 E : if (FAILED(hr)) {
120 i : LOG(ERROR) << "Failed to get DIA table: "
121 : << com::LogHr(hr) << ".";
122 i : return kSearchErrored;
123 : }
124 E : if (fetched == 0)
125 i : break;
126 :
127 E : hr = table.QueryInterface(iid, out_table);
128 E : if (SUCCEEDED(hr))
129 E : return kSearchSucceeded;
130 E : }
131 :
132 : // The search completed, even though we didn't find what we were looking for.
133 i : return kSearchFailed;
134 E : }
135 :
136 : SearchResult FindDiaDebugStream(const wchar_t* name,
137 : IDiaSession* dia_session,
138 E : IDiaEnumDebugStreamData** dia_debug_stream) {
139 E : DCHECK(name != NULL);
140 E : DCHECK(dia_session != NULL);
141 E : DCHECK(dia_debug_stream != NULL);
142 :
143 E : *dia_debug_stream = NULL;
144 :
145 E : HRESULT hr = E_FAIL;
146 E : ScopedComPtr<IDiaEnumDebugStreams> debug_streams;
147 E : if (FAILED(hr = dia_session->getEnumDebugStreams(debug_streams.Receive()))) {
148 i : LOG(ERROR) << "Unable to get debug streams: " << com::LogHr(hr) << ".";
149 i : return kSearchErrored;
150 : }
151 :
152 : // Iterate through the debug streams.
153 E : while (true) {
154 E : ScopedComPtr<IDiaEnumDebugStreamData> debug_stream;
155 E : ULONG count = 0;
156 E : HRESULT hr = debug_streams->Next(1, debug_stream.Receive(), &count);
157 E : if (FAILED(hr) || (hr != S_FALSE && count != 1)) {
158 i : LOG(ERROR) << "Unable to load debug stream: "
159 : << com::LogHr(hr) << ".";
160 i : return kSearchErrored;
161 E : } else if (hr == S_FALSE) {
162 : // No more records.
163 E : break;
164 : }
165 :
166 E : ScopedBstr stream_name;
167 E : if (FAILED(hr = debug_stream->get_name(stream_name.Receive()))) {
168 i : LOG(ERROR) << "Unable to get debug stream name: "
169 : << com::LogHr(hr) << ".";
170 i : return kSearchErrored;
171 : }
172 :
173 : // Found the stream?
174 E : if (wcscmp(com::ToString(stream_name), name) == 0) {
175 E : *dia_debug_stream = debug_stream.Detach();
176 E : return kSearchSucceeded;
177 : }
178 E : }
179 :
180 E : return kSearchFailed;
181 E : }
182 :
183 E : bool GetSymTag(IDiaSymbol* symbol, enum SymTagEnum* sym_tag) {
184 E : DCHECK(symbol != NULL);
185 E : DCHECK(sym_tag != NULL);
186 E : DWORD tmp_tag = SymTagNull;
187 E : *sym_tag = SymTagNull;
188 E : HRESULT hr = symbol->get_symTag(&tmp_tag);
189 E : if (hr != S_OK) {
190 i : LOG(ERROR) << "Error getting sym tag: " << com::LogHr(hr) << ".";
191 i : return false;
192 : }
193 E : *sym_tag = static_cast<enum SymTagEnum>(tmp_tag);
194 E : return true;
195 E : }
196 :
197 E : bool IsSymTag(IDiaSymbol* symbol, enum SymTagEnum expected_sym_tag) {
198 E : DCHECK(symbol != NULL);
199 E : DCHECK(expected_sym_tag != SymTagNull);
200 :
201 E : enum SymTagEnum sym_tag = SymTagNull;
202 E : if (!GetSymTag(symbol, &sym_tag))
203 i : return false;
204 :
205 E : return sym_tag == expected_sym_tag;
206 E : }
207 :
208 : ChildVisitor::ChildVisitor(IDiaSymbol* parent, enum SymTagEnum type)
209 E : : parent_(parent), type_(type), child_callback_(NULL) {
210 E : DCHECK(parent != NULL);
211 E : }
212 :
213 E : bool ChildVisitor::VisitChildren(const VisitSymbolCallback& child_callback) {
214 E : DCHECK(child_callback_ == NULL);
215 :
216 E : child_callback_ = &child_callback;
217 E : bool ret = VisitChildrenImpl();
218 E : child_callback_ = NULL;
219 :
220 E : return ret;
221 E : }
222 :
223 E : bool ChildVisitor::VisitChildrenImpl() {
224 E : DCHECK(child_callback_ != NULL);
225 :
226 : // Retrieve an enumerator for all children in this PDB.
227 E : base::win::ScopedComPtr<IDiaEnumSymbols> children;
228 : HRESULT hr = parent_->findChildren(type_,
229 : NULL,
230 : nsNone,
231 E : children.Receive());
232 E : if (FAILED(hr)) {
233 i : LOG(ERROR) << "Unable to get children: " << com::LogHr(hr);
234 i : return false;
235 : }
236 :
237 E : return EnumerateChildren(children);
238 E : }
239 :
240 E : bool ChildVisitor::EnumerateChildren(IDiaEnumSymbols* children) {
241 E : DCHECK(children!= NULL);
242 :
243 E : while (true) {
244 E : base::win::ScopedComPtr<IDiaSymbol> child;
245 E : ULONG fetched = 0;
246 E : HRESULT hr = children->Next(1, child.Receive(), &fetched);
247 E : if (FAILED(hr)) {
248 i : DCHECK_EQ(0U, fetched);
249 i : DCHECK(child == NULL);
250 i : LOG(ERROR) << "Unable to iterate children: " << com::LogHr(hr);
251 i : return false;
252 : }
253 E : if (hr == S_FALSE)
254 E : break;
255 :
256 E : DCHECK_EQ(1U, fetched);
257 E : DCHECK(child != NULL);
258 :
259 E : if (!VisitChild(child))
260 E : return false;
261 E : }
262 :
263 E : return true;
264 E : }
265 :
266 E : bool ChildVisitor::VisitChild(IDiaSymbol* child) {
267 E : DCHECK(child_callback_ != NULL);
268 :
269 E : return child_callback_->Run(child);
270 E : }
271 :
272 E : CompilandVisitor::CompilandVisitor(IDiaSession* session) : session_(session) {
273 E : DCHECK(session != NULL);
274 E : }
275 :
276 : bool CompilandVisitor::VisitAllCompilands(
277 E : const VisitCompilandCallback& compiland_callback) {
278 E : base::win::ScopedComPtr<IDiaSymbol> global;
279 E : HRESULT hr = session_->get_globalScope(global.Receive());
280 E : if (FAILED(hr)) {
281 i : LOG(ERROR) << "Unable to get global scope: " << com::LogHr(hr);
282 i : return false;
283 : }
284 :
285 E : ChildVisitor visitor(global, SymTagCompiland);
286 :
287 E : return visitor.VisitChildren(compiland_callback);
288 E : }
289 :
290 : LineVisitor::LineVisitor(IDiaSession* session, IDiaSymbol* compiland)
291 E : : session_(session), compiland_(compiland), line_callback_(NULL) {
292 E : DCHECK(session != NULL);
293 E : }
294 :
295 E : bool LineVisitor::VisitLines(const VisitLineCallback& line_callback) {
296 E : DCHECK(line_callback_ == NULL);
297 :
298 E : line_callback_ = &line_callback;
299 E : bool ret = VisitLinesImpl();
300 E : line_callback_ = NULL;
301 :
302 E : return ret;
303 E : }
304 :
305 : bool LineVisitor::EnumerateCompilandSource(IDiaSymbol* compiland,
306 E : IDiaSourceFile* source_file) {
307 E : DCHECK(compiland != NULL);
308 E : DCHECK(source_file != NULL);
309 :
310 E : base::win::ScopedComPtr<IDiaEnumLineNumbers> line_numbers;
311 : HRESULT hr = session_->findLines(compiland,
312 : source_file,
313 E : line_numbers.Receive());
314 E : if (FAILED(hr)) {
315 : // This seems to happen for the occasional header file.
316 i : return true;
317 : }
318 :
319 E : while (true) {
320 E : base::win::ScopedComPtr<IDiaLineNumber> line_number;
321 E : ULONG fetched = 0;
322 E : hr = line_numbers->Next(1, line_number.Receive(), &fetched);
323 E : if (FAILED(hr)) {
324 i : DCHECK_EQ(0U, fetched);
325 i : DCHECK(line_number == NULL);
326 i : LOG(ERROR) << "Unable to iterate line numbers: " << com::LogHr(hr);
327 i : return false;
328 : }
329 E : if (hr == S_FALSE)
330 E : break;
331 :
332 E : DCHECK_EQ(1U, fetched);
333 E : DCHECK(line_number != NULL);
334 :
335 E : if (!VisitSourceLine(line_number))
336 i : return false;
337 E : }
338 :
339 E : return true;
340 E : }
341 :
342 : bool LineVisitor::EnumerateCompilandSources(IDiaSymbol* compiland,
343 E : IDiaEnumSourceFiles* source_files) {
344 E : DCHECK(compiland != NULL);
345 E : DCHECK(source_files != NULL);
346 :
347 E : while (true) {
348 E : base::win::ScopedComPtr<IDiaSourceFile> source_file;
349 E : ULONG fetched = 0;
350 E : HRESULT hr = source_files->Next(1, source_file.Receive(), &fetched);
351 E : if (FAILED(hr)) {
352 i : DCHECK_EQ(0U, fetched);
353 i : DCHECK(source_file == NULL);
354 i : LOG(ERROR) << "Unable to iterate source files: " << com::LogHr(hr);
355 i : return false;
356 : }
357 E : if (hr == S_FALSE)
358 E : break;
359 :
360 E : DCHECK_EQ(1U, fetched);
361 E : DCHECK(compiland != NULL);
362 :
363 E : if (!EnumerateCompilandSource(compiland, source_file))
364 i : return false;
365 E : }
366 :
367 E : return true;
368 E : }
369 :
370 E : bool LineVisitor::VisitLinesImpl() {
371 E : DCHECK(session_ != NULL);
372 E : DCHECK(compiland_ != NULL);
373 E : DCHECK(line_callback_ != NULL);
374 :
375 : // Enumerate all source files referenced by this compiland.
376 E : base::win::ScopedComPtr<IDiaEnumSourceFiles> source_files;
377 : HRESULT hr = session_->findFile(compiland_,
378 : NULL,
379 : nsNone,
380 E : source_files.Receive());
381 E : if (FAILED(hr)) {
382 i : LOG(ERROR) << "Unable to get source files: " << com::LogHr(hr);
383 i : return false;
384 : }
385 :
386 E : return EnumerateCompilandSources(compiland_, source_files);
387 E : }
388 :
389 E : bool LineVisitor::VisitSourceLine(IDiaLineNumber* line_number) {
390 E : DCHECK(line_callback_ != NULL);
391 :
392 E : return line_callback_->Run(line_number);
393 E : }
394 :
395 : } // namespace pe
|