| // Copyright 2014 The Crashpad Authors. All rights reserved. |
| // |
| // 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 "minidump/minidump_module_writer.h" |
| |
| #include <stddef.h> |
| |
| #include <limits> |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "minidump/minidump_string_writer.h" |
| #include "minidump/minidump_writer_util.h" |
| #include "snapshot/module_snapshot.h" |
| #include "util/file/file_writer.h" |
| #include "util/misc/implicit_cast.h" |
| #include "util/numeric/in_range_cast.h" |
| #include "util/numeric/safe_assignment.h" |
| |
| namespace crashpad { |
| |
| MinidumpModuleCodeViewRecordWriter::~MinidumpModuleCodeViewRecordWriter() {} |
| |
| namespace internal { |
| |
| template <typename CodeViewRecordType> |
| MinidumpModuleCodeViewRecordPDBLinkWriter< |
| CodeViewRecordType>::MinidumpModuleCodeViewRecordPDBLinkWriter() |
| : MinidumpModuleCodeViewRecordWriter(), codeview_record_(), pdb_name_() { |
| codeview_record_.signature = CodeViewRecordType::kSignature; |
| } |
| |
| template <typename CodeViewRecordType> |
| MinidumpModuleCodeViewRecordPDBLinkWriter< |
| CodeViewRecordType>::~MinidumpModuleCodeViewRecordPDBLinkWriter() {} |
| |
| template <typename CodeViewRecordType> |
| size_t |
| MinidumpModuleCodeViewRecordPDBLinkWriter<CodeViewRecordType>::SizeOfObject() { |
| DCHECK_GE(state(), kStateFrozen); |
| |
| // NUL-terminate. |
| return offsetof(decltype(codeview_record_), pdb_name) + |
| (pdb_name_.size() + 1) * sizeof(pdb_name_[0]); |
| } |
| |
| template <typename CodeViewRecordType> |
| bool MinidumpModuleCodeViewRecordPDBLinkWriter<CodeViewRecordType>::WriteObject( |
| FileWriterInterface* file_writer) { |
| DCHECK_EQ(state(), kStateWritable); |
| |
| WritableIoVec iov; |
| iov.iov_base = &codeview_record_; |
| iov.iov_len = offsetof(decltype(codeview_record_), pdb_name); |
| std::vector<WritableIoVec> iovecs(1, iov); |
| |
| // NUL-terminate. |
| iov.iov_base = &pdb_name_[0]; |
| iov.iov_len = (pdb_name_.size() + 1) * sizeof(pdb_name_[0]); |
| iovecs.push_back(iov); |
| |
| return file_writer->WriteIoVec(&iovecs); |
| } |
| |
| } // namespace internal |
| |
| template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter< |
| CodeViewRecordPDB20>; |
| |
| MinidumpModuleCodeViewRecordPDB20Writer:: |
| ~MinidumpModuleCodeViewRecordPDB20Writer() {} |
| |
| void MinidumpModuleCodeViewRecordPDB20Writer::SetTimestampAndAge( |
| time_t timestamp, |
| uint32_t age) { |
| DCHECK_EQ(state(), kStateMutable); |
| |
| internal::MinidumpWriterUtil::AssignTimeT(&codeview_record()->timestamp, |
| timestamp); |
| |
| codeview_record()->age = age; |
| } |
| |
| template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter< |
| CodeViewRecordPDB70>; |
| |
| MinidumpModuleCodeViewRecordPDB70Writer:: |
| ~MinidumpModuleCodeViewRecordPDB70Writer() {} |
| |
| void MinidumpModuleCodeViewRecordPDB70Writer::InitializeFromSnapshot( |
| const ModuleSnapshot* module_snapshot) { |
| DCHECK_EQ(state(), kStateMutable); |
| |
| SetPDBName(module_snapshot->DebugFileName()); |
| |
| UUID uuid; |
| uint32_t age; |
| module_snapshot->UUIDAndAge(&uuid, &age); |
| SetUUIDAndAge(uuid, age); |
| } |
| |
| MinidumpModuleCodeViewRecordBuildIDWriter:: |
| MinidumpModuleCodeViewRecordBuildIDWriter() |
| : MinidumpModuleCodeViewRecordWriter(), build_id_() {} |
| |
| MinidumpModuleCodeViewRecordBuildIDWriter:: |
| ~MinidumpModuleCodeViewRecordBuildIDWriter() {} |
| |
| size_t MinidumpModuleCodeViewRecordBuildIDWriter::SizeOfObject() { |
| DCHECK_GE(state(), kStateFrozen); |
| return offsetof(CodeViewRecordBuildID, build_id) + build_id_.size(); |
| } |
| |
| void MinidumpModuleCodeViewRecordBuildIDWriter::SetBuildID( |
| const std::vector<uint8_t>& build_id) { |
| DCHECK_EQ(state(), kStateMutable); |
| build_id_ = build_id; |
| } |
| |
| bool MinidumpModuleCodeViewRecordBuildIDWriter::WriteObject( |
| FileWriterInterface* file_writer) { |
| DCHECK_EQ(state(), kStateWritable); |
| |
| CodeViewRecordBuildID cv; |
| cv.signature = CodeViewRecordBuildID::kSignature; |
| |
| WritableIoVec iov; |
| iov.iov_base = &cv; |
| iov.iov_len = offsetof(CodeViewRecordBuildID, build_id); |
| std::vector<WritableIoVec> iovecs(1, iov); |
| |
| if (!build_id_.empty()) { |
| iov.iov_base = build_id_.data(); |
| iov.iov_len = build_id_.size(); |
| iovecs.push_back(iov); |
| } |
| |
| return file_writer->WriteIoVec(&iovecs); |
| } |
| |
| MinidumpModuleMiscDebugRecordWriter::MinidumpModuleMiscDebugRecordWriter() |
| : internal::MinidumpWritable(), |
| image_debug_misc_(), |
| data_(), |
| data_utf16_() {} |
| |
| MinidumpModuleMiscDebugRecordWriter::~MinidumpModuleMiscDebugRecordWriter() {} |
| |
| void MinidumpModuleMiscDebugRecordWriter::SetData(const std::string& data, |
| bool utf16) { |
| DCHECK_EQ(state(), kStateMutable); |
| |
| if (!utf16) { |
| data_utf16_.clear(); |
| image_debug_misc_.Unicode = 0; |
| data_ = data; |
| } else { |
| data_.clear(); |
| image_debug_misc_.Unicode = 1; |
| data_utf16_ = internal::MinidumpWriterUtil::ConvertUTF8ToUTF16(data); |
| } |
| } |
| |
| bool MinidumpModuleMiscDebugRecordWriter::Freeze() { |
| DCHECK_EQ(state(), kStateMutable); |
| |
| if (!MinidumpWritable::Freeze()) { |
| return false; |
| } |
| |
| // NUL-terminate. |
| if (!image_debug_misc_.Unicode) { |
| DCHECK(data_utf16_.empty()); |
| image_debug_misc_.Length = base::checked_cast<uint32_t>( |
| offsetof(decltype(image_debug_misc_), Data) + |
| (data_.size() + 1) * sizeof(data_[0])); |
| } else { |
| DCHECK(data_.empty()); |
| image_debug_misc_.Length = base::checked_cast<uint32_t>( |
| offsetof(decltype(image_debug_misc_), Data) + |
| (data_utf16_.size() + 1) * sizeof(data_utf16_[0])); |
| } |
| |
| return true; |
| } |
| |
| size_t MinidumpModuleMiscDebugRecordWriter::SizeOfObject() { |
| DCHECK_GE(state(), kStateFrozen); |
| |
| return image_debug_misc_.Length; |
| } |
| |
| bool MinidumpModuleMiscDebugRecordWriter::WriteObject( |
| FileWriterInterface* file_writer) { |
| DCHECK_EQ(state(), kStateWritable); |
| |
| const size_t base_length = offsetof(decltype(image_debug_misc_), Data); |
| |
| WritableIoVec iov; |
| iov.iov_base = &image_debug_misc_; |
| iov.iov_len = base_length; |
| std::vector<WritableIoVec> iovecs(1, iov); |
| |
| if (!image_debug_misc_.Unicode) { |
| DCHECK(data_utf16_.empty()); |
| iov.iov_base = &data_[0]; |
| } else { |
| DCHECK(data_.empty()); |
| iov.iov_base = &data_utf16_[0]; |
| } |
| iov.iov_len = image_debug_misc_.Length - base_length; |
| iovecs.push_back(iov); |
| |
| return file_writer->WriteIoVec(&iovecs); |
| } |
| |
| MinidumpModuleWriter::MinidumpModuleWriter() |
| : MinidumpWritable(), |
| module_(), |
| name_(), |
| codeview_record_(nullptr), |
| misc_debug_record_(nullptr) { |
| module_.VersionInfo.dwSignature = VS_FFI_SIGNATURE; |
| module_.VersionInfo.dwStrucVersion = VS_FFI_STRUCVERSION; |
| } |
| |
| MinidumpModuleWriter::~MinidumpModuleWriter() {} |
| |
| void MinidumpModuleWriter::InitializeFromSnapshot( |
| const ModuleSnapshot* module_snapshot) { |
| DCHECK_EQ(state(), kStateMutable); |
| DCHECK(!name_); |
| DCHECK(!codeview_record_); |
| DCHECK(!misc_debug_record_); |
| |
| SetName(module_snapshot->Name()); |
| |
| SetImageBaseAddress(module_snapshot->Address()); |
| SetImageSize(InRangeCast<uint32_t>(module_snapshot->Size(), |
| std::numeric_limits<uint32_t>::max())); |
| SetTimestamp(module_snapshot->Timestamp()); |
| |
| uint16_t v[4]; |
| module_snapshot->FileVersion(&v[0], &v[1], &v[2], &v[3]); |
| SetFileVersion(v[0], v[1], v[2], v[3]); |
| |
| module_snapshot->SourceVersion(&v[0], &v[1], &v[2], &v[3]); |
| SetProductVersion(v[0], v[1], v[2], v[3]); |
| |
| uint32_t file_type; |
| switch (module_snapshot->GetModuleType()) { |
| case ModuleSnapshot::kModuleTypeExecutable: |
| file_type = VFT_APP; |
| break; |
| case ModuleSnapshot::kModuleTypeSharedLibrary: |
| case ModuleSnapshot::kModuleTypeLoadableModule: |
| file_type = VFT_DLL; |
| break; |
| default: |
| file_type = VFT_UNKNOWN; |
| break; |
| } |
| SetFileTypeAndSubtype(file_type, VFT2_UNKNOWN); |
| |
| auto build_id = module_snapshot->BuildID(); |
| |
| std::unique_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record; |
| if (!build_id.empty()) { |
| auto cv_record_build_id = |
| std::make_unique<MinidumpModuleCodeViewRecordBuildIDWriter>(); |
| cv_record_build_id->SetBuildID(build_id); |
| codeview_record = std::move(cv_record_build_id); |
| } else { |
| auto cv_record_pdb70 = |
| std::make_unique<MinidumpModuleCodeViewRecordPDB70Writer>(); |
| cv_record_pdb70->InitializeFromSnapshot(module_snapshot); |
| codeview_record = std::move(cv_record_pdb70); |
| } |
| |
| SetCodeViewRecord(std::move(codeview_record)); |
| } |
| |
| const MINIDUMP_MODULE* MinidumpModuleWriter::MinidumpModule() const { |
| DCHECK_EQ(state(), kStateWritable); |
| |
| return &module_; |
| } |
| |
| void MinidumpModuleWriter::SetName(const std::string& name) { |
| DCHECK_EQ(state(), kStateMutable); |
| |
| if (!name_) { |
| name_.reset(new internal::MinidumpUTF16StringWriter()); |
| } |
| name_->SetUTF8(name); |
| } |
| |
| void MinidumpModuleWriter::SetCodeViewRecord( |
| std::unique_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record) { |
| DCHECK_EQ(state(), kStateMutable); |
| |
| codeview_record_ = std::move(codeview_record); |
| } |
| |
| void MinidumpModuleWriter::SetMiscDebugRecord( |
| std::unique_ptr<MinidumpModuleMiscDebugRecordWriter> misc_debug_record) { |
| DCHECK_EQ(state(), kStateMutable); |
| |
| misc_debug_record_ = std::move(misc_debug_record); |
| } |
| |
| void MinidumpModuleWriter::SetTimestamp(time_t timestamp) { |
| DCHECK_EQ(state(), kStateMutable); |
| |
| internal::MinidumpWriterUtil::AssignTimeT(&module_.TimeDateStamp, timestamp); |
| } |
| |
| void MinidumpModuleWriter::SetFileVersion(uint16_t version_0, |
| uint16_t version_1, |
| uint16_t version_2, |
| uint16_t version_3) { |
| DCHECK_EQ(state(), kStateMutable); |
| |
| module_.VersionInfo.dwFileVersionMS = |
| (implicit_cast<uint32_t>(version_0) << 16) | version_1; |
| module_.VersionInfo.dwFileVersionLS = |
| (implicit_cast<uint32_t>(version_2) << 16) | version_3; |
| } |
| |
| void MinidumpModuleWriter::SetProductVersion(uint16_t version_0, |
| uint16_t version_1, |
| uint16_t version_2, |
| uint16_t version_3) { |
| DCHECK_EQ(state(), kStateMutable); |
| |
| module_.VersionInfo.dwProductVersionMS = |
| (implicit_cast<uint32_t>(version_0) << 16) | version_1; |
| module_.VersionInfo.dwProductVersionLS = |
| (implicit_cast<uint32_t>(version_2) << 16) | version_3; |
| } |
| |
| void MinidumpModuleWriter::SetFileFlagsAndMask(uint32_t file_flags, |
| uint32_t file_flags_mask) { |
| DCHECK_EQ(state(), kStateMutable); |
| DCHECK_EQ(file_flags & ~file_flags_mask, 0u); |
| |
| module_.VersionInfo.dwFileFlags = file_flags; |
| module_.VersionInfo.dwFileFlagsMask = file_flags_mask; |
| } |
| |
| bool MinidumpModuleWriter::Freeze() { |
| DCHECK_EQ(state(), kStateMutable); |
| CHECK(name_); |
| |
| if (!MinidumpWritable::Freeze()) { |
| return false; |
| } |
| |
| name_->RegisterRVA(&module_.ModuleNameRva); |
| |
| if (codeview_record_) { |
| codeview_record_->RegisterLocationDescriptor(&module_.CvRecord); |
| } |
| |
| if (misc_debug_record_) { |
| misc_debug_record_->RegisterLocationDescriptor(&module_.MiscRecord); |
| } |
| |
| return true; |
| } |
| |
| size_t MinidumpModuleWriter::SizeOfObject() { |
| DCHECK_GE(state(), kStateFrozen); |
| |
| // This object doesn’t directly write anything itself. Its MINIDUMP_MODULE is |
| // written by its parent as part of a MINIDUMP_MODULE_LIST, and its children |
| // are responsible for writing themselves. |
| return 0; |
| } |
| |
| std::vector<internal::MinidumpWritable*> MinidumpModuleWriter::Children() { |
| DCHECK_GE(state(), kStateFrozen); |
| DCHECK(name_); |
| |
| std::vector<MinidumpWritable*> children; |
| children.push_back(name_.get()); |
| if (codeview_record_) { |
| children.push_back(codeview_record_.get()); |
| } |
| if (misc_debug_record_) { |
| children.push_back(misc_debug_record_.get()); |
| } |
| |
| return children; |
| } |
| |
| bool MinidumpModuleWriter::WriteObject(FileWriterInterface* file_writer) { |
| DCHECK_EQ(state(), kStateWritable); |
| |
| // This object doesn’t directly write anything itself. Its MINIDUMP_MODULE is |
| // written by its parent as part of a MINIDUMP_MODULE_LIST, and its children |
| // are responsible for writing themselves. |
| return true; |
| } |
| |
| MinidumpModuleListWriter::MinidumpModuleListWriter() |
| : MinidumpStreamWriter(), modules_(), module_list_base_() {} |
| |
| MinidumpModuleListWriter::~MinidumpModuleListWriter() {} |
| |
| void MinidumpModuleListWriter::InitializeFromSnapshot( |
| const std::vector<const ModuleSnapshot*>& module_snapshots) { |
| DCHECK_EQ(state(), kStateMutable); |
| DCHECK(modules_.empty()); |
| |
| for (const ModuleSnapshot* module_snapshot : module_snapshots) { |
| auto module = std::make_unique<MinidumpModuleWriter>(); |
| module->InitializeFromSnapshot(module_snapshot); |
| AddModule(std::move(module)); |
| } |
| } |
| |
| void MinidumpModuleListWriter::AddModule( |
| std::unique_ptr<MinidumpModuleWriter> module) { |
| DCHECK_EQ(state(), kStateMutable); |
| |
| modules_.push_back(std::move(module)); |
| } |
| |
| bool MinidumpModuleListWriter::Freeze() { |
| DCHECK_EQ(state(), kStateMutable); |
| |
| if (!MinidumpStreamWriter::Freeze()) { |
| return false; |
| } |
| |
| size_t module_count = modules_.size(); |
| if (!AssignIfInRange(&module_list_base_.NumberOfModules, module_count)) { |
| LOG(ERROR) << "module_count " << module_count << " out of range"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| size_t MinidumpModuleListWriter::SizeOfObject() { |
| DCHECK_GE(state(), kStateFrozen); |
| |
| return sizeof(module_list_base_) + modules_.size() * sizeof(MINIDUMP_MODULE); |
| } |
| |
| std::vector<internal::MinidumpWritable*> MinidumpModuleListWriter::Children() { |
| DCHECK_GE(state(), kStateFrozen); |
| |
| std::vector<MinidumpWritable*> children; |
| for (const auto& module : modules_) { |
| children.push_back(module.get()); |
| } |
| |
| return children; |
| } |
| |
| bool MinidumpModuleListWriter::WriteObject(FileWriterInterface* file_writer) { |
| DCHECK_EQ(state(), kStateWritable); |
| |
| WritableIoVec iov; |
| iov.iov_base = &module_list_base_; |
| iov.iov_len = sizeof(module_list_base_); |
| std::vector<WritableIoVec> iovecs(1, iov); |
| |
| for (const auto& module : modules_) { |
| iov.iov_base = module->MinidumpModule(); |
| iov.iov_len = sizeof(MINIDUMP_MODULE); |
| iovecs.push_back(iov); |
| } |
| |
| return file_writer->WriteIoVec(&iovecs); |
| } |
| |
| MinidumpStreamType MinidumpModuleListWriter::StreamType() const { |
| return kMinidumpStreamTypeModuleList; |
| } |
| |
| } // namespace crashpad |