diff --git a/compat/non_win/dbghelp.h b/compat/non_win/dbghelp.h index ecf8bab8..2b7c974c 100644 --- a/compat/non_win/dbghelp.h +++ b/compat/non_win/dbghelp.h @@ -160,6 +160,9 @@ enum MINIDUMP_STREAM_TYPE { //! \brief The stream contains information about active `HANDLE`s. HandleDataStream = 12, + //! \brief The stream type for MINIDUMP_UNLOADED_MODULE_LIST. + UnloadedModuleListStream = 14, + //! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, //! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4. //! @@ -674,6 +677,9 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_HANDLE_DESCRIPTOR_2 }; //! \brief Represents the header for a handle data stream. +//! +//! A list of MINIDUMP_HANDLE_DESCRIPTOR or MINIDUMP_HANDLE_DESCRIPTOR_2 +//! structures will immediately follow in the stream. struct __attribute((packed, aligned(4))) MINIDUMP_HANDLE_DATA_STREAM { //! \brief The size of the header information for the stream, in bytes. This //! value is `sizeof(MINIDUMP_HANDLE_DATA_STREAM)`. @@ -691,6 +697,58 @@ struct __attribute((packed, aligned(4))) MINIDUMP_HANDLE_DATA_STREAM { uint32_t Reserved; }; +//! \brief Information about a specific module that was recorded as being +//! unloaded at the time the snapshot was taken. +//! +//! An unloaded module may be a shared library or a loadable module. +//! +//! \sa MINIDUMP_UNLOADED_MODULE_LIST +struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE { + //! \brief The base address where the module was loaded in the address space + //! of the process that the minidump file contains a snapshot of. + uint64_t BaseOfImage; + + //! \brief The size of the unloaded module. + uint32_t SizeOfImage; + + //! \brief The module’s checksum, or `0` if unknown. + //! + //! On Windows, this field comes from the `CheckSum` field of the module’s + //! `IMAGE_OPTIONAL_HEADER` structure, if present. It reflects the checksum at + //! the time the module was linked. + uint32_t CheckSum; + + //! \brief The module’s timestamp, in `time_t` units, seconds since the POSIX + //! epoch. + //! + //! On Windows, this field comes from the `TimeDateStamp` field of the + //! module’s `IMAGE_HEADER` structure. It reflects the timestamp at the time + //! the module was linked. + uint32_t TimeDateStamp; + + //! \brief ::RVA of a MINIDUMP_STRING containing the module’s path or file + //! name. + RVA ModuleNameRva; +}; + +//! \brief Information about all modules recorded as unloaded when the snapshot +//! was taken. +//! +//! A list of MINIDUMP_UNLOADED_MODULE structures will immediately follow in the +//! stream. +struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE_LIST { + //! \brief The size of the header information for the stream, in bytes. This + //! value is `sizeof(MINIDUMP_UNLOADED_MODULE_LIST)`. + uint32_t SizeOfHeader; + + //! \brief The size of a descriptor in the stream, in bytes. This value is + //! `sizeof(MINIDUMP_UNLOADED_MODULE)`. + uint32_t SizeOfEntry; + + //! \brief The number of entries in the stream. + uint32_t NumberOfEntries; +}; + //! \anchor MINIDUMP_MISCx //! \name MINIDUMP_MISC* //! diff --git a/handler/win/crashy_test_program.cc b/handler/win/crashy_test_program.cc index f9e8501f..51ff0129 100644 --- a/handler/win/crashy_test_program.cc +++ b/handler/win/crashy_test_program.cc @@ -165,6 +165,16 @@ int CrashyMain(int argc, wchar_t* argv[]) { return EXIT_FAILURE; } + // Load and unload some uncommonly used modules so we can see them in the list + // reported by `lm`. At least two so that we confirm we got the size of + // RTL_UNLOAD_EVENT_TRACE right. + CHECK(GetModuleHandle(L"lz32.dll") == nullptr); + CHECK(GetModuleHandle(L"wmerror.dll") == nullptr); + HMODULE lz32 = LoadLibrary(L"lz32.dll"); + HMODULE wmerror = LoadLibrary(L"wmerror.dll"); + FreeLibrary(lz32); + FreeLibrary(wmerror); + // Make sure data pointed to by the stack is captured. const int kDataSize = 512; int* pointed_to_data = new int[kDataSize]; diff --git a/minidump/minidump.gyp b/minidump/minidump.gyp index 0e7fb0ad..3f714668 100644 --- a/minidump/minidump.gyp +++ b/minidump/minidump.gyp @@ -70,6 +70,8 @@ 'minidump_thread_id_map.h', 'minidump_thread_writer.cc', 'minidump_thread_writer.h', + 'minidump_unloaded_module_writer.cc', + 'minidump_unloaded_module_writer.h', 'minidump_writable.cc', 'minidump_writable.h', 'minidump_writer_util.cc', diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index 5e43055a..68fa6e04 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -76,6 +76,11 @@ enum MinidumpStreamType : uint32_t { //! \sa HandleDataStream kMinidumpStreamTypeHandleData = HandleDataStream, + //! \brief The stream type for MINIDUMP_UNLOADED_MODULE_LIST. + //! + //! \sa UnloadedModuleListStream + kMinidumpStreamTypeUnloadedModuleList = UnloadedModuleListStream, + //! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, //! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4. //! diff --git a/minidump/minidump_file_writer.cc b/minidump/minidump_file_writer.cc index 9b540d32..0c79c78d 100644 --- a/minidump/minidump_file_writer.cc +++ b/minidump/minidump_file_writer.cc @@ -27,6 +27,7 @@ #include "minidump/minidump_system_info_writer.h" #include "minidump/minidump_thread_id_map.h" #include "minidump/minidump_thread_writer.h" +#include "minidump/minidump_unloaded_module_writer.h" #include "minidump/minidump_writer_util.h" #include "snapshot/exception_snapshot.h" #include "snapshot/process_snapshot.h" @@ -95,6 +96,14 @@ void MinidumpFileWriter::InitializeFromSnapshot( module_list->InitializeFromSnapshot(process_snapshot->Modules()); AddStream(std::move(module_list)); + auto unloaded_modules = process_snapshot->UnloadedModules(); + if (!unloaded_modules.empty()) { + auto unloaded_module_list = + make_scoped_ptr(new MinidumpUnloadedModuleListWriter()); + unloaded_module_list->InitializeFromSnapshot(unloaded_modules); + AddStream(std::move(unloaded_module_list)); + } + auto crashpad_info = make_scoped_ptr(new MinidumpCrashpadInfoWriter()); crashpad_info->InitializeFromSnapshot(process_snapshot); diff --git a/minidump/minidump_test.gyp b/minidump/minidump_test.gyp index 6f8dc198..30a179f1 100644 --- a/minidump/minidump_test.gyp +++ b/minidump/minidump_test.gyp @@ -49,6 +49,7 @@ 'minidump_system_info_writer_test.cc', 'minidump_thread_id_map_test.cc', 'minidump_thread_writer_test.cc', + 'minidump_unloaded_module_writer_test.cc', 'minidump_writable_test.cc', 'test/minidump_context_test_util.cc', 'test/minidump_context_test_util.h', diff --git a/minidump/minidump_unloaded_module_writer.cc b/minidump/minidump_unloaded_module_writer.cc new file mode 100644 index 00000000..7a520ba7 --- /dev/null +++ b/minidump/minidump_unloaded_module_writer.cc @@ -0,0 +1,203 @@ +// Copyright 2016 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_unloaded_module_writer.h" + +#include + +#include "minidump/minidump_writer_util.h" +#include "util/file/file_writer.h" +#include "util/numeric/in_range_cast.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpUnloadedModuleWriter::MinidumpUnloadedModuleWriter() + : MinidumpWritable(), unloaded_module_(), name_() {} + +MinidumpUnloadedModuleWriter::~MinidumpUnloadedModuleWriter() { +} + +void MinidumpUnloadedModuleWriter::InitializeFromSnapshot( + const UnloadedModuleSnapshot& unloaded_module_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!name_); + + SetName(unloaded_module_snapshot.Name()); + + SetImageBaseAddress(unloaded_module_snapshot.Address()); + SetImageSize(InRangeCast(unloaded_module_snapshot.Size(), + std::numeric_limits::max())); + SetTimestamp(unloaded_module_snapshot.Timestamp()); + SetChecksum(unloaded_module_snapshot.Checksum()); +} + +const MINIDUMP_UNLOADED_MODULE* +MinidumpUnloadedModuleWriter::MinidumpUnloadedModule() const { + DCHECK_EQ(state(), kStateWritable); + + return &unloaded_module_; +} + +void MinidumpUnloadedModuleWriter::SetName(const std::string& name) { + DCHECK_EQ(state(), kStateMutable); + + if (!name_) { + name_.reset(new internal::MinidumpUTF16StringWriter()); + } + name_->SetUTF8(name); +} + +void MinidumpUnloadedModuleWriter::SetTimestamp(time_t timestamp) { + DCHECK_EQ(state(), kStateMutable); + + internal::MinidumpWriterUtil::AssignTimeT(&unloaded_module_.TimeDateStamp, + timestamp); +} + +bool MinidumpUnloadedModuleWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK(name_); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + name_->RegisterRVA(&unloaded_module_.ModuleNameRva); + + return true; +} + +size_t MinidumpUnloadedModuleWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // This object doesn’t directly write anything itself. Its + // MINIDUMP_UNLOADED_MODULE is written by its parent as part of a + // MINIDUMP_UNLOADED_MODULE_LIST, and its children are responsible for writing + // themselves. + return 0; +} + +std::vector +MinidumpUnloadedModuleWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK(name_); + + std::vector children(1, name_.get()); + return children; +} + +bool MinidumpUnloadedModuleWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + // This object doesn’t directly write anything itself. Its + // MINIDUMP_UNLOADED_MODULE is written by its parent as part of a + // MINIDUMP_UNLOADED_MODULE_LIST, and its children are responsible for writing + // themselves. + return true; +} + +MinidumpUnloadedModuleListWriter::MinidumpUnloadedModuleListWriter() + : MinidumpStreamWriter(), + unloaded_modules_(), + unloaded_module_list_base_() {} + +MinidumpUnloadedModuleListWriter::~MinidumpUnloadedModuleListWriter() { +} + +void MinidumpUnloadedModuleListWriter::InitializeFromSnapshot( + const std::vector& unloaded_module_snapshots) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(unloaded_modules_.empty()); + + for (auto unloaded_module_snapshot : unloaded_module_snapshots) { + auto unloaded_module = make_scoped_ptr(new MinidumpUnloadedModuleWriter()); + unloaded_module->InitializeFromSnapshot(unloaded_module_snapshot); + AddUnloadedModule(std::move(unloaded_module)); + } +} + +void MinidumpUnloadedModuleListWriter::AddUnloadedModule( + scoped_ptr unloaded_module) { + DCHECK_EQ(state(), kStateMutable); + + unloaded_modules_.push_back(unloaded_module.release()); +} + +bool MinidumpUnloadedModuleListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + unloaded_module_list_base_.SizeOfHeader = + sizeof(MINIDUMP_UNLOADED_MODULE_LIST); + unloaded_module_list_base_.SizeOfEntry = sizeof(MINIDUMP_UNLOADED_MODULE); + + size_t unloaded_module_count = unloaded_modules_.size(); + if (!AssignIfInRange(&unloaded_module_list_base_.NumberOfEntries, + unloaded_module_count)) { + LOG(ERROR) << "unloaded_module_count " << unloaded_module_count + << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpUnloadedModuleListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(unloaded_module_list_base_) + + unloaded_modules_.size() * sizeof(MINIDUMP_UNLOADED_MODULE); +} + +std::vector +MinidumpUnloadedModuleListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children; + for (MinidumpUnloadedModuleWriter* unloaded_module : unloaded_modules_) { + children.push_back(unloaded_module); + } + + return children; +} + +bool MinidumpUnloadedModuleListWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &unloaded_module_list_base_; + iov.iov_len = sizeof(unloaded_module_list_base_); + std::vector iovecs(1, iov); + + for (const MinidumpUnloadedModuleWriter* unloaded_module : + unloaded_modules_) { + iov.iov_base = unloaded_module->MinidumpUnloadedModule(); + iov.iov_len = sizeof(MINIDUMP_UNLOADED_MODULE); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpUnloadedModuleListWriter::StreamType() const { + return kMinidumpStreamTypeUnloadedModuleList; +} + +} // namespace crashpad diff --git a/minidump/minidump_unloaded_module_writer.h b/minidump/minidump_unloaded_module_writer.h new file mode 100644 index 00000000..dbefd602 --- /dev/null +++ b/minidump/minidump_unloaded_module_writer.h @@ -0,0 +1,154 @@ +// Copyright 2016 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_ + +#include +#include +#include + +#include +#include + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_string_writer.h" +#include "minidump/minidump_writable.h" +#include "snapshot/unloaded_module_snapshot.h" + +namespace crashpad { + +//! \brief The writer for a MINIDUMP_UNLOADED_MODULE object in a minidump file. +//! +//! Because MINIDUMP_UNLOADED_MODULE objects only appear as elements of +//! MINIDUMP_UNLOADED_MODULE_LIST objects, this class does not write any data on +//! its own. It makes its MINIDUMP_UNLOADED_MODULE data available to its +//! MinidumpUnloadedModuleListWriter parent, which writes it as part of a +//! MINIDUMP_UNLOADED_MODULE_LIST. +class MinidumpUnloadedModuleWriter final : public internal::MinidumpWritable { + public: + MinidumpUnloadedModuleWriter(); + ~MinidumpUnloadedModuleWriter() override; + + //! \brief Initializes the MINIDUMP_UNLOADED_MODULE based on \a + //! unloaded_module_snapshot. + //! + //! \param[in] unloaded_module_snapshot The unloaded module snapshot to use as + //! source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot( + const UnloadedModuleSnapshot& unloaded_module_snapshot); + + //! \brief Returns a MINIDUMP_UNLOADED_MODULE referencing this object’s data. + //! + //! This method is expected to be called by a MinidumpUnloadedModuleListWriter + //! in order to obtain a MINIDUMP_UNLOADED_MODULE to include in its list. + //! + //! \note Valid in #kStateWritable. + const MINIDUMP_UNLOADED_MODULE* MinidumpUnloadedModule() const; + + //! \brief Arranges for MINIDUMP_UNLOADED_MODULE::ModuleNameRva to point to a + //! MINIDUMP_STRING containing \a name. + //! + //! \note Valid in #kStateMutable. + void SetName(const std::string& name); + + //! \brief Sets MINIDUMP_UNLOADED_MODULE::BaseOfImage. + void SetImageBaseAddress(uint64_t image_base_address) { + unloaded_module_.BaseOfImage = image_base_address; + } + + //! \brief Sets MINIDUMP_UNLOADED_MODULE::SizeOfImage. + void SetImageSize(uint32_t image_size) { + unloaded_module_.SizeOfImage = image_size; + } + + //! \brief Sets MINIDUMP_UNLOADED_MODULE::CheckSum. + void SetChecksum(uint32_t checksum) { unloaded_module_.CheckSum = checksum; } + + //! \brief Sets MINIDUMP_UNLOADED_MODULE::TimeDateStamp. + //! + //! \note Valid in #kStateMutable. + void SetTimestamp(time_t timestamp); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MINIDUMP_UNLOADED_MODULE unloaded_module_; + scoped_ptr name_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpUnloadedModuleWriter); +}; + +//! \brief The writer for a MINIDUMP_UNLOADED_MODULE_LIST stream in a minidump +//! file, containing a list of MINIDUMP_UNLOADED_MODULE objects. +class MinidumpUnloadedModuleListWriter final + : public internal::MinidumpStreamWriter { + public: + MinidumpUnloadedModuleListWriter(); + ~MinidumpUnloadedModuleListWriter() override; + + //! \brief Adds an initialized MINIDUMP_UNLOADED_MODULE for each unloaded + //! module in \a unloaded_module_snapshots to the + //! MINIDUMP_UNLOADED_MODULE_LIST. + //! + //! \param[in] unloaded_module_snapshots The unloaded module snapshots to use + //! as source data. + //! + //! \note Valid in #kStateMutable. AddUnloadedModule() may not be called + //! before this this method, and it is not normally necessary to call + //! AddUnloadedModule() after this method. + void InitializeFromSnapshot( + const std::vector& unloaded_module_snapshots); + + //! \brief Adds a MinidumpUnloadedModuleWriter to the + //! MINIDUMP_UNLOADED_MODULE_LIST. + //! + //! This object takes ownership of \a unloaded_module and becomes its parent + //! in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void AddUnloadedModule( + scoped_ptr unloaded_module); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + PointerVector unloaded_modules_; + MINIDUMP_UNLOADED_MODULE_LIST unloaded_module_list_base_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpUnloadedModuleListWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_ diff --git a/minidump/minidump_unloaded_module_writer_test.cc b/minidump/minidump_unloaded_module_writer_test.cc new file mode 100644 index 00000000..9032399e --- /dev/null +++ b/minidump/minidump_unloaded_module_writer_test.cc @@ -0,0 +1,162 @@ +// Copyright 2016 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_unloaded_module_writer.h" + +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +void ExpectUnloadedModule(const MINIDUMP_UNLOADED_MODULE* expected, + const MINIDUMP_UNLOADED_MODULE* observed, + const std::string& file_contents, + const std::string& expected_module_name) { + EXPECT_EQ(expected->BaseOfImage, observed->BaseOfImage); + EXPECT_EQ(expected->SizeOfImage, observed->SizeOfImage); + EXPECT_EQ(expected->CheckSum, observed->CheckSum); + EXPECT_EQ(expected->TimeDateStamp, observed->TimeDateStamp); + EXPECT_NE(0u, observed->ModuleNameRva); + base::string16 observed_module_name_utf16 = + MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva); + base::string16 expected_module_name_utf16 = + base::UTF8ToUTF16(expected_module_name); + EXPECT_EQ(expected_module_name_utf16, observed_module_name_utf16); +} + +void GetUnloadedModuleListStream( + const std::string& file_contents, + const MINIDUMP_UNLOADED_MODULE_LIST** unloaded_module_list) { + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kUnloadedModuleListStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + const size_t kUnloadedModulesOffset = + kUnloadedModuleListStreamOffset + sizeof(MINIDUMP_UNLOADED_MODULE_LIST); + + ASSERT_GE(file_contents.size(), kUnloadedModulesOffset); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(kMinidumpStreamTypeUnloadedModuleList, directory[0].StreamType); + EXPECT_EQ(kUnloadedModuleListStreamOffset, directory[0].Location.Rva); + + *unloaded_module_list = + MinidumpWritableAtLocationDescriptor( + file_contents, directory[0].Location); + ASSERT_TRUE(unloaded_module_list); +} + +TEST(MinidumpUnloadedModuleWriter, EmptyModule) { + MinidumpFileWriter minidump_file_writer; + auto unloaded_module_list_writer = + make_scoped_ptr(new MinidumpUnloadedModuleListWriter()); + + const char kModuleName[] = "test_dll"; + + auto unloaded_module_writer = + make_scoped_ptr(new MinidumpUnloadedModuleWriter()); + unloaded_module_writer->SetName(kModuleName); + + unloaded_module_list_writer->AddUnloadedModule( + std::move(unloaded_module_writer)); + minidump_file_writer.AddStream(std::move(unloaded_module_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_UNLOADED_MODULE_LIST) + + 1 * sizeof(MINIDUMP_UNLOADED_MODULE)); + + const MINIDUMP_UNLOADED_MODULE_LIST* unloaded_module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetUnloadedModuleListStream(string_file.string(), &unloaded_module_list)); + + EXPECT_EQ(1u, unloaded_module_list->NumberOfEntries); + + MINIDUMP_UNLOADED_MODULE expected = {}; + ASSERT_NO_FATAL_FAILURE( + ExpectUnloadedModule(&expected, + reinterpret_cast( + &unloaded_module_list[1]), + string_file.string(), + kModuleName)); +} + +TEST(MinidumpUnloadedModuleWriter, OneModule) { + MinidumpFileWriter minidump_file_writer; + auto unloaded_module_list_writer = + make_scoped_ptr(new MinidumpUnloadedModuleListWriter()); + + const char kModuleName[] = "statically_linked"; + const uint64_t kModuleBase = 0x10da69000; + const uint32_t kModuleSize = 0x1000; + const uint32_t kChecksum = 0x76543210; + const time_t kTimestamp = 0x386d4380; + + auto unloaded_module_writer = + make_scoped_ptr(new MinidumpUnloadedModuleWriter()); + unloaded_module_writer->SetName(kModuleName); + unloaded_module_writer->SetImageBaseAddress(kModuleBase); + unloaded_module_writer->SetImageSize(kModuleSize); + unloaded_module_writer->SetChecksum(kChecksum); + unloaded_module_writer->SetTimestamp(kTimestamp); + + unloaded_module_list_writer->AddUnloadedModule( + std::move(unloaded_module_writer)); + minidump_file_writer.AddStream(std::move(unloaded_module_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_UNLOADED_MODULE_LIST) + + 1 * sizeof(MINIDUMP_UNLOADED_MODULE)); + + const MINIDUMP_UNLOADED_MODULE_LIST* unloaded_module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetUnloadedModuleListStream(string_file.string(), &unloaded_module_list)); + + EXPECT_EQ(1u, unloaded_module_list->NumberOfEntries); + + MINIDUMP_UNLOADED_MODULE expected = {}; + expected.BaseOfImage = kModuleBase; + expected.SizeOfImage = kModuleSize; + expected.CheckSum = kChecksum; + expected.TimeDateStamp = kTimestamp; + + ASSERT_NO_FATAL_FAILURE( + ExpectUnloadedModule(&expected, + reinterpret_cast( + &unloaded_module_list[1]), + string_file.string(), + kModuleName)); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/minidump/test/minidump_writable_test_util.cc b/minidump/test/minidump_writable_test_util.cc index c8ea4f44..66c0591d 100644 --- a/minidump/test/minidump_writable_test_util.cc +++ b/minidump/test/minidump_writable_test_util.cc @@ -173,6 +173,14 @@ struct MinidumpModuleListTraits { } }; +struct MinidumpUnloadedModuleListTraits { + using ListType = MINIDUMP_UNLOADED_MODULE_LIST; + enum : size_t { kElementSize = sizeof(MINIDUMP_UNLOADED_MODULE) }; + static size_t ElementCount(const ListType* list) { + return list->NumberOfEntries; + } +}; + struct MinidumpThreadListTraits { using ListType = MINIDUMP_THREAD_LIST; enum : size_t { kElementSize = sizeof(MINIDUMP_THREAD) }; @@ -252,6 +260,15 @@ const MINIDUMP_MODULE_LIST* MinidumpWritableAtLocationDescriptor< file_contents, location); } +template <> +const MINIDUMP_UNLOADED_MODULE_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor( + file_contents, location); +} + template <> const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor< MINIDUMP_THREAD_LIST>(const std::string& file_contents, diff --git a/minidump/test/minidump_writable_test_util.h b/minidump/test/minidump_writable_test_util.h index ea3ec003..dd8f5531 100644 --- a/minidump/test/minidump_writable_test_util.h +++ b/minidump/test/minidump_writable_test_util.h @@ -89,6 +89,7 @@ MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_DIRECTORY); // variable-sized lists. MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_LIST); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MODULE_LIST); +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_UNLOADED_MODULE_LIST); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_THREAD_LIST); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_HANDLE_DATA_STREAM); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_INFO_LIST); @@ -186,6 +187,12 @@ const MINIDUMP_MODULE_LIST* MinidumpWritableAtLocationDescriptor< MINIDUMP_MODULE_LIST>(const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location); +template <> +const MINIDUMP_UNLOADED_MODULE_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + template <> const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor< MINIDUMP_THREAD_LIST>(const std::string& file_contents, diff --git a/snapshot/mac/process_snapshot_mac.cc b/snapshot/mac/process_snapshot_mac.cc index 3d44e935..5794b8bc 100644 --- a/snapshot/mac/process_snapshot_mac.cc +++ b/snapshot/mac/process_snapshot_mac.cc @@ -186,6 +186,12 @@ std::vector ProcessSnapshotMac::Modules() const { return modules; } +std::vector ProcessSnapshotMac::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + const ExceptionSnapshot* ProcessSnapshotMac::Exception() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return exception_.get(); diff --git a/snapshot/mac/process_snapshot_mac.h b/snapshot/mac/process_snapshot_mac.h index 92c73763..05ea329a 100644 --- a/snapshot/mac/process_snapshot_mac.h +++ b/snapshot/mac/process_snapshot_mac.h @@ -38,6 +38,7 @@ #include "snapshot/process_snapshot.h" #include "snapshot/system_snapshot.h" #include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" #include "util/mach/mach_extensions.h" #include "util/misc/initialization_state_dcheck.h" #include "util/misc/uuid.h" @@ -126,6 +127,7 @@ class ProcessSnapshotMac final : public ProcessSnapshot { const SystemSnapshot* System() const override; std::vector Threads() const override; std::vector Modules() const override; + std::vector UnloadedModules() const override; const ExceptionSnapshot* Exception() const override; std::vector MemoryMap() const override; std::vector Handles() const override; diff --git a/snapshot/minidump/process_snapshot_minidump.cc b/snapshot/minidump/process_snapshot_minidump.cc index 6a1572a8..d2b5984c 100644 --- a/snapshot/minidump/process_snapshot_minidump.cc +++ b/snapshot/minidump/process_snapshot_minidump.cc @@ -28,6 +28,7 @@ ProcessSnapshotMinidump::ProcessSnapshotMinidump() stream_directory_(), stream_map_(), modules_(), + unloaded_modules_(), crashpad_info_(), annotations_simple_map_(), file_reader_(nullptr), @@ -171,6 +172,13 @@ std::vector ProcessSnapshotMinidump::Modules() const { return modules; } +std::vector ProcessSnapshotMinidump::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return unloaded_modules_; +} + const ExceptionSnapshot* ProcessSnapshotMinidump::Exception() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // https://crashpad.chromium.org/bug/10 diff --git a/snapshot/minidump/process_snapshot_minidump.h b/snapshot/minidump/process_snapshot_minidump.h index ad1af3e8..2f6bf54c 100644 --- a/snapshot/minidump/process_snapshot_minidump.h +++ b/snapshot/minidump/process_snapshot_minidump.h @@ -33,6 +33,7 @@ #include "snapshot/process_snapshot.h" #include "snapshot/system_snapshot.h" #include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" #include "util/file/file_reader.h" #include "util/misc/initialization_state_dcheck.h" #include "util/misc/uuid.h" @@ -69,6 +70,7 @@ class ProcessSnapshotMinidump final : public ProcessSnapshot { const SystemSnapshot* System() const override; std::vector Threads() const override; std::vector Modules() const override; + std::vector UnloadedModules() const override; const ExceptionSnapshot* Exception() const override; std::vector MemoryMap() const override; std::vector Handles() const override; @@ -94,6 +96,7 @@ class ProcessSnapshotMinidump final : public ProcessSnapshot { std::vector stream_directory_; std::map stream_map_; PointerVector modules_; + std::vector unloaded_modules_; MinidumpCrashpadInfo crashpad_info_; std::map annotations_simple_map_; FileReaderInterface* file_reader_; // weak diff --git a/snapshot/process_snapshot.h b/snapshot/process_snapshot.h index 19d34607..8d5520cd 100644 --- a/snapshot/process_snapshot.h +++ b/snapshot/process_snapshot.h @@ -33,6 +33,7 @@ class MemorySnapshot; class ModuleSnapshot; class SystemSnapshot; class ThreadSnapshot; +class UnloadedModuleSnapshot; //! \brief An abstract interface to a snapshot representing the state of a //! process. @@ -146,6 +147,12 @@ class ProcessSnapshot { //! ProcessSnapshot object that they were obtained from. virtual std::vector Modules() const = 0; + //! \brief Returns UnloadedModuleSnapshot objects reflecting the code modules + //! the were recorded as unloaded at the time of the snapshot. + //! + //! \return A vector of UnloadedModuleSnapshot objects. + virtual std::vector UnloadedModules() const = 0; + //! \brief Returns ThreadSnapshot objects reflecting the threads (lightweight //! processes) existing in the snapshot process at the time of the //! snapshot. diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index d0011528..5020073e 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -91,6 +91,8 @@ 'process_snapshot.h', 'system_snapshot.h', 'thread_snapshot.h', + 'unloaded_module_snapshot.cc', + 'unloaded_module_snapshot.h', 'win/cpu_context_win.cc', 'win/cpu_context_win.h', 'win/exception_snapshot_win.cc', diff --git a/snapshot/test/test_process_snapshot.cc b/snapshot/test/test_process_snapshot.cc index c022dc41..74f9fe35 100644 --- a/snapshot/test/test_process_snapshot.cc +++ b/snapshot/test/test_process_snapshot.cc @@ -96,6 +96,11 @@ std::vector TestProcessSnapshot::Modules() const { return modules; } +std::vector TestProcessSnapshot::UnloadedModules() + const { + return unloaded_modules_; +} + const ExceptionSnapshot* TestProcessSnapshot::Exception() const { return exception_.get(); } diff --git a/snapshot/test/test_process_snapshot.h b/snapshot/test/test_process_snapshot.h index f771944e..f47b856d 100644 --- a/snapshot/test/test_process_snapshot.h +++ b/snapshot/test/test_process_snapshot.h @@ -33,6 +33,7 @@ #include "snapshot/process_snapshot.h" #include "snapshot/system_snapshot.h" #include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" #include "util/misc/uuid.h" #include "util/stdlib/pointer_container.h" @@ -92,6 +93,15 @@ class TestProcessSnapshot final : public ProcessSnapshot { modules_.push_back(module.release()); } + //! \brief Adds an unloaded module snapshot to be returned by + //! UnloadedModules(). + //! + //! \param[in] unloaded_module The unloaded module snapshot that will be + //! included in UnloadedModules(). + void AddModule(const UnloadedModuleSnapshot& unloaded_module) { + unloaded_modules_.push_back(unloaded_module); + } + //! \brief Sets the exception snapshot to be returned by Exception(). //! //! \param[in] exception The exception snapshot that Exception() will return. @@ -139,6 +149,7 @@ class TestProcessSnapshot final : public ProcessSnapshot { const SystemSnapshot* System() const override; std::vector Threads() const override; std::vector Modules() const override; + std::vector UnloadedModules() const override; const ExceptionSnapshot* Exception() const override; std::vector MemoryMap() const override; std::vector Handles() const override; @@ -157,6 +168,7 @@ class TestProcessSnapshot final : public ProcessSnapshot { scoped_ptr system_; PointerVector threads_; PointerVector modules_; + std::vector unloaded_modules_; scoped_ptr exception_; PointerVector memory_map_; std::vector handles_; diff --git a/snapshot/unloaded_module_snapshot.cc b/snapshot/unloaded_module_snapshot.cc new file mode 100644 index 00000000..45f606a6 --- /dev/null +++ b/snapshot/unloaded_module_snapshot.cc @@ -0,0 +1,33 @@ +// Copyright 2016 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 "snapshot/unloaded_module_snapshot.h" + +namespace crashpad { + +UnloadedModuleSnapshot::UnloadedModuleSnapshot(uint64_t address, + uint64_t size, + uint32_t checksum, + uint32_t timestamp, + const std::string& name) + : name_(name), + address_(address), + size_(size), + checksum_(checksum), + timestamp_(timestamp) {} + +UnloadedModuleSnapshot::~UnloadedModuleSnapshot() { +} + +} // namespace crashpad diff --git a/snapshot/unloaded_module_snapshot.h b/snapshot/unloaded_module_snapshot.h new file mode 100644 index 00000000..03e100e1 --- /dev/null +++ b/snapshot/unloaded_module_snapshot.h @@ -0,0 +1,61 @@ +// Copyright 2016 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. + +#ifndef CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_ + +#include + +#include + +namespace crashpad { + +//! \brief Information about an unloaded module that was previously loaded into +//! a snapshot process. +class UnloadedModuleSnapshot { + public: + UnloadedModuleSnapshot(uint64_t address, + uint64_t size, + uint32_t checksum, + uint32_t timestamp, + const std::string& name); + ~UnloadedModuleSnapshot(); + + //! \brief The base address of the module in the target processes' address + //! space. + uint64_t Address() const { return address_; } + + //! \brief The size of the module. + uint64_t Size() const { return size_; } + + //! \brief The checksum of the image. + uint32_t Checksum() const { return checksum_; } + + //! \brief The time and date stamp in `time_t` format. + uint32_t Timestamp() const { return timestamp_; } + + //! \brief The name of the module. + std::string Name() const { return name_; } + + private: + std::string name_; + uint64_t address_; + uint64_t size_; + uint32_t checksum_; + uint32_t timestamp_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_ diff --git a/snapshot/win/end_to_end_test.py b/snapshot/win/end_to_end_test.py index bf4f3034..d4f2da02 100755 --- a/snapshot/win/end_to_end_test.py +++ b/snapshot/win/end_to_end_test.py @@ -235,6 +235,11 @@ def RunTests(cdb_path, out.Check(r'Event\s+\d+', 'capture some event handles') out.Check(r'File\s+\d+', 'capture some file handles') + out = CdbRun(cdb_path, dump_path, 'lm') + out.Check(r'Unloaded modules:', 'captured some unloaded modules') + out.Check(r'lz32\.dll', 'found expected unloaded module lz32') + out.Check(r'wmerror\.dll', 'found expected unloaded module wmerror') + out = CdbRun(cdb_path, destroyed_dump_path, '.ecxr;!peb;k 2') out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved') out.Check(r'ntdll\.dll', 'ntdll present', re.IGNORECASE) diff --git a/snapshot/win/process_snapshot_win.cc b/snapshot/win/process_snapshot_win.cc index 535c9a73..9c72855f 100644 --- a/snapshot/win/process_snapshot_win.cc +++ b/snapshot/win/process_snapshot_win.cc @@ -21,6 +21,7 @@ #include "base/strings/utf_string_conversions.h" #include "snapshot/win/memory_snapshot_win.h" #include "snapshot/win/module_snapshot_win.h" +#include "util/win/nt_internals.h" #include "util/win/registration_protocol_win.h" #include "util/win/time.h" @@ -67,6 +68,7 @@ bool ProcessSnapshotWin::Initialize( } InitializeModules(); + InitializeUnloadedModules(); GetCrashpadOptionsInternal(&options_); @@ -177,6 +179,12 @@ std::vector ProcessSnapshotWin::Modules() const { return modules; } +std::vector ProcessSnapshotWin::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return unloaded_modules_; +} + const ExceptionSnapshot* ProcessSnapshotWin::Exception() const { return exception_.get(); } @@ -242,6 +250,53 @@ void ProcessSnapshotWin::InitializeModules() { } } +void ProcessSnapshotWin::InitializeUnloadedModules() { + // As documented by https://msdn.microsoft.com/en-us/library/cc678403.aspx + // we can retrieve the location for our unload events, and use that address in + // the target process. Unfortunately, this of course only works for + // 64-reading-64 and 32-reading-32, so at the moment, we simply do not + // retrieve unloaded modules for 64-reading-32. See + // https://crashpad.chromium.org/bug/89. + +#if defined(ARCH_CPU_X86_64) + if (!process_reader_.Is64Bit()) { + LOG(ERROR) + << "reading unloaded modules across bitness not currently supported"; + return; + } + using Traits = process_types::internal::Traits64; +#elif defined(ARCH_CPU_X86) + using Traits = process_types::internal::Traits32; +#else +#error port +#endif + + RTL_UNLOAD_EVENT_TRACE* unload_event_trace_address = + RtlGetUnloadEventTrace(); + WinVMAddress address_in_target_process = + reinterpret_cast(unload_event_trace_address); + + std::vector> events( + RTL_UNLOAD_EVENT_TRACE_NUMBER); + if (!process_reader_.ReadMemory(address_in_target_process, + events.size() * sizeof(events[0]), + &events[0])) { + return; + } + + for (const RTL_UNLOAD_EVENT_TRACE& uet : events) { + if (uet.ImageName[0]) { + unloaded_modules_.push_back(UnloadedModuleSnapshot( + uet.BaseAddress, + uet.SizeOfImage, + uet.CheckSum, + uet.TimeDateStamp, + base::UTF16ToUTF8( + base::StringPiece16(uet.ImageName, arraysize(uet.ImageName))))); + } + } +} + void ProcessSnapshotWin::GetCrashpadOptionsInternal( CrashpadInfoClientOptions* options) { CrashpadInfoClientOptions local_options; diff --git a/snapshot/win/process_snapshot_win.h b/snapshot/win/process_snapshot_win.h index c2307bee..4d33d7be 100644 --- a/snapshot/win/process_snapshot_win.h +++ b/snapshot/win/process_snapshot_win.h @@ -34,6 +34,7 @@ #include "snapshot/process_snapshot.h" #include "snapshot/system_snapshot.h" #include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" #include "snapshot/win/exception_snapshot_win.h" #include "snapshot/win/memory_map_region_snapshot_win.h" #include "snapshot/win/memory_snapshot_win.h" @@ -134,6 +135,7 @@ class ProcessSnapshotWin final : public ProcessSnapshot { const SystemSnapshot* System() const override; std::vector Threads() const override; std::vector Modules() const override; + std::vector UnloadedModules() const override; const ExceptionSnapshot* Exception() const override; std::vector MemoryMap() const override; std::vector Handles() const override; @@ -146,6 +148,9 @@ class ProcessSnapshotWin final : public ProcessSnapshot { // Initializes modules_ on behalf of Initialize(). void InitializeModules(); + // Initializes unloaded_modules_ on behalf of Initialize(). + void InitializeUnloadedModules(); + // Initializes options_ on behalf of Initialize(). void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options); @@ -182,6 +187,7 @@ class ProcessSnapshotWin final : public ProcessSnapshot { PointerVector extra_memory_; PointerVector threads_; PointerVector modules_; + std::vector unloaded_modules_; scoped_ptr exception_; PointerVector memory_map_; ProcessReaderWin process_reader_; diff --git a/util/win/nt_internals.cc b/util/win/nt_internals.cc index 46192b35..6fc0999d 100644 --- a/util/win/nt_internals.cc +++ b/util/win/nt_internals.cc @@ -26,6 +26,8 @@ NTSTATUS NTAPI NtOpenThread(HANDLE* ThreadHandle, OBJECT_ATTRIBUTES* ObjectAttributes, CLIENT_ID* ClientId); +void* NTAPI RtlGetUnloadEventTrace(); + namespace crashpad { NTSTATUS NtQuerySystemInformation( @@ -83,6 +85,14 @@ NTSTATUS NtQueryObject(HANDLE handle, return_length); } +template +RTL_UNLOAD_EVENT_TRACE* RtlGetUnloadEventTrace() { + static const auto rtl_get_unload_event_trace = + GET_FUNCTION_REQUIRED(L"ntdll.dll", ::RtlGetUnloadEventTrace); + return reinterpret_cast*>( + rtl_get_unload_event_trace()); +} + // Explicit instantiations with the only 2 valid template arguments to avoid // putting the body of the function in the header. template NTSTATUS NtOpenThread( @@ -99,4 +109,10 @@ template NTSTATUS NtOpenThread( const process_types::CLIENT_ID* client_id); +template RTL_UNLOAD_EVENT_TRACE* +RtlGetUnloadEventTrace(); + +template RTL_UNLOAD_EVENT_TRACE* +RtlGetUnloadEventTrace(); + } // namespace crashpad diff --git a/util/win/nt_internals.h b/util/win/nt_internals.h index aab884f5..b6221018 100644 --- a/util/win/nt_internals.h +++ b/util/win/nt_internals.h @@ -54,4 +54,26 @@ NTSTATUS NtQueryObject(HANDLE handle, ULONG object_information_length, ULONG* return_length); +// From https://msdn.microsoft.com/en-us/library/bb432428(VS.85).aspx and +// http://processhacker.sourceforge.net/doc/struct___r_t_l___u_n_l_o_a_d___e_v_e_n_t___t_r_a_c_e.html +#define RTL_UNLOAD_EVENT_TRACE_NUMBER 64 + +template +struct RTL_UNLOAD_EVENT_TRACE { + typename Traits::Pointer BaseAddress; + typename Traits::UnsignedIntegral SizeOfImage; + ULONG Sequence; + ULONG TimeDateStamp; + ULONG CheckSum; + WCHAR ImageName[32]; + ULONG Version0; + union { + ULONG Version1; + typename Traits::Pad alignment_for_x64; + }; +}; + +template +RTL_UNLOAD_EVENT_TRACE* RtlGetUnloadEventTrace(); + } // namespace crashpad