diff --git a/compat/non_win/dbghelp.h b/compat/non_win/dbghelp.h index 64d599d4..8537687a 100644 --- a/compat/non_win/dbghelp.h +++ b/compat/non_win/dbghelp.h @@ -535,16 +535,14 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MODULE { VS_FIXEDFILEINFO VersionInfo; //! \brief A pointer to the module’s CodeView record, typically a link to its - //! debugging information in crashpad::MinidumpModuleCodeViewRecordPDB70 - //! format. + //! debugging information in crashpad::CodeViewRecordPDB70 format. //! //! The specific format of the CodeView record is indicated by its signature, //! the first 32-bit value in the structure. For links to debugging //! information in contemporary usage, this is normally a - //! crashpad::MinidumpModuleCodeViewRecordPDB70 structure, but may be a - //! crashpad::MinidumpModuleCodeViewRecordPDB20 structure instead. These - //! structures identify a link to debugging data within a `.pdb` (Program - //! Database) file. See Matching //! Debug Information, PDB Files. //! @@ -565,8 +563,8 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MODULE { //! On Windows, the CodeView record is taken from a module’s //! IMAGE_DEBUG_DIRECTORY entry whose Type field has the value //! IMAGE_DEBUG_TYPE_CODEVIEW (`2`), if any. Records in - //! crashpad::MinidumpModuleCodeViewRecordPDB70 format are generated by Visual - //! Studio .NET (2002) (version 7.0) and later. + //! crashpad::CodeViewRecordPDB70 format are generated by Visual Studio .NET + //! (2002) (version 7.0) and later. //! //! When the CodeView record is not present, the fields of this //! MINIDUMP_LOCATION_DESCRIPTOR will be `0`. diff --git a/minidump/minidump_extensions.cc b/minidump/minidump_extensions.cc index da67d0c5..a956817e 100644 --- a/minidump/minidump_extensions.cc +++ b/minidump/minidump_extensions.cc @@ -16,8 +16,6 @@ namespace crashpad { -const uint32_t MinidumpModuleCodeViewRecordPDB20::kSignature; -const uint32_t MinidumpModuleCodeViewRecordPDB70::kSignature; const uint32_t MinidumpModuleCrashpadInfo::kVersion; const uint32_t MinidumpCrashpadInfo::kVersion; diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index b8bf77a1..95fdbddf 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -22,6 +22,7 @@ #include "base/compiler_specific.h" #include "build/build_config.h" +#include "util/misc/pdb_structures.h" #include "util/misc/uuid.h" // C4200 is "nonstandard extension used : zero-sized array in struct/union". @@ -216,112 +217,6 @@ enum MinidumpOS : uint32_t { kMinidumpOSUnknown = 0xffffffff, }; -//! \brief A CodeView record linking to a `.pdb` 2.0 file. -//! -//! This format provides an indirect link to debugging data by referencing an -//! external `.pdb` file by its name, timestamp, and age. This structure may be -//! pointed to by MINIDUMP_MODULE::CvRecord. It has been superseded by -//! MinidumpModuleCodeViewRecordPDB70. -//! -//! For more information about this structure and format, see Matching -//! Debug Information, PDB Files, and Undocumented -//! Windows 2000 Secrets, Windows 2000 Debugging Support/Microsoft Symbol -//! File Internals/CodeView Subsections. -//! -//! \sa IMAGE_DEBUG_MISC -struct MinidumpModuleCodeViewRecordPDB20 { - //! \brief The magic number identifying this structure version, stored in - //! #signature. - //! - //! In a hex dump, this will appear as “NB10” when produced by a little-endian - //! machine. - static const uint32_t kSignature = '01BN'; - - //! \brief The magic number identifying this structure version, the value of - //! #kSignature. - uint32_t signature; - - //! \brief The offset to CodeView data. - //! - //! In this structure, this field always has the value `0` because no CodeView - //! data is present, there is only a link to CodeView data stored in an - //! external file. - uint32_t offset; - - //! \brief The time that the `.pdb` file was created, in `time_t` format, the - //! number of seconds since the POSIX epoch. - uint32_t timestamp; - - //! \brief The revision of the `.pdb` file. - //! - //! A `.pdb` file’s age indicates incremental changes to it. When a `.pdb` - //! file is created, it has age `1`, and subsequent updates increase this - //! value. - uint32_t age; - - //! \brief The path or file name of the `.pdb` file associated with the - //! module. - //! - //! This is a NUL-terminated string. On Windows, it will be encoded in the - //! code page of the system that linked the module. On other operating - //! systems, UTF-8 may be used. - uint8_t pdb_name[1]; -}; - -//! \brief A CodeView record linking to a `.pdb` 7.0 file. -//! -//! This format provides an indirect link to debugging data by referencing an -//! external `.pdb` file by its name, %UUID, and age. This structure may be -//! pointed to by MINIDUMP_MODULE::CvRecord. -//! -//! For more information about this structure and format, see Matching -//! Debug Information, PDB Files. -//! -//! \sa MinidumpModuleCodeViewRecordPDB20 -//! \sa IMAGE_DEBUG_MISC -struct MinidumpModuleCodeViewRecordPDB70 { - // UUID has a constructor, which makes it non-POD, which makes this structure - // non-POD. In order for the default constructor to zero-initialize other - // members, an explicit constructor must be provided. - MinidumpModuleCodeViewRecordPDB70() - : signature(), - uuid(), - age(), - pdb_name() { - } - - //! \brief The magic number identifying this structure version, stored in - //! #signature. - //! - //! In a hex dump, this will appear as “RSDS” when produced by a little-endian - //! machine. - static const uint32_t kSignature = 'SDSR'; - - //! \brief The magic number identifying this structure version, the value of - //! #kSignature. - uint32_t signature; - - //! \brief The `.pdb` file’s unique identifier. - UUID uuid; - - //! \brief The revision of the `.pdb` file. - //! - //! A `.pdb` file’s age indicates incremental changes to it. When a `.pdb` - //! file is created, it has age `1`, and subsequent updates increase this - //! value. - uint32_t age; - - //! \brief The path or file name of the `.pdb` file associated with the - //! module. - //! - //! This is a NUL-terminated string. On Windows, it will be encoded in the - //! code page of the system that linked the module. On other operating - //! systems, UTF-8 may be used. - uint8_t pdb_name[1]; -}; //! \brief A list of ::RVA pointers. struct ALIGNAS(4) PACKED MinidumpRVAList { diff --git a/minidump/minidump_module_writer.cc b/minidump/minidump_module_writer.cc index dc5eddfa..33c83a75 100644 --- a/minidump/minidump_module_writer.cc +++ b/minidump/minidump_module_writer.cc @@ -77,7 +77,7 @@ bool MinidumpModuleCodeViewRecordPDBLinkWriter::WriteObject( } // namespace internal template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter< - MinidumpModuleCodeViewRecordPDB20>; + CodeViewRecordPDB20>; MinidumpModuleCodeViewRecordPDB20Writer:: ~MinidumpModuleCodeViewRecordPDB20Writer() { @@ -95,7 +95,7 @@ void MinidumpModuleCodeViewRecordPDB20Writer::SetTimestampAndAge( } template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter< - MinidumpModuleCodeViewRecordPDB70>; + CodeViewRecordPDB70>; MinidumpModuleCodeViewRecordPDB70Writer:: ~MinidumpModuleCodeViewRecordPDB70Writer() { diff --git a/minidump/minidump_module_writer.h b/minidump/minidump_module_writer.h index 2bec5968..ecf3cb85 100644 --- a/minidump/minidump_module_writer.h +++ b/minidump/minidump_module_writer.h @@ -86,42 +86,38 @@ class MinidumpModuleCodeViewRecordPDBLinkWriter } // namespace internal -//! \brief The writer for a MinidumpModuleCodeViewRecordPDB20 object in a -//! minidump file. +//! \brief The writer for a CodeViewRecordPDB20 object in a minidump file. //! //! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead. class MinidumpModuleCodeViewRecordPDB20Writer final : public internal::MinidumpModuleCodeViewRecordPDBLinkWriter< - MinidumpModuleCodeViewRecordPDB20> { + CodeViewRecordPDB20> { public: MinidumpModuleCodeViewRecordPDB20Writer() : internal::MinidumpModuleCodeViewRecordPDBLinkWriter< - MinidumpModuleCodeViewRecordPDB20>() {} + CodeViewRecordPDB20>() {} ~MinidumpModuleCodeViewRecordPDB20Writer() override; - //! \brief Sets MinidumpModuleCodeViewRecordPDB20::timestamp and - //! MinidumpModuleCodeViewRecordPDB20::age. + //! \brief Sets CodeViewRecordPDB20::timestamp and CodeViewRecordPDB20::age. void SetTimestampAndAge(time_t timestamp, uint32_t age); private: DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDB20Writer); }; -//! \brief The writer for a MinidumpModuleCodeViewRecordPDB70 object in a -//! minidump file. +//! \brief The writer for a CodeViewRecordPDB70 object in a minidump file. class MinidumpModuleCodeViewRecordPDB70Writer final : public internal::MinidumpModuleCodeViewRecordPDBLinkWriter< - MinidumpModuleCodeViewRecordPDB70> { + CodeViewRecordPDB70> { public: MinidumpModuleCodeViewRecordPDB70Writer() : internal::MinidumpModuleCodeViewRecordPDBLinkWriter< - MinidumpModuleCodeViewRecordPDB70>() {} + CodeViewRecordPDB70>() {} ~MinidumpModuleCodeViewRecordPDB70Writer() override; - //! \brief Initializes the MinidumpModuleCodeViewRecordPDB70 based on \a - //! module_snapshot. + //! \brief Initializes the CodeViewRecordPDB70 based on \a module_snapshot. //! //! \param[in] module_snapshot The module snapshot to use as source data. //! @@ -130,8 +126,7 @@ class MinidumpModuleCodeViewRecordPDB70Writer final //! methods after this method. void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot); - //! \brief Sets MinidumpModuleCodeViewRecordPDB70::uuid and - //! MinidumpModuleCodeViewRecordPDB70::age. + //! \brief Sets CodeViewRecordPDB70::uuid and CodeViewRecordPDB70::age. void SetUUIDAndAge(const UUID& uuid, uint32_t age) { codeview_record()->uuid = uuid; codeview_record()->age = age; diff --git a/minidump/minidump_module_writer_test.cc b/minidump/minidump_module_writer_test.cc index ab2ff20d..0605474f 100644 --- a/minidump/minidump_module_writer_test.cc +++ b/minidump/minidump_module_writer_test.cc @@ -100,10 +100,9 @@ void ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR* codeview_record, std::string observed_pdb_name; if (expected_pdb_uuid) { // The CodeView record should be a PDB 7.0 link. - const MinidumpModuleCodeViewRecordPDB70* codeview_pdb70_record = - MinidumpWritableAtLocationDescriptor< - MinidumpModuleCodeViewRecordPDB70>(file_contents, - *codeview_record); + const CodeViewRecordPDB70* codeview_pdb70_record = + MinidumpWritableAtLocationDescriptor( + file_contents, *codeview_record); ASSERT_TRUE(codeview_pdb70_record); EXPECT_EQ(0, memcmp(expected_pdb_uuid, @@ -113,14 +112,12 @@ void ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR* codeview_record, observed_pdb_name.assign( reinterpret_cast(&codeview_pdb70_record->pdb_name[0]), - codeview_record->DataSize - - offsetof(MinidumpModuleCodeViewRecordPDB70, pdb_name)); + codeview_record->DataSize - offsetof(CodeViewRecordPDB70, pdb_name)); } else { // The CodeView record should be a PDB 2.0 link. - const MinidumpModuleCodeViewRecordPDB20* codeview_pdb20_record = - MinidumpWritableAtLocationDescriptor< - MinidumpModuleCodeViewRecordPDB20>(file_contents, - *codeview_record); + const CodeViewRecordPDB20* codeview_pdb20_record = + MinidumpWritableAtLocationDescriptor( + file_contents, *codeview_record); ASSERT_TRUE(codeview_pdb20_record); EXPECT_EQ(static_cast(expected_pdb_timestamp), codeview_pdb20_record->timestamp); @@ -128,8 +125,7 @@ void ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR* codeview_record, observed_pdb_name.assign( reinterpret_cast(&codeview_pdb20_record->pdb_name[0]), - codeview_record->DataSize - - offsetof(MinidumpModuleCodeViewRecordPDB20, pdb_name)); + codeview_record->DataSize - offsetof(CodeViewRecordPDB20, pdb_name)); } // Check for, and then remove, the NUL terminator. diff --git a/minidump/test/minidump_writable_test_util.cc b/minidump/test/minidump_writable_test_util.cc index f1ab4abd..71e8b913 100644 --- a/minidump/test/minidump_writable_test_util.cc +++ b/minidump/test/minidump_writable_test_util.cc @@ -291,21 +291,19 @@ const T* MinidumpCVPDBAtLocationDescriptor( } // namespace template <> -const MinidumpModuleCodeViewRecordPDB20* -MinidumpWritableAtLocationDescriptor( - const std::string& file_contents, - const MINIDUMP_LOCATION_DESCRIPTOR& location) { - return MinidumpCVPDBAtLocationDescriptor( - file_contents, location); +const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor< + CodeViewRecordPDB20>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpCVPDBAtLocationDescriptor(file_contents, + location); } template <> -const MinidumpModuleCodeViewRecordPDB70* -MinidumpWritableAtLocationDescriptor( - const std::string& file_contents, - const MINIDUMP_LOCATION_DESCRIPTOR& location) { - return MinidumpCVPDBAtLocationDescriptor( - file_contents, location); +const CodeViewRecordPDB70* MinidumpWritableAtLocationDescriptor< + CodeViewRecordPDB70>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpCVPDBAtLocationDescriptor(file_contents, + location); } TestUInt32MinidumpWritable::TestUInt32MinidumpWritable(uint32_t value) diff --git a/minidump/test/minidump_writable_test_util.h b/minidump/test/minidump_writable_test_util.h index ac7ac038..ce489686 100644 --- a/minidump/test/minidump_writable_test_util.h +++ b/minidump/test/minidump_writable_test_util.h @@ -98,8 +98,8 @@ MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpSimpleStringDictionary); // data). MINIDUMP_ALLOW_OVERSIZED_DATA(IMAGE_DEBUG_MISC); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_STRING); -MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCodeViewRecordPDB20); -MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCodeViewRecordPDB70); +MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB20); +MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB70); MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpUTF8String); // minidump_file_writer_test accesses its variable-sized test streams via a @@ -142,10 +142,10 @@ const T* TMinidumpWritableAtLocationDescriptor( //! specializations ensure that the size given by \a location matches the //! size expected of a stream containing the number of elements it claims to //! have. -//! - With an IMAGE_DEBUG_MISC, MinidumpModuleCodeViewRecordPDB20, or -//! MinidumpModuleCodeViewRecordPDB70 template parameter, template -//! specializations ensure that the structure has the expected format -//! including any magic number and the `NUL`-terminated string. +//! - With an IMAGE_DEBUG_MISC, CodeViewRecordPDB20, or CodeViewRecordPDB70 +//! template parameter, template specializations ensure that the structure +//! has the expected format including any magic number and the `NUL`- +//! terminated string. //! //! \param[in] file_contents The contents of the minidump file. //! \param[in] location A MINIDUMP_LOCATION_DESCRIPTOR giving the offset within @@ -190,16 +190,14 @@ const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor< const MINIDUMP_LOCATION_DESCRIPTOR& location); template <> -const MinidumpModuleCodeViewRecordPDB20* -MinidumpWritableAtLocationDescriptor( - const std::string& file_contents, - const MINIDUMP_LOCATION_DESCRIPTOR& location); +const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor< + CodeViewRecordPDB20>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); template <> -const MinidumpModuleCodeViewRecordPDB70* -MinidumpWritableAtLocationDescriptor( - const std::string& file_contents, - const MINIDUMP_LOCATION_DESCRIPTOR& location); +const CodeViewRecordPDB70* MinidumpWritableAtLocationDescriptor< + CodeViewRecordPDB70>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); template <> const MinidumpModuleCrashpadInfoList* diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index a49ee96e..b71b80d6 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -78,6 +78,7 @@ 'win/cpu_context_win_test.cc', 'win/exception_snapshot_win_test.cc', 'win/pe_image_annotations_reader_test.cc', + 'win/pe_image_reader_test.cc', 'win/process_reader_win_test.cc', 'win/system_snapshot_win_test.cc', ], diff --git a/snapshot/win/module_snapshot_win.cc b/snapshot/win/module_snapshot_win.cc index 1819a45b..d375b514 100644 --- a/snapshot/win/module_snapshot_win.cc +++ b/snapshot/win/module_snapshot_win.cc @@ -148,7 +148,11 @@ ModuleSnapshot::ModuleType ModuleSnapshotWin::GetModuleType() const { void ModuleSnapshotWin::UUID(crashpad::UUID* uuid) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - CHECK(false) << "TODO(scottmg)"; + // TODO(scottmg): Also pass the age and pdbname through to snapshot? + DWORD age; + std::string pdbname; + if (!pe_image_reader_->DebugDirectoryInformation(uuid, &age, &pdbname)) + *uuid = crashpad::UUID(); } std::vector ModuleSnapshotWin::AnnotationsVector() const { diff --git a/snapshot/win/pe_image_reader.cc b/snapshot/win/pe_image_reader.cc index 01755612..61219bfa 100644 --- a/snapshot/win/pe_image_reader.cc +++ b/snapshot/win/pe_image_reader.cc @@ -17,9 +17,11 @@ #include #include "base/logging.h" +#include "base/memory/scoped_ptr.h" #include "base/strings/stringprintf.h" #include "client/crashpad_info.h" #include "snapshot/win/process_reader_win.h" +#include "util/misc/pdb_structures.h" namespace crashpad { @@ -110,13 +112,70 @@ bool PEImageReader::GetCrashpadInfo( return true; } -bool PEImageReader::GetSectionByName(const std::string& name, - IMAGE_SECTION_HEADER* section) const { - if (name.size() > sizeof(section->Name)) { - LOG(WARNING) << "supplied section name too long " << name; +bool PEImageReader::DebugDirectoryInformation(UUID* uuid, + DWORD* age, + std::string* pdbname) { + WinVMAddress nt_headers_address; + IMAGE_NT_HEADERS nt_headers; + if (!ReadNtHeaders(&nt_headers_address, &nt_headers)) return false; + + const IMAGE_DATA_DIRECTORY& data_directory = + nt_headers.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; + if (data_directory.VirtualAddress == 0 || data_directory.Size == 0) + return false; + IMAGE_DEBUG_DIRECTORY debug_directory; + if (data_directory.Size % sizeof(debug_directory) != 0) + return false; + for (size_t offset = 0; offset < data_directory.Size; + offset += sizeof(debug_directory)) { + if (!CheckedReadMemory(Address() + data_directory.VirtualAddress + offset, + sizeof(debug_directory), + &debug_directory)) { + LOG(WARNING) << "could not read data directory"; + return false; + } + + if (debug_directory.Type != IMAGE_DEBUG_TYPE_CODEVIEW) + continue; + + if (debug_directory.SizeOfData < sizeof(CodeViewRecordPDB70)) { + LOG(WARNING) << "CodeView debug entry of unexpected size"; + continue; + } + scoped_ptr data(new char[debug_directory.SizeOfData]); + if (!CheckedReadMemory(Address() + debug_directory.AddressOfRawData, + debug_directory.SizeOfData, + data.get())) { + LOG(WARNING) << "could not read debug directory"; + return false; + } + + if (*reinterpret_cast(data.get()) != + CodeViewRecordPDB70::kSignature) { + // TODO(scottmg): Consider supporting other record types, see + // https://code.google.com/p/crashpad/issues/detail?id=47. + LOG(WARNING) << "encountered non-7.0 CodeView debug record"; + continue; + } + + CodeViewRecordPDB70* codeview = + reinterpret_cast(data.get()); + *uuid = codeview->uuid; + *age = codeview->age; + // This is a NUL-terminated string encoded in the codepage of the system + // where the binary was linked. We have no idea what that was, so we just + // assume ASCII. + *pdbname = std::string(reinterpret_cast(&codeview->pdb_name[0])); + return true; } + return false; +} + +// TODO(scottmg): This needs to be made cross-bitness supporting. +bool PEImageReader::ReadNtHeaders(WinVMAddress* nt_headers_address, + IMAGE_NT_HEADERS* nt_headers) const { IMAGE_DOS_HEADER dos_header; if (!CheckedReadMemory(Address(), sizeof(IMAGE_DOS_HEADER), &dos_header)) { LOG(WARNING) << "could not read dos header of " << module_name_; @@ -128,20 +187,33 @@ bool PEImageReader::GetSectionByName(const std::string& name, return false; } - // TODO(scottmg): This is reading a same-bitness sized structure. - IMAGE_NT_HEADERS nt_headers; - WinVMAddress nt_headers_address = Address() + dos_header.e_lfanew; + *nt_headers_address = Address() + dos_header.e_lfanew; if (!CheckedReadMemory( - nt_headers_address, sizeof(IMAGE_NT_HEADERS), &nt_headers)) { + *nt_headers_address, sizeof(IMAGE_NT_HEADERS), nt_headers)) { LOG(WARNING) << "could not read nt headers of " << module_name_; return false; } - if (nt_headers.Signature != IMAGE_NT_SIGNATURE) { + if (nt_headers->Signature != IMAGE_NT_SIGNATURE) { LOG(WARNING) << "invalid signature in nt headers of " << module_name_; return false; } + return true; +} + +bool PEImageReader::GetSectionByName(const std::string& name, + IMAGE_SECTION_HEADER* section) const { + if (name.size() > sizeof(section->Name)) { + LOG(WARNING) << "supplied section name too long " << name; + return false; + } + + WinVMAddress nt_headers_address; + IMAGE_NT_HEADERS nt_headers; + if (!ReadNtHeaders(&nt_headers_address, &nt_headers)) + return false; + WinVMAddress first_section_address = nt_headers_address + offsetof(IMAGE_NT_HEADERS, OptionalHeader) + nt_headers.FileHeader.SizeOfOptionalHeader; diff --git a/snapshot/win/pe_image_reader.h b/snapshot/win/pe_image_reader.h index 71b4863e..2273a8e6 100644 --- a/snapshot/win/pe_image_reader.h +++ b/snapshot/win/pe_image_reader.h @@ -21,6 +21,7 @@ #include "base/basictypes.h" #include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" #include "util/win/address_types.h" #include "util/win/checked_win_address_range.h" @@ -93,7 +94,21 @@ class PEImageReader { //! messages. Other failures will result in messages being logged. bool GetCrashpadInfo(process_types::CrashpadInfo* crashpad_info) const; + //! \brief Obtains information from the module's debug directory, if any. + //! + //! \param[out] uuid The unique identifier of the executable/PDB. + //! \param[out] age The age field for the pdb (the number of times it's been + //! relinked). + //! \param[out] pdbname Name of the pdb file. + //! \return `true` on success, or `false` if the module has no debug directory + //! entry. + bool DebugDirectoryInformation(UUID* uuid, DWORD* age, std::string* pdbname); + private: + //! \brief Reads the `IMAGE_NT_HEADERS` from the beginning of the image. + bool ReadNtHeaders(WinVMAddress* nt_header_address, + IMAGE_NT_HEADERS* nt_headers) const; + //! \brief Finds a given section by name in the image. bool GetSectionByName(const std::string& name, IMAGE_SECTION_HEADER* section) const; diff --git a/snapshot/win/pe_image_reader_test.cc b/snapshot/win/pe_image_reader_test.cc new file mode 100644 index 00000000..4376f2b8 --- /dev/null +++ b/snapshot/win/pe_image_reader_test.cc @@ -0,0 +1,54 @@ +// Copyright 2015 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/win/pe_image_reader.h" + +#include + +#include "gtest/gtest.h" +#include "snapshot/win/process_reader_win.h" + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +namespace crashpad { +namespace test { +namespace { + +TEST(PEImageReader, DebugDirectory) { + PEImageReader pe_image_reader; + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess())); + HMODULE self = reinterpret_cast(&__ImageBase); + MODULEINFO module_info; + ASSERT_TRUE(GetModuleInformation( + GetCurrentProcess(), self, &module_info, sizeof(module_info))); + EXPECT_EQ(self, module_info.lpBaseOfDll); + EXPECT_TRUE(pe_image_reader.Initialize(&process_reader, + reinterpret_cast(self), + module_info.SizeOfImage, + "self")); + UUID uuid; + DWORD age; + std::string pdbname; + EXPECT_TRUE(pe_image_reader.DebugDirectoryInformation(&uuid, &age, &pdbname)); + EXPECT_NE(-1, pdbname.find("crashpad_snapshot_test")); + const std::string suffix(".pdb"); + EXPECT_EQ( + 0, + pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix)); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/util/misc/pdb_structures.cc b/util/misc/pdb_structures.cc new file mode 100644 index 00000000..c62f11c0 --- /dev/null +++ b/util/misc/pdb_structures.cc @@ -0,0 +1,22 @@ +// Copyright 2015 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 "util/misc/pdb_structures.h" + +namespace crashpad { + +const uint32_t CodeViewRecordPDB20::kSignature; +const uint32_t CodeViewRecordPDB70::kSignature; + +} // namespace crashpad diff --git a/util/misc/pdb_structures.h b/util/misc/pdb_structures.h new file mode 100644 index 00000000..04d76eec --- /dev/null +++ b/util/misc/pdb_structures.h @@ -0,0 +1,131 @@ +// Copyright 2015 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_UTIL_MISC_PDB_STRUCTURES_H_ +#define CRASHPAD_UTIL_MISC_PDB_STRUCTURES_H_ + +#include "util/misc/uuid.h" + +namespace crashpad { + +//! \brief A CodeView record linking to a `.pdb` 2.0 file. +//! +//! This format provides an indirect link to debugging data by referencing an +//! external `.pdb` file by its name, timestamp, and age. This structure may be +//! pointed to by MINIDUMP_MODULE::CvRecord. It has been superseded by +//! CodeViewRecordPDB70. +//! +//! For more information about this structure and format, see Matching +//! Debug Information, PDB Files, and Undocumented +//! Windows 2000 Secrets, Windows 2000 Debugging Support/Microsoft Symbol +//! File Internals/CodeView Subsections. +//! +//! \sa IMAGE_DEBUG_MISC +struct CodeViewRecordPDB20 { + //! \brief The magic number identifying this structure version, stored in + //! #signature. + //! + //! In a hex dump, this will appear as “NB10” when produced by a little-endian + //! machine. + static const uint32_t kSignature = '01BN'; + + //! \brief The magic number identifying this structure version, the value of + //! #kSignature. + uint32_t signature; + + //! \brief The offset to CodeView data. + //! + //! In this structure, this field always has the value `0` because no CodeView + //! data is present, there is only a link to CodeView data stored in an + //! external file. + uint32_t offset; + + //! \brief The time that the `.pdb` file was created, in `time_t` format, the + //! number of seconds since the POSIX epoch. + uint32_t timestamp; + + //! \brief The revision of the `.pdb` file. + //! + //! A `.pdb` file’s age indicates incremental changes to it. When a `.pdb` + //! file is created, it has age `1`, and subsequent updates increase this + //! value. + uint32_t age; + + //! \brief The path or file name of the `.pdb` file associated with the + //! module. + //! + //! This is a NUL-terminated string. On Windows, it will be encoded in the + //! code page of the system that linked the module. On other operating + //! systems, UTF-8 may be used. + uint8_t pdb_name[1]; +}; + +//! \brief A CodeView record linking to a `.pdb` 7.0 file. +//! +//! This format provides an indirect link to debugging data by referencing an +//! external `.pdb` file by its name, %UUID, and age. This structure may be +//! pointed to by MINIDUMP_MODULE::CvRecord. +//! +//! For more information about this structure and format, see Matching +//! Debug Information, PDB Files. +//! +//! \sa CodeViewRecordPDB20 +//! \sa IMAGE_DEBUG_MISC +struct CodeViewRecordPDB70 { + // UUID has a constructor, which makes it non-POD, which makes this structure + // non-POD. In order for the default constructor to zero-initialize other + // members, an explicit constructor must be provided. + CodeViewRecordPDB70() + : signature(), + uuid(), + age(), + pdb_name() { + } + + //! \brief The magic number identifying this structure version, stored in + //! #signature. + //! + //! In a hex dump, this will appear as “RSDS” when produced by a little-endian + //! machine. + static const uint32_t kSignature = 'SDSR'; + + //! \brief The magic number identifying this structure version, the value of + //! #kSignature. + uint32_t signature; + + //! \brief The `.pdb` file’s unique identifier. + UUID uuid; + + //! \brief The revision of the `.pdb` file. + //! + //! A `.pdb` file’s age indicates incremental changes to it. When a `.pdb` + //! file is created, it has age `1`, and subsequent updates increase this + //! value. + uint32_t age; + + //! \brief The path or file name of the `.pdb` file associated with the + //! module. + //! + //! This is a NUL-terminated string. On Windows, it will be encoded in the + //! code page of the system that linked the module. On other operating + //! systems, UTF-8 may be used. + uint8_t pdb_name[1]; +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_PDB_STRUCTURES_H_ diff --git a/util/util.gyp b/util/util.gyp index df377592..69dab9fa 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -91,6 +91,8 @@ 'misc/initialization_state.h', 'misc/initialization_state_dcheck.cc', 'misc/initialization_state_dcheck.h', + 'misc/pdb_structures.cc', + 'misc/pdb_structures.h', 'misc/scoped_forbid_return.cc', 'misc/scoped_forbid_return.h', 'misc/symbolic_constants_common.h',