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;
//! \brief A pointer to the modules 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 <a
//! crashpad::CodeViewRecordPDB70 structure, but may be a
//! crashpad::CodeViewRecordPDB20 structure instead. These structures identify
//! a link to debugging data within a `.pdb` (Program Database) file. See <a
//! href="http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles">Matching
//! 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
//! 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`.

View File

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

View File

@ -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 <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.
struct ALIGNAS(4) PACKED MinidumpRVAList {

View File

@ -77,7 +77,7 @@ bool MinidumpModuleCodeViewRecordPDBLinkWriter<CodeViewRecordType>::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() {

View File

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

View File

@ -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<CodeViewRecordPDB70>(
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<const char*>(&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<CodeViewRecordPDB20>(
file_contents, *codeview_record);
ASSERT_TRUE(codeview_pdb20_record);
EXPECT_EQ(static_cast<uint32_t>(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<const char*>(&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.

View File

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

View File

@ -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,15 +190,13 @@ const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
const MinidumpModuleCodeViewRecordPDB20*
MinidumpWritableAtLocationDescriptor<MinidumpModuleCodeViewRecordPDB20>(
const std::string& file_contents,
const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor<
CodeViewRecordPDB20>(const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
const MinidumpModuleCodeViewRecordPDB70*
MinidumpWritableAtLocationDescriptor<MinidumpModuleCodeViewRecordPDB70>(
const std::string& file_contents,
const CodeViewRecordPDB70* MinidumpWritableAtLocationDescriptor<
CodeViewRecordPDB70>(const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>

View File

@ -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',
],

View File

@ -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<std::string> ModuleSnapshotWin::AnnotationsVector() const {

View File

@ -17,9 +17,11 @@
#include <string.h>
#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<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;
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;

View File

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

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_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',