mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-27 15:32:10 +08:00
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:
parent
78bba8808b
commit
3ef04d14f2
@ -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 <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 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`.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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` 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 <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` 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 {
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -291,21 +291,19 @@ const T* MinidumpCVPDBAtLocationDescriptor(
|
||||
} // namespace
|
||||
|
||||
template <>
|
||||
const MinidumpModuleCodeViewRecordPDB20*
|
||||
MinidumpWritableAtLocationDescriptor<MinidumpModuleCodeViewRecordPDB20>(
|
||||
const std::string& file_contents,
|
||||
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
|
||||
return MinidumpCVPDBAtLocationDescriptor<MinidumpModuleCodeViewRecordPDB20>(
|
||||
file_contents, location);
|
||||
const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor<
|
||||
CodeViewRecordPDB20>(const std::string& file_contents,
|
||||
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
|
||||
return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB20>(file_contents,
|
||||
location);
|
||||
}
|
||||
|
||||
template <>
|
||||
const MinidumpModuleCodeViewRecordPDB70*
|
||||
MinidumpWritableAtLocationDescriptor<MinidumpModuleCodeViewRecordPDB70>(
|
||||
const std::string& file_contents,
|
||||
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
|
||||
return MinidumpCVPDBAtLocationDescriptor<MinidumpModuleCodeViewRecordPDB70>(
|
||||
file_contents, location);
|
||||
const CodeViewRecordPDB70* MinidumpWritableAtLocationDescriptor<
|
||||
CodeViewRecordPDB70>(const std::string& file_contents,
|
||||
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
|
||||
return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB70>(file_contents,
|
||||
location);
|
||||
}
|
||||
|
||||
TestUInt32MinidumpWritable::TestUInt32MinidumpWritable(uint32_t value)
|
||||
|
@ -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<MinidumpModuleCodeViewRecordPDB20>(
|
||||
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<MinidumpModuleCodeViewRecordPDB70>(
|
||||
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*
|
||||
|
@ -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',
|
||||
],
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
54
snapshot/win/pe_image_reader_test.cc
Normal file
54
snapshot/win/pe_image_reader_test.cc
Normal 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
|
22
util/misc/pdb_structures.cc
Normal file
22
util/misc/pdb_structures.cc
Normal 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
131
util/misc/pdb_structures.h
Normal 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` 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 <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` 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_
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user