Implement ModuleSnapshotWin::UUID

Reads CodeView PDB GUID from Debug Directory of PE header.

R=mark@chromium.org
BUG=crashpad:1

Review URL: https://codereview.chromium.org/1311003003 .
This commit is contained in:
Scott Graham 2015-09-01 09:32:09 -07:00
parent 78bba8808b
commit 3ef04d14f2
16 changed files with 359 additions and 180 deletions

View File

@ -535,16 +535,14 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MODULE {
VS_FIXEDFILEINFO VersionInfo; VS_FIXEDFILEINFO VersionInfo;
//! \brief A pointer to the modules CodeView record, typically a link to its //! \brief A pointer to the modules CodeView record, typically a link to its
//! debugging information in crashpad::MinidumpModuleCodeViewRecordPDB70 //! debugging information in crashpad::CodeViewRecordPDB70 format.
//! format.
//! //!
//! The specific format of the CodeView record is indicated by its signature, //! The specific format of the CodeView record is indicated by its signature,
//! the first 32-bit value in the structure. For links to debugging //! the first 32-bit value in the structure. For links to debugging
//! information in contemporary usage, this is normally a //! information in contemporary usage, this is normally a
//! crashpad::MinidumpModuleCodeViewRecordPDB70 structure, but may be a //! crashpad::CodeViewRecordPDB70 structure, but may be a
//! crashpad::MinidumpModuleCodeViewRecordPDB20 structure instead. These //! crashpad::CodeViewRecordPDB20 structure instead. These structures identify
//! structures identify a link to debugging data within a `.pdb` (Program //! a link to debugging data within a `.pdb` (Program Database) file. See <a
//! Database) file. See <a
//! href="http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles">Matching //! href="http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles">Matching
//! Debug Information</a>, PDB Files. //! Debug Information</a>, PDB Files.
//! //!
@ -565,8 +563,8 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MODULE {
//! On Windows, the CodeView record is taken from a modules //! On Windows, the CodeView record is taken from a modules
//! IMAGE_DEBUG_DIRECTORY entry whose Type field has the value //! IMAGE_DEBUG_DIRECTORY entry whose Type field has the value
//! IMAGE_DEBUG_TYPE_CODEVIEW (`2`), if any. Records in //! IMAGE_DEBUG_TYPE_CODEVIEW (`2`), if any. Records in
//! crashpad::MinidumpModuleCodeViewRecordPDB70 format are generated by Visual //! crashpad::CodeViewRecordPDB70 format are generated by Visual Studio .NET
//! Studio .NET (2002) (version 7.0) and later. //! (2002) (version 7.0) and later.
//! //!
//! When the CodeView record is not present, the fields of this //! When the CodeView record is not present, the fields of this
//! MINIDUMP_LOCATION_DESCRIPTOR will be `0`. //! MINIDUMP_LOCATION_DESCRIPTOR will be `0`.

View File

@ -16,8 +16,6 @@
namespace crashpad { namespace crashpad {
const uint32_t MinidumpModuleCodeViewRecordPDB20::kSignature;
const uint32_t MinidumpModuleCodeViewRecordPDB70::kSignature;
const uint32_t MinidumpModuleCrashpadInfo::kVersion; const uint32_t MinidumpModuleCrashpadInfo::kVersion;
const uint32_t MinidumpCrashpadInfo::kVersion; const uint32_t MinidumpCrashpadInfo::kVersion;

View File

@ -22,6 +22,7 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "util/misc/pdb_structures.h"
#include "util/misc/uuid.h" #include "util/misc/uuid.h"
// C4200 is "nonstandard extension used : zero-sized array in struct/union". // C4200 is "nonstandard extension used : zero-sized array in struct/union".
@ -216,112 +217,6 @@ enum MinidumpOS : uint32_t {
kMinidumpOSUnknown = 0xffffffff, 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 <a
//! href="http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles">Matching
//! Debug Information</a>, PDB Files, and <a
//! href="http://undocumented.rawol.com/sbs-w2k-1-windows-2000-debugging-support.pdf#page=63">Undocumented
//! Windows 2000 Secrets</a>, 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` files 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 <a
//! href="http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles">Matching
//! Debug Information</a>, 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` files unique identifier.
UUID uuid;
//! \brief The revision of the `.pdb` file.
//!
//! A `.pdb` files 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. //! \brief A list of ::RVA pointers.
struct ALIGNAS(4) PACKED MinidumpRVAList { struct ALIGNAS(4) PACKED MinidumpRVAList {

View File

@ -77,7 +77,7 @@ bool MinidumpModuleCodeViewRecordPDBLinkWriter<CodeViewRecordType>::WriteObject(
} // namespace internal } // namespace internal
template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter< template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
MinidumpModuleCodeViewRecordPDB20>; CodeViewRecordPDB20>;
MinidumpModuleCodeViewRecordPDB20Writer:: MinidumpModuleCodeViewRecordPDB20Writer::
~MinidumpModuleCodeViewRecordPDB20Writer() { ~MinidumpModuleCodeViewRecordPDB20Writer() {
@ -95,7 +95,7 @@ void MinidumpModuleCodeViewRecordPDB20Writer::SetTimestampAndAge(
} }
template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter< template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
MinidumpModuleCodeViewRecordPDB70>; CodeViewRecordPDB70>;
MinidumpModuleCodeViewRecordPDB70Writer:: MinidumpModuleCodeViewRecordPDB70Writer::
~MinidumpModuleCodeViewRecordPDB70Writer() { ~MinidumpModuleCodeViewRecordPDB70Writer() {

View File

@ -86,42 +86,38 @@ class MinidumpModuleCodeViewRecordPDBLinkWriter
} // namespace internal } // namespace internal
//! \brief The writer for a MinidumpModuleCodeViewRecordPDB20 object in a //! \brief The writer for a CodeViewRecordPDB20 object in a minidump file.
//! minidump file.
//! //!
//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead. //! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead.
class MinidumpModuleCodeViewRecordPDB20Writer final class MinidumpModuleCodeViewRecordPDB20Writer final
: public internal::MinidumpModuleCodeViewRecordPDBLinkWriter< : public internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
MinidumpModuleCodeViewRecordPDB20> { CodeViewRecordPDB20> {
public: public:
MinidumpModuleCodeViewRecordPDB20Writer() MinidumpModuleCodeViewRecordPDB20Writer()
: internal::MinidumpModuleCodeViewRecordPDBLinkWriter< : internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
MinidumpModuleCodeViewRecordPDB20>() {} CodeViewRecordPDB20>() {}
~MinidumpModuleCodeViewRecordPDB20Writer() override; ~MinidumpModuleCodeViewRecordPDB20Writer() override;
//! \brief Sets MinidumpModuleCodeViewRecordPDB20::timestamp and //! \brief Sets CodeViewRecordPDB20::timestamp and CodeViewRecordPDB20::age.
//! MinidumpModuleCodeViewRecordPDB20::age.
void SetTimestampAndAge(time_t timestamp, uint32_t age); void SetTimestampAndAge(time_t timestamp, uint32_t age);
private: private:
DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDB20Writer); DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDB20Writer);
}; };
//! \brief The writer for a MinidumpModuleCodeViewRecordPDB70 object in a //! \brief The writer for a CodeViewRecordPDB70 object in a minidump file.
//! minidump file.
class MinidumpModuleCodeViewRecordPDB70Writer final class MinidumpModuleCodeViewRecordPDB70Writer final
: public internal::MinidumpModuleCodeViewRecordPDBLinkWriter< : public internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
MinidumpModuleCodeViewRecordPDB70> { CodeViewRecordPDB70> {
public: public:
MinidumpModuleCodeViewRecordPDB70Writer() MinidumpModuleCodeViewRecordPDB70Writer()
: internal::MinidumpModuleCodeViewRecordPDBLinkWriter< : internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
MinidumpModuleCodeViewRecordPDB70>() {} CodeViewRecordPDB70>() {}
~MinidumpModuleCodeViewRecordPDB70Writer() override; ~MinidumpModuleCodeViewRecordPDB70Writer() override;
//! \brief Initializes the MinidumpModuleCodeViewRecordPDB70 based on \a //! \brief Initializes the CodeViewRecordPDB70 based on \a module_snapshot.
//! module_snapshot.
//! //!
//! \param[in] module_snapshot The module snapshot to use as source data. //! \param[in] module_snapshot The module snapshot to use as source data.
//! //!
@ -130,8 +126,7 @@ class MinidumpModuleCodeViewRecordPDB70Writer final
//! methods after this method. //! methods after this method.
void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot); void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot);
//! \brief Sets MinidumpModuleCodeViewRecordPDB70::uuid and //! \brief Sets CodeViewRecordPDB70::uuid and CodeViewRecordPDB70::age.
//! MinidumpModuleCodeViewRecordPDB70::age.
void SetUUIDAndAge(const UUID& uuid, uint32_t age) { void SetUUIDAndAge(const UUID& uuid, uint32_t age) {
codeview_record()->uuid = uuid; codeview_record()->uuid = uuid;
codeview_record()->age = age; codeview_record()->age = age;

View File

@ -100,10 +100,9 @@ void ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR* codeview_record,
std::string observed_pdb_name; std::string observed_pdb_name;
if (expected_pdb_uuid) { if (expected_pdb_uuid) {
// The CodeView record should be a PDB 7.0 link. // The CodeView record should be a PDB 7.0 link.
const MinidumpModuleCodeViewRecordPDB70* codeview_pdb70_record = const CodeViewRecordPDB70* codeview_pdb70_record =
MinidumpWritableAtLocationDescriptor< MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB70>(
MinidumpModuleCodeViewRecordPDB70>(file_contents, file_contents, *codeview_record);
*codeview_record);
ASSERT_TRUE(codeview_pdb70_record); ASSERT_TRUE(codeview_pdb70_record);
EXPECT_EQ(0, EXPECT_EQ(0,
memcmp(expected_pdb_uuid, memcmp(expected_pdb_uuid,
@ -113,14 +112,12 @@ void ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR* codeview_record,
observed_pdb_name.assign( observed_pdb_name.assign(
reinterpret_cast<const char*>(&codeview_pdb70_record->pdb_name[0]), reinterpret_cast<const char*>(&codeview_pdb70_record->pdb_name[0]),
codeview_record->DataSize - codeview_record->DataSize - offsetof(CodeViewRecordPDB70, pdb_name));
offsetof(MinidumpModuleCodeViewRecordPDB70, pdb_name));
} else { } else {
// The CodeView record should be a PDB 2.0 link. // The CodeView record should be a PDB 2.0 link.
const MinidumpModuleCodeViewRecordPDB20* codeview_pdb20_record = const CodeViewRecordPDB20* codeview_pdb20_record =
MinidumpWritableAtLocationDescriptor< MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB20>(
MinidumpModuleCodeViewRecordPDB20>(file_contents, file_contents, *codeview_record);
*codeview_record);
ASSERT_TRUE(codeview_pdb20_record); ASSERT_TRUE(codeview_pdb20_record);
EXPECT_EQ(static_cast<uint32_t>(expected_pdb_timestamp), EXPECT_EQ(static_cast<uint32_t>(expected_pdb_timestamp),
codeview_pdb20_record->timestamp); codeview_pdb20_record->timestamp);
@ -128,8 +125,7 @@ void ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR* codeview_record,
observed_pdb_name.assign( observed_pdb_name.assign(
reinterpret_cast<const char*>(&codeview_pdb20_record->pdb_name[0]), reinterpret_cast<const char*>(&codeview_pdb20_record->pdb_name[0]),
codeview_record->DataSize - codeview_record->DataSize - offsetof(CodeViewRecordPDB20, pdb_name));
offsetof(MinidumpModuleCodeViewRecordPDB20, pdb_name));
} }
// Check for, and then remove, the NUL terminator. // Check for, and then remove, the NUL terminator.

View File

@ -291,21 +291,19 @@ const T* MinidumpCVPDBAtLocationDescriptor(
} // namespace } // namespace
template <> template <>
const MinidumpModuleCodeViewRecordPDB20* const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor<
MinidumpWritableAtLocationDescriptor<MinidumpModuleCodeViewRecordPDB20>( CodeViewRecordPDB20>(const std::string& file_contents,
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) { const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpCVPDBAtLocationDescriptor<MinidumpModuleCodeViewRecordPDB20>( return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB20>(file_contents,
file_contents, location); location);
} }
template <> template <>
const MinidumpModuleCodeViewRecordPDB70* const CodeViewRecordPDB70* MinidumpWritableAtLocationDescriptor<
MinidumpWritableAtLocationDescriptor<MinidumpModuleCodeViewRecordPDB70>( CodeViewRecordPDB70>(const std::string& file_contents,
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) { const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpCVPDBAtLocationDescriptor<MinidumpModuleCodeViewRecordPDB70>( return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB70>(file_contents,
file_contents, location); location);
} }
TestUInt32MinidumpWritable::TestUInt32MinidumpWritable(uint32_t value) TestUInt32MinidumpWritable::TestUInt32MinidumpWritable(uint32_t value)

View File

@ -98,8 +98,8 @@ MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpSimpleStringDictionary);
// data). // data).
MINIDUMP_ALLOW_OVERSIZED_DATA(IMAGE_DEBUG_MISC); MINIDUMP_ALLOW_OVERSIZED_DATA(IMAGE_DEBUG_MISC);
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_STRING); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_STRING);
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCodeViewRecordPDB20); MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB20);
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCodeViewRecordPDB70); MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB70);
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpUTF8String); MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpUTF8String);
// minidump_file_writer_test accesses its variable-sized test streams via a // 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 //! specializations ensure that the size given by \a location matches the
//! size expected of a stream containing the number of elements it claims to //! size expected of a stream containing the number of elements it claims to
//! have. //! have.
//! - With an IMAGE_DEBUG_MISC, MinidumpModuleCodeViewRecordPDB20, or //! - With an IMAGE_DEBUG_MISC, CodeViewRecordPDB20, or CodeViewRecordPDB70
//! MinidumpModuleCodeViewRecordPDB70 template parameter, template //! template parameter, template specializations ensure that the structure
//! specializations ensure that the structure has the expected format //! has the expected format including any magic number and the `NUL`-
//! including any magic number and the `NUL`-terminated string. //! terminated string.
//! //!
//! \param[in] file_contents The contents of the minidump file. //! \param[in] file_contents The contents of the minidump file.
//! \param[in] location A MINIDUMP_LOCATION_DESCRIPTOR giving the offset within //! \param[in] location A MINIDUMP_LOCATION_DESCRIPTOR giving the offset within
@ -190,15 +190,13 @@ const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
const MINIDUMP_LOCATION_DESCRIPTOR& location); const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <> template <>
const MinidumpModuleCodeViewRecordPDB20* const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor<
MinidumpWritableAtLocationDescriptor<MinidumpModuleCodeViewRecordPDB20>( CodeViewRecordPDB20>(const std::string& file_contents,
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location); const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <> template <>
const MinidumpModuleCodeViewRecordPDB70* const CodeViewRecordPDB70* MinidumpWritableAtLocationDescriptor<
MinidumpWritableAtLocationDescriptor<MinidumpModuleCodeViewRecordPDB70>( CodeViewRecordPDB70>(const std::string& file_contents,
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location); const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <> template <>

View File

@ -78,6 +78,7 @@
'win/cpu_context_win_test.cc', 'win/cpu_context_win_test.cc',
'win/exception_snapshot_win_test.cc', 'win/exception_snapshot_win_test.cc',
'win/pe_image_annotations_reader_test.cc', 'win/pe_image_annotations_reader_test.cc',
'win/pe_image_reader_test.cc',
'win/process_reader_win_test.cc', 'win/process_reader_win_test.cc',
'win/system_snapshot_win_test.cc', 'win/system_snapshot_win_test.cc',
], ],

View File

@ -148,7 +148,11 @@ ModuleSnapshot::ModuleType ModuleSnapshotWin::GetModuleType() const {
void ModuleSnapshotWin::UUID(crashpad::UUID* uuid) const { void ModuleSnapshotWin::UUID(crashpad::UUID* uuid) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); 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<std::string> ModuleSnapshotWin::AnnotationsVector() const { std::vector<std::string> ModuleSnapshotWin::AnnotationsVector() const {

View File

@ -17,9 +17,11 @@
#include <string.h> #include <string.h>
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "client/crashpad_info.h" #include "client/crashpad_info.h"
#include "snapshot/win/process_reader_win.h" #include "snapshot/win/process_reader_win.h"
#include "util/misc/pdb_structures.h"
namespace crashpad { namespace crashpad {
@ -110,13 +112,70 @@ bool PEImageReader::GetCrashpadInfo(
return true; return true;
} }
bool PEImageReader::GetSectionByName(const std::string& name, bool PEImageReader::DebugDirectoryInformation(UUID* uuid,
IMAGE_SECTION_HEADER* section) const { DWORD* age,
if (name.size() > sizeof(section->Name)) { std::string* pdbname) {
LOG(WARNING) << "supplied section name too long " << name; 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; 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<char[]> 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<DWORD*>(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<CodeViewRecordPDB70*>(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<char*>(&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; IMAGE_DOS_HEADER dos_header;
if (!CheckedReadMemory(Address(), sizeof(IMAGE_DOS_HEADER), &dos_header)) { if (!CheckedReadMemory(Address(), sizeof(IMAGE_DOS_HEADER), &dos_header)) {
LOG(WARNING) << "could not read dos header of " << module_name_; LOG(WARNING) << "could not read dos header of " << module_name_;
@ -128,20 +187,33 @@ bool PEImageReader::GetSectionByName(const std::string& name,
return false; return false;
} }
// TODO(scottmg): This is reading a same-bitness sized structure. *nt_headers_address = Address() + dos_header.e_lfanew;
IMAGE_NT_HEADERS nt_headers;
WinVMAddress nt_headers_address = Address() + dos_header.e_lfanew;
if (!CheckedReadMemory( 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_; LOG(WARNING) << "could not read nt headers of " << module_name_;
return false; 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_; LOG(WARNING) << "invalid signature in nt headers of " << module_name_;
return false; 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 = WinVMAddress first_section_address =
nt_headers_address + offsetof(IMAGE_NT_HEADERS, OptionalHeader) + nt_headers_address + offsetof(IMAGE_NT_HEADERS, OptionalHeader) +
nt_headers.FileHeader.SizeOfOptionalHeader; nt_headers.FileHeader.SizeOfOptionalHeader;

View File

@ -21,6 +21,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "util/misc/initialization_state_dcheck.h" #include "util/misc/initialization_state_dcheck.h"
#include "util/misc/uuid.h"
#include "util/win/address_types.h" #include "util/win/address_types.h"
#include "util/win/checked_win_address_range.h" #include "util/win/checked_win_address_range.h"
@ -93,7 +94,21 @@ class PEImageReader {
//! messages. Other failures will result in messages being logged. //! messages. Other failures will result in messages being logged.
bool GetCrashpadInfo(process_types::CrashpadInfo* crashpad_info) const; 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: 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. //! \brief Finds a given section by name in the image.
bool GetSectionByName(const std::string& name, bool GetSectionByName(const std::string& name,
IMAGE_SECTION_HEADER* section) const; IMAGE_SECTION_HEADER* section) const;

View File

@ -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 <psapi.h>
#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<HMODULE>(&__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<WinVMAddress>(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

View File

@ -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

131
util/misc/pdb_structures.h Normal file
View File

@ -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 <a
//! href="http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles">Matching
//! Debug Information</a>, PDB Files, and <a
//! href="http://undocumented.rawol.com/sbs-w2k-1-windows-2000-debugging-support.pdf#page=63">Undocumented
//! Windows 2000 Secrets</a>, 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` files 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 <a
//! href="http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles">Matching
//! Debug Information</a>, 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` files unique identifier.
UUID uuid;
//! \brief The revision of the `.pdb` file.
//!
//! A `.pdb` files 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_

View File

@ -91,6 +91,8 @@
'misc/initialization_state.h', 'misc/initialization_state.h',
'misc/initialization_state_dcheck.cc', 'misc/initialization_state_dcheck.cc',
'misc/initialization_state_dcheck.h', 'misc/initialization_state_dcheck.h',
'misc/pdb_structures.cc',
'misc/pdb_structures.h',
'misc/scoped_forbid_return.cc', 'misc/scoped_forbid_return.cc',
'misc/scoped_forbid_return.h', 'misc/scoped_forbid_return.h',
'misc/symbolic_constants_common.h', 'misc/symbolic_constants_common.h',