1 : // Copyright 2015 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/refinery/types/dia_crawler.h"
16 :
17 : #include <unordered_map>
18 :
19 : #include "base/strings/string_util.h"
20 : #include "base/strings/stringprintf.h"
21 : #include "base/win/scoped_bstr.h"
22 : #include "syzygy/pe/dia_util.h"
23 : #include "syzygy/refinery/types/type_namer.h"
24 : #include "syzygy/refinery/types/type_repository.h"
25 :
26 m : namespace refinery {
27 :
28 m : namespace {
29 :
30 m : bool GetSymFlags(IDiaSymbol* symbol, Type::Flags* flags) {
31 m : DCHECK(symbol); DCHECK(flags);
32 m : *flags = 0;
33 :
34 m : bool is_const = false;
35 m : bool is_volatile = false;
36 m : if (!pe::GetSymQualifiers(symbol, &is_const, &is_volatile))
37 m : return false;
38 :
39 m : if (is_const)
40 m : *flags |= UserDefinedType::FLAG_CONST;
41 m : if (is_volatile)
42 m : *flags |= UserDefinedType::FLAG_VOLATILE;
43 :
44 m : return true;
45 m : }
46 :
47 m : bool GetSymSize(IDiaSymbol* symbol, size_t* size) {
48 m : DCHECK(symbol); DCHECK(size);
49 :
50 m : ULONGLONG length = 0;
51 m : HRESULT hr = symbol->get_length(&length);
52 m : if (hr != S_OK)
53 m : return false;
54 :
55 m : *size = static_cast<size_t>(length);
56 m : return true;
57 m : }
58 :
59 m : bool GetSymBitPos(IDiaSymbol* symbol, size_t* bit_position) {
60 m : DCHECK(symbol); DCHECK(bit_position);
61 :
62 m : DWORD temp = 0;
63 m : HRESULT hr = symbol->get_bitPosition(&temp);
64 m : if (hr != S_OK)
65 m : return false;
66 :
67 m : *bit_position = static_cast<size_t>(temp);
68 m : return true;
69 m : }
70 :
71 m : bool GetSymArrayIndexType(IDiaSymbol* symbol,
72 m : base::win::ScopedComPtr<IDiaSymbol>* type) {
73 m : DCHECK(symbol);
74 m : DCHECK(type);
75 m : base::win::ScopedComPtr<IDiaSymbol> tmp;
76 m : HRESULT hr = symbol->get_arrayIndexType(tmp.Receive());
77 m : if (hr != S_OK)
78 m : return false;
79 :
80 m : *type = tmp;
81 m : return true;
82 m : }
83 :
84 m : bool GetSymIndexId(IDiaSymbol* symbol, DWORD* index_id) {
85 m : DCHECK(symbol); DCHECK(index_id);
86 m : DWORD tmp = 0;
87 m : HRESULT hr = symbol->get_symIndexId(&tmp);
88 m : if (!SUCCEEDED(hr))
89 m : return false;
90 m : *index_id = tmp;
91 m : return true;
92 m : }
93 :
94 m : bool GetSymPtrMode(IDiaSymbol* symbol, PointerType::Mode* is_reference) {
95 m : DCHECK(symbol);
96 m : DCHECK(is_reference);
97 m : BOOL is_ref;
98 m : HRESULT hr = symbol->get_reference(&is_ref);
99 m : if (hr != S_OK)
100 m : return false;
101 m : *is_reference = PointerType::PTR_MODE_PTR;
102 m : if (is_ref)
103 m : *is_reference = PointerType::PTR_MODE_REF;
104 m : return true;
105 m : }
106 :
107 m : bool GetSymCallingConvention(IDiaSymbol* symbol,
108 m : FunctionType::CallConvention* call_convention) {
109 m : DCHECK(symbol);
110 m : DCHECK(call_convention);
111 m : DWORD tmp = 0;
112 m : HRESULT hr = symbol->get_callingConvention(&tmp);
113 m : if (!SUCCEEDED(hr))
114 m : return false;
115 m : *call_convention = static_cast<FunctionType::CallConvention>(tmp);
116 m : return true;
117 m : }
118 :
119 m : bool GetSymUdtKind(IDiaSymbol* symbol, UserDefinedType::UdtKind* udt_kind) {
120 m : DCHECK(symbol);
121 m : DCHECK(udt_kind);
122 m : DWORD cci_udt_kind;
123 m : HRESULT hr = symbol->get_udtKind(&cci_udt_kind);
124 m : if (hr != S_OK)
125 m : return false;
126 :
127 m : switch (cci_udt_kind) {
128 m : case UdtStruct: {
129 m : *udt_kind = UserDefinedType::UDT_STRUCT;
130 m : break;
131 m : }
132 m : case UdtClass: {
133 m : *udt_kind = UserDefinedType::UDT_CLASS;
134 m : break;
135 m : }
136 m : case UdtUnion: {
137 m : *udt_kind = UserDefinedType::UDT_UNION;
138 m : break;
139 m : }
140 m : case UdtInterface: {
141 m : NOTREACHED() << "Stumbled upon interface UDT kind which we don't expect.";
142 m : }
143 m : }
144 :
145 m : return true;
146 m : }
147 :
148 m : class TypeCreator {
149 m : public:
150 m : explicit TypeCreator(TypeRepository* repository);
151 :
152 : // Crawls @p global, creates all types and assigns names to pointers.
153 m : bool CreateTypes(IDiaSymbol* global);
154 :
155 m : private:
156 m : bool CreateTypesOfKind(enum SymTagEnum kind, IDiaSymbol* global);
157 m : bool CreateGlobalDataTypes(IDiaSymbol* global);
158 :
159 : // Finds or creates the type corresponding to @p symbol.
160 : // The type will be registered by a unique name in @p existing_types_.
161 m : TypePtr FindOrCreateType(IDiaSymbol* symbol);
162 :
163 m : TypePtr CreateType(IDiaSymbol* symbol);
164 m : TypePtr CreateUDT(IDiaSymbol* symbol);
165 m : TypePtr CreateEnum(IDiaSymbol* symbol);
166 m : TypePtr CreateFunctionType(IDiaSymbol* symbol);
167 m : TypePtr CreateBaseType(IDiaSymbol* symbol);
168 m : TypePtr CreatePointerType(IDiaSymbol* symbol);
169 m : TypePtr CreateTypedefType(IDiaSymbol* symbol);
170 m : TypePtr CreateArrayType(IDiaSymbol* symbol);
171 :
172 m : TypePtr CreateGlobalType(IDiaSymbol* symbol,
173 m : const base::string16& name,
174 m : uint64_t rva);
175 :
176 m : bool FinalizeUDT(IDiaSymbol* symbol, UserDefinedTypePtr udt);
177 m : bool FinalizePointer(IDiaSymbol* symbol, PointerTypePtr ptr);
178 m : bool FinalizeArray(IDiaSymbol* symbol, ArrayTypePtr type);
179 m : bool FinalizeFunction(IDiaSymbol* symbol, FunctionTypePtr type);
180 m : bool FinalizeType(IDiaSymbol* symbol, TypePtr type);
181 :
182 m : struct CreatedType {
183 m : CreatedType() : type_id(kNoTypeId), is_finalized(false) {
184 m : }
185 :
186 m : TypeId type_id;
187 m : bool is_finalized;
188 m : };
189 m : typedef std::unordered_map<DWORD, CreatedType> CreatedTypeMap;
190 :
191 : // Maps from DIA symbol index ID to the created TypeId. Also keeps a flag
192 : // that's set when a type is finalized, as DIA has a nasty habit of
193 : // enumerating the same type multiple times.
194 m : CreatedTypeMap created_types_;
195 m : TypeRepository* repository_;
196 m : };
197 :
198 m : TypeCreator::TypeCreator(TypeRepository* repository)
199 m : : repository_(repository) {
200 m : DCHECK(repository);
201 m : }
202 :
203 m : bool TypeCreator::CreateTypesOfKind(enum SymTagEnum kind, IDiaSymbol* global) {
204 m : base::win::ScopedComPtr<IDiaEnumSymbols> matching_types;
205 m : HRESULT hr = global->findChildren(kind,
206 m : nullptr,
207 m : nsNone,
208 m : matching_types.Receive());
209 m : if (!SUCCEEDED(hr))
210 m : return false;
211 :
212 : // The function get_Count from DIA has either a bug or is really badly
213 : // implemented thus taking forever to finish. Therefore we simply load next
214 : // symbol until reaching the end.
215 m : base::win::ScopedComPtr<IDiaSymbol> symbol;
216 m : ULONG received = 0;
217 m : hr = matching_types->Next(1, symbol.Receive(), &received);
218 :
219 m : while (hr == S_OK) {
220 m : scoped_refptr<Type> type = FindOrCreateType(symbol.get());
221 m : if (!type)
222 m : return false;
223 :
224 m : if (!FinalizeType(symbol.get(), type))
225 m : return false;
226 :
227 m : symbol.Release();
228 m : received = 0;
229 m : hr = matching_types->Next(1, symbol.Receive(), &received);
230 m : }
231 :
232 m : if (!SUCCEEDED(hr))
233 m : return false;
234 :
235 m : return true;
236 m : }
237 :
238 m : bool TypeCreator::CreateGlobalDataTypes(IDiaSymbol* global) {
239 m : base::win::ScopedComPtr<IDiaEnumSymbols> matching_types;
240 m : HRESULT hr = global->findChildren(SymTagData, nullptr, nsNone,
241 m : matching_types.Receive());
242 m : if (!SUCCEEDED(hr))
243 m : return false;
244 :
245 m : ULONG received = 0;
246 m : while (true) {
247 m : base::win::ScopedComPtr<IDiaSymbol> symbol;
248 m : hr = matching_types->Next(1, symbol.Receive(), &received);
249 m : if (hr != S_OK)
250 m : break;
251 :
252 : // Filter here for symbols that have all the required properties.
253 m : LocationType location_type = LocIsNull;
254 m : DataKind data_kind = DataIsUnknown;
255 m : if (!pe::GetLocationType(symbol.get(), &location_type))
256 m : return false;
257 m : if (location_type != LocIsStatic)
258 m : continue;
259 :
260 m : if (!pe::GetDataKind(symbol.get(), &data_kind))
261 m : return false;
262 :
263 m : switch (data_kind) {
264 m : case DataIsUnknown:
265 m : case DataIsLocal:
266 m : case DataIsParam:
267 m : case DataIsObjectPtr:
268 m : case DataIsMember:
269 m : case DataIsStaticMember:
270 m : case DataIsConstant:
271 m : continue;
272 :
273 m : case DataIsStaticLocal:
274 m : case DataIsFileStatic:
275 m : case DataIsGlobal:
276 : // This data should have an RVA.
277 m : break;
278 m : }
279 :
280 m : base::string16 name;
281 m : if (!pe::GetSymName(symbol.get(), &name))
282 m : return false;
283 :
284 m : DWORD rva = 0;
285 m : HRESULT hr = symbol->get_relativeVirtualAddress(&rva);
286 m : if (hr != S_OK) {
287 : // This condition occurs for precisely two symbols that we've noticed.
288 m : if (name == L"__safe_se_handler_count" ||
289 m : name == L"__safe_se_handler_table") {
290 m : continue;
291 m : }
292 :
293 : // Make sure to err out for other cases for now.
294 : // TODO(siggi): Revisit this once the reason for this anomaly is
295 : // understood.
296 m : LOG(ERROR) << "Symbol " << name << " has no RVA!";
297 m : return false;
298 m : }
299 :
300 : // See whether the type has been created.
301 m : DWORD index_id = 0;
302 m : if (!GetSymIndexId(symbol.get(), &index_id))
303 m : return false;
304 :
305 m : auto it = created_types_.find(index_id);
306 m : if (it != created_types_.end())
307 m : continue;
308 :
309 : // Ok, we need to create it.
310 m : TypePtr created = CreateGlobalType(symbol.get(), name, rva);
311 m : DCHECK(created);
312 m : CreatedType& entry = created_types_[index_id];
313 m : entry.type_id = repository_->AddType(created);
314 m : entry.is_finalized = false;
315 :
316 m : if (!FinalizeType(symbol.get(), created))
317 m : return false;
318 m : }
319 :
320 m : if (!SUCCEEDED(hr))
321 m : return false;
322 :
323 m : return true;
324 m : }
325 :
326 m : bool TypeCreator::FinalizeType(IDiaSymbol* symbol, TypePtr type) {
327 : // See whether this type needs finalizing.
328 m : DWORD index_id = 0;
329 m : if (!GetSymIndexId(symbol, &index_id))
330 m : return false;
331 :
332 m : DCHECK_EQ(type->type_id(), created_types_[index_id].type_id);
333 m : if (created_types_[index_id].is_finalized) {
334 : // This is a re-visit of the same type. DIA has a nasty habit of doing
335 : // this, e.g. yielding the same type multiple times in an iteration.
336 m : return true;
337 m : }
338 :
339 m : created_types_[index_id].is_finalized = true;
340 :
341 m : switch (type->kind()) {
342 m : case Type::USER_DEFINED_TYPE_KIND: {
343 m : UserDefinedTypePtr udt;
344 m : if (!type->CastTo(&udt))
345 m : return false;
346 :
347 m : return FinalizeUDT(symbol, udt);
348 m : }
349 :
350 m : case Type::POINTER_TYPE_KIND: {
351 m : PointerTypePtr ptr;
352 m : if (!type->CastTo(&ptr))
353 m : return false;
354 :
355 m : return FinalizePointer(symbol, ptr);
356 m : }
357 :
358 m : case Type::ARRAY_TYPE_KIND: {
359 m : ArrayTypePtr array;
360 m : if (!type->CastTo(&array))
361 m : return false;
362 :
363 m : return FinalizeArray(symbol, array);
364 m : }
365 :
366 m : case Type::FUNCTION_TYPE_KIND: {
367 m : FunctionTypePtr function;
368 m : if (!type->CastTo(&function))
369 m : return false;
370 :
371 m : return FinalizeFunction(symbol, function);
372 m : }
373 :
374 m : default:
375 m : return true;
376 m : }
377 m : }
378 :
379 m : bool TypeCreator::CreateTypes(IDiaSymbol* global) {
380 m : if (!CreateTypesOfKind(SymTagUDT, global) ||
381 m : !CreateTypesOfKind(SymTagEnum, global) ||
382 m : !CreateTypesOfKind(SymTagTypedef, global) ||
383 m : !CreateTypesOfKind(SymTagPointerType, global) ||
384 m : !CreateTypesOfKind(SymTagArrayType, global) ||
385 m : !CreateTypesOfKind(SymTagFunctionType, global) ||
386 m : !CreateGlobalDataTypes(global)) {
387 m : return false;
388 m : }
389 :
390 m : return true;
391 m : }
392 :
393 m : TypePtr TypeCreator::FindOrCreateType(IDiaSymbol* symbol) {
394 m : DCHECK(symbol);
395 :
396 m : DWORD index_id = 0;
397 m : if (!GetSymIndexId(symbol, &index_id))
398 m : return false;
399 :
400 m : auto it = created_types_.find(index_id);
401 m : if (it != created_types_.end())
402 m : return repository_->GetType(it->second.type_id);
403 :
404 : // Note that this will recurse on pointer types, but the recursion should
405 : // terminate on a basic type or a UDT at some point - assuming the type
406 : // graph is sane.
407 : // TODO(siggi): It'd be better never to recurse, and this can be avoided for
408 : // pointers by doing two-phase construction on them as for UDTs. To assign
409 : // unique, human-readable names to pointers requires another pass yet.
410 m : TypePtr created = CreateType(symbol);
411 m : DCHECK(created);
412 m : CreatedType& entry = created_types_[index_id];
413 m : entry.type_id = repository_->AddType(created);
414 m : entry.is_finalized = false;
415 :
416 : // Pointers to base types will not get enumerated by DIA and therefore need to
417 : // be finalized manually. We do so here.
418 m : if (created->kind() == Type::POINTER_TYPE_KIND) {
419 m : base::win::ScopedComPtr<IDiaSymbol> contained_type_sym;
420 m : if (!pe::GetSymType(symbol, &contained_type_sym))
421 m : return nullptr;
422 m : enum SymTagEnum contained_sym_tag = SymTagNull;
423 m : if (!pe::GetSymTag(contained_type_sym.get(), &contained_sym_tag))
424 m : return nullptr;
425 m : if (contained_sym_tag == SymTagBaseType) {
426 m : if (!FinalizeType(symbol, created))
427 m : return nullptr;
428 m : }
429 m : }
430 :
431 m : return created;
432 m : }
433 :
434 m : TypePtr TypeCreator::CreateType(IDiaSymbol* symbol) {
435 m : DCHECK(symbol);
436 :
437 m : enum SymTagEnum sym_tag = SymTagNull;
438 m : if (!pe::GetSymTag(symbol, &sym_tag))
439 m : return nullptr;
440 :
441 m : switch (sym_tag) {
442 m : case SymTagUDT:
443 m : return CreateUDT(symbol);
444 m : case SymTagEnum:
445 m : return CreateEnum(symbol);
446 m : case SymTagBaseType:
447 m : return CreateBaseType(symbol);
448 m : case SymTagFunctionType:
449 m : return CreateFunctionType(symbol);
450 m : case SymTagPointerType:
451 m : return CreatePointerType(symbol);
452 m : case SymTagTypedef:
453 m : return CreateTypedefType(symbol);
454 m : case SymTagArrayType:
455 m : return CreateArrayType(symbol);
456 m : case SymTagVTableShape:
457 m : return new WildcardType(L"VTableShape", 0);
458 m : case SymTagVTable:
459 m : return new WildcardType(L"VTable", 0);
460 m : default:
461 m : return nullptr;
462 m : }
463 m : }
464 :
465 m : TypePtr TypeCreator::CreateUDT(IDiaSymbol* symbol) {
466 m : DCHECK(symbol);
467 m : DCHECK(pe::IsSymTag(symbol, SymTagUDT));
468 :
469 m : base::string16 name;
470 m : size_t size = 0;
471 m : UserDefinedType::UdtKind udt_kind;
472 m : if (!pe::GetSymName(symbol, &name) || !GetSymSize(symbol, &size) ||
473 m : !GetSymUdtKind(symbol, &udt_kind)) {
474 m : return nullptr;
475 m : }
476 :
477 m : return new UserDefinedType(name, size, udt_kind);
478 m : }
479 :
480 m : TypePtr TypeCreator::CreateEnum(IDiaSymbol* symbol) {
481 m : DCHECK(symbol);
482 m : DCHECK(pe::IsSymTag(symbol, SymTagEnum));
483 :
484 m : base::string16 name;
485 m : size_t size = 0;
486 m : if (!pe::GetSymName(symbol, &name) || !GetSymSize(symbol, &size))
487 m : return nullptr;
488 :
489 : // TODO(siggi): Implement an enum type.
490 m : return new WildcardType(name, size);
491 m : }
492 :
493 m : bool TypeCreator::FinalizeUDT(IDiaSymbol* symbol, UserDefinedTypePtr udt) {
494 m : DCHECK(symbol);
495 m : DCHECK(udt);
496 m : DCHECK(pe::IsSymTag(symbol, SymTagUDT));
497 :
498 : // Enumerate the fields and add them.
499 m : base::win::ScopedComPtr<IDiaEnumSymbols> enum_children;
500 m : HRESULT hr = symbol->findChildren(
501 m : SymTagNull, NULL, nsNone, enum_children.Receive());
502 m : if (!SUCCEEDED(hr))
503 m : return false;
504 :
505 m : LONG count = 0;
506 m : hr = enum_children->get_Count(&count);
507 m : if (!SUCCEEDED(hr))
508 m : return false;
509 :
510 m : UserDefinedType::Fields fields;
511 m : UserDefinedType::Functions functions;
512 m : for (LONG i = 0; i < count; ++i) {
513 m : base::win::ScopedComPtr<IDiaSymbol> field_sym;
514 m : hr = enum_children->Item(i, field_sym.Receive());
515 m : if (!SUCCEEDED(hr))
516 m : return false;
517 :
518 m : enum SymTagEnum sym_tag = SymTagNull;
519 m : if (!pe::GetSymTag(field_sym.get(), &sym_tag))
520 m : return false;
521 :
522 : // We only care about data and functions.
523 m : if (sym_tag == SymTagData) {
524 : // TODO(siggi): Also process VTables?
525 m : DataKind data_kind = DataIsUnknown;
526 m : if (!pe::GetDataKind(field_sym.get(), &data_kind))
527 m : return false;
528 : // We only care about member data.
529 m : if (data_kind != DataIsMember)
530 m : continue;
531 :
532 : // The location udt and the symbol udt are a little conflated in the case
533 : // of bitfields. For bitfieds, the bit length and bit offset of the udt
534 : // are stored against the data symbol, and not its udt.
535 m : LocationType loc_type = LocIsNull;
536 m : if (!pe::GetLocationType(field_sym.get(), &loc_type))
537 m : return false;
538 m : DCHECK(loc_type == LocIsThisRel || loc_type == LocIsBitField);
539 :
540 m : base::win::ScopedComPtr<IDiaSymbol> field_type_sym;
541 m : base::string16 field_name;
542 m : ptrdiff_t field_offset = 0;
543 m : size_t field_size = 0;
544 m : Type::Flags field_flags = 0;
545 m : if (!pe::GetSymType(field_sym.get(), &field_type_sym) ||
546 m : !pe::GetSymName(field_sym.get(), &field_name) ||
547 m : !pe::GetSymOffset(field_sym.get(), &field_offset) ||
548 m : !GetSymSize(field_type_sym.get(), &field_size) ||
549 m : !GetSymFlags(field_type_sym.get(), &field_flags)) {
550 m : return false;
551 m : }
552 :
553 m : TypePtr field_type;
554 m : field_type = FindOrCreateType(field_type_sym.get());
555 m : size_t bit_length = 0;
556 m : size_t bit_pos = 0;
557 m : if (loc_type == LocIsBitField) {
558 : // For bitfields we need the bit size and length.
559 m : if (!GetSymSize(field_sym.get(), &bit_length) ||
560 m : !GetSymBitPos(field_sym.get(), &bit_pos)) {
561 m : return false;
562 m : }
563 m : } else if (loc_type != LocIsThisRel) {
564 m : NOTREACHED() << "Impossible location udt!";
565 m : }
566 :
567 m : fields.push_back(new UserDefinedType::MemberField(
568 m : field_name, field_offset, field_flags, bit_pos, bit_length,
569 m : field_type->type_id(), repository_));
570 m : } else if (sym_tag == SymTagFunction) {
571 m : base::win::ScopedComPtr<IDiaSymbol> function_type_sym;
572 m : base::string16 function_name;
573 :
574 m : if (!pe::GetSymType(field_sym.get(), &function_type_sym) ||
575 m : !pe::GetSymName(field_sym.get(), &function_name)) {
576 m : return false;
577 m : }
578 :
579 m : TypePtr function_type;
580 m : function_type = FindOrCreateType(function_type_sym.get());
581 m : if (!function_type)
582 m : return false;
583 :
584 m : functions.push_back(
585 m : UserDefinedType::Function(function_name, function_type->type_id()));
586 m : }
587 m : }
588 :
589 m : DCHECK_EQ(0UL, udt->fields().size());
590 m : DCHECK_EQ(0UL, udt->functions().size());
591 m : udt->Finalize(&fields, &functions);
592 m : return true;
593 m : }
594 :
595 m : bool TypeCreator::FinalizePointer(IDiaSymbol* symbol, PointerTypePtr ptr) {
596 m : DCHECK(symbol);
597 m : DCHECK(ptr);
598 m : DCHECK(pe::IsSymTag(symbol, SymTagPointerType));
599 :
600 m : base::win::ScopedComPtr<IDiaSymbol> contained_type_sym;
601 m : if (!pe::GetSymType(symbol, &contained_type_sym))
602 m : return false;
603 :
604 m : Type::Flags flags = 0;
605 m : if (!GetSymFlags(contained_type_sym.get(), &flags))
606 m : return false;
607 :
608 m : TypePtr contained_type = FindOrCreateType(contained_type_sym.get());
609 m : if (!contained_type)
610 m : return false;
611 :
612 m : ptr->Finalize(flags, contained_type->type_id());
613 m : return true;
614 m : }
615 :
616 m : bool TypeCreator::FinalizeArray(IDiaSymbol* symbol, ArrayTypePtr array) {
617 m : DCHECK(symbol);
618 m : DCHECK(array);
619 m : DCHECK(pe::IsSymTag(symbol, SymTagArrayType));
620 :
621 m : base::win::ScopedComPtr<IDiaSymbol> index_type_sym;
622 m : if (!GetSymArrayIndexType(symbol, &index_type_sym))
623 m : return false;
624 :
625 m : size_t element_count = 0;
626 m : if (!pe::GetSymCount(symbol, &element_count))
627 m : return false;
628 :
629 m : base::win::ScopedComPtr<IDiaSymbol> element_type_sym;
630 m : if (!pe::GetSymType(symbol, &element_type_sym))
631 m : return false;
632 :
633 m : Type::Flags flags = 0;
634 m : if (!GetSymFlags(element_type_sym.get(), &flags))
635 m : return false;
636 :
637 m : TypePtr index_type = FindOrCreateType(index_type_sym.get());
638 m : if (!index_type)
639 m : return false;
640 m : TypePtr element_type = FindOrCreateType(element_type_sym.get());
641 m : if (!element_type)
642 m : return false;
643 :
644 m : array->Finalize(flags, index_type->type_id(), element_count,
645 m : element_type->type_id());
646 m : return true;
647 m : }
648 :
649 m : bool TypeCreator::FinalizeFunction(IDiaSymbol* symbol,
650 m : FunctionTypePtr function) {
651 m : DCHECK(symbol);
652 m : DCHECK(function);
653 m : DCHECK(pe::IsSymTag(symbol, SymTagFunctionType));
654 :
655 : // Determine the return type.
656 m : base::win::ScopedComPtr<IDiaSymbol> return_type_sym;
657 m : if (!pe::GetSymType(symbol, &return_type_sym))
658 m : return false;
659 :
660 m : Type::Flags return_flags = 0;
661 m : if (!GetSymFlags(return_type_sym.get(), &return_flags))
662 m : return false;
663 :
664 m : TypePtr return_type = FindOrCreateType(return_type_sym.get());
665 m : if (!return_type)
666 m : return false;
667 :
668 : // Determine the containing class, if any.
669 m : TypeId containing_class_id = kNoTypeId;
670 m : base::win::ScopedComPtr<IDiaSymbol> parent_type_sym;
671 m : if (!pe::GetSymClassParent(symbol, &parent_type_sym))
672 m : return false;
673 m : if (parent_type_sym.get() != nullptr) {
674 m : TypePtr parent_type = FindOrCreateType(parent_type_sym.get());
675 m : if (!parent_type)
676 m : return false;
677 m : containing_class_id = parent_type->type_id();
678 m : }
679 :
680 : // Process arguments.
681 m : base::win::ScopedComPtr<IDiaEnumSymbols> argument_types;
682 m : HRESULT hr = symbol->findChildren(SymTagFunctionArgType, nullptr, nsNone,
683 m : argument_types.Receive());
684 m : if (!SUCCEEDED(hr))
685 m : return false;
686 :
687 m : base::win::ScopedComPtr<IDiaSymbol> arg_sym;
688 m : ULONG received = 0;
689 m : hr = argument_types->Next(1, arg_sym.Receive(), &received);
690 :
691 m : FunctionType::Arguments args;
692 m : while (hr == S_OK) {
693 m : base::win::ScopedComPtr<IDiaSymbol> arg_type_sym;
694 m : if (!pe::GetSymType(arg_sym.get(), &arg_type_sym))
695 m : return false;
696 :
697 m : TypePtr arg_type = FindOrCreateType(arg_type_sym.get());
698 m : if (!arg_type)
699 m : return false;
700 :
701 m : Type::Flags arg_flags = 0;
702 m : if (!GetSymFlags(arg_type_sym.get(), &arg_flags))
703 m : return false;
704 :
705 m : args.push_back(FunctionType::ArgumentType(arg_flags, arg_type->type_id()));
706 :
707 m : arg_sym.Release();
708 m : received = 0;
709 m : hr = argument_types->Next(1, arg_sym.Receive(), &received);
710 m : }
711 :
712 m : if (!SUCCEEDED(hr))
713 m : return false;
714 :
715 m : function->Finalize(
716 m : FunctionType::ArgumentType(return_flags, return_type->type_id()), args,
717 m : containing_class_id);
718 m : return true;
719 m : }
720 :
721 m : TypePtr TypeCreator::CreateBaseType(IDiaSymbol* symbol) {
722 : // Note that the void base type has zero size.
723 m : DCHECK(symbol);
724 m : DCHECK(pe::IsSymTag(symbol, SymTagBaseType));
725 :
726 m : base::string16 base_type_name;
727 m : size_t size = 0;
728 m : if (!GetSymBaseTypeName(symbol, &base_type_name) ||
729 m : !GetSymSize(symbol, &size)) {
730 m : return nullptr;
731 m : }
732 :
733 m : return new BasicType(base_type_name, size);
734 m : }
735 :
736 m : TypePtr TypeCreator::CreateFunctionType(IDiaSymbol* symbol) {
737 m : DCHECK(symbol);
738 m : DCHECK(pe::IsSymTag(symbol, SymTagFunctionType));
739 :
740 m : FunctionType::CallConvention call_convention;
741 :
742 m : if (!GetSymCallingConvention(symbol, &call_convention))
743 m : return nullptr;
744 :
745 m : return new FunctionType(call_convention);
746 m : }
747 :
748 m : TypePtr TypeCreator::CreatePointerType(IDiaSymbol* symbol) {
749 : // Note that the void base type has zero size.
750 m : DCHECK(symbol);
751 m : DCHECK(pe::IsSymTag(symbol, SymTagPointerType));
752 :
753 m : size_t size = 0;
754 m : PointerType::Mode ptr_mode = PointerType::PTR_MODE_PTR;
755 m : if (!GetSymSize(symbol, &size) || !GetSymPtrMode(symbol, &ptr_mode))
756 m : return nullptr;
757 :
758 m : return new PointerType(size, ptr_mode);
759 m : }
760 :
761 m : TypePtr TypeCreator::CreateTypedefType(IDiaSymbol* symbol) {
762 m : DCHECK(symbol);
763 m : DCHECK(pe::IsSymTag(symbol, SymTagTypedef));
764 :
765 m : base::string16 name;
766 m : if (!pe::GetSymName(symbol, &name))
767 m : return nullptr;
768 :
769 : // TODO(siggi): Implement a typedef type.
770 m : return new WildcardType(name, 0);
771 m : }
772 :
773 m : TypePtr TypeCreator::CreateArrayType(IDiaSymbol* symbol) {
774 m : DCHECK(symbol);
775 m : DCHECK(pe::IsSymTag(symbol, SymTagArrayType));
776 :
777 m : size_t size = 0;
778 m : if (!GetSymSize(symbol, &size)) {
779 m : return nullptr;
780 m : }
781 :
782 m : return new ArrayType(size);
783 m : }
784 :
785 m : TypePtr TypeCreator::CreateGlobalType(IDiaSymbol* symbol,
786 m : const base::string16& name,
787 m : uint64_t rva) {
788 m : DCHECK(symbol);
789 m : DCHECK(pe::IsSymTag(symbol, SymTagData));
790 :
791 m : base::win::ScopedComPtr<IDiaSymbol> global_type;
792 m : if (!pe::GetSymType(symbol, &global_type))
793 m : return nullptr;
794 :
795 m : TypePtr type = FindOrCreateType(global_type.get());
796 m : if (!type)
797 m : return false;
798 :
799 m : return new GlobalType(name, rva, type->type_id(), type->size());
800 m : }
801 :
802 m : } // namespace
803 :
804 m : DiaCrawler::DiaCrawler() {
805 m : }
806 :
807 m : DiaCrawler::~DiaCrawler() {
808 m : }
809 :
810 m : bool DiaCrawler::InitializeForFile(const base::FilePath& path) {
811 m : base::win::ScopedComPtr<IDiaDataSource> source;
812 m : if (!pe::CreateDiaSource(source.Receive()))
813 m : return false;
814 :
815 m : base::win::ScopedComPtr<IDiaSession> session;
816 m : if (!pe::CreateDiaSession(path, source.get(), session.Receive()))
817 m : return false;
818 :
819 m : return InitializeForSession(source, session);
820 m : }
821 :
822 m : bool DiaCrawler::InitializeForSession(
823 m : base::win::ScopedComPtr<IDiaDataSource> source,
824 m : base::win::ScopedComPtr<IDiaSession> session) {
825 m : DCHECK(source.get()); DCHECK(session.get());
826 :
827 m : HRESULT hr = session->get_globalScope(global_.Receive());
828 m : if (!SUCCEEDED(hr) || !global_)
829 m : return false;
830 :
831 m : source_ = source;
832 m : session_ = session;
833 :
834 m : return true;
835 m : }
836 :
837 m : bool DiaCrawler::GetTypes(TypeRepository* types) {
838 m : DCHECK(types); DCHECK(global_);
839 :
840 : // For each type in the PDB:
841 : // Create a unique name for the type.
842 : // Find or create the type by its unique name.
843 : // Finalize the type, e.g.
844 : // For each relevant "child" of the type.
845 : // Create a unique name for the child.
846 : // Find or create the child by its unique name.
847 m : TypeCreator creator(types);
848 :
849 m : return creator.CreateTypes(global_.get());
850 m : }
851 :
852 m : bool DiaCrawler::GetVFTableRVAs(base::hash_set<RelativeAddress>* vftable_rvas) {
853 m : DCHECK(vftable_rvas); DCHECK(global_);
854 m : vftable_rvas->clear();
855 :
856 : // VFTables are represented as public symbols. Note: we search through all
857 : // public symbols as we match on the undecorated name, not on the name.
858 m : base::win::ScopedComPtr<IDiaEnumSymbols> public_symbols;
859 m : HRESULT hr = global_->findChildren(SymTagPublicSymbol, nullptr, nsNone,
860 m : public_symbols.Receive());
861 m : if (!SUCCEEDED(hr))
862 m : return false;
863 :
864 : // Note: the function get_Count from DIA has either a bug or is really badly
865 : // implemented thus taking forever to finish. Therefore we simply load next
866 : // symbol until reaching the end. Unfortunately, this also means we don't use
867 : // it for reserving the container's size.
868 m : base::win::ScopedComPtr<IDiaSymbol> symbol;
869 m : ULONG received = 0;
870 m : hr = public_symbols->Next(1, symbol.Receive(), &received);
871 :
872 m : while (hr == S_OK) {
873 m : base::string16 undecorated_name;
874 m : if (!pe::GetSymUndecoratedName(symbol.get(), &undecorated_name))
875 m : return false; // Public symbols are expected to have names.
876 :
877 : // Vftable names should look like:
878 : // const std::Foo::`vftable'
879 : // const testing::Foo::`vftable'{for `testing::Foo'}
880 m : if (undecorated_name.find(L"::`vftable'") != base::string16::npos) {
881 m : LocationType location_type = LocIsNull;
882 m : if (!pe::GetLocationType(symbol.get(), &location_type))
883 m : return false;
884 m : if (location_type != LocIsStatic) {
885 m : LOG(ERROR) << "Unexpected vftable location type: " << location_type;
886 m : return false;
887 m : }
888 :
889 m : DWORD rva = 0U;
890 m : HRESULT hr = symbol->get_relativeVirtualAddress(&rva);
891 m : if (hr != S_OK) {
892 m : LOG(ERROR) << "Unable to get vftable's RVA: " << common::LogHr(hr)
893 m : << ".";
894 m : return false;
895 m : }
896 :
897 m : vftable_rvas->insert(static_cast<RelativeAddress>(rva));
898 m : }
899 :
900 m : symbol.Release();
901 m : received = 0;
902 m : hr = public_symbols->Next(1, symbol.Receive(), &received);
903 m : }
904 :
905 m : if (!SUCCEEDED(hr))
906 m : return false;
907 :
908 m : return true;
909 m : }
910 :
911 m : } // namespace refinery
|