| // Copyright 2015 The Crashpad Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "snapshot/win/module_snapshot_win.h" |
| |
| #include <utility> |
| |
| #include "base/strings/utf_string_conversions.h" |
| #include "client/crashpad_info.h" |
| #include "client/simple_address_range_bag.h" |
| #include "snapshot/memory_snapshot_generic.h" |
| #include "snapshot/win/pe_image_annotations_reader.h" |
| #include "snapshot/win/pe_image_reader.h" |
| #include "util/misc/tri_state.h" |
| #include "util/misc/uuid.h" |
| |
| namespace crashpad { |
| namespace internal { |
| |
| ModuleSnapshotWin::ModuleSnapshotWin() |
| : ModuleSnapshot(), |
| name_(), |
| pdb_name_(), |
| uuid_(), |
| memory_range_(), |
| streams_(), |
| vs_fixed_file_info_(), |
| initialized_vs_fixed_file_info_(), |
| process_reader_(nullptr), |
| pe_image_reader_(), |
| crashpad_info_(), |
| timestamp_(0), |
| age_(0), |
| initialized_() {} |
| |
| ModuleSnapshotWin::~ModuleSnapshotWin() {} |
| |
| bool ModuleSnapshotWin::Initialize( |
| ProcessReaderWin* process_reader, |
| const ProcessInfo::Module& process_reader_module) { |
| INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
| |
| process_reader_ = process_reader; |
| name_ = process_reader_module.name; |
| timestamp_ = process_reader_module.timestamp; |
| pe_image_reader_.reset(new PEImageReader()); |
| if (!pe_image_reader_->Initialize(process_reader_, |
| process_reader_module.dll_base, |
| process_reader_module.size, |
| base::WideToUTF8(name_))) { |
| return false; |
| } |
| |
| DWORD age_dword; |
| if (pe_image_reader_->DebugDirectoryInformation( |
| &uuid_, &age_dword, &pdb_name_)) { |
| static_assert(sizeof(DWORD) == sizeof(uint32_t), "unexpected age size"); |
| age_ = age_dword; |
| } else { |
| // If we fully supported all old debugging formats, we would want to extract |
| // and emit a different type of CodeView record here (as old Microsoft tools |
| // would do). As we don't expect to ever encounter a module that wouldn't be |
| // using .PDB that we actually have symbols for, we simply set a plausible |
| // name here, but this will never correspond to symbols that we have. |
| pdb_name_ = base::WideToUTF8(name_); |
| } |
| |
| if (!memory_range_.Initialize(process_reader_->Memory(), |
| process_reader_->Is64Bit())) { |
| return false; |
| } |
| |
| WinVMAddress crashpad_info_address; |
| WinVMSize crashpad_info_size; |
| if (pe_image_reader_->GetCrashpadInfoSection(&crashpad_info_address, |
| &crashpad_info_size)) { |
| ProcessMemoryRange info_range; |
| info_range.Initialize(memory_range_); |
| info_range.RestrictRange(crashpad_info_address, |
| crashpad_info_address + crashpad_info_size); |
| |
| auto info = std::make_unique<CrashpadInfoReader>(); |
| if (info->Initialize(&info_range, crashpad_info_address)) { |
| crashpad_info_ = std::move(info); |
| } |
| } |
| |
| INITIALIZATION_STATE_SET_VALID(initialized_); |
| return true; |
| } |
| |
| void ModuleSnapshotWin::GetCrashpadOptions(CrashpadInfoClientOptions* options) { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| if (process_reader_->Is64Bit()) |
| GetCrashpadOptionsInternal<process_types::internal::Traits64>(options); |
| else |
| GetCrashpadOptionsInternal<process_types::internal::Traits32>(options); |
| } |
| |
| std::string ModuleSnapshotWin::Name() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| return base::WideToUTF8(name_); |
| } |
| |
| uint64_t ModuleSnapshotWin::Address() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| return pe_image_reader_->Address(); |
| } |
| |
| uint64_t ModuleSnapshotWin::Size() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| return pe_image_reader_->Size(); |
| } |
| |
| time_t ModuleSnapshotWin::Timestamp() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| return timestamp_; |
| } |
| |
| void ModuleSnapshotWin::FileVersion(uint16_t* version_0, |
| uint16_t* version_1, |
| uint16_t* version_2, |
| uint16_t* version_3) const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| const VS_FIXEDFILEINFO* ffi = VSFixedFileInfo(); |
| if (ffi) { |
| *version_0 = ffi->dwFileVersionMS >> 16; |
| *version_1 = ffi->dwFileVersionMS & 0xffff; |
| *version_2 = ffi->dwFileVersionLS >> 16; |
| *version_3 = ffi->dwFileVersionLS & 0xffff; |
| } else { |
| *version_0 = 0; |
| *version_1 = 0; |
| *version_2 = 0; |
| *version_3 = 0; |
| } |
| } |
| |
| void ModuleSnapshotWin::SourceVersion(uint16_t* version_0, |
| uint16_t* version_1, |
| uint16_t* version_2, |
| uint16_t* version_3) const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| const VS_FIXEDFILEINFO* ffi = VSFixedFileInfo(); |
| if (ffi) { |
| *version_0 = ffi->dwProductVersionMS >> 16; |
| *version_1 = ffi->dwProductVersionMS & 0xffff; |
| *version_2 = ffi->dwProductVersionLS >> 16; |
| *version_3 = ffi->dwProductVersionLS & 0xffff; |
| } else { |
| *version_0 = 0; |
| *version_1 = 0; |
| *version_2 = 0; |
| *version_3 = 0; |
| } |
| } |
| |
| ModuleSnapshot::ModuleType ModuleSnapshotWin::GetModuleType() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| const VS_FIXEDFILEINFO* ffi = VSFixedFileInfo(); |
| if (ffi) { |
| switch (ffi->dwFileType) { |
| case VFT_APP: |
| return ModuleSnapshot::kModuleTypeExecutable; |
| case VFT_DLL: |
| return ModuleSnapshot::kModuleTypeSharedLibrary; |
| case VFT_DRV: |
| case VFT_VXD: |
| return ModuleSnapshot::kModuleTypeLoadableModule; |
| } |
| } |
| return ModuleSnapshot::kModuleTypeUnknown; |
| } |
| |
| void ModuleSnapshotWin::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| *uuid = uuid_; |
| *age = age_; |
| } |
| |
| std::string ModuleSnapshotWin::DebugFileName() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| return pdb_name_; |
| } |
| |
| std::vector<uint8_t> ModuleSnapshotWin::BuildID() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| return std::vector<uint8_t>(); |
| } |
| |
| std::vector<std::string> ModuleSnapshotWin::AnnotationsVector() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| // These correspond to system-logged things on Mac. We don't currently track |
| // any of these on Windows, but could in the future. See |
| // https://crashpad.chromium.org/bug/38. |
| return std::vector<std::string>(); |
| } |
| |
| std::map<std::string, std::string> ModuleSnapshotWin::AnnotationsSimpleMap() |
| const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| PEImageAnnotationsReader annotations_reader( |
| process_reader_, pe_image_reader_.get(), name_); |
| return annotations_reader.SimpleMap(); |
| } |
| |
| std::vector<AnnotationSnapshot> ModuleSnapshotWin::AnnotationObjects() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| PEImageAnnotationsReader annotations_reader( |
| process_reader_, pe_image_reader_.get(), name_); |
| return annotations_reader.AnnotationsList(); |
| } |
| |
| std::set<CheckedRange<uint64_t>> ModuleSnapshotWin::ExtraMemoryRanges() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| std::set<CheckedRange<uint64_t>> ranges; |
| if (process_reader_->Is64Bit()) |
| GetCrashpadExtraMemoryRanges<process_types::internal::Traits64>(&ranges); |
| else |
| GetCrashpadExtraMemoryRanges<process_types::internal::Traits32>(&ranges); |
| return ranges; |
| } |
| |
| std::vector<const UserMinidumpStream*> |
| ModuleSnapshotWin::CustomMinidumpStreams() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| streams_.clear(); |
| if (process_reader_->Is64Bit()) { |
| GetCrashpadUserMinidumpStreams<process_types::internal::Traits64>( |
| &streams_); |
| } else { |
| GetCrashpadUserMinidumpStreams<process_types::internal::Traits32>( |
| &streams_); |
| } |
| |
| std::vector<const UserMinidumpStream*> result; |
| for (const auto& stream : streams_) { |
| result.push_back(stream.get()); |
| } |
| return result; |
| } |
| |
| template <class Traits> |
| void ModuleSnapshotWin::GetCrashpadOptionsInternal( |
| CrashpadInfoClientOptions* options) { |
| if (!crashpad_info_) { |
| options->crashpad_handler_behavior = TriState::kUnset; |
| options->system_crash_reporter_forwarding = TriState::kUnset; |
| options->gather_indirectly_referenced_memory = TriState::kUnset; |
| options->indirectly_referenced_memory_cap = 0; |
| return; |
| } |
| |
| options->crashpad_handler_behavior = |
| crashpad_info_->CrashpadHandlerBehavior(); |
| options->system_crash_reporter_forwarding = |
| crashpad_info_->SystemCrashReporterForwarding(); |
| options->gather_indirectly_referenced_memory = |
| crashpad_info_->GatherIndirectlyReferencedMemory(); |
| options->indirectly_referenced_memory_cap = |
| crashpad_info_->IndirectlyReferencedMemoryCap(); |
| } |
| |
| const VS_FIXEDFILEINFO* ModuleSnapshotWin::VSFixedFileInfo() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| |
| if (initialized_vs_fixed_file_info_.is_uninitialized()) { |
| initialized_vs_fixed_file_info_.set_invalid(); |
| if (pe_image_reader_->VSFixedFileInfo(&vs_fixed_file_info_)) { |
| initialized_vs_fixed_file_info_.set_valid(); |
| } |
| } |
| |
| return initialized_vs_fixed_file_info_.is_valid() ? &vs_fixed_file_info_ |
| : nullptr; |
| } |
| |
| template <class Traits> |
| void ModuleSnapshotWin::GetCrashpadExtraMemoryRanges( |
| std::set<CheckedRange<uint64_t>>* ranges) const { |
| if (!crashpad_info_ || !crashpad_info_->ExtraMemoryRanges()) |
| return; |
| |
| std::vector<SimpleAddressRangeBag::Entry> simple_ranges( |
| SimpleAddressRangeBag::num_entries); |
| if (!process_reader_->Memory()->Read( |
| crashpad_info_->ExtraMemoryRanges(), |
| simple_ranges.size() * sizeof(simple_ranges[0]), |
| &simple_ranges[0])) { |
| LOG(WARNING) << "could not read simple address_ranges from " |
| << base::WideToUTF8(name_); |
| return; |
| } |
| |
| for (const auto& entry : simple_ranges) { |
| if (entry.base != 0 || entry.size != 0) { |
| // Deduplication here is fine. |
| ranges->insert(CheckedRange<uint64_t>(entry.base, entry.size)); |
| } |
| } |
| } |
| |
| template <class Traits> |
| void ModuleSnapshotWin::GetCrashpadUserMinidumpStreams( |
| std::vector<std::unique_ptr<const UserMinidumpStream>>* streams) const { |
| if (!crashpad_info_) |
| return; |
| |
| for (uint64_t cur = crashpad_info_->UserDataMinidumpStreamHead(); cur;) { |
| internal::UserDataMinidumpStreamListEntry list_entry; |
| if (!process_reader_->Memory()->Read( |
| cur, sizeof(list_entry), &list_entry)) { |
| LOG(WARNING) << "could not read user data stream entry from " |
| << base::WideToUTF8(name_); |
| return; |
| } |
| |
| if (list_entry.size != 0) { |
| std::unique_ptr<internal::MemorySnapshotGeneric> memory( |
| new internal::MemorySnapshotGeneric()); |
| memory->Initialize( |
| process_reader_->Memory(), list_entry.base_address, list_entry.size); |
| streams->push_back(std::make_unique<UserMinidumpStream>( |
| list_entry.stream_type, memory.release())); |
| } |
| |
| cur = list_entry.next; |
| } |
| } |
| |
| } // namespace internal |
| } // namespace crashpad |