[minidump] Add support for RVA64 / MINIDUMP_LOCATION_DESCRIPTOR64

Newer minidump stream types, like MINIDUMP_THREAD_NAME_LIST, use
64-bit RVAs (which have 64-bit location descriptors) instead of 32-bit
RVAs and location descriptors.

This adds support to MinidumpWritable for the new 64-bit RVA64 and
MINIDUMP_LOCATION_DESCRIPTOR64 types.

Bug: crashpad:327
Change-Id: Icd67bca600756a68ef9ba7d5a429f935eebf726f
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3673776
Commit-Queue: Ben Hamilton <benhamilton@google.com>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Ben Hamilton 2022-05-31 11:21:19 -06:00 committed by Crashpad LUCI CQ
parent b1ffe93562
commit a903f7541f
4 changed files with 254 additions and 86 deletions

View File

@ -42,6 +42,15 @@
//! \sa MINIDUMP_LOCATION_DESCRIPTOR
typedef uint32_t RVA;
//! \brief A 64-bit offset within a minidump file, relative to the start of its
//! MINIDUMP_HEADER.
//!
//! RVA stands for “relative virtual address”. Within a minidump file, RVAs are
//! used as pointers to link structures together.
//!
//! \sa MINIDUMP_LOCATION_DESCRIPTOR64
typedef uint64_t RVA64;
//! \brief A pointer to a structure or union within a minidump file.
struct __attribute__((packed, aligned(4))) MINIDUMP_LOCATION_DESCRIPTOR {
//! \brief The size of the referenced structure or union, in bytes.
@ -52,6 +61,16 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_LOCATION_DESCRIPTOR {
RVA Rva;
};
//! \brief A 64-bit pointer to a structure or union within a minidump file.
struct __attribute__((packed, aligned(4))) MINIDUMP_LOCATION_DESCRIPTOR64 {
//! \brief The size of the referenced structure or union, in bytes.
uint64_t DataSize;
//! \brief The 64-bit relative virtual address of the structure or union
//! within the minidump file.
RVA64 Rva;
};
//! \brief A pointer to a snapshot of a region of memory contained within a
//! minidump file.
//!

View File

@ -75,6 +75,12 @@ void MinidumpWritable::RegisterRVA(RVA* rva) {
registered_rvas_.push_back(rva);
}
void MinidumpWritable::RegisterRVA(RVA64* rva64) {
DCHECK_LE(state_, kStateFrozen);
registered_rva64s_.push_back(rva64);
}
void MinidumpWritable::RegisterLocationDescriptor(
MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor) {
DCHECK_LE(state_, kStateFrozen);
@ -82,12 +88,20 @@ void MinidumpWritable::RegisterLocationDescriptor(
registered_location_descriptors_.push_back(location_descriptor);
}
void MinidumpWritable::RegisterLocationDescriptor(
MINIDUMP_LOCATION_DESCRIPTOR64* location_descriptor64) {
DCHECK_LE(state_, kStateFrozen);
registered_location_descriptor64s_.push_back(location_descriptor64);
}
MinidumpWritable::MinidumpWritable()
: registered_rvas_(),
registered_rva64s_(),
registered_location_descriptors_(),
registered_location_descriptor64s_(),
leading_pad_bytes_(0),
state_(kStateMutable) {
}
state_(kStateMutable) {}
bool MinidumpWritable::Freeze() {
DCHECK_EQ(state_, kStateMutable);
@ -160,10 +174,10 @@ size_t MinidumpWritable::WillWriteAtOffset(
return kInvalidSize;
}
// Populate the RVA fields in other objects that have registered to point to
// this one. Typically, a parent object will have registered to point to its
// children, but this can also occur where no parent-child relationship
// exists.
// Populate the 32-bit RVA fields in other objects that have registered to
// point to this one. Typically, a parent object will have registered to
// point to its children, but this can also occur where no parent-child
// relationship exists.
if (!registered_rvas_.empty() ||
!registered_location_descriptors_.empty()) {
RVA local_rva;
@ -191,12 +205,45 @@ size_t MinidumpWritable::WillWriteAtOffset(
}
}
// This object is now considered writable. However, if it contains RVA or
// MINIDUMP_LOCATION_DESCRIPTOR fields, they may not be fully updated yet,
// because its the repsonsibility of these fields pointees to update them.
// Once WillWriteAtOffset has completed running for both phases on an entire
// tree, and the entire tree has moved into kStateFrozen, all RVA and
// MINIDUMP_LOCATION_DESCRIPTOR fields within that tree will be populated.
// Populate the 64-bit RVA fields in other objects that have registered to
// point to this one. Typically, a parent object will have registered to
// point to its children, but this can also occur where no parent-child
// relationship exists.
if (!registered_rva64s_.empty() ||
!registered_location_descriptor64s_.empty()) {
RVA64 local_rva64;
if (!AssignIfInRange(&local_rva64, local_offset)) {
LOG(ERROR) << "offset " << local_offset << " out of range";
return kInvalidSize;
}
for (RVA64* rva64 : registered_rva64s_) {
*rva64 = local_rva64;
}
if (!registered_location_descriptor64s_.empty()) {
decltype(registered_location_descriptor64s_[0]->DataSize) local_size;
if (!AssignIfInRange(&local_size, size)) {
LOG(ERROR) << "size " << size << " out of range";
return kInvalidSize;
}
for (MINIDUMP_LOCATION_DESCRIPTOR64* location_descriptor :
registered_location_descriptor64s_) {
location_descriptor->DataSize = local_size;
location_descriptor->Rva = local_rva64;
}
}
}
// This object is now considered writable. However, if it contains RVA/RVA64
// or MINIDUMP_LOCATION_DESCRIPTOR/MINIDUMP_LOCATION_DESCRIPTOR64 fields,
// they may not be fully updated yet, because its the repsonsibility of
// these fields pointees to update them. Once WillWriteAtOffset has
// completed running for both phases on an entire tree, and the entire tree
// has moved into kStateFrozen, all RVA/RVA64 and
// MINIDUMP_LOCATION_DESCRIPTOR/MINIDUMP_LOCATION_DESCRIPTOR64 fields within
// that tree will be populated.
state_ = kStateWritable;
} else {
if (phase == kPhaseEarly) {

View File

@ -71,6 +71,9 @@ class MinidumpWritable {
// to be able to register their own pointers with distinct objects.
void RegisterRVA(RVA* rva);
//! \brief 64-bit specialization of RegisterRVA.
void RegisterRVA(RVA64* rva);
//! \brief Registers a location descriptor as one that should point to the
//! object on which this method is called.
//!
@ -89,6 +92,10 @@ class MinidumpWritable {
void RegisterLocationDescriptor(
MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor);
//! \brief 64-bit specialization of RegisterLocationDescriptor.
void RegisterLocationDescriptor(
MINIDUMP_LOCATION_DESCRIPTOR64* location_descriptor64);
protected:
//! \brief Identifies the state of an object.
//!
@ -267,9 +274,15 @@ class MinidumpWritable {
private:
std::vector<RVA*> registered_rvas_; // weak
std::vector<RVA64*> registered_rva64s_; // weak
// weak
std::vector<MINIDUMP_LOCATION_DESCRIPTOR*> registered_location_descriptors_;
// weak
std::vector<MINIDUMP_LOCATION_DESCRIPTOR64*>
registered_location_descriptor64s_;
size_t leading_pad_bytes_;
State state_;
};

View File

@ -485,14 +485,15 @@ TEST(MinidumpWritable, MinidumpWritable) {
}
}
class TestRVAMinidumpWritable final : public BaseTestMinidumpWritable {
template <typename RVAType>
class TTestRVAMinidumpWritable final : public BaseTestMinidumpWritable {
public:
TestRVAMinidumpWritable() : BaseTestMinidumpWritable(), rva_() {}
TTestRVAMinidumpWritable() : BaseTestMinidumpWritable(), rva_() {}
TestRVAMinidumpWritable(const TestRVAMinidumpWritable&) = delete;
TestRVAMinidumpWritable& operator=(const TestRVAMinidumpWritable&) = delete;
TTestRVAMinidumpWritable(const TTestRVAMinidumpWritable&) = delete;
TTestRVAMinidumpWritable& operator=(const TTestRVAMinidumpWritable&) = delete;
~TestRVAMinidumpWritable() {}
~TTestRVAMinidumpWritable() {}
void SetRVA(MinidumpWritable* other) { other->RegisterRVA(&rva_); }
@ -509,15 +510,46 @@ class TestRVAMinidumpWritable final : public BaseTestMinidumpWritable {
}
private:
RVA rva_;
RVAType rva_;
};
RVA RVAAtIndex(const std::string& string, size_t index) {
return *reinterpret_cast<const RVA*>(&string[index * sizeof(RVA)]);
template <typename RVAType>
RVAType TRVAAtIndex(const std::string& string, size_t index) {
return *reinterpret_cast<const RVAType*>(&string[index * sizeof(RVAType)]);
}
TEST(MinidumpWritable, RVA) {
template <typename T>
struct TestNames {};
template <>
struct TestNames<RVA> {
static constexpr char kName[] = "RVA";
};
template <>
struct TestNames<RVA64> {
static constexpr char kName[] = "RVA64";
};
class TestTypeNames {
public:
template <typename T>
static std::string GetName(int) {
return std::string(TestNames<T>::kName);
}
};
template <typename RVAType>
class MinidumpWritable : public ::testing::Test {};
using RVATypes = ::testing::Types<RVA, RVA64>;
TYPED_TEST_SUITE(MinidumpWritable, RVATypes, TestTypeNames);
TYPED_TEST(MinidumpWritable, RVA) {
StringFile string_file;
using TestRVAMinidumpWritable = TTestRVAMinidumpWritable<TypeParam>;
const auto RVAAtIndex = TRVAAtIndex<TypeParam>;
constexpr size_t kRVASize = sizeof(TypeParam);
{
SCOPED_TRACE("unset");
@ -525,8 +557,8 @@ TEST(MinidumpWritable, RVA) {
TestRVAMinidumpWritable rva_writable;
EXPECT_TRUE(rva_writable.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), sizeof(RVA));
EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * sizeof(RVA));
ASSERT_EQ(string_file.string().size(), kRVASize);
EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * kRVASize);
rva_writable.Verify();
}
@ -537,8 +569,8 @@ TEST(MinidumpWritable, RVA) {
rva_writable.SetRVA(&rva_writable);
EXPECT_TRUE(rva_writable.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), sizeof(RVA));
EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * sizeof(RVA));
ASSERT_EQ(string_file.string().size(), kRVASize);
EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * kRVASize);
rva_writable.Verify();
}
@ -552,9 +584,9 @@ TEST(MinidumpWritable, RVA) {
parent.AddChild(&child);
EXPECT_TRUE(parent.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), 2 * sizeof(RVA));
EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * sizeof(RVA));
EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 1 * sizeof(RVA));
ASSERT_EQ(string_file.string().size(), 2 * kRVASize);
EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * kRVASize);
EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 1 * kRVASize);
parent.Verify();
}
@ -567,9 +599,9 @@ TEST(MinidumpWritable, RVA) {
parent.AddChild(&child);
EXPECT_TRUE(parent.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), 2 * sizeof(RVA));
EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * sizeof(RVA));
EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * sizeof(RVA));
ASSERT_EQ(string_file.string().size(), 2 * kRVASize);
EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * kRVASize);
EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * kRVASize);
parent.Verify();
}
@ -583,9 +615,9 @@ TEST(MinidumpWritable, RVA) {
parent.AddChild(&child);
EXPECT_TRUE(parent.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), 2 * sizeof(RVA));
EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * sizeof(RVA));
EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * sizeof(RVA));
ASSERT_EQ(string_file.string().size(), 2 * kRVASize);
EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * kRVASize);
EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * kRVASize);
parent.Verify();
}
@ -607,28 +639,29 @@ TEST(MinidumpWritable, RVA) {
child.AddChild(&grandchild_2);
EXPECT_TRUE(parent.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), 5 * sizeof(RVA));
EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * sizeof(RVA));
EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * sizeof(RVA));
EXPECT_EQ(RVAAtIndex(string_file.string(), 2), 1 * sizeof(RVA));
EXPECT_EQ(RVAAtIndex(string_file.string(), 3), 1 * sizeof(RVA));
EXPECT_EQ(RVAAtIndex(string_file.string(), 4), 1 * sizeof(RVA));
ASSERT_EQ(string_file.string().size(), 5 * kRVASize);
EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * kRVASize);
EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * kRVASize);
EXPECT_EQ(RVAAtIndex(string_file.string(), 2), 1 * kRVASize);
EXPECT_EQ(RVAAtIndex(string_file.string(), 3), 1 * kRVASize);
EXPECT_EQ(RVAAtIndex(string_file.string(), 4), 1 * kRVASize);
parent.Verify();
}
}
class TestLocationDescriptorMinidumpWritable final
template <typename MinidumpLocationDescriptorType>
class TTestLocationDescriptorMinidumpWritable final
: public BaseTestMinidumpWritable {
public:
TestLocationDescriptorMinidumpWritable()
TTestLocationDescriptorMinidumpWritable()
: BaseTestMinidumpWritable(), location_descriptor_(), string_() {}
TestLocationDescriptorMinidumpWritable(
const TestLocationDescriptorMinidumpWritable&) = delete;
TestLocationDescriptorMinidumpWritable& operator=(
const TestLocationDescriptorMinidumpWritable&) = delete;
TTestLocationDescriptorMinidumpWritable(
const TTestLocationDescriptorMinidumpWritable&) = delete;
TTestLocationDescriptorMinidumpWritable& operator=(
const TTestLocationDescriptorMinidumpWritable&) = delete;
~TestLocationDescriptorMinidumpWritable() {}
~TTestLocationDescriptorMinidumpWritable() {}
void SetLocationDescriptor(MinidumpWritable* other) {
other->RegisterLocationDescriptor(&location_descriptor_);
@ -658,30 +691,61 @@ class TestLocationDescriptorMinidumpWritable final
}
private:
MINIDUMP_LOCATION_DESCRIPTOR location_descriptor_;
MinidumpLocationDescriptorType location_descriptor_;
std::string string_;
};
struct LocationDescriptorAndData {
MINIDUMP_LOCATION_DESCRIPTOR location_descriptor;
template <typename MinidumpLocationDescriptorType>
struct TLocationDescriptorAndData {
MinidumpLocationDescriptorType location_descriptor;
char string[1];
};
const LocationDescriptorAndData* LDDAtIndex(const std::string& string,
size_t index) {
return reinterpret_cast<const LocationDescriptorAndData*>(&string[index]);
template <typename MinidumpLocationDescriptorType>
const TLocationDescriptorAndData<MinidumpLocationDescriptorType>* TLDDAtIndex(
const std::string& string,
size_t index) {
return reinterpret_cast<
const TLocationDescriptorAndData<MinidumpLocationDescriptorType>*>(
&string[index]);
}
TEST(MinidumpWritable, LocationDescriptor) {
template <typename MinidumpLocationDescriptorType>
class MinidumpWritableLocationDescriptor : public ::testing::Test {};
template <>
struct TestNames<MINIDUMP_LOCATION_DESCRIPTOR> {
static constexpr char kName[] = "MINIDUMP_LOCATION_DESCRIPTOR";
};
template <>
struct TestNames<MINIDUMP_LOCATION_DESCRIPTOR64> {
static constexpr char kName[] = "MINIDUMP_LOCATION_DESCRIPTOR64";
};
using MinidumpLocationDescriptorTypes =
::testing::Types<MINIDUMP_LOCATION_DESCRIPTOR,
MINIDUMP_LOCATION_DESCRIPTOR64>;
TYPED_TEST_SUITE(MinidumpWritableLocationDescriptor,
MinidumpLocationDescriptorTypes,
TestTypeNames);
TYPED_TEST(MinidumpWritableLocationDescriptor, LocationDescriptor) {
StringFile string_file;
using LocationDescriptorAndData = TLocationDescriptorAndData<TypeParam>;
const auto LDDAtIndex = TLDDAtIndex<TypeParam>;
using TestLocationDescriptorMinidumpWritable =
TTestLocationDescriptorMinidumpWritable<TypeParam>;
constexpr size_t kMinidumpLocationDescriptorSize = sizeof(TypeParam);
{
SCOPED_TRACE("unset");
string_file.Reset();
TestLocationDescriptorMinidumpWritable location_descriptor_writable;
EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), 9u);
ASSERT_EQ(string_file.string().size(), kMinidumpLocationDescriptorSize + 1);
const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
EXPECT_EQ(ldd->location_descriptor.DataSize, 0u);
EXPECT_EQ(ldd->location_descriptor.Rva, 0u);
@ -696,9 +760,10 @@ TEST(MinidumpWritable, LocationDescriptor) {
&location_descriptor_writable);
EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), 9u);
ASSERT_EQ(string_file.string().size(), kMinidumpLocationDescriptorSize + 1);
const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
EXPECT_EQ(ldd->location_descriptor.DataSize, 9u);
EXPECT_EQ(ldd->location_descriptor.DataSize,
kMinidumpLocationDescriptorSize + 1);
EXPECT_EQ(ldd->location_descriptor.Rva, 0u);
location_descriptor_writable.Verify();
}
@ -712,9 +777,10 @@ TEST(MinidumpWritable, LocationDescriptor) {
location_descriptor_writable.SetString("zz");
EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), 11u);
ASSERT_EQ(string_file.string().size(), kMinidumpLocationDescriptorSize + 3);
const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
EXPECT_EQ(ldd->location_descriptor.DataSize, 11u);
EXPECT_EQ(ldd->location_descriptor.DataSize,
kMinidumpLocationDescriptorSize + 3);
EXPECT_EQ(ldd->location_descriptor.Rva, 0u);
EXPECT_STREQ("zz", ldd->string);
location_descriptor_writable.Verify();
@ -732,14 +798,18 @@ TEST(MinidumpWritable, LocationDescriptor) {
parent.AddChild(&child);
EXPECT_TRUE(parent.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), 22u);
ASSERT_EQ(string_file.string().size(),
kMinidumpLocationDescriptorSize * 2 + 6);
const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
EXPECT_EQ(ldd->location_descriptor.DataSize, 11u);
EXPECT_EQ(ldd->location_descriptor.DataSize,
kMinidumpLocationDescriptorSize + 3);
EXPECT_EQ(ldd->location_descriptor.Rva, 0u);
EXPECT_STREQ("yy", ldd->string);
ldd = LDDAtIndex(string_file.string(), 12);
EXPECT_EQ(ldd->location_descriptor.DataSize, 10u);
EXPECT_EQ(ldd->location_descriptor.Rva, 12u);
ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize + 4);
EXPECT_EQ(ldd->location_descriptor.DataSize,
kMinidumpLocationDescriptorSize + 2);
EXPECT_EQ(ldd->location_descriptor.Rva,
kMinidumpLocationDescriptorSize + 4);
EXPECT_STREQ("x", ldd->string);
parent.Verify();
}
@ -755,12 +825,15 @@ TEST(MinidumpWritable, LocationDescriptor) {
parent.AddChild(&child);
EXPECT_TRUE(parent.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), 23u);
ASSERT_EQ(string_file.string().size(),
kMinidumpLocationDescriptorSize * 2 + 7);
const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
EXPECT_EQ(ldd->location_descriptor.DataSize, 11u);
EXPECT_EQ(ldd->location_descriptor.Rva, 12u);
EXPECT_EQ(ldd->location_descriptor.DataSize,
kMinidumpLocationDescriptorSize + 3);
EXPECT_EQ(ldd->location_descriptor.Rva,
kMinidumpLocationDescriptorSize + 4);
EXPECT_STREQ("www", ldd->string);
ldd = LDDAtIndex(string_file.string(), 12);
ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize + 4);
EXPECT_EQ(ldd->location_descriptor.DataSize, 0u);
EXPECT_EQ(ldd->location_descriptor.Rva, 0u);
EXPECT_STREQ("vv", ldd->string);
@ -779,13 +852,17 @@ TEST(MinidumpWritable, LocationDescriptor) {
parent.AddChild(&child);
EXPECT_TRUE(parent.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), 29u);
ASSERT_EQ(string_file.string().size(),
kMinidumpLocationDescriptorSize * 2 + 13);
const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
EXPECT_EQ(ldd->location_descriptor.DataSize, 13u);
EXPECT_EQ(ldd->location_descriptor.Rva, 16u);
EXPECT_EQ(ldd->location_descriptor.DataSize,
kMinidumpLocationDescriptorSize + 5);
EXPECT_EQ(ldd->location_descriptor.Rva,
kMinidumpLocationDescriptorSize + 8);
EXPECT_STREQ("uuuu", ldd->string);
ldd = LDDAtIndex(string_file.string(), 16);
EXPECT_EQ(ldd->location_descriptor.DataSize, 13u);
ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize + 8);
EXPECT_EQ(ldd->location_descriptor.DataSize,
kMinidumpLocationDescriptorSize + 5);
EXPECT_EQ(ldd->location_descriptor.Rva, 0u);
EXPECT_STREQ("tttt", ldd->string);
parent.Verify();
@ -814,26 +891,38 @@ TEST(MinidumpWritable, LocationDescriptor) {
child.AddChild(&grandchild_2);
EXPECT_TRUE(parent.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), 58u);
ASSERT_EQ(string_file.string().size(),
kMinidumpLocationDescriptorSize * 5 + 18);
const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
EXPECT_EQ(ldd->location_descriptor.DataSize, 10u);
EXPECT_EQ(ldd->location_descriptor.Rva, 12u);
EXPECT_EQ(ldd->location_descriptor.DataSize,
kMinidumpLocationDescriptorSize + 2);
EXPECT_EQ(ldd->location_descriptor.Rva,
kMinidumpLocationDescriptorSize + 4);
EXPECT_STREQ("s", ldd->string);
ldd = LDDAtIndex(string_file.string(), 12);
ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize + 4);
EXPECT_EQ(ldd->location_descriptor.DataSize, 0u);
EXPECT_EQ(ldd->location_descriptor.Rva, 0u);
EXPECT_STREQ("r", ldd->string);
ldd = LDDAtIndex(string_file.string(), 24);
EXPECT_EQ(ldd->location_descriptor.DataSize, 10u);
EXPECT_EQ(ldd->location_descriptor.Rva, 12u);
ldd = LDDAtIndex(string_file.string(),
kMinidumpLocationDescriptorSize * 2 + 8);
EXPECT_EQ(ldd->location_descriptor.DataSize,
kMinidumpLocationDescriptorSize + 2);
EXPECT_EQ(ldd->location_descriptor.Rva,
kMinidumpLocationDescriptorSize + 4);
EXPECT_STREQ("q", ldd->string);
ldd = LDDAtIndex(string_file.string(), 36);
EXPECT_EQ(ldd->location_descriptor.DataSize, 10u);
EXPECT_EQ(ldd->location_descriptor.Rva, 12u);
ldd = LDDAtIndex(string_file.string(),
kMinidumpLocationDescriptorSize * 3 + 12);
EXPECT_EQ(ldd->location_descriptor.DataSize,
kMinidumpLocationDescriptorSize + 2);
EXPECT_EQ(ldd->location_descriptor.Rva,
kMinidumpLocationDescriptorSize + 4);
EXPECT_STREQ("p", ldd->string);
ldd = LDDAtIndex(string_file.string(), 48);
EXPECT_EQ(ldd->location_descriptor.DataSize, 10u);
EXPECT_EQ(ldd->location_descriptor.Rva, 12u);
ldd = LDDAtIndex(string_file.string(),
kMinidumpLocationDescriptorSize * 4 + 16);
EXPECT_EQ(ldd->location_descriptor.DataSize,
kMinidumpLocationDescriptorSize + 2);
EXPECT_EQ(ldd->location_descriptor.Rva,
kMinidumpLocationDescriptorSize + 4);
EXPECT_STREQ("o", ldd->string);
parent.Verify();
}