2010-02-09 18:08:56 +01:00
|
|
|
// Copyright (c) 2010 Google Inc.
|
2006-09-20 23:16:16 +02:00
|
|
|
// All rights reserved.
|
2006-09-06 04:56:44 +02:00
|
|
|
//
|
2006-09-20 23:16:16 +02:00
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
|
// modification, are permitted provided that the following conditions are
|
|
|
|
|
// met:
|
2006-09-06 04:56:44 +02:00
|
|
|
//
|
2006-09-20 23:16:16 +02:00
|
|
|
// * Redistributions of source code must retain the above copyright
|
|
|
|
|
// notice, this list of conditions and the following disclaimer.
|
|
|
|
|
// * Redistributions in binary form must reproduce the above
|
|
|
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
|
|
|
// in the documentation and/or other materials provided with the
|
|
|
|
|
// distribution.
|
|
|
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
|
|
|
// contributors may be used to endorse or promote products derived from
|
|
|
|
|
// this software without specific prior written permission.
|
2006-09-06 04:56:44 +02:00
|
|
|
//
|
2006-09-20 23:16:16 +02:00
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
// minidump.cc: A minidump reader.
|
|
|
|
|
//
|
|
|
|
|
// See minidump.h for documentation.
|
|
|
|
|
//
|
|
|
|
|
// Author: Mark Mentovai
|
|
|
|
|
|
2010-06-25 18:57:07 +02:00
|
|
|
#include "google_breakpad/processor/minidump.h"
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2010-06-25 18:57:07 +02:00
|
|
|
#include <assert.h>
|
2006-09-08 18:41:17 +02:00
|
|
|
#include <fcntl.h>
|
2013-03-06 15:06:52 +01:00
|
|
|
#include <stddef.h>
|
2006-09-06 04:56:44 +02:00
|
|
|
#include <stdio.h>
|
2008-09-15 20:16:49 +02:00
|
|
|
#include <string.h>
|
2006-09-06 04:56:44 +02:00
|
|
|
#include <time.h>
|
2011-01-11 21:27:29 +01:00
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
|
#include <io.h>
|
2011-01-11 21:27:29 +01:00
|
|
|
#define PRIx64 "llx"
|
|
|
|
|
#define PRIx32 "lx"
|
|
|
|
|
#define snprintf _snprintf
|
2006-10-25 23:25:41 +02:00
|
|
|
#else // _WIN32
|
2011-01-11 21:27:29 +01:00
|
|
|
#include <unistd.h>
|
2006-10-25 23:25:41 +02:00
|
|
|
#endif // _WIN32
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2009-12-09 02:24:37 +01:00
|
|
|
#include <fstream>
|
|
|
|
|
#include <iostream>
|
2007-05-21 23:02:04 +02:00
|
|
|
#include <limits>
|
2006-09-06 04:56:44 +02:00
|
|
|
#include <map>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
2006-09-20 18:20:15 +02:00
|
|
|
#include "processor/range_map-inl.h"
|
2006-10-25 23:25:41 +02:00
|
|
|
|
2013-01-17 16:53:56 +01:00
|
|
|
#include "common/scoped_ptr.h"
|
2014-09-08 21:10:42 +02:00
|
|
|
#include "google_breakpad/processor/dump_context.h"
|
2006-12-05 23:52:28 +01:00
|
|
|
#include "processor/basic_code_module.h"
|
|
|
|
|
#include "processor/basic_code_modules.h"
|
2007-05-17 20:34:37 +02:00
|
|
|
#include "processor/logging.h"
|
2013-01-17 16:53:56 +01:00
|
|
|
|
2007-02-14 20:51:05 +01:00
|
|
|
namespace google_breakpad {
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
|
2009-12-09 02:24:37 +01:00
|
|
|
using std::istream;
|
|
|
|
|
using std::ifstream;
|
2007-05-21 23:02:04 +02:00
|
|
|
using std::numeric_limits;
|
2006-09-06 04:56:44 +02:00
|
|
|
using std::vector;
|
|
|
|
|
|
2013-11-23 02:45:20 +01:00
|
|
|
// Returns true iff |context_size| matches exactly one of the sizes of the
|
|
|
|
|
// various MDRawContext* types.
|
|
|
|
|
// TODO(blundell): This function can be removed once
|
|
|
|
|
// http://code.google.com/p/google-breakpad/issues/detail?id=550 is fixed.
|
|
|
|
|
static bool IsContextSizeUnique(uint32_t context_size) {
|
|
|
|
|
int num_matching_contexts = 0;
|
|
|
|
|
if (context_size == sizeof(MDRawContextX86))
|
|
|
|
|
num_matching_contexts++;
|
|
|
|
|
if (context_size == sizeof(MDRawContextPPC))
|
|
|
|
|
num_matching_contexts++;
|
|
|
|
|
if (context_size == sizeof(MDRawContextPPC64))
|
|
|
|
|
num_matching_contexts++;
|
|
|
|
|
if (context_size == sizeof(MDRawContextAMD64))
|
|
|
|
|
num_matching_contexts++;
|
|
|
|
|
if (context_size == sizeof(MDRawContextSPARC))
|
|
|
|
|
num_matching_contexts++;
|
|
|
|
|
if (context_size == sizeof(MDRawContextARM))
|
|
|
|
|
num_matching_contexts++;
|
|
|
|
|
if (context_size == sizeof(MDRawContextARM64))
|
|
|
|
|
num_matching_contexts++;
|
|
|
|
|
if (context_size == sizeof(MDRawContextMIPS))
|
|
|
|
|
num_matching_contexts++;
|
|
|
|
|
return num_matching_contexts == 1;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Swapping routines
|
|
|
|
|
//
|
|
|
|
|
// Inlining these doesn't increase code size significantly, and it saves
|
|
|
|
|
// a whole lot of unnecessary jumping back and forth.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Swapping an 8-bit quantity is a no-op. This function is only provided
|
|
|
|
|
// to account for certain templatized operations that require swapping for
|
2013-03-06 15:04:42 +01:00
|
|
|
// wider types but handle uint8_t too
|
2006-09-06 04:56:44 +02:00
|
|
|
// (MinidumpMemoryRegion::GetMemoryAtAddressInternal).
|
2013-03-06 15:04:42 +01:00
|
|
|
static inline void Swap(uint8_t* value) {
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Optimization: don't need to AND the furthest right shift, because we're
|
|
|
|
|
// shifting an unsigned quantity. The standard requires zero-filling in this
|
|
|
|
|
// case. If the quantities were signed, a bitmask whould be needed for this
|
|
|
|
|
// right shift to avoid an arithmetic shift (which retains the sign bit).
|
|
|
|
|
// The furthest left shift never needs to be ANDed bitmask.
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
static inline void Swap(uint16_t* value) {
|
2006-09-06 04:56:44 +02:00
|
|
|
*value = (*value >> 8) |
|
|
|
|
|
(*value << 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
static inline void Swap(uint32_t* value) {
|
2006-09-06 04:56:44 +02:00
|
|
|
*value = (*value >> 24) |
|
|
|
|
|
((*value >> 8) & 0x0000ff00) |
|
|
|
|
|
((*value << 8) & 0x00ff0000) |
|
|
|
|
|
(*value << 24);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
static inline void Swap(uint64_t* value) {
|
|
|
|
|
uint32_t* value32 = reinterpret_cast<uint32_t*>(value);
|
2007-05-31 21:44:52 +02:00
|
|
|
Swap(&value32[0]);
|
|
|
|
|
Swap(&value32[1]);
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t temp = value32[0];
|
2007-05-31 21:44:52 +02:00
|
|
|
value32[0] = value32[1];
|
|
|
|
|
value32[1] = temp;
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-02-07 05:24:03 +01:00
|
|
|
// Given a pointer to a 128-bit int in the minidump data, set the "low"
|
|
|
|
|
// and "high" fields appropriately.
|
2013-03-06 15:04:42 +01:00
|
|
|
static void Normalize128(uint128_struct* value, bool is_big_endian) {
|
2007-02-07 05:24:03 +01:00
|
|
|
// The struct format is [high, low], so if the format is big-endian,
|
|
|
|
|
// the most significant bytes will already be in the high field.
|
|
|
|
|
if (!is_big_endian) {
|
2013-03-06 15:04:42 +01:00
|
|
|
uint64_t temp = value->low;
|
2007-02-07 05:24:03 +01:00
|
|
|
value->low = value->high;
|
|
|
|
|
value->high = temp;
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2007-02-07 05:24:03 +01:00
|
|
|
// This just swaps each int64 half of the 128-bit value.
|
|
|
|
|
// The value should also be normalized by calling Normalize128().
|
2013-03-06 15:04:42 +01:00
|
|
|
static void Swap(uint128_struct* value) {
|
2007-02-07 05:24:03 +01:00
|
|
|
Swap(&value->low);
|
|
|
|
|
Swap(&value->high);
|
2006-09-22 03:10:25 +02:00
|
|
|
}
|
|
|
|
|
|
2013-08-02 20:15:57 +02:00
|
|
|
// Swapping signed integers
|
|
|
|
|
static inline void Swap(int16_t* value) {
|
|
|
|
|
Swap(reinterpret_cast<uint16_t*>(value));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void Swap(int32_t* value) {
|
|
|
|
|
Swap(reinterpret_cast<uint32_t*>(value));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void Swap(int64_t* value) {
|
|
|
|
|
Swap(reinterpret_cast<uint64_t*>(value));
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
static inline void Swap(MDLocationDescriptor* location_descriptor) {
|
|
|
|
|
Swap(&location_descriptor->data_size);
|
|
|
|
|
Swap(&location_descriptor->rva);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline void Swap(MDMemoryDescriptor* memory_descriptor) {
|
|
|
|
|
Swap(&memory_descriptor->start_of_memory_range);
|
|
|
|
|
Swap(&memory_descriptor->memory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline void Swap(MDGUID* guid) {
|
|
|
|
|
Swap(&guid->data1);
|
|
|
|
|
Swap(&guid->data2);
|
|
|
|
|
Swap(&guid->data3);
|
|
|
|
|
// Don't swap guid->data4[] because it contains 8-bit quantities.
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-02 20:15:57 +02:00
|
|
|
static inline void Swap(MDSystemTime* system_time) {
|
|
|
|
|
Swap(&system_time->year);
|
|
|
|
|
Swap(&system_time->month);
|
|
|
|
|
Swap(&system_time->day_of_week);
|
|
|
|
|
Swap(&system_time->day);
|
|
|
|
|
Swap(&system_time->hour);
|
|
|
|
|
Swap(&system_time->minute);
|
|
|
|
|
Swap(&system_time->second);
|
|
|
|
|
Swap(&system_time->milliseconds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void Swap(uint16_t* data, size_t size_in_bytes) {
|
|
|
|
|
size_t data_length = size_in_bytes / sizeof(data[0]);
|
|
|
|
|
for (size_t i = 0; i < data_length; i++) {
|
|
|
|
|
Swap(&data[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Character conversion routines
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Standard wide-character conversion routines depend on the system's own
|
|
|
|
|
// idea of what width a wide character should be: some use 16 bits, and
|
|
|
|
|
// some use 32 bits. For the purposes of a minidump, wide strings are
|
|
|
|
|
// always represented with 16-bit UTF-16 chracters. iconv isn't available
|
|
|
|
|
// everywhere, and its interface varies where it is available. iconv also
|
|
|
|
|
// deals purely with char* pointers, so in addition to considering the swap
|
|
|
|
|
// parameter, a converter that uses iconv would also need to take the host
|
|
|
|
|
// CPU's endianness into consideration. It doesn't seems worth the trouble
|
|
|
|
|
// of making it a dependency when we don't care about anything but UTF-16.
|
2013-03-06 15:04:42 +01:00
|
|
|
static string* UTF16ToUTF8(const vector<uint16_t>& in,
|
2013-08-02 20:15:57 +02:00
|
|
|
bool swap) {
|
2006-10-23 22:25:42 +02:00
|
|
|
scoped_ptr<string> out(new string());
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
// Set the string's initial capacity to the number of UTF-16 characters,
|
|
|
|
|
// because the UTF-8 representation will always be at least this long.
|
|
|
|
|
// If the UTF-8 representation is longer, the string will grow dynamically.
|
|
|
|
|
out->reserve(in.size());
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
for (vector<uint16_t>::const_iterator iterator = in.begin();
|
2006-09-06 04:56:44 +02:00
|
|
|
iterator != in.end();
|
|
|
|
|
++iterator) {
|
|
|
|
|
// Get a 16-bit value from the input
|
2013-03-06 15:04:42 +01:00
|
|
|
uint16_t in_word = *iterator;
|
2006-09-06 04:56:44 +02:00
|
|
|
if (swap)
|
|
|
|
|
Swap(&in_word);
|
|
|
|
|
|
|
|
|
|
// Convert the input value (in_word) into a Unicode code point (unichar).
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t unichar;
|
2006-09-06 04:56:44 +02:00
|
|
|
if (in_word >= 0xdc00 && in_word <= 0xdcff) {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "UTF16ToUTF8 found low surrogate " <<
|
|
|
|
|
HexString(in_word) << " without high";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
|
|
|
|
} else if (in_word >= 0xd800 && in_word <= 0xdbff) {
|
|
|
|
|
// High surrogate.
|
|
|
|
|
unichar = (in_word - 0xd7c0) << 10;
|
|
|
|
|
if (++iterator == in.end()) {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " <<
|
|
|
|
|
HexString(in_word) << " at end of string";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
|
|
|
|
}
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t high_word = in_word;
|
2006-09-06 04:56:44 +02:00
|
|
|
in_word = *iterator;
|
|
|
|
|
if (in_word < 0xdc00 || in_word > 0xdcff) {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " <<
|
|
|
|
|
HexString(high_word) << " without low " <<
|
|
|
|
|
HexString(in_word);
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
unichar |= in_word & 0x03ff;
|
|
|
|
|
} else {
|
|
|
|
|
// The ordinary case, a single non-surrogate Unicode character encoded
|
|
|
|
|
// as a single 16-bit value.
|
|
|
|
|
unichar = in_word;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert the Unicode code point (unichar) into its UTF-8 representation,
|
|
|
|
|
// appending it to the out string.
|
|
|
|
|
if (unichar < 0x80) {
|
2013-06-04 18:51:01 +02:00
|
|
|
(*out) += static_cast<char>(unichar);
|
2006-09-06 04:56:44 +02:00
|
|
|
} else if (unichar < 0x800) {
|
2013-06-04 18:51:01 +02:00
|
|
|
(*out) += 0xc0 | static_cast<char>(unichar >> 6);
|
|
|
|
|
(*out) += 0x80 | static_cast<char>(unichar & 0x3f);
|
2006-09-06 04:56:44 +02:00
|
|
|
} else if (unichar < 0x10000) {
|
2013-06-04 18:51:01 +02:00
|
|
|
(*out) += 0xe0 | static_cast<char>(unichar >> 12);
|
|
|
|
|
(*out) += 0x80 | static_cast<char>((unichar >> 6) & 0x3f);
|
|
|
|
|
(*out) += 0x80 | static_cast<char>(unichar & 0x3f);
|
2006-09-06 04:56:44 +02:00
|
|
|
} else if (unichar < 0x200000) {
|
2013-06-04 18:51:01 +02:00
|
|
|
(*out) += 0xf0 | static_cast<char>(unichar >> 18);
|
|
|
|
|
(*out) += 0x80 | static_cast<char>((unichar >> 12) & 0x3f);
|
|
|
|
|
(*out) += 0x80 | static_cast<char>((unichar >> 6) & 0x3f);
|
|
|
|
|
(*out) += 0x80 | static_cast<char>(unichar & 0x3f);
|
2006-09-06 04:56:44 +02:00
|
|
|
} else {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "UTF16ToUTF8 cannot represent high value " <<
|
|
|
|
|
HexString(unichar) << " in UTF-8";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return out.release();
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-02 18:43:57 +01:00
|
|
|
// Return the smaller of the number of code units in the UTF-16 string,
|
|
|
|
|
// not including the terminating null word, or maxlen.
|
2013-03-06 15:04:42 +01:00
|
|
|
static size_t UTF16codeunits(const uint16_t *string, size_t maxlen) {
|
2009-12-02 18:43:57 +01:00
|
|
|
size_t count = 0;
|
|
|
|
|
while (count < maxlen && string[count] != 0)
|
|
|
|
|
count++;
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-02 20:15:57 +02:00
|
|
|
static inline void Swap(MDTimeZoneInformation* time_zone) {
|
|
|
|
|
Swap(&time_zone->bias);
|
|
|
|
|
// Skip time_zone->standard_name. No need to swap UTF-16 fields.
|
|
|
|
|
// The swap will be done as part of the conversion to UTF-8.
|
|
|
|
|
Swap(&time_zone->standard_date);
|
|
|
|
|
Swap(&time_zone->standard_bias);
|
|
|
|
|
// Skip time_zone->daylight_name. No need to swap UTF-16 fields.
|
|
|
|
|
// The swap will be done as part of the conversion to UTF-8.
|
|
|
|
|
Swap(&time_zone->daylight_date);
|
|
|
|
|
Swap(&time_zone->daylight_bias);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ConvertUTF16BufferToUTF8String(const uint16_t* utf16_data,
|
|
|
|
|
size_t max_length_in_bytes,
|
|
|
|
|
string* utf8_result,
|
|
|
|
|
bool swap) {
|
|
|
|
|
// Since there is no explicit byte length for each string, use
|
|
|
|
|
// UTF16codeunits to calculate word length, then derive byte
|
|
|
|
|
// length from that.
|
|
|
|
|
size_t max_word_length = max_length_in_bytes / sizeof(utf16_data[0]);
|
|
|
|
|
size_t word_length = UTF16codeunits(utf16_data, max_word_length);
|
|
|
|
|
if (word_length > 0) {
|
|
|
|
|
size_t byte_length = word_length * sizeof(utf16_data[0]);
|
|
|
|
|
vector<uint16_t> utf16_vector(word_length);
|
|
|
|
|
memcpy(&utf16_vector[0], &utf16_data[0], byte_length);
|
|
|
|
|
scoped_ptr<string> temp(UTF16ToUTF8(utf16_vector, swap));
|
|
|
|
|
if (temp.get()) {
|
|
|
|
|
utf8_result->assign(*temp);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
utf8_result->clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2014-06-03 21:35:41 +02:00
|
|
|
|
|
|
|
|
// For fields that may or may not be valid, PrintValueOrInvalid will print the
|
|
|
|
|
// string "(invalid)" if the field is not valid, and will print the value if
|
|
|
|
|
// the field is valid. The value is printed as hexadecimal or decimal.
|
|
|
|
|
|
|
|
|
|
enum NumberFormat {
|
|
|
|
|
kNumberFormatDecimal,
|
|
|
|
|
kNumberFormatHexadecimal,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void PrintValueOrInvalid(bool valid,
|
|
|
|
|
NumberFormat number_format,
|
|
|
|
|
uint32_t value) {
|
|
|
|
|
if (!valid) {
|
|
|
|
|
printf("(invalid)\n");
|
|
|
|
|
} else if (number_format == kNumberFormatDecimal) {
|
|
|
|
|
printf("%d\n", value);
|
|
|
|
|
} else {
|
|
|
|
|
printf("0x%x\n", value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-17 20:03:31 +02:00
|
|
|
// Converts a time_t to a string showing the time in UTC.
|
|
|
|
|
string TimeTToUTCString(time_t tt) {
|
|
|
|
|
struct tm timestruct;
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
gmtime_s(×truct, &tt);
|
|
|
|
|
#else
|
|
|
|
|
gmtime_r(&tt, ×truct);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
char timestr[20];
|
|
|
|
|
int rv = strftime(timestr, 20, "%Y-%m-%d %H:%M:%S", ×truct);
|
|
|
|
|
if (rv == 0) {
|
|
|
|
|
return string();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string(timestr);
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-03 21:35:41 +02:00
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
//
|
|
|
|
|
// MinidumpObject
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpObject::MinidumpObject(Minidump* minidump)
|
2014-09-08 21:10:42 +02:00
|
|
|
: DumpObject(),
|
|
|
|
|
minidump_(minidump) {
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpStream
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpStream::MinidumpStream(Minidump* minidump)
|
|
|
|
|
: MinidumpObject(minidump) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpContext
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpContext::MinidumpContext(Minidump* minidump)
|
2014-09-08 21:10:42 +02:00
|
|
|
: DumpContext(),
|
|
|
|
|
minidump_(minidump) {
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
2006-09-22 03:10:25 +02:00
|
|
|
MinidumpContext::~MinidumpContext() {
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpContext::Read(uint32_t expected_size) {
|
2006-09-06 04:56:44 +02:00
|
|
|
valid_ = false;
|
|
|
|
|
|
2013-11-23 02:45:20 +01:00
|
|
|
// Certain raw context types are currently assumed to have unique sizes.
|
|
|
|
|
if (!IsContextSizeUnique(sizeof(MDRawContextAMD64))) {
|
|
|
|
|
BPLOG(ERROR) << "sizeof(MDRawContextAMD64) cannot match the size of any "
|
|
|
|
|
<< "other raw context";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!IsContextSizeUnique(sizeof(MDRawContextPPC64))) {
|
|
|
|
|
BPLOG(ERROR) << "sizeof(MDRawContextPPC64) cannot match the size of any "
|
|
|
|
|
<< "other raw context";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!IsContextSizeUnique(sizeof(MDRawContextARM64))) {
|
|
|
|
|
BPLOG(ERROR) << "sizeof(MDRawContextARM64) cannot match the size of any "
|
|
|
|
|
<< "other raw context";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-22 03:10:25 +02:00
|
|
|
FreeContext();
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-09-22 03:10:25 +02:00
|
|
|
// First, figure out what type of CPU this context structure is for.
|
2007-10-31 20:20:31 +01:00
|
|
|
// For some reason, the AMD64 Context doesn't have context_flags
|
|
|
|
|
// at the beginning of the structure, so special case it here.
|
|
|
|
|
if (expected_size == sizeof(MDRawContextAMD64)) {
|
|
|
|
|
BPLOG(INFO) << "MinidumpContext: looks like AMD64 context";
|
|
|
|
|
|
|
|
|
|
scoped_ptr<MDRawContextAMD64> context_amd64(new MDRawContextAMD64());
|
|
|
|
|
if (!minidump_->ReadBytes(context_amd64.get(),
|
|
|
|
|
sizeof(MDRawContextAMD64))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not read amd64 context";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
if (minidump_->swap())
|
|
|
|
|
Swap(&context_amd64->context_flags);
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t cpu_type = context_amd64->context_flags & MD_CONTEXT_CPU_MASK;
|
2012-12-08 04:18:52 +01:00
|
|
|
if (cpu_type == 0) {
|
|
|
|
|
if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
|
|
|
|
|
context_amd64->context_flags |= cpu_type;
|
|
|
|
|
} else {
|
|
|
|
|
BPLOG(ERROR) << "Failed to preserve the current stream position";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
if (cpu_type != MD_CONTEXT_AMD64) {
|
2013-11-23 02:45:20 +01:00
|
|
|
// TODO: Fall through to switch below.
|
|
|
|
|
// http://code.google.com/p/google-breakpad/issues/detail?id=550
|
2007-10-31 20:20:31 +01:00
|
|
|
BPLOG(ERROR) << "MinidumpContext not actually amd64 context";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
// Do this after reading the entire MDRawContext structure because
|
|
|
|
|
// GetSystemInfo may seek minidump to a new position.
|
|
|
|
|
if (!CheckAgainstSystemInfo(cpu_type)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext amd64 does not match system info";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
// Normalize the 128-bit types in the dump.
|
|
|
|
|
// Since this is AMD64, by definition, the values are little-endian.
|
|
|
|
|
for (unsigned int vr_index = 0;
|
|
|
|
|
vr_index < MD_CONTEXT_AMD64_VR_COUNT;
|
|
|
|
|
++vr_index)
|
|
|
|
|
Normalize128(&context_amd64->vector_register[vr_index], false);
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&context_amd64->p1_home);
|
|
|
|
|
Swap(&context_amd64->p2_home);
|
|
|
|
|
Swap(&context_amd64->p3_home);
|
|
|
|
|
Swap(&context_amd64->p4_home);
|
|
|
|
|
Swap(&context_amd64->p5_home);
|
|
|
|
|
Swap(&context_amd64->p6_home);
|
|
|
|
|
// context_flags is already swapped
|
|
|
|
|
Swap(&context_amd64->mx_csr);
|
|
|
|
|
Swap(&context_amd64->cs);
|
|
|
|
|
Swap(&context_amd64->ds);
|
|
|
|
|
Swap(&context_amd64->es);
|
|
|
|
|
Swap(&context_amd64->fs);
|
|
|
|
|
Swap(&context_amd64->ss);
|
|
|
|
|
Swap(&context_amd64->eflags);
|
|
|
|
|
Swap(&context_amd64->dr0);
|
|
|
|
|
Swap(&context_amd64->dr1);
|
|
|
|
|
Swap(&context_amd64->dr2);
|
|
|
|
|
Swap(&context_amd64->dr3);
|
|
|
|
|
Swap(&context_amd64->dr6);
|
|
|
|
|
Swap(&context_amd64->dr7);
|
|
|
|
|
Swap(&context_amd64->rax);
|
|
|
|
|
Swap(&context_amd64->rcx);
|
|
|
|
|
Swap(&context_amd64->rdx);
|
|
|
|
|
Swap(&context_amd64->rbx);
|
|
|
|
|
Swap(&context_amd64->rsp);
|
|
|
|
|
Swap(&context_amd64->rbp);
|
|
|
|
|
Swap(&context_amd64->rsi);
|
|
|
|
|
Swap(&context_amd64->rdi);
|
|
|
|
|
Swap(&context_amd64->r8);
|
|
|
|
|
Swap(&context_amd64->r9);
|
|
|
|
|
Swap(&context_amd64->r10);
|
|
|
|
|
Swap(&context_amd64->r11);
|
|
|
|
|
Swap(&context_amd64->r12);
|
|
|
|
|
Swap(&context_amd64->r13);
|
|
|
|
|
Swap(&context_amd64->r14);
|
|
|
|
|
Swap(&context_amd64->r15);
|
|
|
|
|
Swap(&context_amd64->rip);
|
2013-06-04 18:51:01 +02:00
|
|
|
// FIXME: I'm not sure what actually determines
|
2007-10-31 20:20:31 +01:00
|
|
|
// which member of the union {flt_save, sse_registers}
|
|
|
|
|
// is valid. We're not currently using either,
|
|
|
|
|
// but it would be good to have them swapped properly.
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
for (unsigned int vr_index = 0;
|
|
|
|
|
vr_index < MD_CONTEXT_AMD64_VR_COUNT;
|
|
|
|
|
++vr_index)
|
|
|
|
|
Swap(&context_amd64->vector_register[vr_index]);
|
|
|
|
|
Swap(&context_amd64->vector_control);
|
|
|
|
|
Swap(&context_amd64->debug_control);
|
|
|
|
|
Swap(&context_amd64->last_branch_to_rip);
|
|
|
|
|
Swap(&context_amd64->last_branch_from_rip);
|
|
|
|
|
Swap(&context_amd64->last_exception_to_rip);
|
|
|
|
|
Swap(&context_amd64->last_exception_from_rip);
|
|
|
|
|
}
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2014-09-08 21:10:42 +02:00
|
|
|
SetContextFlags(context_amd64->context_flags);
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2014-09-08 21:10:42 +02:00
|
|
|
SetContextAMD64(context_amd64.release());
|
2013-06-04 18:51:01 +02:00
|
|
|
} else if (expected_size == sizeof(MDRawContextPPC64)) {
|
|
|
|
|
// |context_flags| of MDRawContextPPC64 is 64 bits, but other MDRawContext
|
|
|
|
|
// in the else case have 32 bits |context_flags|, so special case it here.
|
2013-04-13 01:24:02 +02:00
|
|
|
uint64_t context_flags;
|
|
|
|
|
if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not read context flags";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (minidump_->swap())
|
|
|
|
|
Swap(&context_flags);
|
|
|
|
|
|
|
|
|
|
uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
|
|
|
|
|
scoped_ptr<MDRawContextPPC64> context_ppc64(new MDRawContextPPC64());
|
|
|
|
|
|
2013-11-23 02:45:20 +01:00
|
|
|
if (cpu_type == 0) {
|
|
|
|
|
if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
|
|
|
|
|
context_ppc64->context_flags |= cpu_type;
|
|
|
|
|
} else {
|
|
|
|
|
BPLOG(ERROR) << "Failed to preserve the current stream position";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cpu_type != MD_CONTEXT_PPC64) {
|
|
|
|
|
// TODO: Fall through to switch below.
|
|
|
|
|
// http://code.google.com/p/google-breakpad/issues/detail?id=550
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext not actually ppc64 context";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2013-06-04 18:51:01 +02:00
|
|
|
|
2013-04-13 01:24:02 +02:00
|
|
|
// Set the context_flags member, which has already been read, and
|
|
|
|
|
// read the rest of the structure beginning with the first member
|
|
|
|
|
// after context_flags.
|
|
|
|
|
context_ppc64->context_flags = context_flags;
|
|
|
|
|
|
|
|
|
|
size_t flags_size = sizeof(context_ppc64->context_flags);
|
|
|
|
|
uint8_t* context_after_flags =
|
|
|
|
|
reinterpret_cast<uint8_t*>(context_ppc64.get()) + flags_size;
|
|
|
|
|
if (!minidump_->ReadBytes(context_after_flags,
|
|
|
|
|
sizeof(MDRawContextPPC64) - flags_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not read ppc64 context";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Do this after reading the entire MDRawContext structure because
|
|
|
|
|
// GetSystemInfo may seek minidump to a new position.
|
|
|
|
|
if (!CheckAgainstSystemInfo(cpu_type)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext ppc64 does not match system info";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
// context_ppc64->context_flags was already swapped.
|
|
|
|
|
Swap(&context_ppc64->srr0);
|
|
|
|
|
Swap(&context_ppc64->srr1);
|
|
|
|
|
for (unsigned int gpr_index = 0;
|
|
|
|
|
gpr_index < MD_CONTEXT_PPC64_GPR_COUNT;
|
|
|
|
|
++gpr_index) {
|
|
|
|
|
Swap(&context_ppc64->gpr[gpr_index]);
|
|
|
|
|
}
|
|
|
|
|
Swap(&context_ppc64->cr);
|
|
|
|
|
Swap(&context_ppc64->xer);
|
|
|
|
|
Swap(&context_ppc64->lr);
|
|
|
|
|
Swap(&context_ppc64->ctr);
|
|
|
|
|
Swap(&context_ppc64->vrsave);
|
|
|
|
|
for (unsigned int fpr_index = 0;
|
|
|
|
|
fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT;
|
|
|
|
|
++fpr_index) {
|
|
|
|
|
Swap(&context_ppc64->float_save.fpregs[fpr_index]);
|
|
|
|
|
}
|
|
|
|
|
// Don't swap context_ppc64->float_save.fpscr_pad because it is only
|
|
|
|
|
// used for padding.
|
|
|
|
|
Swap(&context_ppc64->float_save.fpscr);
|
|
|
|
|
for (unsigned int vr_index = 0;
|
|
|
|
|
vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT;
|
|
|
|
|
++vr_index) {
|
|
|
|
|
Normalize128(&context_ppc64->vector_save.save_vr[vr_index], true);
|
|
|
|
|
Swap(&context_ppc64->vector_save.save_vr[vr_index]);
|
|
|
|
|
}
|
|
|
|
|
Swap(&context_ppc64->vector_save.save_vscr);
|
|
|
|
|
// Don't swap the padding fields in vector_save.
|
|
|
|
|
Swap(&context_ppc64->vector_save.save_vrvalid);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 21:10:42 +02:00
|
|
|
SetContextFlags(static_cast<uint32_t>(context_ppc64->context_flags));
|
2013-06-04 18:51:01 +02:00
|
|
|
|
|
|
|
|
// Check for data loss when converting context flags from uint64_t into
|
|
|
|
|
// uint32_t
|
2014-09-08 21:10:42 +02:00
|
|
|
if (static_cast<uint64_t>(GetContextFlags()) !=
|
2013-06-04 18:51:01 +02:00
|
|
|
context_ppc64->context_flags) {
|
|
|
|
|
BPLOG(ERROR) << "Data loss detected when converting PPC64 context_flags";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2013-04-13 01:24:02 +02:00
|
|
|
|
2014-09-08 21:10:42 +02:00
|
|
|
SetContextPPC64(context_ppc64.release());
|
2013-11-23 02:45:20 +01:00
|
|
|
} else if (expected_size == sizeof(MDRawContextARM64)) {
|
|
|
|
|
// |context_flags| of MDRawContextARM64 is 64 bits, but other MDRawContext
|
|
|
|
|
// in the else case have 32 bits |context_flags|, so special case it here.
|
|
|
|
|
uint64_t context_flags;
|
|
|
|
|
|
|
|
|
|
BPLOG(INFO) << "MinidumpContext: looks like ARM64 context";
|
|
|
|
|
|
|
|
|
|
if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not read context flags";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (minidump_->swap())
|
|
|
|
|
Swap(&context_flags);
|
|
|
|
|
|
|
|
|
|
scoped_ptr<MDRawContextARM64> context_arm64(new MDRawContextARM64());
|
|
|
|
|
|
|
|
|
|
uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
|
|
|
|
|
if (cpu_type == 0) {
|
|
|
|
|
if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
|
|
|
|
|
context_arm64->context_flags |= cpu_type;
|
|
|
|
|
} else {
|
|
|
|
|
BPLOG(ERROR) << "Failed to preserve the current stream position";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cpu_type != MD_CONTEXT_ARM64) {
|
|
|
|
|
// TODO: Fall through to switch below.
|
|
|
|
|
// http://code.google.com/p/google-breakpad/issues/detail?id=550
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext not actually arm64 context";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the context_flags member, which has already been read, and
|
|
|
|
|
// read the rest of the structure beginning with the first member
|
|
|
|
|
// after context_flags.
|
|
|
|
|
context_arm64->context_flags = context_flags;
|
|
|
|
|
|
|
|
|
|
size_t flags_size = sizeof(context_arm64->context_flags);
|
|
|
|
|
uint8_t* context_after_flags =
|
|
|
|
|
reinterpret_cast<uint8_t*>(context_arm64.get()) + flags_size;
|
|
|
|
|
if (!minidump_->ReadBytes(context_after_flags,
|
|
|
|
|
sizeof(MDRawContextARM64) - flags_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not read arm64 context";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Do this after reading the entire MDRawContext structure because
|
|
|
|
|
// GetSystemInfo may seek minidump to a new position.
|
|
|
|
|
if (!CheckAgainstSystemInfo(cpu_type)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext arm64 does not match system info";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
// context_arm64->context_flags was already swapped.
|
|
|
|
|
for (unsigned int ireg_index = 0;
|
|
|
|
|
ireg_index < MD_CONTEXT_ARM64_GPR_COUNT;
|
|
|
|
|
++ireg_index) {
|
|
|
|
|
Swap(&context_arm64->iregs[ireg_index]);
|
|
|
|
|
}
|
|
|
|
|
Swap(&context_arm64->cpsr);
|
|
|
|
|
Swap(&context_arm64->float_save.fpsr);
|
|
|
|
|
Swap(&context_arm64->float_save.fpcr);
|
|
|
|
|
for (unsigned int fpr_index = 0;
|
|
|
|
|
fpr_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT;
|
|
|
|
|
++fpr_index) {
|
|
|
|
|
// While ARM64 is bi-endian, iOS (currently the only platform
|
|
|
|
|
// for which ARM64 support has been brought up) uses ARM64 exclusively
|
|
|
|
|
// in little-endian mode.
|
|
|
|
|
Normalize128(&context_arm64->float_save.regs[fpr_index], false);
|
|
|
|
|
Swap(&context_arm64->float_save.regs[fpr_index]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-08 21:10:42 +02:00
|
|
|
SetContextFlags(static_cast<uint32_t>(context_arm64->context_flags));
|
2013-12-17 23:21:40 +01:00
|
|
|
|
|
|
|
|
// Check for data loss when converting context flags from uint64_t into
|
|
|
|
|
// uint32_t
|
2014-09-08 21:10:42 +02:00
|
|
|
if (static_cast<uint64_t>(GetContextFlags()) !=
|
2013-12-17 23:21:40 +01:00
|
|
|
context_arm64->context_flags) {
|
|
|
|
|
BPLOG(ERROR) << "Data loss detected when converting ARM64 context_flags";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 21:10:42 +02:00
|
|
|
SetContextARM64(context_arm64.release());
|
2013-06-04 18:51:01 +02:00
|
|
|
} else {
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t context_flags;
|
2007-10-31 20:20:31 +01:00
|
|
|
if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not read context flags";
|
|
|
|
|
return false;
|
2006-09-22 03:10:25 +02:00
|
|
|
}
|
2007-10-31 20:20:31 +01:00
|
|
|
if (minidump_->swap())
|
|
|
|
|
Swap(&context_flags);
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
|
2011-08-31 00:22:08 +02:00
|
|
|
if (cpu_type == 0) {
|
|
|
|
|
// Unfortunately the flag for MD_CONTEXT_ARM that was taken
|
|
|
|
|
// from a Windows CE SDK header conflicts in practice with
|
|
|
|
|
// the CONTEXT_XSTATE flag. MD_CONTEXT_ARM has been renumbered,
|
|
|
|
|
// but handle dumps with the legacy value gracefully here.
|
|
|
|
|
if (context_flags & MD_CONTEXT_ARM_OLD) {
|
|
|
|
|
context_flags |= MD_CONTEXT_ARM;
|
|
|
|
|
context_flags &= ~MD_CONTEXT_ARM_OLD;
|
|
|
|
|
cpu_type = MD_CONTEXT_ARM;
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-10-31 20:20:31 +01:00
|
|
|
|
2012-12-08 04:18:52 +01:00
|
|
|
if (cpu_type == 0) {
|
|
|
|
|
if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
|
|
|
|
|
context_flags |= cpu_type;
|
|
|
|
|
} else {
|
|
|
|
|
BPLOG(ERROR) << "Failed to preserve the current stream position";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
// Allocate the context structure for the correct CPU and fill it. The
|
|
|
|
|
// casts are slightly unorthodox, but it seems better to do that than to
|
|
|
|
|
// maintain a separate pointer for each type of CPU context structure
|
|
|
|
|
// when only one of them will be used.
|
|
|
|
|
switch (cpu_type) {
|
|
|
|
|
case MD_CONTEXT_X86: {
|
|
|
|
|
if (expected_size != sizeof(MDRawContextX86)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext x86 size mismatch, " <<
|
|
|
|
|
expected_size << " != " << sizeof(MDRawContextX86);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
scoped_ptr<MDRawContextX86> context_x86(new MDRawContextX86());
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
// Set the context_flags member, which has already been read, and
|
|
|
|
|
// read the rest of the structure beginning with the first member
|
|
|
|
|
// after context_flags.
|
|
|
|
|
context_x86->context_flags = context_flags;
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
size_t flags_size = sizeof(context_x86->context_flags);
|
2013-03-06 15:04:42 +01:00
|
|
|
uint8_t* context_after_flags =
|
|
|
|
|
reinterpret_cast<uint8_t*>(context_x86.get()) + flags_size;
|
2007-10-31 20:20:31 +01:00
|
|
|
if (!minidump_->ReadBytes(context_after_flags,
|
|
|
|
|
sizeof(MDRawContextX86) - flags_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not read x86 context";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
// Do this after reading the entire MDRawContext structure because
|
|
|
|
|
// GetSystemInfo may seek minidump to a new position.
|
|
|
|
|
if (!CheckAgainstSystemInfo(cpu_type)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext x86 does not match system info";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
// context_x86->context_flags was already swapped.
|
|
|
|
|
Swap(&context_x86->dr0);
|
|
|
|
|
Swap(&context_x86->dr1);
|
|
|
|
|
Swap(&context_x86->dr2);
|
|
|
|
|
Swap(&context_x86->dr3);
|
|
|
|
|
Swap(&context_x86->dr6);
|
|
|
|
|
Swap(&context_x86->dr7);
|
|
|
|
|
Swap(&context_x86->float_save.control_word);
|
|
|
|
|
Swap(&context_x86->float_save.status_word);
|
|
|
|
|
Swap(&context_x86->float_save.tag_word);
|
|
|
|
|
Swap(&context_x86->float_save.error_offset);
|
|
|
|
|
Swap(&context_x86->float_save.error_selector);
|
|
|
|
|
Swap(&context_x86->float_save.data_offset);
|
|
|
|
|
Swap(&context_x86->float_save.data_selector);
|
|
|
|
|
// context_x86->float_save.register_area[] contains 8-bit quantities
|
|
|
|
|
// and does not need to be swapped.
|
|
|
|
|
Swap(&context_x86->float_save.cr0_npx_state);
|
|
|
|
|
Swap(&context_x86->gs);
|
|
|
|
|
Swap(&context_x86->fs);
|
|
|
|
|
Swap(&context_x86->es);
|
|
|
|
|
Swap(&context_x86->ds);
|
|
|
|
|
Swap(&context_x86->edi);
|
|
|
|
|
Swap(&context_x86->esi);
|
|
|
|
|
Swap(&context_x86->ebx);
|
|
|
|
|
Swap(&context_x86->edx);
|
|
|
|
|
Swap(&context_x86->ecx);
|
|
|
|
|
Swap(&context_x86->eax);
|
|
|
|
|
Swap(&context_x86->ebp);
|
|
|
|
|
Swap(&context_x86->eip);
|
|
|
|
|
Swap(&context_x86->cs);
|
|
|
|
|
Swap(&context_x86->eflags);
|
|
|
|
|
Swap(&context_x86->esp);
|
|
|
|
|
Swap(&context_x86->ss);
|
|
|
|
|
// context_x86->extended_registers[] contains 8-bit quantities and
|
|
|
|
|
// does not need to be swapped.
|
|
|
|
|
}
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2014-09-08 21:10:42 +02:00
|
|
|
SetContextX86(context_x86.release());
|
2007-10-31 20:20:31 +01:00
|
|
|
|
|
|
|
|
break;
|
2007-02-07 05:24:03 +01:00
|
|
|
}
|
|
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
case MD_CONTEXT_PPC: {
|
|
|
|
|
if (expected_size != sizeof(MDRawContextPPC)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext ppc size mismatch, " <<
|
|
|
|
|
expected_size << " != " << sizeof(MDRawContextPPC);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scoped_ptr<MDRawContextPPC> context_ppc(new MDRawContextPPC());
|
|
|
|
|
|
|
|
|
|
// Set the context_flags member, which has already been read, and
|
|
|
|
|
// read the rest of the structure beginning with the first member
|
|
|
|
|
// after context_flags.
|
|
|
|
|
context_ppc->context_flags = context_flags;
|
|
|
|
|
|
|
|
|
|
size_t flags_size = sizeof(context_ppc->context_flags);
|
2013-03-06 15:04:42 +01:00
|
|
|
uint8_t* context_after_flags =
|
|
|
|
|
reinterpret_cast<uint8_t*>(context_ppc.get()) + flags_size;
|
2007-10-31 20:20:31 +01:00
|
|
|
if (!minidump_->ReadBytes(context_after_flags,
|
|
|
|
|
sizeof(MDRawContextPPC) - flags_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not read ppc context";
|
|
|
|
|
return false;
|
2006-09-22 03:10:25 +02:00
|
|
|
}
|
2007-10-31 20:20:31 +01:00
|
|
|
|
|
|
|
|
// Do this after reading the entire MDRawContext structure because
|
|
|
|
|
// GetSystemInfo may seek minidump to a new position.
|
|
|
|
|
if (!CheckAgainstSystemInfo(cpu_type)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext ppc does not match system info";
|
|
|
|
|
return false;
|
2006-09-22 03:10:25 +02:00
|
|
|
}
|
2007-10-31 20:20:31 +01:00
|
|
|
|
|
|
|
|
// Normalize the 128-bit types in the dump.
|
|
|
|
|
// Since this is PowerPC, by definition, the values are big-endian.
|
2006-09-22 03:10:25 +02:00
|
|
|
for (unsigned int vr_index = 0;
|
|
|
|
|
vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT;
|
|
|
|
|
++vr_index) {
|
2007-10-31 20:20:31 +01:00
|
|
|
Normalize128(&context_ppc->vector_save.save_vr[vr_index], true);
|
2006-09-22 03:10:25 +02:00
|
|
|
}
|
|
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
// context_ppc->context_flags was already swapped.
|
|
|
|
|
Swap(&context_ppc->srr0);
|
|
|
|
|
Swap(&context_ppc->srr1);
|
|
|
|
|
for (unsigned int gpr_index = 0;
|
|
|
|
|
gpr_index < MD_CONTEXT_PPC_GPR_COUNT;
|
|
|
|
|
++gpr_index) {
|
|
|
|
|
Swap(&context_ppc->gpr[gpr_index]);
|
|
|
|
|
}
|
|
|
|
|
Swap(&context_ppc->cr);
|
|
|
|
|
Swap(&context_ppc->xer);
|
|
|
|
|
Swap(&context_ppc->lr);
|
|
|
|
|
Swap(&context_ppc->ctr);
|
|
|
|
|
Swap(&context_ppc->mq);
|
|
|
|
|
Swap(&context_ppc->vrsave);
|
|
|
|
|
for (unsigned int fpr_index = 0;
|
|
|
|
|
fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT;
|
|
|
|
|
++fpr_index) {
|
|
|
|
|
Swap(&context_ppc->float_save.fpregs[fpr_index]);
|
|
|
|
|
}
|
|
|
|
|
// Don't swap context_ppc->float_save.fpscr_pad because it is only
|
|
|
|
|
// used for padding.
|
|
|
|
|
Swap(&context_ppc->float_save.fpscr);
|
|
|
|
|
for (unsigned int vr_index = 0;
|
|
|
|
|
vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT;
|
|
|
|
|
++vr_index) {
|
|
|
|
|
Swap(&context_ppc->vector_save.save_vr[vr_index]);
|
|
|
|
|
}
|
|
|
|
|
Swap(&context_ppc->vector_save.save_vscr);
|
|
|
|
|
// Don't swap the padding fields in vector_save.
|
|
|
|
|
Swap(&context_ppc->vector_save.save_vrvalid);
|
|
|
|
|
}
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2014-09-08 21:10:42 +02:00
|
|
|
SetContextPPC(context_ppc.release());
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
break;
|
2007-09-26 20:28:05 +02:00
|
|
|
}
|
|
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
case MD_CONTEXT_SPARC: {
|
|
|
|
|
if (expected_size != sizeof(MDRawContextSPARC)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext sparc size mismatch, " <<
|
|
|
|
|
expected_size << " != " << sizeof(MDRawContextSPARC);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2007-09-26 20:28:05 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
scoped_ptr<MDRawContextSPARC> context_sparc(new MDRawContextSPARC());
|
2007-09-26 20:28:05 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
// Set the context_flags member, which has already been read, and
|
|
|
|
|
// read the rest of the structure beginning with the first member
|
|
|
|
|
// after context_flags.
|
|
|
|
|
context_sparc->context_flags = context_flags;
|
2007-09-26 20:28:05 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
size_t flags_size = sizeof(context_sparc->context_flags);
|
2013-03-06 15:04:42 +01:00
|
|
|
uint8_t* context_after_flags =
|
|
|
|
|
reinterpret_cast<uint8_t*>(context_sparc.get()) + flags_size;
|
2007-10-31 20:20:31 +01:00
|
|
|
if (!minidump_->ReadBytes(context_after_flags,
|
|
|
|
|
sizeof(MDRawContextSPARC) - flags_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not read sparc context";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2007-09-26 20:28:05 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
// Do this after reading the entire MDRawContext structure because
|
|
|
|
|
// GetSystemInfo may seek minidump to a new position.
|
|
|
|
|
if (!CheckAgainstSystemInfo(cpu_type)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext sparc does not match system info";
|
|
|
|
|
return false;
|
2007-09-26 20:28:05 +02:00
|
|
|
}
|
2007-10-31 20:20:31 +01:00
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
// context_sparc->context_flags was already swapped.
|
|
|
|
|
for (unsigned int gpr_index = 0;
|
|
|
|
|
gpr_index < MD_CONTEXT_SPARC_GPR_COUNT;
|
|
|
|
|
++gpr_index) {
|
|
|
|
|
Swap(&context_sparc->g_r[gpr_index]);
|
|
|
|
|
}
|
|
|
|
|
Swap(&context_sparc->ccr);
|
|
|
|
|
Swap(&context_sparc->pc);
|
|
|
|
|
Swap(&context_sparc->npc);
|
|
|
|
|
Swap(&context_sparc->y);
|
|
|
|
|
Swap(&context_sparc->asi);
|
|
|
|
|
Swap(&context_sparc->fprs);
|
|
|
|
|
for (unsigned int fpr_index = 0;
|
|
|
|
|
fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT;
|
|
|
|
|
++fpr_index) {
|
|
|
|
|
Swap(&context_sparc->float_save.regs[fpr_index]);
|
|
|
|
|
}
|
|
|
|
|
Swap(&context_sparc->float_save.filler);
|
|
|
|
|
Swap(&context_sparc->float_save.fsr);
|
2007-09-26 20:28:05 +02:00
|
|
|
}
|
2014-09-08 21:10:42 +02:00
|
|
|
SetContextSPARC(context_sparc.release());
|
2007-09-26 20:28:05 +02:00
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2007-09-26 20:28:05 +02:00
|
|
|
|
2009-12-19 22:43:53 +01:00
|
|
|
case MD_CONTEXT_ARM: {
|
|
|
|
|
if (expected_size != sizeof(MDRawContextARM)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext arm size mismatch, " <<
|
|
|
|
|
expected_size << " != " << sizeof(MDRawContextARM);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scoped_ptr<MDRawContextARM> context_arm(new MDRawContextARM());
|
|
|
|
|
|
|
|
|
|
// Set the context_flags member, which has already been read, and
|
|
|
|
|
// read the rest of the structure beginning with the first member
|
|
|
|
|
// after context_flags.
|
|
|
|
|
context_arm->context_flags = context_flags;
|
|
|
|
|
|
|
|
|
|
size_t flags_size = sizeof(context_arm->context_flags);
|
2013-03-06 15:04:42 +01:00
|
|
|
uint8_t* context_after_flags =
|
|
|
|
|
reinterpret_cast<uint8_t*>(context_arm.get()) + flags_size;
|
2009-12-19 22:43:53 +01:00
|
|
|
if (!minidump_->ReadBytes(context_after_flags,
|
|
|
|
|
sizeof(MDRawContextARM) - flags_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not read arm context";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Do this after reading the entire MDRawContext structure because
|
|
|
|
|
// GetSystemInfo may seek minidump to a new position.
|
|
|
|
|
if (!CheckAgainstSystemInfo(cpu_type)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext arm does not match system info";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
// context_arm->context_flags was already swapped.
|
|
|
|
|
for (unsigned int ireg_index = 0;
|
|
|
|
|
ireg_index < MD_CONTEXT_ARM_GPR_COUNT;
|
|
|
|
|
++ireg_index) {
|
|
|
|
|
Swap(&context_arm->iregs[ireg_index]);
|
|
|
|
|
}
|
|
|
|
|
Swap(&context_arm->cpsr);
|
|
|
|
|
Swap(&context_arm->float_save.fpscr);
|
|
|
|
|
for (unsigned int fpr_index = 0;
|
|
|
|
|
fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT;
|
|
|
|
|
++fpr_index) {
|
|
|
|
|
Swap(&context_arm->float_save.regs[fpr_index]);
|
|
|
|
|
}
|
|
|
|
|
for (unsigned int fpe_index = 0;
|
|
|
|
|
fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT;
|
|
|
|
|
++fpe_index) {
|
|
|
|
|
Swap(&context_arm->float_save.extra[fpe_index]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-08 21:10:42 +02:00
|
|
|
SetContextARM(context_arm.release());
|
2009-12-19 22:43:53 +01:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-11 13:37:04 +02:00
|
|
|
case MD_CONTEXT_MIPS: {
|
|
|
|
|
if (expected_size != sizeof(MDRawContextMIPS)) {
|
2014-09-08 21:10:42 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpContext MIPS size mismatch, "
|
|
|
|
|
<< expected_size
|
|
|
|
|
<< " != "
|
2013-09-11 13:37:04 +02:00
|
|
|
<< sizeof(MDRawContextMIPS);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scoped_ptr<MDRawContextMIPS> context_mips(new MDRawContextMIPS());
|
|
|
|
|
|
|
|
|
|
// Set the context_flags member, which has already been read, and
|
|
|
|
|
// read the rest of the structure beginning with the first member
|
|
|
|
|
// after context_flags.
|
|
|
|
|
context_mips->context_flags = context_flags;
|
|
|
|
|
|
|
|
|
|
size_t flags_size = sizeof(context_mips->context_flags);
|
|
|
|
|
uint8_t* context_after_flags =
|
|
|
|
|
reinterpret_cast<uint8_t*>(context_mips.get()) + flags_size;
|
|
|
|
|
if (!minidump_->ReadBytes(context_after_flags,
|
|
|
|
|
sizeof(MDRawContextMIPS) - flags_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not read MIPS context";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Do this after reading the entire MDRawContext structure because
|
|
|
|
|
// GetSystemInfo may seek minidump to a new position.
|
|
|
|
|
if (!CheckAgainstSystemInfo(cpu_type)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext MIPS does not match system info";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
// context_mips->context_flags was already swapped.
|
|
|
|
|
for (int ireg_index = 0;
|
|
|
|
|
ireg_index < MD_CONTEXT_MIPS_GPR_COUNT;
|
|
|
|
|
++ireg_index) {
|
|
|
|
|
Swap(&context_mips->iregs[ireg_index]);
|
|
|
|
|
}
|
|
|
|
|
Swap(&context_mips->mdhi);
|
|
|
|
|
Swap(&context_mips->mdlo);
|
|
|
|
|
for (int dsp_index = 0;
|
|
|
|
|
dsp_index < MD_CONTEXT_MIPS_DSP_COUNT;
|
|
|
|
|
++dsp_index) {
|
|
|
|
|
Swap(&context_mips->hi[dsp_index]);
|
|
|
|
|
Swap(&context_mips->lo[dsp_index]);
|
|
|
|
|
}
|
|
|
|
|
Swap(&context_mips->dsp_control);
|
|
|
|
|
Swap(&context_mips->epc);
|
|
|
|
|
Swap(&context_mips->badvaddr);
|
|
|
|
|
Swap(&context_mips->status);
|
|
|
|
|
Swap(&context_mips->cause);
|
|
|
|
|
for (int fpr_index = 0;
|
|
|
|
|
fpr_index < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT;
|
|
|
|
|
++fpr_index) {
|
|
|
|
|
Swap(&context_mips->float_save.regs[fpr_index]);
|
|
|
|
|
}
|
|
|
|
|
Swap(&context_mips->float_save.fpcsr);
|
|
|
|
|
Swap(&context_mips->float_save.fir);
|
|
|
|
|
}
|
2014-09-08 21:10:42 +02:00
|
|
|
SetContextMIPS(context_mips.release());
|
2013-09-11 13:37:04 +02:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
default: {
|
2010-09-09 23:15:32 +02:00
|
|
|
// Unknown context type - Don't log as an error yet. Let the
|
2009-10-19 20:10:49 +02:00
|
|
|
// caller work that out.
|
|
|
|
|
BPLOG(INFO) << "MinidumpContext unknown context type " <<
|
2007-10-31 20:20:31 +01:00
|
|
|
HexString(cpu_type);
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2006-09-22 03:10:25 +02:00
|
|
|
}
|
2014-09-08 21:10:42 +02:00
|
|
|
SetContextFlags(context_flags);
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpContext::CheckAgainstSystemInfo(uint32_t context_cpu_type) {
|
2006-11-02 02:02:39 +01:00
|
|
|
// It's OK if the minidump doesn't contain an MD_SYSTEM_INFO_STREAM,
|
2006-09-22 03:10:25 +02:00
|
|
|
// as this function just implements a sanity check.
|
|
|
|
|
MinidumpSystemInfo* system_info = minidump_->GetSystemInfo();
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!system_info) {
|
|
|
|
|
BPLOG(INFO) << "MinidumpContext could not be compared against "
|
|
|
|
|
"MinidumpSystemInfo";
|
2006-09-22 03:10:25 +02:00
|
|
|
return true;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-22 03:10:25 +02:00
|
|
|
|
2006-11-02 02:02:39 +01:00
|
|
|
// If there is an MD_SYSTEM_INFO_STREAM, it should contain valid system info.
|
2006-09-22 03:10:25 +02:00
|
|
|
const MDRawSystemInfo* raw_system_info = system_info->system_info();
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!raw_system_info) {
|
|
|
|
|
BPLOG(INFO) << "MinidumpContext could not be compared against "
|
|
|
|
|
"MDRawSystemInfo";
|
2006-09-22 03:10:25 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-22 03:10:25 +02:00
|
|
|
|
|
|
|
|
MDCPUArchitecture system_info_cpu_type = static_cast<MDCPUArchitecture>(
|
|
|
|
|
raw_system_info->processor_architecture);
|
|
|
|
|
|
|
|
|
|
// Compare the CPU type of the context record to the CPU type in the
|
|
|
|
|
// minidump's system info stream.
|
2007-05-17 20:34:37 +02:00
|
|
|
bool return_value = false;
|
2006-09-22 03:10:25 +02:00
|
|
|
switch (context_cpu_type) {
|
|
|
|
|
case MD_CONTEXT_X86:
|
2007-05-17 20:34:37 +02:00
|
|
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_X86 ||
|
2007-11-19 06:53:21 +01:00
|
|
|
system_info_cpu_type == MD_CPU_ARCHITECTURE_X86_WIN64 ||
|
|
|
|
|
system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64) {
|
2007-05-17 20:34:37 +02:00
|
|
|
return_value = true;
|
2006-09-22 03:10:25 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MD_CONTEXT_PPC:
|
2007-05-17 20:34:37 +02:00
|
|
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_PPC)
|
|
|
|
|
return_value = true;
|
2006-09-22 03:10:25 +02:00
|
|
|
break;
|
2007-09-26 20:28:05 +02:00
|
|
|
|
2013-04-13 01:24:02 +02:00
|
|
|
case MD_CONTEXT_PPC64:
|
|
|
|
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_PPC64)
|
|
|
|
|
return_value = true;
|
|
|
|
|
break;
|
|
|
|
|
|
2007-10-31 20:20:31 +01:00
|
|
|
case MD_CONTEXT_AMD64:
|
|
|
|
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64)
|
|
|
|
|
return_value = true;
|
|
|
|
|
break;
|
|
|
|
|
|
2007-09-26 20:28:05 +02:00
|
|
|
case MD_CONTEXT_SPARC:
|
|
|
|
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_SPARC)
|
|
|
|
|
return_value = true;
|
|
|
|
|
break;
|
2009-12-19 22:43:53 +01:00
|
|
|
|
|
|
|
|
case MD_CONTEXT_ARM:
|
|
|
|
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM)
|
|
|
|
|
return_value = true;
|
|
|
|
|
break;
|
2013-09-11 13:37:04 +02:00
|
|
|
|
2013-11-23 02:45:20 +01:00
|
|
|
case MD_CONTEXT_ARM64:
|
|
|
|
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM64)
|
|
|
|
|
return_value = true;
|
|
|
|
|
break;
|
|
|
|
|
|
2013-09-11 13:37:04 +02:00
|
|
|
case MD_CONTEXT_MIPS:
|
|
|
|
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS)
|
|
|
|
|
return_value = true;
|
|
|
|
|
break;
|
2006-09-22 03:10:25 +02:00
|
|
|
}
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " <<
|
|
|
|
|
HexString(context_cpu_type) <<
|
2010-12-15 22:55:56 +01:00
|
|
|
" wrong for MinidumpSystemInfo CPU " <<
|
2007-05-17 20:34:37 +02:00
|
|
|
HexString(system_info_cpu_type);
|
|
|
|
|
|
|
|
|
|
return return_value;
|
2006-09-22 03:10:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
//
|
|
|
|
|
// MinidumpMemoryRegion
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t MinidumpMemoryRegion::max_bytes_ = 1024 * 1024; // 1MB
|
2007-05-31 17:54:06 +02:00
|
|
|
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
MinidumpMemoryRegion::MinidumpMemoryRegion(Minidump* minidump)
|
2006-09-07 17:56:38 +02:00
|
|
|
: MinidumpObject(minidump),
|
|
|
|
|
descriptor_(NULL),
|
|
|
|
|
memory_(NULL) {
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpMemoryRegion::~MinidumpMemoryRegion() {
|
|
|
|
|
delete memory_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MinidumpMemoryRegion::SetDescriptor(MDMemoryDescriptor* descriptor) {
|
|
|
|
|
descriptor_ = descriptor;
|
|
|
|
|
valid_ = descriptor &&
|
2007-05-21 23:02:04 +02:00
|
|
|
descriptor_->memory.data_size <=
|
2013-03-06 15:04:42 +01:00
|
|
|
numeric_limits<uint64_t>::max() -
|
2007-05-21 23:02:04 +02:00
|
|
|
descriptor_->start_of_memory_range;
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
const uint8_t* MinidumpMemoryRegion::GetMemory() const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetMemory";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (!memory_) {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (descriptor_->memory.data_size == 0) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryRegion is empty";
|
2006-11-27 22:42:07 +01:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-11-27 22:42:07 +01:00
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_->SeekSet(descriptor_->memory.rva)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryRegion could not seek to memory region";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-31 17:54:06 +02:00
|
|
|
if (descriptor_->memory.data_size > max_bytes_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryRegion size " <<
|
|
|
|
|
descriptor_->memory.data_size << " exceeds maximum " <<
|
|
|
|
|
max_bytes_;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
scoped_ptr< vector<uint8_t> > memory(
|
|
|
|
|
new vector<uint8_t>(descriptor_->memory.data_size));
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_->ReadBytes(&(*memory)[0], descriptor_->memory.data_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryRegion could not read memory region";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
memory_ = memory.release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &(*memory_)[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint64_t MinidumpMemoryRegion::GetBase() const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetBase";
|
2013-03-06 15:04:42 +01:00
|
|
|
return static_cast<uint64_t>(-1);
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return descriptor_->start_of_memory_range;
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t MinidumpMemoryRegion::GetSize() const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetSize";
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return descriptor_->memory.data_size;
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MinidumpMemoryRegion::FreeMemory() {
|
|
|
|
|
delete memory_;
|
|
|
|
|
memory_ = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpMemoryRegion::GetMemoryAtAddressInternal(uint64_t address,
|
2010-02-05 18:17:24 +01:00
|
|
|
T* value) const {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG_IF(ERROR, !value) << "MinidumpMemoryRegion::GetMemoryAtAddressInternal "
|
|
|
|
|
"requires |value|";
|
|
|
|
|
assert(value);
|
|
|
|
|
*value = 0;
|
|
|
|
|
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for "
|
|
|
|
|
"GetMemoryAtAddressInternal";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2010-09-09 23:15:32 +02:00
|
|
|
// Common failure case
|
2006-09-06 04:56:44 +02:00
|
|
|
if (address < descriptor_->start_of_memory_range ||
|
2013-03-06 15:04:42 +01:00
|
|
|
sizeof(T) > numeric_limits<uint64_t>::max() - address ||
|
2006-09-06 04:56:44 +02:00
|
|
|
address + sizeof(T) > descriptor_->start_of_memory_range +
|
|
|
|
|
descriptor_->memory.data_size) {
|
2010-09-09 23:15:32 +02:00
|
|
|
BPLOG(INFO) << "MinidumpMemoryRegion request out of range: " <<
|
2007-05-17 20:34:37 +02:00
|
|
|
HexString(address) << "+" << sizeof(T) << "/" <<
|
|
|
|
|
HexString(descriptor_->start_of_memory_range) << "+" <<
|
|
|
|
|
HexString(descriptor_->memory.data_size);
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
const uint8_t* memory = GetMemory();
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!memory) {
|
|
|
|
|
// GetMemory already logged a perfectly good message.
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
// If the CPU requires memory accesses to be aligned, this can crash.
|
|
|
|
|
// x86 and ppc are able to cope, though.
|
|
|
|
|
*value = *reinterpret_cast<const T*>(
|
|
|
|
|
&memory[address - descriptor_->start_of_memory_range]);
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap())
|
|
|
|
|
Swap(value);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
|
|
|
|
uint8_t* value) const {
|
2006-09-06 04:56:44 +02:00
|
|
|
return GetMemoryAtAddressInternal(address, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
|
|
|
|
uint16_t* value) const {
|
2006-09-06 04:56:44 +02:00
|
|
|
return GetMemoryAtAddressInternal(address, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
|
|
|
|
uint32_t* value) const {
|
2006-09-06 04:56:44 +02:00
|
|
|
return GetMemoryAtAddressInternal(address, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
|
|
|
|
uint64_t* value) const {
|
2006-09-06 04:56:44 +02:00
|
|
|
return GetMemoryAtAddressInternal(address, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-09-08 21:10:42 +02:00
|
|
|
void MinidumpMemoryRegion::Print() const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryRegion cannot print invalid data";
|
2006-09-06 04:56:44 +02:00
|
|
|
return;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
const uint8_t* memory = GetMemory();
|
2006-09-06 04:56:44 +02:00
|
|
|
if (memory) {
|
|
|
|
|
printf("0x");
|
|
|
|
|
for (unsigned int byte_index = 0;
|
|
|
|
|
byte_index < descriptor_->memory.data_size;
|
|
|
|
|
byte_index++) {
|
|
|
|
|
printf("%02x", memory[byte_index]);
|
|
|
|
|
}
|
|
|
|
|
printf("\n");
|
|
|
|
|
} else {
|
|
|
|
|
printf("No memory\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpThread
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpThread::MinidumpThread(Minidump* minidump)
|
2006-09-07 17:56:38 +02:00
|
|
|
: MinidumpObject(minidump),
|
|
|
|
|
thread_(),
|
|
|
|
|
memory_(NULL),
|
|
|
|
|
context_(NULL) {
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpThread::~MinidumpThread() {
|
|
|
|
|
delete memory_;
|
|
|
|
|
delete context_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool MinidumpThread::Read() {
|
|
|
|
|
// Invalidate cached data.
|
|
|
|
|
delete memory_;
|
|
|
|
|
memory_ = NULL;
|
|
|
|
|
delete context_;
|
|
|
|
|
context_ = NULL;
|
|
|
|
|
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_->ReadBytes(&thread_, sizeof(thread_))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThread cannot read thread";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&thread_.thread_id);
|
|
|
|
|
Swap(&thread_.suspend_count);
|
|
|
|
|
Swap(&thread_.priority_class);
|
|
|
|
|
Swap(&thread_.priority);
|
|
|
|
|
Swap(&thread_.teb);
|
|
|
|
|
Swap(&thread_.stack);
|
|
|
|
|
Swap(&thread_.thread_context);
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-21 23:02:04 +02:00
|
|
|
// Check for base + size overflow or undersize.
|
2013-06-27 22:34:30 +02:00
|
|
|
if (thread_.stack.memory.rva == 0 ||
|
|
|
|
|
thread_.stack.memory.data_size == 0 ||
|
2013-03-06 15:04:42 +01:00
|
|
|
thread_.stack.memory.data_size > numeric_limits<uint64_t>::max() -
|
2007-05-21 23:02:04 +02:00
|
|
|
thread_.stack.start_of_memory_range) {
|
2012-11-06 17:50:01 +01:00
|
|
|
// This is ok, but log an error anyway.
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpThread has a memory region problem, " <<
|
|
|
|
|
HexString(thread_.stack.start_of_memory_range) << "+" <<
|
2013-06-27 22:34:30 +02:00
|
|
|
HexString(thread_.stack.memory.data_size) <<
|
|
|
|
|
", RVA 0x" << HexString(thread_.stack.memory.rva);
|
2012-11-06 17:50:01 +01:00
|
|
|
} else {
|
|
|
|
|
memory_ = new MinidumpMemoryRegion(minidump_);
|
|
|
|
|
memory_->SetDescriptor(&thread_.stack);
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-27 22:34:30 +02:00
|
|
|
uint64_t MinidumpThread::GetStartOfStackMemoryRange() const {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "GetStartOfStackMemoryRange: Invalid MinidumpThread";
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return thread_.stack.start_of_memory_range;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
MinidumpMemoryRegion* MinidumpThread::GetMemory() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpThread for GetMemory";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return memory_;
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpContext* MinidumpThread::GetContext() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpThread for GetContext";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (!context_) {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_->SeekSet(thread_.thread_context.rva)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThread cannot seek to context";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-10-23 22:25:42 +02:00
|
|
|
scoped_ptr<MinidumpContext> context(new MinidumpContext(minidump_));
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!context->Read(thread_.thread_context.data_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThread cannot read context";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
context_ = context.release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return context_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpThread::GetThreadID(uint32_t *thread_id) const {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG_IF(ERROR, !thread_id) << "MinidumpThread::GetThreadID requires "
|
|
|
|
|
"|thread_id|";
|
|
|
|
|
assert(thread_id);
|
|
|
|
|
*thread_id = 0;
|
|
|
|
|
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpThread for GetThreadID";
|
2006-11-07 00:00:19 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-11-07 00:00:19 +01:00
|
|
|
|
|
|
|
|
*thread_id = thread_.thread_id;
|
|
|
|
|
return true;
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MinidumpThread::Print() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThread cannot print invalid data";
|
2006-09-06 04:56:44 +02:00
|
|
|
return;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
printf("MDRawThread\n");
|
|
|
|
|
printf(" thread_id = 0x%x\n", thread_.thread_id);
|
|
|
|
|
printf(" suspend_count = %d\n", thread_.suspend_count);
|
|
|
|
|
printf(" priority_class = 0x%x\n", thread_.priority_class);
|
|
|
|
|
printf(" priority = 0x%x\n", thread_.priority);
|
2008-02-25 20:32:00 +01:00
|
|
|
printf(" teb = 0x%" PRIx64 "\n", thread_.teb);
|
|
|
|
|
printf(" stack.start_of_memory_range = 0x%" PRIx64 "\n",
|
2006-09-06 04:56:44 +02:00
|
|
|
thread_.stack.start_of_memory_range);
|
|
|
|
|
printf(" stack.memory.data_size = 0x%x\n",
|
|
|
|
|
thread_.stack.memory.data_size);
|
|
|
|
|
printf(" stack.memory.rva = 0x%x\n", thread_.stack.memory.rva);
|
|
|
|
|
printf(" thread_context.data_size = 0x%x\n",
|
|
|
|
|
thread_.thread_context.data_size);
|
|
|
|
|
printf(" thread_context.rva = 0x%x\n",
|
|
|
|
|
thread_.thread_context.rva);
|
|
|
|
|
|
|
|
|
|
MinidumpContext* context = GetContext();
|
|
|
|
|
if (context) {
|
|
|
|
|
printf("\n");
|
|
|
|
|
context->Print();
|
|
|
|
|
} else {
|
|
|
|
|
printf(" (no context)\n");
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MinidumpMemoryRegion* memory = GetMemory();
|
|
|
|
|
if (memory) {
|
|
|
|
|
printf("Stack\n");
|
|
|
|
|
memory->Print();
|
|
|
|
|
} else {
|
|
|
|
|
printf("No stack\n");
|
|
|
|
|
}
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpThreadList
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t MinidumpThreadList::max_threads_ = 4096;
|
2007-05-31 17:54:06 +02:00
|
|
|
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
MinidumpThreadList::MinidumpThreadList(Minidump* minidump)
|
2006-09-07 17:56:38 +02:00
|
|
|
: MinidumpStream(minidump),
|
|
|
|
|
id_to_thread_map_(),
|
|
|
|
|
threads_(NULL),
|
|
|
|
|
thread_count_(0) {
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpThreadList::~MinidumpThreadList() {
|
|
|
|
|
delete threads_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpThreadList::Read(uint32_t expected_size) {
|
2006-09-06 04:56:44 +02:00
|
|
|
// Invalidate cached data.
|
|
|
|
|
id_to_thread_map_.clear();
|
|
|
|
|
delete threads_;
|
|
|
|
|
threads_ = NULL;
|
|
|
|
|
thread_count_ = 0;
|
|
|
|
|
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t thread_count;
|
2007-05-17 20:34:37 +02:00
|
|
|
if (expected_size < sizeof(thread_count)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadList count size mismatch, " <<
|
|
|
|
|
expected_size << " < " << sizeof(thread_count);
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
|
|
|
|
if (!minidump_->ReadBytes(&thread_count, sizeof(thread_count))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadList cannot read thread count";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (minidump_->swap())
|
|
|
|
|
Swap(&thread_count);
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
if (thread_count > numeric_limits<uint32_t>::max() / sizeof(MDRawThread)) {
|
2007-05-21 23:02:04 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpThreadList thread count " << thread_count <<
|
|
|
|
|
" would cause multiplication overflow";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
if (expected_size != sizeof(thread_count) +
|
|
|
|
|
thread_count * sizeof(MDRawThread)) {
|
2007-09-26 20:28:05 +02:00
|
|
|
// may be padded with 4 bytes on 64bit ABIs for alignment
|
|
|
|
|
if (expected_size == sizeof(thread_count) + 4 +
|
|
|
|
|
thread_count * sizeof(MDRawThread)) {
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t useless;
|
2007-09-26 20:28:05 +02:00
|
|
|
if (!minidump_->ReadBytes(&useless, 4)) {
|
2013-06-04 18:51:01 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpThreadList cannot read threadlist padded "
|
|
|
|
|
"bytes";
|
2007-09-26 20:28:05 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadList size mismatch, " << expected_size <<
|
|
|
|
|
" != " << sizeof(thread_count) +
|
|
|
|
|
thread_count * sizeof(MDRawThread);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
2010-09-09 23:15:32 +02:00
|
|
|
|
2007-05-31 17:54:06 +02:00
|
|
|
if (thread_count > max_threads_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadList count " << thread_count <<
|
|
|
|
|
" exceeds maximum " << max_threads_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (thread_count != 0) {
|
2006-11-27 22:42:07 +01:00
|
|
|
scoped_ptr<MinidumpThreads> threads(
|
|
|
|
|
new MinidumpThreads(thread_count, MinidumpThread(minidump_)));
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
for (unsigned int thread_index = 0;
|
|
|
|
|
thread_index < thread_count;
|
|
|
|
|
++thread_index) {
|
|
|
|
|
MinidumpThread* thread = &(*threads)[thread_index];
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
// Assume that the file offset is correct after the last read.
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!thread->Read()) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadList cannot read thread " <<
|
|
|
|
|
thread_index << "/" << thread_count;
|
2006-11-27 22:42:07 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t thread_id;
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!thread->GetThreadID(&thread_id)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadList cannot get thread ID for thread " <<
|
|
|
|
|
thread_index << "/" << thread_count;
|
2006-11-27 22:42:07 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-11-07 00:00:19 +01:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
if (GetThreadByID(thread_id)) {
|
|
|
|
|
// Another thread with this ID is already in the list. Data error.
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpThreadList found multiple threads with ID " <<
|
|
|
|
|
HexString(thread_id) << " at thread " <<
|
|
|
|
|
thread_index << "/" << thread_count;
|
2006-11-27 22:42:07 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
id_to_thread_map_[thread_id] = thread;
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
2006-11-27 22:42:07 +01:00
|
|
|
|
|
|
|
|
threads_ = threads.release();
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_count_ = thread_count;
|
|
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpThread* MinidumpThreadList::GetThreadAtIndex(unsigned int index)
|
|
|
|
|
const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpThreadList for GetThreadAtIndex";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index >= thread_count_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadList index out of range: " <<
|
|
|
|
|
index << "/" << thread_count_;
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
return &(*threads_)[index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
MinidumpThread* MinidumpThreadList::GetThreadByID(uint32_t thread_id) {
|
2006-09-06 04:56:44 +02:00
|
|
|
// Don't check valid_. Read calls this method before everything is
|
|
|
|
|
// validated. It is safe to not check valid_ here.
|
|
|
|
|
return id_to_thread_map_[thread_id];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MinidumpThreadList::Print() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadList cannot print invalid data";
|
2006-09-06 04:56:44 +02:00
|
|
|
return;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
printf("MinidumpThreadList\n");
|
|
|
|
|
printf(" thread_count = %d\n", thread_count_);
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
|
|
for (unsigned int thread_index = 0;
|
|
|
|
|
thread_index < thread_count_;
|
|
|
|
|
++thread_index) {
|
|
|
|
|
printf("thread[%d]\n", thread_index);
|
|
|
|
|
|
|
|
|
|
(*threads_)[thread_index].Print();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpModule
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t MinidumpModule::max_cv_bytes_ = 32768;
|
|
|
|
|
uint32_t MinidumpModule::max_misc_bytes_ = 32768;
|
2007-05-31 17:54:06 +02:00
|
|
|
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
MinidumpModule::MinidumpModule(Minidump* minidump)
|
2006-09-07 17:56:38 +02:00
|
|
|
: MinidumpObject(minidump),
|
2006-12-05 23:52:28 +01:00
|
|
|
module_valid_(false),
|
2007-10-22 18:00:35 +02:00
|
|
|
has_debug_info_(false),
|
2006-09-07 17:56:38 +02:00
|
|
|
module_(),
|
|
|
|
|
name_(NULL),
|
|
|
|
|
cv_record_(NULL),
|
2007-01-17 01:16:37 +01:00
|
|
|
cv_record_signature_(MD_CVINFOUNKNOWN_SIGNATURE),
|
2006-12-05 23:52:28 +01:00
|
|
|
misc_record_(NULL) {
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpModule::~MinidumpModule() {
|
|
|
|
|
delete name_;
|
|
|
|
|
delete cv_record_;
|
|
|
|
|
delete misc_record_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool MinidumpModule::Read() {
|
|
|
|
|
// Invalidate cached data.
|
|
|
|
|
delete name_;
|
|
|
|
|
name_ = NULL;
|
|
|
|
|
delete cv_record_;
|
|
|
|
|
cv_record_ = NULL;
|
2007-01-17 01:16:37 +01:00
|
|
|
cv_record_signature_ = MD_CVINFOUNKNOWN_SIGNATURE;
|
2006-09-06 04:56:44 +02:00
|
|
|
delete misc_record_;
|
|
|
|
|
misc_record_ = NULL;
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
module_valid_ = false;
|
2007-10-19 20:44:51 +02:00
|
|
|
has_debug_info_ = false;
|
2006-09-06 04:56:44 +02:00
|
|
|
valid_ = false;
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_->ReadBytes(&module_, MD_MODULE_SIZE)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule cannot read module";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&module_.base_of_image);
|
|
|
|
|
Swap(&module_.size_of_image);
|
|
|
|
|
Swap(&module_.checksum);
|
|
|
|
|
Swap(&module_.time_date_stamp);
|
|
|
|
|
Swap(&module_.module_name_rva);
|
|
|
|
|
Swap(&module_.version_info.signature);
|
|
|
|
|
Swap(&module_.version_info.struct_version);
|
|
|
|
|
Swap(&module_.version_info.file_version_hi);
|
|
|
|
|
Swap(&module_.version_info.file_version_lo);
|
|
|
|
|
Swap(&module_.version_info.product_version_hi);
|
|
|
|
|
Swap(&module_.version_info.product_version_lo);
|
|
|
|
|
Swap(&module_.version_info.file_flags_mask);
|
|
|
|
|
Swap(&module_.version_info.file_flags);
|
|
|
|
|
Swap(&module_.version_info.file_os);
|
|
|
|
|
Swap(&module_.version_info.file_type);
|
|
|
|
|
Swap(&module_.version_info.file_subtype);
|
|
|
|
|
Swap(&module_.version_info.file_date_hi);
|
|
|
|
|
Swap(&module_.version_info.file_date_lo);
|
|
|
|
|
Swap(&module_.cv_record);
|
|
|
|
|
Swap(&module_.misc_record);
|
|
|
|
|
// Don't swap reserved fields because their contents are unknown (as
|
|
|
|
|
// are their proper widths).
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-21 23:02:04 +02:00
|
|
|
// Check for base + size overflow or undersize.
|
|
|
|
|
if (module_.size_of_image == 0 ||
|
|
|
|
|
module_.size_of_image >
|
2013-03-06 15:04:42 +01:00
|
|
|
numeric_limits<uint64_t>::max() - module_.base_of_image) {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpModule has a module problem, " <<
|
|
|
|
|
HexString(module_.base_of_image) << "+" <<
|
2007-05-21 23:02:04 +02:00
|
|
|
HexString(module_.size_of_image);
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
module_valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool MinidumpModule::ReadAuxiliaryData() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!module_valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpModule for ReadAuxiliaryData";
|
2006-12-05 23:52:28 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
|
|
|
|
// Each module must have a name.
|
|
|
|
|
name_ = minidump_->ReadString(module_.module_name_rva);
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!name_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule could not read name";
|
2006-12-05 23:52:28 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
2007-10-19 20:44:51 +02:00
|
|
|
// At this point, we have enough info for the module to be valid.
|
|
|
|
|
valid_ = true;
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
// CodeView and miscellaneous debug records are only required if the
|
|
|
|
|
// module indicates that they exist.
|
2007-05-17 20:34:37 +02:00
|
|
|
if (module_.cv_record.data_size && !GetCVRecord(NULL)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule has no CodeView record, "
|
|
|
|
|
"but one was expected";
|
2006-12-05 23:52:28 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (module_.misc_record.data_size && !GetMiscRecord(NULL)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule has no miscellaneous debug record, "
|
|
|
|
|
"but one was expected";
|
2006-12-05 23:52:28 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
2007-10-19 20:44:51 +02:00
|
|
|
has_debug_info_ = true;
|
2006-09-06 04:56:44 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
string MinidumpModule::code_file() const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpModule for code_file";
|
2006-12-05 23:52:28 +01:00
|
|
|
return "";
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
|
|
|
|
return *name_;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
string MinidumpModule::code_identifier() const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpModule for code_identifier";
|
2006-12-05 23:52:28 +01:00
|
|
|
return "";
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
2007-10-19 20:44:51 +02:00
|
|
|
if (!has_debug_info_)
|
|
|
|
|
return "";
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
MinidumpSystemInfo *minidump_system_info = minidump_->GetSystemInfo();
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_system_info) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule code_identifier requires "
|
|
|
|
|
"MinidumpSystemInfo";
|
2006-12-05 23:52:28 +01:00
|
|
|
return "";
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
|
|
|
|
const MDRawSystemInfo *raw_system_info = minidump_system_info->system_info();
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!raw_system_info) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule code_identifier requires MDRawSystemInfo";
|
2006-12-05 23:52:28 +01:00
|
|
|
return "";
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
|
|
|
|
string identifier;
|
|
|
|
|
|
|
|
|
|
switch (raw_system_info->platform_id) {
|
|
|
|
|
case MD_OS_WIN32_NT:
|
|
|
|
|
case MD_OS_WIN32_WINDOWS: {
|
2006-12-12 22:52:56 +01:00
|
|
|
// Use the same format that the MS symbol server uses in filesystem
|
|
|
|
|
// hierarchies.
|
2006-12-05 23:52:28 +01:00
|
|
|
char identifier_string[17];
|
2006-12-12 22:52:56 +01:00
|
|
|
snprintf(identifier_string, sizeof(identifier_string), "%08X%x",
|
2006-12-05 23:52:28 +01:00
|
|
|
module_.time_date_stamp, module_.size_of_image);
|
|
|
|
|
identifier = identifier_string;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-29 23:30:48 +02:00
|
|
|
case MD_OS_MAC_OS_X:
|
2011-10-11 16:17:02 +02:00
|
|
|
case MD_OS_IOS:
|
2007-09-26 20:28:05 +02:00
|
|
|
case MD_OS_SOLARIS:
|
2012-07-04 13:56:26 +02:00
|
|
|
case MD_OS_ANDROID:
|
2013-06-04 18:51:01 +02:00
|
|
|
case MD_OS_LINUX:
|
2013-09-06 00:04:05 +02:00
|
|
|
case MD_OS_NACL:
|
2013-04-25 22:36:31 +02:00
|
|
|
case MD_OS_PS3: {
|
2006-12-05 23:52:28 +01:00
|
|
|
// TODO(mmentovai): support uuid extension if present, otherwise fall
|
|
|
|
|
// back to version (from LC_ID_DYLIB?), otherwise fall back to something
|
|
|
|
|
// else.
|
|
|
|
|
identifier = "id";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default: {
|
|
|
|
|
// Without knowing what OS generated the dump, we can't generate a good
|
|
|
|
|
// identifier. Return an empty string, signalling failure.
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpModule code_identifier requires known platform, "
|
|
|
|
|
"found " << HexString(raw_system_info->platform_id);
|
2006-12-05 23:52:28 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return identifier;
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
string MinidumpModule::debug_file() const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpModule for debug_file";
|
2006-12-05 23:52:28 +01:00
|
|
|
return "";
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
2007-10-19 20:44:51 +02:00
|
|
|
if (!has_debug_info_)
|
|
|
|
|
return "";
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
string file;
|
|
|
|
|
// Prefer the CodeView record if present.
|
2006-12-06 06:03:48 +01:00
|
|
|
if (cv_record_) {
|
2007-01-17 01:16:37 +01:00
|
|
|
if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) {
|
|
|
|
|
// It's actually an MDCVInfoPDB70 structure.
|
|
|
|
|
const MDCVInfoPDB70* cv_record_70 =
|
|
|
|
|
reinterpret_cast<const MDCVInfoPDB70*>(&(*cv_record_)[0]);
|
|
|
|
|
assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE);
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
// GetCVRecord guarantees pdb_file_name is null-terminated.
|
|
|
|
|
file = reinterpret_cast<const char*>(cv_record_70->pdb_file_name);
|
2007-01-17 01:16:37 +01:00
|
|
|
} else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) {
|
|
|
|
|
// It's actually an MDCVInfoPDB20 structure.
|
2006-12-05 23:52:28 +01:00
|
|
|
const MDCVInfoPDB20* cv_record_20 =
|
|
|
|
|
reinterpret_cast<const MDCVInfoPDB20*>(&(*cv_record_)[0]);
|
2007-01-17 01:16:37 +01:00
|
|
|
assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE);
|
2006-12-05 23:52:28 +01:00
|
|
|
|
|
|
|
|
// GetCVRecord guarantees pdb_file_name is null-terminated.
|
|
|
|
|
file = reinterpret_cast<const char*>(cv_record_20->pdb_file_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If there's a CodeView record but it doesn't match a known signature,
|
2007-01-17 01:16:37 +01:00
|
|
|
// try the miscellaneous record.
|
2006-12-05 23:52:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (file.empty()) {
|
|
|
|
|
// No usable CodeView record. Try the miscellaneous debug record.
|
2006-12-06 06:03:48 +01:00
|
|
|
if (misc_record_) {
|
|
|
|
|
const MDImageDebugMisc* misc_record =
|
|
|
|
|
reinterpret_cast<const MDImageDebugMisc *>(&(*misc_record_)[0]);
|
2006-12-05 23:52:28 +01:00
|
|
|
if (!misc_record->unicode) {
|
|
|
|
|
// If it's not Unicode, just stuff it into the string. It's unclear
|
|
|
|
|
// if misc_record->data is 0-terminated, so use an explicit size.
|
|
|
|
|
file = string(
|
|
|
|
|
reinterpret_cast<const char*>(misc_record->data),
|
2007-05-31 21:44:52 +02:00
|
|
|
module_.misc_record.data_size - MDImageDebugMisc_minsize);
|
2006-12-05 23:52:28 +01:00
|
|
|
} else {
|
|
|
|
|
// There's a misc_record but it encodes the debug filename in UTF-16.
|
|
|
|
|
// (Actually, because miscellaneous records are so old, it's probably
|
|
|
|
|
// UCS-2.) Convert it to UTF-8 for congruity with the other strings
|
|
|
|
|
// that this method (and all other methods in the Minidump family)
|
|
|
|
|
// return.
|
|
|
|
|
|
|
|
|
|
unsigned int bytes =
|
2007-05-31 21:44:52 +02:00
|
|
|
module_.misc_record.data_size - MDImageDebugMisc_minsize;
|
2006-12-05 23:52:28 +01:00
|
|
|
if (bytes % 2 == 0) {
|
|
|
|
|
unsigned int utf16_words = bytes / 2;
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
// UTF16ToUTF8 expects a vector<uint16_t>, so create a temporary one
|
2006-12-05 23:52:28 +01:00
|
|
|
// and copy the UTF-16 data into it.
|
2013-03-06 15:04:42 +01:00
|
|
|
vector<uint16_t> string_utf16(utf16_words);
|
2006-12-05 23:52:28 +01:00
|
|
|
if (utf16_words)
|
|
|
|
|
memcpy(&string_utf16[0], &misc_record->data, bytes);
|
|
|
|
|
|
|
|
|
|
// GetMiscRecord already byte-swapped the data[] field if it contains
|
|
|
|
|
// UTF-16, so pass false as the swap argument.
|
|
|
|
|
scoped_ptr<string> new_file(UTF16ToUTF8(string_utf16, false));
|
|
|
|
|
file = *new_file;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-09 23:15:32 +02:00
|
|
|
// Relatively common case
|
|
|
|
|
BPLOG_IF(INFO, file.empty()) << "MinidumpModule could not determine "
|
|
|
|
|
"debug_file for " << *name_;
|
2007-05-17 20:34:37 +02:00
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
return file;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string MinidumpModule::debug_identifier() const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpModule for debug_identifier";
|
2006-12-05 23:52:28 +01:00
|
|
|
return "";
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
2007-10-19 20:44:51 +02:00
|
|
|
if (!has_debug_info_)
|
|
|
|
|
return "";
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
string identifier;
|
|
|
|
|
|
|
|
|
|
// Use the CodeView record if present.
|
2006-12-06 06:03:48 +01:00
|
|
|
if (cv_record_) {
|
2007-01-17 01:16:37 +01:00
|
|
|
if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) {
|
|
|
|
|
// It's actually an MDCVInfoPDB70 structure.
|
|
|
|
|
const MDCVInfoPDB70* cv_record_70 =
|
|
|
|
|
reinterpret_cast<const MDCVInfoPDB70*>(&(*cv_record_)[0]);
|
|
|
|
|
assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE);
|
|
|
|
|
|
2006-12-12 22:52:56 +01:00
|
|
|
// Use the same format that the MS symbol server uses in filesystem
|
|
|
|
|
// hierarchies.
|
2006-12-05 23:52:28 +01:00
|
|
|
char identifier_string[41];
|
|
|
|
|
snprintf(identifier_string, sizeof(identifier_string),
|
2006-12-12 22:52:56 +01:00
|
|
|
"%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x",
|
2006-12-05 23:52:28 +01:00
|
|
|
cv_record_70->signature.data1,
|
|
|
|
|
cv_record_70->signature.data2,
|
|
|
|
|
cv_record_70->signature.data3,
|
|
|
|
|
cv_record_70->signature.data4[0],
|
|
|
|
|
cv_record_70->signature.data4[1],
|
|
|
|
|
cv_record_70->signature.data4[2],
|
|
|
|
|
cv_record_70->signature.data4[3],
|
|
|
|
|
cv_record_70->signature.data4[4],
|
|
|
|
|
cv_record_70->signature.data4[5],
|
|
|
|
|
cv_record_70->signature.data4[6],
|
|
|
|
|
cv_record_70->signature.data4[7],
|
|
|
|
|
cv_record_70->age);
|
|
|
|
|
identifier = identifier_string;
|
2007-01-17 01:16:37 +01:00
|
|
|
} else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) {
|
|
|
|
|
// It's actually an MDCVInfoPDB20 structure.
|
2006-12-05 23:52:28 +01:00
|
|
|
const MDCVInfoPDB20* cv_record_20 =
|
|
|
|
|
reinterpret_cast<const MDCVInfoPDB20*>(&(*cv_record_)[0]);
|
2007-01-17 01:16:37 +01:00
|
|
|
assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE);
|
2006-12-05 23:52:28 +01:00
|
|
|
|
2006-12-12 22:52:56 +01:00
|
|
|
// Use the same format that the MS symbol server uses in filesystem
|
|
|
|
|
// hierarchies.
|
2006-12-05 23:52:28 +01:00
|
|
|
char identifier_string[17];
|
|
|
|
|
snprintf(identifier_string, sizeof(identifier_string),
|
2006-12-12 22:52:56 +01:00
|
|
|
"%08X%x", cv_record_20->signature, cv_record_20->age);
|
2006-12-05 23:52:28 +01:00
|
|
|
identifier = identifier_string;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-01-17 01:16:37 +01:00
|
|
|
// TODO(mmentovai): if there's no usable CodeView record, there might be a
|
2006-12-05 23:52:28 +01:00
|
|
|
// miscellaneous debug record. It only carries a filename, though, and no
|
|
|
|
|
// identifier. I'm not sure what the right thing to do for the identifier
|
|
|
|
|
// is in that case, but I don't expect to find many modules without a
|
2007-02-14 20:51:05 +01:00
|
|
|
// CodeView record (or some other Breakpad extension structure in place of
|
2006-12-05 23:52:28 +01:00
|
|
|
// a CodeView record). Treat it as an error (empty identifier) for now.
|
|
|
|
|
|
|
|
|
|
// TODO(mmentovai): on the Mac, provide fallbacks as in code_identifier().
|
|
|
|
|
|
2010-09-09 23:15:32 +02:00
|
|
|
// Relatively common case
|
|
|
|
|
BPLOG_IF(INFO, identifier.empty()) << "MinidumpModule could not determine "
|
|
|
|
|
"debug_identifier for " << *name_;
|
2007-05-17 20:34:37 +02:00
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
return identifier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string MinidumpModule::version() const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpModule for version";
|
2006-12-05 23:52:28 +01:00
|
|
|
return "";
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
|
|
|
|
string version;
|
|
|
|
|
|
|
|
|
|
if (module_.version_info.signature == MD_VSFIXEDFILEINFO_SIGNATURE &&
|
|
|
|
|
module_.version_info.struct_version & MD_VSFIXEDFILEINFO_VERSION) {
|
|
|
|
|
char version_string[24];
|
|
|
|
|
snprintf(version_string, sizeof(version_string), "%u.%u.%u.%u",
|
|
|
|
|
module_.version_info.file_version_hi >> 16,
|
|
|
|
|
module_.version_info.file_version_hi & 0xffff,
|
|
|
|
|
module_.version_info.file_version_lo >> 16,
|
|
|
|
|
module_.version_info.file_version_lo & 0xffff);
|
|
|
|
|
version = version_string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(mmentovai): possibly support other struct types in place of
|
|
|
|
|
// the one used with MD_VSFIXEDFILEINFO_SIGNATURE. We can possibly use
|
|
|
|
|
// a different structure that better represents versioning facilities on
|
|
|
|
|
// Mac OS X and Linux, instead of forcing them to adhere to the dotted
|
|
|
|
|
// quad of 16-bit ints that Windows uses.
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG_IF(INFO, version.empty()) << "MinidumpModule could not determine "
|
|
|
|
|
"version for " << *name_;
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
return version;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const CodeModule* MinidumpModule::Copy() const {
|
|
|
|
|
return new BasicCodeModule(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
const uint8_t* MinidumpModule::GetCVRecord(uint32_t* size) {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!module_valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpModule for GetCVRecord";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (!cv_record_) {
|
2007-01-17 01:16:37 +01:00
|
|
|
// This just guards against 0-sized CodeView records; more specific checks
|
|
|
|
|
// are used when the signature is checked against various structure types.
|
2007-05-17 20:34:37 +02:00
|
|
|
if (module_.cv_record.data_size == 0) {
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_->SeekSet(module_.cv_record.rva)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule could not seek to CodeView record";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-31 17:54:06 +02:00
|
|
|
if (module_.cv_record.data_size > max_cv_bytes_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule CodeView record size " <<
|
|
|
|
|
module_.cv_record.data_size << " exceeds maximum " <<
|
|
|
|
|
max_cv_bytes_;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
// Allocating something that will be accessed as MDCVInfoPDB70 or
|
2013-03-06 15:04:42 +01:00
|
|
|
// MDCVInfoPDB20 but is allocated as uint8_t[] can cause alignment
|
2006-09-06 04:56:44 +02:00
|
|
|
// problems. x86 and ppc are able to cope, though. This allocation
|
|
|
|
|
// style is needed because the MDCVInfoPDB70 or MDCVInfoPDB20 are
|
|
|
|
|
// variable-sized due to their pdb_file_name fields; these structures
|
2007-05-31 21:44:52 +02:00
|
|
|
// are not MDCVInfoPDB70_minsize or MDCVInfoPDB20_minsize and treating
|
2006-09-06 04:56:44 +02:00
|
|
|
// them as such would result in incomplete structures or overruns.
|
2013-03-06 15:04:42 +01:00
|
|
|
scoped_ptr< vector<uint8_t> > cv_record(
|
|
|
|
|
new vector<uint8_t>(module_.cv_record.data_size));
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_->ReadBytes(&(*cv_record)[0], module_.cv_record.data_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule could not read CodeView record";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t signature = MD_CVINFOUNKNOWN_SIGNATURE;
|
2007-01-17 01:16:37 +01:00
|
|
|
if (module_.cv_record.data_size > sizeof(signature)) {
|
|
|
|
|
MDCVInfoPDB70* cv_record_signature =
|
|
|
|
|
reinterpret_cast<MDCVInfoPDB70*>(&(*cv_record)[0]);
|
|
|
|
|
signature = cv_record_signature->cv_signature;
|
|
|
|
|
if (minidump_->swap())
|
|
|
|
|
Swap(&signature);
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (signature == MD_CVINFOPDB70_SIGNATURE) {
|
|
|
|
|
// Now that the structure type is known, recheck the size.
|
2007-05-31 21:44:52 +02:00
|
|
|
if (MDCVInfoPDB70_minsize > module_.cv_record.data_size) {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpModule CodeView7 record size mismatch, " <<
|
2007-05-31 21:44:52 +02:00
|
|
|
MDCVInfoPDB70_minsize << " > " <<
|
2007-05-17 20:34:37 +02:00
|
|
|
module_.cv_record.data_size;
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
2007-01-17 01:16:37 +01:00
|
|
|
MDCVInfoPDB70* cv_record_70 =
|
|
|
|
|
reinterpret_cast<MDCVInfoPDB70*>(&(*cv_record)[0]);
|
2006-09-06 04:56:44 +02:00
|
|
|
Swap(&cv_record_70->cv_signature);
|
|
|
|
|
Swap(&cv_record_70->signature);
|
|
|
|
|
Swap(&cv_record_70->age);
|
|
|
|
|
// Don't swap cv_record_70.pdb_file_name because it's an array of 8-bit
|
2007-01-17 01:16:37 +01:00
|
|
|
// quantities. (It's a path, is it UTF-8?)
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
2007-01-17 01:16:37 +01:00
|
|
|
|
|
|
|
|
// The last field of either structure is null-terminated 8-bit character
|
|
|
|
|
// data. Ensure that it's null-terminated.
|
2007-05-17 20:34:37 +02:00
|
|
|
if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule CodeView7 record string is not "
|
|
|
|
|
"0-terminated";
|
2007-01-17 01:16:37 +01:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
} else if (signature == MD_CVINFOPDB20_SIGNATURE) {
|
2007-01-17 01:16:37 +01:00
|
|
|
// Now that the structure type is known, recheck the size.
|
2007-05-31 21:44:52 +02:00
|
|
|
if (MDCVInfoPDB20_minsize > module_.cv_record.data_size) {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpModule CodeView2 record size mismatch, " <<
|
2007-05-31 21:44:52 +02:00
|
|
|
MDCVInfoPDB20_minsize << " > " <<
|
2007-05-17 20:34:37 +02:00
|
|
|
module_.cv_record.data_size;
|
2007-01-17 01:16:37 +01:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
MDCVInfoPDB20* cv_record_20 =
|
2007-01-17 01:16:37 +01:00
|
|
|
reinterpret_cast<MDCVInfoPDB20*>(&(*cv_record)[0]);
|
2006-09-06 04:56:44 +02:00
|
|
|
Swap(&cv_record_20->cv_header.signature);
|
|
|
|
|
Swap(&cv_record_20->cv_header.offset);
|
|
|
|
|
Swap(&cv_record_20->signature);
|
|
|
|
|
Swap(&cv_record_20->age);
|
|
|
|
|
// Don't swap cv_record_20.pdb_file_name because it's an array of 8-bit
|
|
|
|
|
// quantities. (It's a path, is it UTF-8?)
|
|
|
|
|
}
|
2007-01-17 01:16:37 +01:00
|
|
|
|
|
|
|
|
// The last field of either structure is null-terminated 8-bit character
|
|
|
|
|
// data. Ensure that it's null-terminated.
|
2007-05-17 20:34:37 +02:00
|
|
|
if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') {
|
|
|
|
|
BPLOG(ERROR) << "MindumpModule CodeView2 record string is not "
|
|
|
|
|
"0-terminated";
|
2007-01-17 01:16:37 +01:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
2007-01-17 01:16:37 +01:00
|
|
|
// If the signature doesn't match something above, it's not something
|
2007-02-14 20:51:05 +01:00
|
|
|
// that Breakpad can presently handle directly. Because some modules in
|
2007-01-17 01:16:37 +01:00
|
|
|
// the wild contain such CodeView records as MD_CVINFOCV50_SIGNATURE,
|
|
|
|
|
// don't bail out here - allow the data to be returned to the user,
|
|
|
|
|
// although byte-swapping can't be done.
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
// Store the vector type because that's how storage was allocated, but
|
2013-03-06 15:04:42 +01:00
|
|
|
// return it casted to uint8_t*.
|
2006-09-06 04:56:44 +02:00
|
|
|
cv_record_ = cv_record.release();
|
2007-01-17 01:16:37 +01:00
|
|
|
cv_record_signature_ = signature;
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
2007-01-17 01:16:37 +01:00
|
|
|
if (size)
|
|
|
|
|
*size = module_.cv_record.data_size;
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
return &(*cv_record_)[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
const MDImageDebugMisc* MinidumpModule::GetMiscRecord(uint32_t* size) {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!module_valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpModule for GetMiscRecord";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (!misc_record_) {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (module_.misc_record.data_size == 0) {
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-31 21:44:52 +02:00
|
|
|
if (MDImageDebugMisc_minsize > module_.misc_record.data_size) {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record "
|
2007-05-31 21:44:52 +02:00
|
|
|
"size mismatch, " << MDImageDebugMisc_minsize << " > " <<
|
2007-05-17 20:34:37 +02:00
|
|
|
module_.misc_record.data_size;
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!minidump_->SeekSet(module_.misc_record.rva)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule could not seek to miscellaneous "
|
|
|
|
|
"debugging record";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-31 17:54:06 +02:00
|
|
|
if (module_.misc_record.data_size > max_misc_bytes_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record size " <<
|
|
|
|
|
module_.misc_record.data_size << " exceeds maximum " <<
|
|
|
|
|
max_misc_bytes_;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
// Allocating something that will be accessed as MDImageDebugMisc but
|
2013-03-06 15:04:42 +01:00
|
|
|
// is allocated as uint8_t[] can cause alignment problems. x86 and
|
2006-09-06 04:56:44 +02:00
|
|
|
// ppc are able to cope, though. This allocation style is needed
|
|
|
|
|
// because the MDImageDebugMisc is variable-sized due to its data field;
|
2007-05-31 21:44:52 +02:00
|
|
|
// this structure is not MDImageDebugMisc_minsize and treating it as such
|
2006-09-06 04:56:44 +02:00
|
|
|
// would result in an incomplete structure or an overrun.
|
2013-03-06 15:04:42 +01:00
|
|
|
scoped_ptr< vector<uint8_t> > misc_record_mem(
|
|
|
|
|
new vector<uint8_t>(module_.misc_record.data_size));
|
2006-09-06 04:56:44 +02:00
|
|
|
MDImageDebugMisc* misc_record =
|
|
|
|
|
reinterpret_cast<MDImageDebugMisc*>(&(*misc_record_mem)[0]);
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_->ReadBytes(misc_record, module_.misc_record.data_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule could not read miscellaneous debugging "
|
|
|
|
|
"record";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&misc_record->data_type);
|
|
|
|
|
Swap(&misc_record->length);
|
|
|
|
|
// Don't swap misc_record.unicode because it's an 8-bit quantity.
|
|
|
|
|
// Don't swap the reserved fields for the same reason, and because
|
|
|
|
|
// they don't contain any valid data.
|
|
|
|
|
if (misc_record->unicode) {
|
|
|
|
|
// There is a potential alignment problem, but shouldn't be a problem
|
|
|
|
|
// in practice due to the layout of MDImageDebugMisc.
|
2013-03-06 15:04:42 +01:00
|
|
|
uint16_t* data16 = reinterpret_cast<uint16_t*>(&(misc_record->data));
|
2006-09-06 04:56:44 +02:00
|
|
|
unsigned int dataBytes = module_.misc_record.data_size -
|
2007-05-31 21:44:52 +02:00
|
|
|
MDImageDebugMisc_minsize;
|
2013-08-02 20:15:57 +02:00
|
|
|
Swap(data16, dataBytes);
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (module_.misc_record.data_size != misc_record->length) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record data "
|
|
|
|
|
"size mismatch, " << module_.misc_record.data_size <<
|
|
|
|
|
" != " << misc_record->length;
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
// Store the vector type because that's how storage was allocated, but
|
|
|
|
|
// return it casted to MDImageDebugMisc*.
|
|
|
|
|
misc_record_ = misc_record_mem.release();
|
|
|
|
|
}
|
|
|
|
|
|
2007-01-17 01:16:37 +01:00
|
|
|
if (size)
|
|
|
|
|
*size = module_.misc_record.data_size;
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
return reinterpret_cast<MDImageDebugMisc*>(&(*misc_record_)[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MinidumpModule::Print() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule cannot print invalid data";
|
2006-09-06 04:56:44 +02:00
|
|
|
return;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
printf("MDRawModule\n");
|
2008-02-25 20:32:00 +01:00
|
|
|
printf(" base_of_image = 0x%" PRIx64 "\n",
|
2006-09-06 04:56:44 +02:00
|
|
|
module_.base_of_image);
|
|
|
|
|
printf(" size_of_image = 0x%x\n",
|
|
|
|
|
module_.size_of_image);
|
|
|
|
|
printf(" checksum = 0x%x\n",
|
|
|
|
|
module_.checksum);
|
2014-06-17 20:03:31 +02:00
|
|
|
printf(" time_date_stamp = 0x%x %s\n",
|
|
|
|
|
module_.time_date_stamp,
|
|
|
|
|
TimeTToUTCString(module_.time_date_stamp).c_str());
|
2006-09-06 04:56:44 +02:00
|
|
|
printf(" module_name_rva = 0x%x\n",
|
|
|
|
|
module_.module_name_rva);
|
|
|
|
|
printf(" version_info.signature = 0x%x\n",
|
|
|
|
|
module_.version_info.signature);
|
|
|
|
|
printf(" version_info.struct_version = 0x%x\n",
|
|
|
|
|
module_.version_info.struct_version);
|
|
|
|
|
printf(" version_info.file_version = 0x%x:0x%x\n",
|
|
|
|
|
module_.version_info.file_version_hi,
|
|
|
|
|
module_.version_info.file_version_lo);
|
|
|
|
|
printf(" version_info.product_version = 0x%x:0x%x\n",
|
|
|
|
|
module_.version_info.product_version_hi,
|
|
|
|
|
module_.version_info.product_version_lo);
|
|
|
|
|
printf(" version_info.file_flags_mask = 0x%x\n",
|
|
|
|
|
module_.version_info.file_flags_mask);
|
|
|
|
|
printf(" version_info.file_flags = 0x%x\n",
|
|
|
|
|
module_.version_info.file_flags);
|
|
|
|
|
printf(" version_info.file_os = 0x%x\n",
|
|
|
|
|
module_.version_info.file_os);
|
|
|
|
|
printf(" version_info.file_type = 0x%x\n",
|
|
|
|
|
module_.version_info.file_type);
|
|
|
|
|
printf(" version_info.file_subtype = 0x%x\n",
|
|
|
|
|
module_.version_info.file_subtype);
|
|
|
|
|
printf(" version_info.file_date = 0x%x:0x%x\n",
|
|
|
|
|
module_.version_info.file_date_hi,
|
|
|
|
|
module_.version_info.file_date_lo);
|
|
|
|
|
printf(" cv_record.data_size = %d\n",
|
|
|
|
|
module_.cv_record.data_size);
|
|
|
|
|
printf(" cv_record.rva = 0x%x\n",
|
|
|
|
|
module_.cv_record.rva);
|
|
|
|
|
printf(" misc_record.data_size = %d\n",
|
|
|
|
|
module_.misc_record.data_size);
|
|
|
|
|
printf(" misc_record.rva = 0x%x\n",
|
|
|
|
|
module_.misc_record.rva);
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
printf(" (code_file) = \"%s\"\n", code_file().c_str());
|
|
|
|
|
printf(" (code_identifier) = \"%s\"\n",
|
|
|
|
|
code_identifier().c_str());
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t cv_record_size;
|
|
|
|
|
const uint8_t *cv_record = GetCVRecord(&cv_record_size);
|
2006-09-06 04:56:44 +02:00
|
|
|
if (cv_record) {
|
2007-01-17 01:16:37 +01:00
|
|
|
if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) {
|
|
|
|
|
const MDCVInfoPDB70* cv_record_70 =
|
|
|
|
|
reinterpret_cast<const MDCVInfoPDB70*>(cv_record);
|
|
|
|
|
assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE);
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
printf(" (cv_record).cv_signature = 0x%x\n",
|
2007-01-17 01:16:37 +01:00
|
|
|
cv_record_70->cv_signature);
|
2006-09-06 04:56:44 +02:00
|
|
|
printf(" (cv_record).signature = %08x-%04x-%04x-%02x%02x-",
|
2007-01-17 01:16:37 +01:00
|
|
|
cv_record_70->signature.data1,
|
|
|
|
|
cv_record_70->signature.data2,
|
|
|
|
|
cv_record_70->signature.data3,
|
|
|
|
|
cv_record_70->signature.data4[0],
|
|
|
|
|
cv_record_70->signature.data4[1]);
|
2006-09-06 04:56:44 +02:00
|
|
|
for (unsigned int guidIndex = 2;
|
|
|
|
|
guidIndex < 8;
|
|
|
|
|
++guidIndex) {
|
2007-01-17 01:16:37 +01:00
|
|
|
printf("%02x", cv_record_70->signature.data4[guidIndex]);
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
printf("\n");
|
|
|
|
|
printf(" (cv_record).age = %d\n",
|
2007-01-17 01:16:37 +01:00
|
|
|
cv_record_70->age);
|
2006-09-06 04:56:44 +02:00
|
|
|
printf(" (cv_record).pdb_file_name = \"%s\"\n",
|
2007-01-17 01:16:37 +01:00
|
|
|
cv_record_70->pdb_file_name);
|
|
|
|
|
} else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) {
|
2006-09-06 04:56:44 +02:00
|
|
|
const MDCVInfoPDB20* cv_record_20 =
|
2007-01-17 01:16:37 +01:00
|
|
|
reinterpret_cast<const MDCVInfoPDB20*>(cv_record);
|
|
|
|
|
assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE);
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
printf(" (cv_record).cv_header.signature = 0x%x\n",
|
|
|
|
|
cv_record_20->cv_header.signature);
|
|
|
|
|
printf(" (cv_record).cv_header.offset = 0x%x\n",
|
|
|
|
|
cv_record_20->cv_header.offset);
|
2014-06-17 20:03:31 +02:00
|
|
|
printf(" (cv_record).signature = 0x%x %s\n",
|
|
|
|
|
cv_record_20->signature,
|
|
|
|
|
TimeTToUTCString(cv_record_20->signature).c_str());
|
2006-09-06 04:56:44 +02:00
|
|
|
printf(" (cv_record).age = %d\n",
|
|
|
|
|
cv_record_20->age);
|
|
|
|
|
printf(" (cv_record).pdb_file_name = \"%s\"\n",
|
|
|
|
|
cv_record_20->pdb_file_name);
|
2007-01-17 01:16:37 +01:00
|
|
|
} else {
|
|
|
|
|
printf(" (cv_record) = ");
|
|
|
|
|
for (unsigned int cv_byte_index = 0;
|
|
|
|
|
cv_byte_index < cv_record_size;
|
|
|
|
|
++cv_byte_index) {
|
|
|
|
|
printf("%02x", cv_record[cv_byte_index]);
|
|
|
|
|
}
|
|
|
|
|
printf("\n");
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
printf(" (cv_record) = (null)\n");
|
|
|
|
|
}
|
|
|
|
|
|
2007-01-17 01:16:37 +01:00
|
|
|
const MDImageDebugMisc* misc_record = GetMiscRecord(NULL);
|
2006-09-06 04:56:44 +02:00
|
|
|
if (misc_record) {
|
|
|
|
|
printf(" (misc_record).data_type = 0x%x\n",
|
|
|
|
|
misc_record->data_type);
|
|
|
|
|
printf(" (misc_record).length = 0x%x\n",
|
|
|
|
|
misc_record->length);
|
|
|
|
|
printf(" (misc_record).unicode = %d\n",
|
|
|
|
|
misc_record->unicode);
|
2014-06-17 20:03:31 +02:00
|
|
|
if (misc_record->unicode) {
|
|
|
|
|
string misc_record_data_utf8;
|
|
|
|
|
ConvertUTF16BufferToUTF8String(
|
|
|
|
|
reinterpret_cast<const uint16_t*>(misc_record->data),
|
|
|
|
|
misc_record->length - offsetof(MDImageDebugMisc, data),
|
|
|
|
|
&misc_record_data_utf8,
|
|
|
|
|
false); // already swapped
|
|
|
|
|
printf(" (misc_record).data = \"%s\"\n",
|
|
|
|
|
misc_record_data_utf8.c_str());
|
|
|
|
|
} else {
|
2006-09-06 04:56:44 +02:00
|
|
|
printf(" (misc_record).data = \"%s\"\n",
|
|
|
|
|
misc_record->data);
|
2014-06-17 20:03:31 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
} else {
|
|
|
|
|
printf(" (misc_record) = (null)\n");
|
|
|
|
|
}
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
printf(" (debug_file) = \"%s\"\n", debug_file().c_str());
|
|
|
|
|
printf(" (debug_identifier) = \"%s\"\n",
|
|
|
|
|
debug_identifier().c_str());
|
|
|
|
|
printf(" (version) = \"%s\"\n", version().c_str());
|
2006-09-06 04:56:44 +02:00
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpModuleList
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t MinidumpModuleList::max_modules_ = 1024;
|
2007-05-31 17:54:06 +02:00
|
|
|
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
MinidumpModuleList::MinidumpModuleList(Minidump* minidump)
|
2006-09-07 17:56:38 +02:00
|
|
|
: MinidumpStream(minidump),
|
2013-03-06 15:04:42 +01:00
|
|
|
range_map_(new RangeMap<uint64_t, unsigned int>()),
|
2006-09-07 17:56:38 +02:00
|
|
|
modules_(NULL),
|
|
|
|
|
module_count_(0) {
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpModuleList::~MinidumpModuleList() {
|
2006-11-06 20:39:47 +01:00
|
|
|
delete range_map_;
|
2006-09-06 04:56:44 +02:00
|
|
|
delete modules_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpModuleList::Read(uint32_t expected_size) {
|
2006-09-06 04:56:44 +02:00
|
|
|
// Invalidate cached data.
|
2006-11-06 20:39:47 +01:00
|
|
|
range_map_->Clear();
|
2006-09-06 04:56:44 +02:00
|
|
|
delete modules_;
|
|
|
|
|
modules_ = NULL;
|
|
|
|
|
module_count_ = 0;
|
|
|
|
|
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t module_count;
|
2007-05-17 20:34:37 +02:00
|
|
|
if (expected_size < sizeof(module_count)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModuleList count size mismatch, " <<
|
|
|
|
|
expected_size << " < " << sizeof(module_count);
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
|
|
|
|
if (!minidump_->ReadBytes(&module_count, sizeof(module_count))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModuleList could not read module count";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (minidump_->swap())
|
|
|
|
|
Swap(&module_count);
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
if (module_count > numeric_limits<uint32_t>::max() / MD_MODULE_SIZE) {
|
2007-05-21 23:02:04 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpModuleList module count " << module_count <<
|
|
|
|
|
" would cause multiplication overflow";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
if (expected_size != sizeof(module_count) +
|
|
|
|
|
module_count * MD_MODULE_SIZE) {
|
2007-09-26 20:28:05 +02:00
|
|
|
// may be padded with 4 bytes on 64bit ABIs for alignment
|
|
|
|
|
if (expected_size == sizeof(module_count) + 4 +
|
|
|
|
|
module_count * MD_MODULE_SIZE) {
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t useless;
|
2007-09-26 20:28:05 +02:00
|
|
|
if (!minidump_->ReadBytes(&useless, 4)) {
|
2013-06-04 18:51:01 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpModuleList cannot read modulelist padded "
|
|
|
|
|
"bytes";
|
2007-09-26 20:28:05 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModuleList size mismatch, " << expected_size <<
|
|
|
|
|
" != " << sizeof(module_count) +
|
|
|
|
|
module_count * MD_MODULE_SIZE;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
2007-05-31 17:54:06 +02:00
|
|
|
if (module_count > max_modules_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModuleList count " << module_count_ <<
|
|
|
|
|
" exceeds maximum " << max_modules_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (module_count != 0) {
|
2006-11-27 22:42:07 +01:00
|
|
|
scoped_ptr<MinidumpModules> modules(
|
|
|
|
|
new MinidumpModules(module_count, MinidumpModule(minidump_)));
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
for (unsigned int module_index = 0;
|
|
|
|
|
module_index < module_count;
|
|
|
|
|
++module_index) {
|
|
|
|
|
MinidumpModule* module = &(*modules)[module_index];
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
// Assume that the file offset is correct after the last read.
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!module->Read()) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModuleList could not read module " <<
|
|
|
|
|
module_index << "/" << module_count;
|
2006-11-27 22:42:07 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Loop through the module list once more to read additional data and
|
|
|
|
|
// build the range map. This is done in a second pass because
|
|
|
|
|
// MinidumpModule::ReadAuxiliaryData seeks around, and if it were
|
|
|
|
|
// included in the loop above, additional seeks would be needed where
|
|
|
|
|
// none are now to read contiguous data.
|
2015-08-10 19:03:29 +02:00
|
|
|
uint64_t last_end_address = 0;
|
2006-12-05 23:52:28 +01:00
|
|
|
for (unsigned int module_index = 0;
|
|
|
|
|
module_index < module_count;
|
|
|
|
|
++module_index) {
|
|
|
|
|
MinidumpModule* module = &(*modules)[module_index];
|
|
|
|
|
|
2008-04-04 23:41:50 +02:00
|
|
|
// ReadAuxiliaryData fails if any data that the module indicates should
|
|
|
|
|
// exist is missing, but we treat some such cases as valid anyway. See
|
|
|
|
|
// issue #222: if a debugging record is of a format that's too large to
|
|
|
|
|
// handle, it shouldn't render the entire dump invalid. Check module
|
|
|
|
|
// validity before giving up.
|
|
|
|
|
if (!module->ReadAuxiliaryData() && !module->valid()) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModuleList could not read required module "
|
|
|
|
|
"auxiliary data for module " <<
|
|
|
|
|
module_index << "/" << module_count;
|
|
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// It is safe to use module->code_file() after successfully calling
|
2008-04-04 23:41:50 +02:00
|
|
|
// module->ReadAuxiliaryData or noting that the module is valid.
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint64_t base_address = module->base_address();
|
|
|
|
|
uint64_t module_size = module->size();
|
|
|
|
|
if (base_address == static_cast<uint64_t>(-1)) {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpModuleList found bad base address "
|
|
|
|
|
"for module " << module_index << "/" << module_count <<
|
|
|
|
|
", " << module->code_file();
|
2006-11-27 22:42:07 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!range_map_->StoreRange(base_address, module_size, module_index)) {
|
2014-12-20 01:47:07 +01:00
|
|
|
// Android's shared memory implementation /dev/ashmem can contain
|
|
|
|
|
// duplicate entries for JITted code, so ignore these.
|
|
|
|
|
// TODO(wfh): Remove this code when Android is fixed.
|
|
|
|
|
// See https://crbug.com/439531
|
|
|
|
|
const string kDevAshmem("/dev/ashmem/");
|
|
|
|
|
if (module->code_file().compare(
|
|
|
|
|
0, kDevAshmem.length(), kDevAshmem) != 0) {
|
2015-08-10 19:03:29 +02:00
|
|
|
if (base_address < last_end_address) {
|
|
|
|
|
// If failed due to apparent range overlap the cause may be
|
|
|
|
|
// the client correction applied for Android packed relocations.
|
|
|
|
|
// If this is the case, back out the client correction and retry.
|
|
|
|
|
module_size -= last_end_address - base_address;
|
|
|
|
|
base_address = last_end_address;
|
|
|
|
|
if (!range_map_->StoreRange(base_address,
|
|
|
|
|
module_size, module_index)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModuleList could not store module " <<
|
|
|
|
|
module_index << "/" << module_count << ", " <<
|
|
|
|
|
module->code_file() << ", " <<
|
|
|
|
|
HexString(base_address) << "+" <<
|
|
|
|
|
HexString(module_size) << ", after adjusting";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModuleList could not store module " <<
|
|
|
|
|
module_index << "/" << module_count << ", " <<
|
|
|
|
|
module->code_file() << ", " <<
|
|
|
|
|
HexString(base_address) << "+" <<
|
|
|
|
|
HexString(module_size);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-12-20 01:47:07 +01:00
|
|
|
} else {
|
|
|
|
|
BPLOG(INFO) << "MinidumpModuleList ignoring overlapping module " <<
|
|
|
|
|
module_index << "/" << module_count << ", " <<
|
|
|
|
|
module->code_file() << ", " <<
|
|
|
|
|
HexString(base_address) << "+" <<
|
|
|
|
|
HexString(module_size);
|
|
|
|
|
}
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2015-08-10 19:03:29 +02:00
|
|
|
last_end_address = base_address + module_size;
|
2006-11-27 22:42:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
modules_ = modules.release();
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module_count_ = module_count;
|
|
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
const MinidumpModule* MinidumpModuleList::GetModuleForAddress(
|
2013-03-06 15:04:42 +01:00
|
|
|
uint64_t address) const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleForAddress";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
unsigned int module_index;
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!range_map_->RetrieveRange(address, &module_index, NULL, NULL)) {
|
|
|
|
|
BPLOG(INFO) << "MinidumpModuleList has no module at " <<
|
|
|
|
|
HexString(address);
|
2006-12-05 23:52:28 +01:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
|
|
|
|
return GetModuleAtIndex(module_index);
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
const MinidumpModule* MinidumpModuleList::GetMainModule() const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpModuleList for GetMainModule";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
// The main code module is the first one present in a minidump file's
|
|
|
|
|
// MDRawModuleList.
|
2012-03-30 23:59:16 +02:00
|
|
|
return GetModuleAtIndex(0);
|
2006-12-05 23:52:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const MinidumpModule* MinidumpModuleList::GetModuleAtSequence(
|
|
|
|
|
unsigned int sequence) const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtSequence";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sequence >= module_count_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModuleList sequence out of range: " <<
|
|
|
|
|
sequence << "/" << module_count_;
|
2006-12-05 23:52:28 +01:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
unsigned int module_index;
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index, NULL, NULL)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModuleList has no module at sequence " << sequence;
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
return GetModuleAtIndex(module_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-12-05 23:52:28 +01:00
|
|
|
const MinidumpModule* MinidumpModuleList::GetModuleAtIndex(
|
|
|
|
|
unsigned int index) const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtIndex";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index >= module_count_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModuleList index out of range: " <<
|
|
|
|
|
index << "/" << module_count_;
|
2006-12-05 23:52:28 +01:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
|
|
|
|
return &(*modules_)[index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const CodeModules* MinidumpModuleList::Copy() const {
|
|
|
|
|
return new BasicCodeModules(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
void MinidumpModuleList::Print() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModuleList cannot print invalid data";
|
2006-09-06 04:56:44 +02:00
|
|
|
return;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
printf("MinidumpModuleList\n");
|
|
|
|
|
printf(" module_count = %d\n", module_count_);
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
|
|
for (unsigned int module_index = 0;
|
|
|
|
|
module_index < module_count_;
|
|
|
|
|
++module_index) {
|
|
|
|
|
printf("module[%d]\n", module_index);
|
|
|
|
|
|
|
|
|
|
(*modules_)[module_index].Print();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpMemoryList
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t MinidumpMemoryList::max_regions_ = 4096;
|
2007-05-31 17:54:06 +02:00
|
|
|
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
MinidumpMemoryList::MinidumpMemoryList(Minidump* minidump)
|
2006-09-07 17:56:38 +02:00
|
|
|
: MinidumpStream(minidump),
|
2013-03-06 15:04:42 +01:00
|
|
|
range_map_(new RangeMap<uint64_t, unsigned int>()),
|
2006-09-07 17:56:38 +02:00
|
|
|
descriptors_(NULL),
|
|
|
|
|
regions_(NULL),
|
|
|
|
|
region_count_(0) {
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpMemoryList::~MinidumpMemoryList() {
|
2006-11-06 20:39:47 +01:00
|
|
|
delete range_map_;
|
2006-09-06 04:56:44 +02:00
|
|
|
delete descriptors_;
|
|
|
|
|
delete regions_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpMemoryList::Read(uint32_t expected_size) {
|
2006-09-06 04:56:44 +02:00
|
|
|
// Invalidate cached data.
|
|
|
|
|
delete descriptors_;
|
|
|
|
|
descriptors_ = NULL;
|
|
|
|
|
delete regions_;
|
|
|
|
|
regions_ = NULL;
|
2006-11-06 20:39:47 +01:00
|
|
|
range_map_->Clear();
|
2006-09-06 04:56:44 +02:00
|
|
|
region_count_ = 0;
|
|
|
|
|
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t region_count;
|
2007-05-17 20:34:37 +02:00
|
|
|
if (expected_size < sizeof(region_count)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryList count size mismatch, " <<
|
|
|
|
|
expected_size << " < " << sizeof(region_count);
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
|
|
|
|
if (!minidump_->ReadBytes(®ion_count, sizeof(region_count))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryList could not read memory region count";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (minidump_->swap())
|
|
|
|
|
Swap(®ion_count);
|
|
|
|
|
|
2007-05-21 23:02:04 +02:00
|
|
|
if (region_count >
|
2013-03-06 15:04:42 +01:00
|
|
|
numeric_limits<uint32_t>::max() / sizeof(MDMemoryDescriptor)) {
|
2007-05-21 23:02:04 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpMemoryList region count " << region_count <<
|
|
|
|
|
" would cause multiplication overflow";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
if (expected_size != sizeof(region_count) +
|
|
|
|
|
region_count * sizeof(MDMemoryDescriptor)) {
|
2007-09-26 20:28:05 +02:00
|
|
|
// may be padded with 4 bytes on 64bit ABIs for alignment
|
|
|
|
|
if (expected_size == sizeof(region_count) + 4 +
|
|
|
|
|
region_count * sizeof(MDMemoryDescriptor)) {
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t useless;
|
2007-09-26 20:28:05 +02:00
|
|
|
if (!minidump_->ReadBytes(&useless, 4)) {
|
2013-06-04 18:51:01 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpMemoryList cannot read memorylist padded "
|
|
|
|
|
"bytes";
|
2007-09-26 20:28:05 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryList size mismatch, " << expected_size <<
|
2013-06-04 18:51:01 +02:00
|
|
|
" != " << sizeof(region_count) +
|
2007-09-26 20:28:05 +02:00
|
|
|
region_count * sizeof(MDMemoryDescriptor);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
2007-05-31 17:54:06 +02:00
|
|
|
if (region_count > max_regions_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryList count " << region_count <<
|
|
|
|
|
" exceeds maximum " << max_regions_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (region_count != 0) {
|
2006-11-27 22:42:07 +01:00
|
|
|
scoped_ptr<MemoryDescriptors> descriptors(
|
|
|
|
|
new MemoryDescriptors(region_count));
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
// Read the entire array in one fell swoop, instead of reading one entry
|
|
|
|
|
// at a time in the loop.
|
|
|
|
|
if (!minidump_->ReadBytes(&(*descriptors)[0],
|
|
|
|
|
sizeof(MDMemoryDescriptor) * region_count)) {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpMemoryList could not read memory region list";
|
2006-11-27 22:42:07 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
scoped_ptr<MemoryRegions> regions(
|
|
|
|
|
new MemoryRegions(region_count, MinidumpMemoryRegion(minidump_)));
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
for (unsigned int region_index = 0;
|
|
|
|
|
region_index < region_count;
|
|
|
|
|
++region_index) {
|
|
|
|
|
MDMemoryDescriptor* descriptor = &(*descriptors)[region_index];
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
if (minidump_->swap())
|
|
|
|
|
Swap(descriptor);
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint64_t base_address = descriptor->start_of_memory_range;
|
|
|
|
|
uint32_t region_size = descriptor->memory.data_size;
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-21 23:02:04 +02:00
|
|
|
// Check for base + size overflow or undersize.
|
|
|
|
|
if (region_size == 0 ||
|
2013-03-06 15:04:42 +01:00
|
|
|
region_size > numeric_limits<uint64_t>::max() - base_address) {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpMemoryList has a memory region problem, " <<
|
|
|
|
|
" region " << region_index << "/" << region_count <<
|
|
|
|
|
", " << HexString(base_address) << "+" <<
|
2007-05-21 23:02:04 +02:00
|
|
|
HexString(region_size);
|
2006-11-27 22:42:07 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!range_map_->StoreRange(base_address, region_size, region_index)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryList could not store memory region " <<
|
|
|
|
|
region_index << "/" << region_count << ", " <<
|
|
|
|
|
HexString(base_address) << "+" <<
|
|
|
|
|
HexString(region_size);
|
2006-11-27 22:42:07 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-11-27 22:42:07 +01:00
|
|
|
|
|
|
|
|
(*regions)[region_index].SetDescriptor(descriptor);
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
descriptors_ = descriptors.release();
|
|
|
|
|
regions_ = regions.release();
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
region_count_ = region_count;
|
|
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionAtIndex(
|
|
|
|
|
unsigned int index) {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionAtIndex";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index >= region_count_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryList index out of range: " <<
|
|
|
|
|
index << "/" << region_count_;
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
return &(*regions_)[index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionForAddress(
|
2013-03-06 15:04:42 +01:00
|
|
|
uint64_t address) {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionForAddress";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
unsigned int region_index;
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!range_map_->RetrieveRange(address, ®ion_index, NULL, NULL)) {
|
|
|
|
|
BPLOG(INFO) << "MinidumpMemoryList has no memory region at " <<
|
|
|
|
|
HexString(address);
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
return GetMemoryRegionAtIndex(region_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MinidumpMemoryList::Print() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryList cannot print invalid data";
|
2006-09-06 04:56:44 +02:00
|
|
|
return;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
printf("MinidumpMemoryList\n");
|
|
|
|
|
printf(" region_count = %d\n", region_count_);
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
|
|
for (unsigned int region_index = 0;
|
|
|
|
|
region_index < region_count_;
|
|
|
|
|
++region_index) {
|
|
|
|
|
MDMemoryDescriptor* descriptor = &(*descriptors_)[region_index];
|
|
|
|
|
printf("region[%d]\n", region_index);
|
|
|
|
|
printf("MDMemoryDescriptor\n");
|
2008-02-25 20:32:00 +01:00
|
|
|
printf(" start_of_memory_range = 0x%" PRIx64 "\n",
|
2006-09-06 04:56:44 +02:00
|
|
|
descriptor->start_of_memory_range);
|
|
|
|
|
printf(" memory.data_size = 0x%x\n", descriptor->memory.data_size);
|
|
|
|
|
printf(" memory.rva = 0x%x\n", descriptor->memory.rva);
|
|
|
|
|
MinidumpMemoryRegion* region = GetMemoryRegionAtIndex(region_index);
|
|
|
|
|
if (region) {
|
|
|
|
|
printf("Memory\n");
|
|
|
|
|
region->Print();
|
|
|
|
|
} else {
|
|
|
|
|
printf("No memory\n");
|
|
|
|
|
}
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpException
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpException::MinidumpException(Minidump* minidump)
|
2006-09-07 17:56:38 +02:00
|
|
|
: MinidumpStream(minidump),
|
|
|
|
|
exception_(),
|
|
|
|
|
context_(NULL) {
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpException::~MinidumpException() {
|
|
|
|
|
delete context_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpException::Read(uint32_t expected_size) {
|
2006-09-06 04:56:44 +02:00
|
|
|
// Invalidate cached data.
|
|
|
|
|
delete context_;
|
|
|
|
|
context_ = NULL;
|
|
|
|
|
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (expected_size != sizeof(exception_)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpException size mismatch, " << expected_size <<
|
|
|
|
|
" != " << sizeof(exception_);
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_->ReadBytes(&exception_, sizeof(exception_))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpException cannot read exception";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&exception_.thread_id);
|
|
|
|
|
// exception_.__align is for alignment only and does not need to be
|
|
|
|
|
// swapped.
|
|
|
|
|
Swap(&exception_.exception_record.exception_code);
|
|
|
|
|
Swap(&exception_.exception_record.exception_flags);
|
|
|
|
|
Swap(&exception_.exception_record.exception_record);
|
|
|
|
|
Swap(&exception_.exception_record.exception_address);
|
|
|
|
|
Swap(&exception_.exception_record.number_parameters);
|
|
|
|
|
// exception_.exception_record.__align is for alignment only and does not
|
|
|
|
|
// need to be swapped.
|
|
|
|
|
for (unsigned int parameter_index = 0;
|
|
|
|
|
parameter_index < MD_EXCEPTION_MAXIMUM_PARAMETERS;
|
|
|
|
|
++parameter_index) {
|
|
|
|
|
Swap(&exception_.exception_record.exception_information[parameter_index]);
|
|
|
|
|
}
|
|
|
|
|
Swap(&exception_.thread_context);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpException::GetThreadID(uint32_t *thread_id) const {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG_IF(ERROR, !thread_id) << "MinidumpException::GetThreadID requires "
|
|
|
|
|
"|thread_id|";
|
|
|
|
|
assert(thread_id);
|
|
|
|
|
*thread_id = 0;
|
|
|
|
|
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpException for GetThreadID";
|
2006-11-07 00:00:19 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-11-07 00:00:19 +01:00
|
|
|
|
|
|
|
|
*thread_id = exception_.thread_id;
|
|
|
|
|
return true;
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpContext* MinidumpException::GetContext() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpException for GetContext";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (!context_) {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_->SeekSet(exception_.thread_context.rva)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpException cannot seek to context";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-10-23 22:25:42 +02:00
|
|
|
scoped_ptr<MinidumpContext> context(new MinidumpContext(minidump_));
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2009-12-19 22:43:53 +01:00
|
|
|
// Don't log as an error if we can still fall back on the thread's context
|
|
|
|
|
// (which must be possible if we got this far.)
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!context->Read(exception_.thread_context.data_size)) {
|
2009-10-19 20:10:49 +02:00
|
|
|
BPLOG(INFO) << "MinidumpException cannot read context";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
context_ = context.release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return context_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MinidumpException::Print() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpException cannot print invalid data";
|
2006-09-06 04:56:44 +02:00
|
|
|
return;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
printf("MDException\n");
|
|
|
|
|
printf(" thread_id = 0x%x\n",
|
|
|
|
|
exception_.thread_id);
|
|
|
|
|
printf(" exception_record.exception_code = 0x%x\n",
|
|
|
|
|
exception_.exception_record.exception_code);
|
|
|
|
|
printf(" exception_record.exception_flags = 0x%x\n",
|
|
|
|
|
exception_.exception_record.exception_flags);
|
2008-02-25 20:32:00 +01:00
|
|
|
printf(" exception_record.exception_record = 0x%" PRIx64 "\n",
|
2006-09-06 04:56:44 +02:00
|
|
|
exception_.exception_record.exception_record);
|
2008-02-25 20:32:00 +01:00
|
|
|
printf(" exception_record.exception_address = 0x%" PRIx64 "\n",
|
2006-09-06 04:56:44 +02:00
|
|
|
exception_.exception_record.exception_address);
|
|
|
|
|
printf(" exception_record.number_parameters = %d\n",
|
|
|
|
|
exception_.exception_record.number_parameters);
|
|
|
|
|
for (unsigned int parameterIndex = 0;
|
|
|
|
|
parameterIndex < exception_.exception_record.number_parameters;
|
|
|
|
|
++parameterIndex) {
|
2008-02-25 20:32:00 +01:00
|
|
|
printf(" exception_record.exception_information[%2d] = 0x%" PRIx64 "\n",
|
2006-09-06 04:56:44 +02:00
|
|
|
parameterIndex,
|
|
|
|
|
exception_.exception_record.exception_information[parameterIndex]);
|
|
|
|
|
}
|
|
|
|
|
printf(" thread_context.data_size = %d\n",
|
|
|
|
|
exception_.thread_context.data_size);
|
|
|
|
|
printf(" thread_context.rva = 0x%x\n",
|
|
|
|
|
exception_.thread_context.rva);
|
|
|
|
|
MinidumpContext* context = GetContext();
|
|
|
|
|
if (context) {
|
|
|
|
|
printf("\n");
|
|
|
|
|
context->Print();
|
|
|
|
|
} else {
|
|
|
|
|
printf(" (no context)\n");
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-02 18:43:57 +01:00
|
|
|
//
|
|
|
|
|
// MinidumpAssertion
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpAssertion::MinidumpAssertion(Minidump* minidump)
|
|
|
|
|
: MinidumpStream(minidump),
|
|
|
|
|
assertion_(),
|
|
|
|
|
expression_(),
|
|
|
|
|
function_(),
|
|
|
|
|
file_() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpAssertion::~MinidumpAssertion() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpAssertion::Read(uint32_t expected_size) {
|
2009-12-02 18:43:57 +01:00
|
|
|
// Invalidate cached data.
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
|
|
|
|
if (expected_size != sizeof(assertion_)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpAssertion size mismatch, " << expected_size <<
|
|
|
|
|
" != " << sizeof(assertion_);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!minidump_->ReadBytes(&assertion_, sizeof(assertion_))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpAssertion cannot read assertion";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Each of {expression, function, file} is a UTF-16 string,
|
|
|
|
|
// we'll convert them to UTF-8 for ease of use.
|
2013-08-02 20:15:57 +02:00
|
|
|
ConvertUTF16BufferToUTF8String(assertion_.expression,
|
|
|
|
|
sizeof(assertion_.expression), &expression_,
|
|
|
|
|
minidump_->swap());
|
|
|
|
|
ConvertUTF16BufferToUTF8String(assertion_.function,
|
|
|
|
|
sizeof(assertion_.function), &function_,
|
|
|
|
|
minidump_->swap());
|
|
|
|
|
ConvertUTF16BufferToUTF8String(assertion_.file, sizeof(assertion_.file),
|
|
|
|
|
&file_, minidump_->swap());
|
2009-12-02 18:43:57 +01:00
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&assertion_.line);
|
|
|
|
|
Swap(&assertion_.type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MinidumpAssertion::Print() {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpAssertion cannot print invalid data";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("MDAssertion\n");
|
|
|
|
|
printf(" expression = %s\n",
|
|
|
|
|
expression_.c_str());
|
|
|
|
|
printf(" function = %s\n",
|
|
|
|
|
function_.c_str());
|
|
|
|
|
printf(" file = %s\n",
|
|
|
|
|
file_.c_str());
|
|
|
|
|
printf(" line = %u\n",
|
|
|
|
|
assertion_.line);
|
|
|
|
|
printf(" type = %u\n",
|
|
|
|
|
assertion_.type);
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpSystemInfo
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpSystemInfo::MinidumpSystemInfo(Minidump* minidump)
|
2006-09-07 17:56:38 +02:00
|
|
|
: MinidumpStream(minidump),
|
|
|
|
|
system_info_(),
|
2006-10-24 21:31:21 +02:00
|
|
|
csd_version_(NULL),
|
|
|
|
|
cpu_vendor_(NULL) {
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpSystemInfo::~MinidumpSystemInfo() {
|
|
|
|
|
delete csd_version_;
|
2006-10-24 21:31:21 +02:00
|
|
|
delete cpu_vendor_;
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpSystemInfo::Read(uint32_t expected_size) {
|
2006-09-06 04:56:44 +02:00
|
|
|
// Invalidate cached data.
|
|
|
|
|
delete csd_version_;
|
|
|
|
|
csd_version_ = NULL;
|
2006-10-24 21:31:21 +02:00
|
|
|
delete cpu_vendor_;
|
|
|
|
|
cpu_vendor_ = NULL;
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (expected_size != sizeof(system_info_)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpSystemInfo size mismatch, " << expected_size <<
|
|
|
|
|
" != " << sizeof(system_info_);
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_->ReadBytes(&system_info_, sizeof(system_info_))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpSystemInfo cannot read system info";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&system_info_.processor_architecture);
|
|
|
|
|
Swap(&system_info_.processor_level);
|
|
|
|
|
Swap(&system_info_.processor_revision);
|
|
|
|
|
// number_of_processors and product_type are 8-bit quantities and need no
|
|
|
|
|
// swapping.
|
|
|
|
|
Swap(&system_info_.major_version);
|
|
|
|
|
Swap(&system_info_.minor_version);
|
|
|
|
|
Swap(&system_info_.build_number);
|
|
|
|
|
Swap(&system_info_.platform_id);
|
|
|
|
|
Swap(&system_info_.csd_version_rva);
|
|
|
|
|
Swap(&system_info_.suite_mask);
|
2006-09-22 03:10:25 +02:00
|
|
|
// Don't swap the reserved2 field because its contents are unknown.
|
|
|
|
|
|
|
|
|
|
if (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 ||
|
|
|
|
|
system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64) {
|
|
|
|
|
for (unsigned int i = 0; i < 3; ++i)
|
|
|
|
|
Swap(&system_info_.cpu.x86_cpu_info.vendor_id[i]);
|
|
|
|
|
Swap(&system_info_.cpu.x86_cpu_info.version_information);
|
|
|
|
|
Swap(&system_info_.cpu.x86_cpu_info.feature_information);
|
|
|
|
|
Swap(&system_info_.cpu.x86_cpu_info.amd_extended_cpu_features);
|
|
|
|
|
} else {
|
|
|
|
|
for (unsigned int i = 0; i < 2; ++i)
|
|
|
|
|
Swap(&system_info_.cpu.other_cpu_info.processor_features[i]);
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-01-10 23:47:56 +01:00
|
|
|
string MinidumpSystemInfo::GetOS() {
|
2012-06-29 00:46:01 +02:00
|
|
|
string os;
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetOS";
|
2012-06-29 00:46:01 +02:00
|
|
|
return os;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2007-01-10 23:47:56 +01:00
|
|
|
|
|
|
|
|
switch (system_info_.platform_id) {
|
|
|
|
|
case MD_OS_WIN32_NT:
|
|
|
|
|
case MD_OS_WIN32_WINDOWS:
|
|
|
|
|
os = "windows";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MD_OS_MAC_OS_X:
|
|
|
|
|
os = "mac";
|
|
|
|
|
break;
|
|
|
|
|
|
2011-10-11 16:17:02 +02:00
|
|
|
case MD_OS_IOS:
|
|
|
|
|
os = "ios";
|
|
|
|
|
break;
|
|
|
|
|
|
2007-01-10 23:47:56 +01:00
|
|
|
case MD_OS_LINUX:
|
|
|
|
|
os = "linux";
|
|
|
|
|
break;
|
2007-05-17 20:34:37 +02:00
|
|
|
|
2007-09-26 20:28:05 +02:00
|
|
|
case MD_OS_SOLARIS:
|
|
|
|
|
os = "solaris";
|
2012-07-04 13:56:26 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MD_OS_ANDROID:
|
|
|
|
|
os = "android";
|
2007-09-26 20:28:05 +02:00
|
|
|
break;
|
|
|
|
|
|
2013-04-25 22:36:31 +02:00
|
|
|
case MD_OS_PS3:
|
|
|
|
|
os = "ps3";
|
|
|
|
|
break;
|
|
|
|
|
|
2013-05-07 01:33:02 +02:00
|
|
|
case MD_OS_NACL:
|
|
|
|
|
os = "nacl";
|
|
|
|
|
break;
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
default:
|
|
|
|
|
BPLOG(ERROR) << "MinidumpSystemInfo unknown OS for platform " <<
|
|
|
|
|
HexString(system_info_.platform_id);
|
|
|
|
|
break;
|
2007-01-10 23:47:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return os;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string MinidumpSystemInfo::GetCPU() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPU";
|
2007-01-10 23:47:56 +01:00
|
|
|
return "";
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2007-01-10 23:47:56 +01:00
|
|
|
|
|
|
|
|
string cpu;
|
|
|
|
|
|
|
|
|
|
switch (system_info_.processor_architecture) {
|
|
|
|
|
case MD_CPU_ARCHITECTURE_X86:
|
|
|
|
|
case MD_CPU_ARCHITECTURE_X86_WIN64:
|
|
|
|
|
cpu = "x86";
|
|
|
|
|
break;
|
|
|
|
|
|
2009-12-19 22:43:53 +01:00
|
|
|
case MD_CPU_ARCHITECTURE_AMD64:
|
|
|
|
|
cpu = "x86-64";
|
|
|
|
|
break;
|
|
|
|
|
|
2007-01-10 23:47:56 +01:00
|
|
|
case MD_CPU_ARCHITECTURE_PPC:
|
|
|
|
|
cpu = "ppc";
|
|
|
|
|
break;
|
2007-05-17 20:34:37 +02:00
|
|
|
|
2013-04-13 01:24:02 +02:00
|
|
|
case MD_CPU_ARCHITECTURE_PPC64:
|
|
|
|
|
cpu = "ppc64";
|
|
|
|
|
break;
|
|
|
|
|
|
2008-03-18 17:10:10 +01:00
|
|
|
case MD_CPU_ARCHITECTURE_SPARC:
|
|
|
|
|
cpu = "sparc";
|
|
|
|
|
break;
|
|
|
|
|
|
2009-12-19 22:43:53 +01:00
|
|
|
case MD_CPU_ARCHITECTURE_ARM:
|
|
|
|
|
cpu = "arm";
|
|
|
|
|
break;
|
|
|
|
|
|
2013-11-23 02:45:20 +01:00
|
|
|
case MD_CPU_ARCHITECTURE_ARM64:
|
|
|
|
|
cpu = "arm64";
|
|
|
|
|
break;
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
default:
|
|
|
|
|
BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " <<
|
|
|
|
|
HexString(system_info_.processor_architecture);
|
|
|
|
|
break;
|
2007-01-10 23:47:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cpu;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
const string* MinidumpSystemInfo::GetCSDVersion() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCSDVersion";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (!csd_version_)
|
|
|
|
|
csd_version_ = minidump_->ReadString(system_info_.csd_version_rva);
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG_IF(ERROR, !csd_version_) << "MinidumpSystemInfo could not read "
|
|
|
|
|
"CSD version";
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
return csd_version_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-10-24 21:31:21 +02:00
|
|
|
const string* MinidumpSystemInfo::GetCPUVendor() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPUVendor";
|
2006-10-24 21:31:21 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-10-24 21:31:21 +02:00
|
|
|
|
|
|
|
|
// CPU vendor information can only be determined from x86 minidumps.
|
|
|
|
|
if (!cpu_vendor_ &&
|
|
|
|
|
(system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 ||
|
|
|
|
|
system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64)) {
|
|
|
|
|
char cpu_vendor_string[13];
|
|
|
|
|
snprintf(cpu_vendor_string, sizeof(cpu_vendor_string),
|
|
|
|
|
"%c%c%c%c%c%c%c%c%c%c%c%c",
|
|
|
|
|
system_info_.cpu.x86_cpu_info.vendor_id[0] & 0xff,
|
|
|
|
|
(system_info_.cpu.x86_cpu_info.vendor_id[0] >> 8) & 0xff,
|
|
|
|
|
(system_info_.cpu.x86_cpu_info.vendor_id[0] >> 16) & 0xff,
|
|
|
|
|
(system_info_.cpu.x86_cpu_info.vendor_id[0] >> 24) & 0xff,
|
|
|
|
|
system_info_.cpu.x86_cpu_info.vendor_id[1] & 0xff,
|
|
|
|
|
(system_info_.cpu.x86_cpu_info.vendor_id[1] >> 8) & 0xff,
|
|
|
|
|
(system_info_.cpu.x86_cpu_info.vendor_id[1] >> 16) & 0xff,
|
|
|
|
|
(system_info_.cpu.x86_cpu_info.vendor_id[1] >> 24) & 0xff,
|
|
|
|
|
system_info_.cpu.x86_cpu_info.vendor_id[2] & 0xff,
|
|
|
|
|
(system_info_.cpu.x86_cpu_info.vendor_id[2] >> 8) & 0xff,
|
|
|
|
|
(system_info_.cpu.x86_cpu_info.vendor_id[2] >> 16) & 0xff,
|
|
|
|
|
(system_info_.cpu.x86_cpu_info.vendor_id[2] >> 24) & 0xff);
|
|
|
|
|
cpu_vendor_ = new string(cpu_vendor_string);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cpu_vendor_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
void MinidumpSystemInfo::Print() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpSystemInfo cannot print invalid data";
|
2006-09-06 04:56:44 +02:00
|
|
|
return;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
printf("MDRawSystemInfo\n");
|
2014-06-03 21:35:41 +02:00
|
|
|
printf(" processor_architecture = 0x%x\n",
|
2006-09-06 04:56:44 +02:00
|
|
|
system_info_.processor_architecture);
|
|
|
|
|
printf(" processor_level = %d\n",
|
|
|
|
|
system_info_.processor_level);
|
2006-10-25 21:41:56 +02:00
|
|
|
printf(" processor_revision = 0x%x\n",
|
|
|
|
|
system_info_.processor_revision);
|
2006-09-06 04:56:44 +02:00
|
|
|
printf(" number_of_processors = %d\n",
|
|
|
|
|
system_info_.number_of_processors);
|
|
|
|
|
printf(" product_type = %d\n",
|
|
|
|
|
system_info_.product_type);
|
|
|
|
|
printf(" major_version = %d\n",
|
|
|
|
|
system_info_.major_version);
|
|
|
|
|
printf(" minor_version = %d\n",
|
|
|
|
|
system_info_.minor_version);
|
|
|
|
|
printf(" build_number = %d\n",
|
|
|
|
|
system_info_.build_number);
|
2014-06-03 21:35:41 +02:00
|
|
|
printf(" platform_id = 0x%x\n",
|
2006-09-06 04:56:44 +02:00
|
|
|
system_info_.platform_id);
|
|
|
|
|
printf(" csd_version_rva = 0x%x\n",
|
|
|
|
|
system_info_.csd_version_rva);
|
|
|
|
|
printf(" suite_mask = 0x%x\n",
|
|
|
|
|
system_info_.suite_mask);
|
2014-06-17 20:03:31 +02:00
|
|
|
if (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 ||
|
|
|
|
|
system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64) {
|
|
|
|
|
printf(" cpu.x86_cpu_info (valid):\n");
|
|
|
|
|
} else {
|
|
|
|
|
printf(" cpu.x86_cpu_info (invalid):\n");
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
for (unsigned int i = 0; i < 3; ++i) {
|
|
|
|
|
printf(" cpu.x86_cpu_info.vendor_id[%d] = 0x%x\n",
|
|
|
|
|
i, system_info_.cpu.x86_cpu_info.vendor_id[i]);
|
|
|
|
|
}
|
|
|
|
|
printf(" cpu.x86_cpu_info.version_information = 0x%x\n",
|
|
|
|
|
system_info_.cpu.x86_cpu_info.version_information);
|
|
|
|
|
printf(" cpu.x86_cpu_info.feature_information = 0x%x\n",
|
|
|
|
|
system_info_.cpu.x86_cpu_info.feature_information);
|
|
|
|
|
printf(" cpu.x86_cpu_info.amd_extended_cpu_features = 0x%x\n",
|
|
|
|
|
system_info_.cpu.x86_cpu_info.amd_extended_cpu_features);
|
2014-06-17 20:03:31 +02:00
|
|
|
if (system_info_.processor_architecture != MD_CPU_ARCHITECTURE_X86 &&
|
|
|
|
|
system_info_.processor_architecture != MD_CPU_ARCHITECTURE_X86_WIN64) {
|
|
|
|
|
printf(" cpu.other_cpu_info (valid):\n");
|
|
|
|
|
for (unsigned int i = 0; i < 2; ++i) {
|
|
|
|
|
printf(" cpu.other_cpu_info.processor_features[%d] = 0x%" PRIx64 "\n",
|
|
|
|
|
i, system_info_.cpu.other_cpu_info.processor_features[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-10-24 21:31:21 +02:00
|
|
|
const string* csd_version = GetCSDVersion();
|
|
|
|
|
if (csd_version) {
|
2006-09-06 04:56:44 +02:00
|
|
|
printf(" (csd_version) = \"%s\"\n",
|
2006-10-24 21:31:21 +02:00
|
|
|
csd_version->c_str());
|
|
|
|
|
} else {
|
2006-09-06 04:56:44 +02:00
|
|
|
printf(" (csd_version) = (null)\n");
|
2006-10-24 21:31:21 +02:00
|
|
|
}
|
|
|
|
|
const string* cpu_vendor = GetCPUVendor();
|
|
|
|
|
if (cpu_vendor) {
|
|
|
|
|
printf(" (cpu_vendor) = \"%s\"\n",
|
|
|
|
|
cpu_vendor->c_str());
|
|
|
|
|
} else {
|
|
|
|
|
printf(" (cpu_vendor) = (null)\n");
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpMiscInfo
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpMiscInfo::MinidumpMiscInfo(Minidump* minidump)
|
2006-09-07 17:56:38 +02:00
|
|
|
: MinidumpStream(minidump),
|
|
|
|
|
misc_info_() {
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpMiscInfo::Read(uint32_t expected_size) {
|
2006-09-06 04:56:44 +02:00
|
|
|
valid_ = false;
|
|
|
|
|
|
|
|
|
|
if (expected_size != MD_MISCINFO_SIZE &&
|
2013-08-02 20:15:57 +02:00
|
|
|
expected_size != MD_MISCINFO2_SIZE &&
|
|
|
|
|
expected_size != MD_MISCINFO3_SIZE &&
|
|
|
|
|
expected_size != MD_MISCINFO4_SIZE) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << expected_size
|
|
|
|
|
<< " != " << MD_MISCINFO_SIZE << ", " << MD_MISCINFO2_SIZE
|
|
|
|
|
<< ", " << MD_MISCINFO3_SIZE << ", " << MD_MISCINFO4_SIZE
|
|
|
|
|
<< ")";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_->ReadBytes(&misc_info_, expected_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMiscInfo cannot read miscellaneous info";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
2013-08-02 20:15:57 +02:00
|
|
|
// Swap version 1 fields
|
2006-09-06 04:56:44 +02:00
|
|
|
Swap(&misc_info_.size_of_info);
|
|
|
|
|
Swap(&misc_info_.flags1);
|
|
|
|
|
Swap(&misc_info_.process_id);
|
|
|
|
|
Swap(&misc_info_.process_create_time);
|
|
|
|
|
Swap(&misc_info_.process_user_time);
|
|
|
|
|
Swap(&misc_info_.process_kernel_time);
|
|
|
|
|
if (misc_info_.size_of_info > MD_MISCINFO_SIZE) {
|
2013-08-02 20:15:57 +02:00
|
|
|
// Swap version 2 fields
|
2006-09-06 04:56:44 +02:00
|
|
|
Swap(&misc_info_.processor_max_mhz);
|
|
|
|
|
Swap(&misc_info_.processor_current_mhz);
|
|
|
|
|
Swap(&misc_info_.processor_mhz_limit);
|
|
|
|
|
Swap(&misc_info_.processor_max_idle_state);
|
|
|
|
|
Swap(&misc_info_.processor_current_idle_state);
|
|
|
|
|
}
|
2013-08-02 20:15:57 +02:00
|
|
|
if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) {
|
|
|
|
|
// Swap version 3 fields
|
|
|
|
|
Swap(&misc_info_.process_integrity_level);
|
|
|
|
|
Swap(&misc_info_.process_execute_flags);
|
|
|
|
|
Swap(&misc_info_.protected_process);
|
|
|
|
|
Swap(&misc_info_.time_zone_id);
|
|
|
|
|
Swap(&misc_info_.time_zone);
|
|
|
|
|
}
|
|
|
|
|
if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) {
|
|
|
|
|
// Swap version 4 fields.
|
|
|
|
|
// Do not swap UTF-16 strings. The swap is done as part of the
|
|
|
|
|
// conversion to UTF-8 (code follows below).
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
2007-05-21 22:09:33 +02:00
|
|
|
if (expected_size != misc_info_.size_of_info) {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " <<
|
2007-05-21 22:09:33 +02:00
|
|
|
expected_size << " != " << misc_info_.size_of_info;
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2013-08-02 20:15:57 +02:00
|
|
|
// Convert UTF-16 strings
|
|
|
|
|
if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) {
|
|
|
|
|
// Convert UTF-16 strings in version 3 fields
|
|
|
|
|
ConvertUTF16BufferToUTF8String(misc_info_.time_zone.standard_name,
|
|
|
|
|
sizeof(misc_info_.time_zone.standard_name),
|
|
|
|
|
&standard_name_, minidump_->swap());
|
|
|
|
|
ConvertUTF16BufferToUTF8String(misc_info_.time_zone.daylight_name,
|
|
|
|
|
sizeof(misc_info_.time_zone.daylight_name),
|
|
|
|
|
&daylight_name_, minidump_->swap());
|
|
|
|
|
}
|
|
|
|
|
if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) {
|
|
|
|
|
// Convert UTF-16 strings in version 4 fields
|
|
|
|
|
ConvertUTF16BufferToUTF8String(misc_info_.build_string,
|
|
|
|
|
sizeof(misc_info_.build_string),
|
|
|
|
|
&build_string_, minidump_->swap());
|
|
|
|
|
ConvertUTF16BufferToUTF8String(misc_info_.dbg_bld_str,
|
|
|
|
|
sizeof(misc_info_.dbg_bld_str),
|
|
|
|
|
&dbg_bld_str_, minidump_->swap());
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MinidumpMiscInfo::Print() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMiscInfo cannot print invalid data";
|
2006-09-06 04:56:44 +02:00
|
|
|
return;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
printf("MDRawMiscInfo\n");
|
2013-08-02 20:15:57 +02:00
|
|
|
// Print version 1 fields
|
2006-09-06 04:56:44 +02:00
|
|
|
printf(" size_of_info = %d\n", misc_info_.size_of_info);
|
|
|
|
|
printf(" flags1 = 0x%x\n", misc_info_.flags1);
|
2014-06-03 21:35:41 +02:00
|
|
|
printf(" process_id = ");
|
|
|
|
|
PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_ID,
|
|
|
|
|
kNumberFormatDecimal, misc_info_.process_id);
|
|
|
|
|
if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES) {
|
|
|
|
|
printf(" process_create_time = 0x%x %s\n",
|
|
|
|
|
misc_info_.process_create_time,
|
2014-06-17 20:03:31 +02:00
|
|
|
TimeTToUTCString(misc_info_.process_create_time).c_str());
|
2014-06-03 21:35:41 +02:00
|
|
|
} else {
|
|
|
|
|
printf(" process_create_time = (invalid)\n");
|
|
|
|
|
}
|
|
|
|
|
printf(" process_user_time = ");
|
|
|
|
|
PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES,
|
|
|
|
|
kNumberFormatDecimal, misc_info_.process_user_time);
|
|
|
|
|
printf(" process_kernel_time = ");
|
|
|
|
|
PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES,
|
|
|
|
|
kNumberFormatDecimal, misc_info_.process_kernel_time);
|
2006-09-06 04:56:44 +02:00
|
|
|
if (misc_info_.size_of_info > MD_MISCINFO_SIZE) {
|
2013-08-02 20:15:57 +02:00
|
|
|
// Print version 2 fields
|
2014-06-03 21:35:41 +02:00
|
|
|
printf(" processor_max_mhz = ");
|
|
|
|
|
PrintValueOrInvalid(misc_info_.flags1 &
|
|
|
|
|
MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
|
|
|
|
|
kNumberFormatDecimal, misc_info_.processor_max_mhz);
|
|
|
|
|
printf(" processor_current_mhz = ");
|
|
|
|
|
PrintValueOrInvalid(misc_info_.flags1 &
|
|
|
|
|
MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
|
|
|
|
|
kNumberFormatDecimal, misc_info_.processor_current_mhz);
|
|
|
|
|
printf(" processor_mhz_limit = ");
|
|
|
|
|
PrintValueOrInvalid(misc_info_.flags1 &
|
|
|
|
|
MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
|
|
|
|
|
kNumberFormatDecimal, misc_info_.processor_mhz_limit);
|
|
|
|
|
printf(" processor_max_idle_state = ");
|
|
|
|
|
PrintValueOrInvalid(misc_info_.flags1 &
|
|
|
|
|
MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
|
|
|
|
|
kNumberFormatDecimal,
|
|
|
|
|
misc_info_.processor_max_idle_state);
|
|
|
|
|
printf(" processor_current_idle_state = ");
|
|
|
|
|
PrintValueOrInvalid(misc_info_.flags1 &
|
|
|
|
|
MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
|
|
|
|
|
kNumberFormatDecimal,
|
|
|
|
|
misc_info_.processor_current_idle_state);
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
2013-08-02 20:15:57 +02:00
|
|
|
if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) {
|
|
|
|
|
// Print version 3 fields
|
2014-06-03 21:35:41 +02:00
|
|
|
printf(" process_integrity_level = ");
|
|
|
|
|
PrintValueOrInvalid(misc_info_.flags1 &
|
|
|
|
|
MD_MISCINFO_FLAGS1_PROCESS_INTEGRITY,
|
|
|
|
|
kNumberFormatHexadecimal,
|
|
|
|
|
misc_info_.process_integrity_level);
|
|
|
|
|
printf(" process_execute_flags = ");
|
|
|
|
|
PrintValueOrInvalid(misc_info_.flags1 &
|
|
|
|
|
MD_MISCINFO_FLAGS1_PROCESS_EXECUTE_FLAGS,
|
|
|
|
|
kNumberFormatHexadecimal,
|
|
|
|
|
misc_info_.process_execute_flags);
|
|
|
|
|
printf(" protected_process = ");
|
|
|
|
|
PrintValueOrInvalid(misc_info_.flags1 &
|
|
|
|
|
MD_MISCINFO_FLAGS1_PROTECTED_PROCESS,
|
|
|
|
|
kNumberFormatDecimal, misc_info_.protected_process);
|
|
|
|
|
printf(" time_zone_id = ");
|
|
|
|
|
PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_TIMEZONE,
|
|
|
|
|
kNumberFormatDecimal, misc_info_.time_zone_id);
|
|
|
|
|
if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_TIMEZONE) {
|
|
|
|
|
printf(" time_zone.bias = %d\n",
|
|
|
|
|
misc_info_.time_zone.bias);
|
|
|
|
|
printf(" time_zone.standard_name = %s\n", standard_name_.c_str());
|
|
|
|
|
printf(" time_zone.standard_date = "
|
|
|
|
|
"%04d-%02d-%02d (%d) %02d:%02d:%02d.%03d\n",
|
|
|
|
|
misc_info_.time_zone.standard_date.year,
|
|
|
|
|
misc_info_.time_zone.standard_date.month,
|
|
|
|
|
misc_info_.time_zone.standard_date.day,
|
|
|
|
|
misc_info_.time_zone.standard_date.day_of_week,
|
|
|
|
|
misc_info_.time_zone.standard_date.hour,
|
|
|
|
|
misc_info_.time_zone.standard_date.minute,
|
|
|
|
|
misc_info_.time_zone.standard_date.second,
|
|
|
|
|
misc_info_.time_zone.standard_date.milliseconds);
|
|
|
|
|
printf(" time_zone.standard_bias = %d\n",
|
|
|
|
|
misc_info_.time_zone.standard_bias);
|
|
|
|
|
printf(" time_zone.daylight_name = %s\n", daylight_name_.c_str());
|
|
|
|
|
printf(" time_zone.daylight_date = "
|
|
|
|
|
"%04d-%02d-%02d (%d) %02d:%02d:%02d.%03d\n",
|
|
|
|
|
misc_info_.time_zone.daylight_date.year,
|
|
|
|
|
misc_info_.time_zone.daylight_date.month,
|
|
|
|
|
misc_info_.time_zone.daylight_date.day,
|
|
|
|
|
misc_info_.time_zone.daylight_date.day_of_week,
|
|
|
|
|
misc_info_.time_zone.daylight_date.hour,
|
|
|
|
|
misc_info_.time_zone.daylight_date.minute,
|
|
|
|
|
misc_info_.time_zone.daylight_date.second,
|
|
|
|
|
misc_info_.time_zone.daylight_date.milliseconds);
|
|
|
|
|
printf(" time_zone.daylight_bias = %d\n",
|
|
|
|
|
misc_info_.time_zone.daylight_bias);
|
|
|
|
|
} else {
|
|
|
|
|
printf(" time_zone.bias = (invalid)\n");
|
|
|
|
|
printf(" time_zone.standard_name = (invalid)\n");
|
|
|
|
|
printf(" time_zone.standard_date = (invalid)\n");
|
|
|
|
|
printf(" time_zone.standard_bias = (invalid)\n");
|
|
|
|
|
printf(" time_zone.daylight_name = (invalid)\n");
|
|
|
|
|
printf(" time_zone.daylight_date = (invalid)\n");
|
|
|
|
|
printf(" time_zone.daylight_bias = (invalid)\n");
|
|
|
|
|
}
|
2013-08-02 20:15:57 +02:00
|
|
|
}
|
|
|
|
|
if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) {
|
|
|
|
|
// Print version 4 fields
|
2014-06-03 21:35:41 +02:00
|
|
|
if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_BUILDSTRING) {
|
|
|
|
|
printf(" build_string = %s\n", build_string_.c_str());
|
|
|
|
|
printf(" dbg_bld_str = %s\n", dbg_bld_str_.c_str());
|
|
|
|
|
} else {
|
|
|
|
|
printf(" build_string = (invalid)\n");
|
|
|
|
|
printf(" dbg_bld_str = (invalid)\n");
|
|
|
|
|
}
|
2013-08-02 20:15:57 +02:00
|
|
|
}
|
2006-11-07 00:00:19 +01:00
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
2007-02-14 20:51:05 +01:00
|
|
|
// MinidumpBreakpadInfo
|
2006-11-07 00:00:19 +01:00
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
2007-02-14 20:51:05 +01:00
|
|
|
MinidumpBreakpadInfo::MinidumpBreakpadInfo(Minidump* minidump)
|
2006-11-07 00:00:19 +01:00
|
|
|
: MinidumpStream(minidump),
|
2007-02-14 20:51:05 +01:00
|
|
|
breakpad_info_() {
|
2006-11-07 00:00:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpBreakpadInfo::Read(uint32_t expected_size) {
|
2006-11-07 00:00:19 +01:00
|
|
|
valid_ = false;
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (expected_size != sizeof(breakpad_info_)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpBreakpadInfo size mismatch, " << expected_size <<
|
|
|
|
|
" != " << sizeof(breakpad_info_);
|
2006-11-07 00:00:19 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-11-07 00:00:19 +01:00
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!minidump_->ReadBytes(&breakpad_info_, sizeof(breakpad_info_))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpBreakpadInfo cannot read Breakpad info";
|
2006-11-07 00:00:19 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-11-07 00:00:19 +01:00
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
2007-02-14 20:51:05 +01:00
|
|
|
Swap(&breakpad_info_.validity);
|
|
|
|
|
Swap(&breakpad_info_.dump_thread_id);
|
|
|
|
|
Swap(&breakpad_info_.requesting_thread_id);
|
2006-11-07 00:00:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpBreakpadInfo::GetDumpThreadID(uint32_t *thread_id) const {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetDumpThreadID "
|
|
|
|
|
"requires |thread_id|";
|
|
|
|
|
assert(thread_id);
|
|
|
|
|
*thread_id = 0;
|
|
|
|
|
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetDumpThreadID";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(breakpad_info_.validity & MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID)) {
|
|
|
|
|
BPLOG(INFO) << "MinidumpBreakpadInfo has no dump thread";
|
2006-11-07 00:00:19 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2007-02-14 20:51:05 +01:00
|
|
|
*thread_id = breakpad_info_.dump_thread_id;
|
2006-11-07 00:00:19 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpBreakpadInfo::GetRequestingThreadID(uint32_t *thread_id)
|
2006-11-07 00:00:19 +01:00
|
|
|
const {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetRequestingThreadID "
|
|
|
|
|
"requires |thread_id|";
|
|
|
|
|
assert(thread_id);
|
|
|
|
|
*thread_id = 0;
|
|
|
|
|
|
|
|
|
|
if (!thread_id || !valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetRequestingThreadID";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(breakpad_info_.validity &
|
|
|
|
|
MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID)) {
|
|
|
|
|
BPLOG(INFO) << "MinidumpBreakpadInfo has no requesting thread";
|
2006-11-07 00:00:19 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2007-02-14 20:51:05 +01:00
|
|
|
*thread_id = breakpad_info_.requesting_thread_id;
|
2006-11-07 00:00:19 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-02-14 20:51:05 +01:00
|
|
|
void MinidumpBreakpadInfo::Print() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpBreakpadInfo cannot print invalid data";
|
2006-11-07 00:00:19 +01:00
|
|
|
return;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-11-07 00:00:19 +01:00
|
|
|
|
2007-02-14 20:51:05 +01:00
|
|
|
printf("MDRawBreakpadInfo\n");
|
|
|
|
|
printf(" validity = 0x%x\n", breakpad_info_.validity);
|
2014-06-03 21:35:41 +02:00
|
|
|
printf(" dump_thread_id = ");
|
|
|
|
|
PrintValueOrInvalid(breakpad_info_.validity &
|
|
|
|
|
MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID,
|
|
|
|
|
kNumberFormatHexadecimal, breakpad_info_.dump_thread_id);
|
|
|
|
|
printf(" requesting_thread_id = ");
|
|
|
|
|
PrintValueOrInvalid(breakpad_info_.validity &
|
|
|
|
|
MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID,
|
|
|
|
|
kNumberFormatHexadecimal,
|
|
|
|
|
breakpad_info_.requesting_thread_id);
|
2006-11-07 00:00:19 +01:00
|
|
|
|
|
|
|
|
printf("\n");
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-01-13 20:05:33 +01:00
|
|
|
//
|
|
|
|
|
// MinidumpMemoryInfo
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpMemoryInfo::MinidumpMemoryInfo(Minidump* minidump)
|
|
|
|
|
: MinidumpObject(minidump),
|
|
|
|
|
memory_info_() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool MinidumpMemoryInfo::IsExecutable() const {
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t protection =
|
2011-01-13 20:05:33 +01:00
|
|
|
memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK;
|
|
|
|
|
return protection == MD_MEMORY_PROTECT_EXECUTE ||
|
|
|
|
|
protection == MD_MEMORY_PROTECT_EXECUTE_READ ||
|
|
|
|
|
protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool MinidumpMemoryInfo::IsWritable() const {
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t protection =
|
2011-01-13 20:05:33 +01:00
|
|
|
memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK;
|
|
|
|
|
return protection == MD_MEMORY_PROTECT_READWRITE ||
|
|
|
|
|
protection == MD_MEMORY_PROTECT_WRITECOPY ||
|
|
|
|
|
protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE ||
|
|
|
|
|
protection == MD_MEMORY_PROTECT_EXECUTE_WRITECOPY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool MinidumpMemoryInfo::Read() {
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
|
|
|
|
if (!minidump_->ReadBytes(&memory_info_, sizeof(memory_info_))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryInfo cannot read memory info";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&memory_info_.base_address);
|
|
|
|
|
Swap(&memory_info_.allocation_base);
|
|
|
|
|
Swap(&memory_info_.allocation_protection);
|
|
|
|
|
Swap(&memory_info_.region_size);
|
|
|
|
|
Swap(&memory_info_.state);
|
|
|
|
|
Swap(&memory_info_.protection);
|
|
|
|
|
Swap(&memory_info_.type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for base + size overflow or undersize.
|
|
|
|
|
if (memory_info_.region_size == 0 ||
|
2013-03-06 15:04:42 +01:00
|
|
|
memory_info_.region_size > numeric_limits<uint64_t>::max() -
|
2011-01-13 20:05:33 +01:00
|
|
|
memory_info_.base_address) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryInfo has a memory region problem, " <<
|
|
|
|
|
HexString(memory_info_.base_address) << "+" <<
|
|
|
|
|
HexString(memory_info_.region_size);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MinidumpMemoryInfo::Print() {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryInfo cannot print invalid data";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("MDRawMemoryInfo\n");
|
|
|
|
|
printf(" base_address = 0x%" PRIx64 "\n",
|
|
|
|
|
memory_info_.base_address);
|
|
|
|
|
printf(" allocation_base = 0x%" PRIx64 "\n",
|
|
|
|
|
memory_info_.allocation_base);
|
|
|
|
|
printf(" allocation_protection = 0x%x\n",
|
|
|
|
|
memory_info_.allocation_protection);
|
|
|
|
|
printf(" region_size = 0x%" PRIx64 "\n", memory_info_.region_size);
|
|
|
|
|
printf(" state = 0x%x\n", memory_info_.state);
|
|
|
|
|
printf(" protection = 0x%x\n", memory_info_.protection);
|
|
|
|
|
printf(" type = 0x%x\n", memory_info_.type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpMemoryInfoList
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpMemoryInfoList::MinidumpMemoryInfoList(Minidump* minidump)
|
|
|
|
|
: MinidumpStream(minidump),
|
2013-03-06 15:04:42 +01:00
|
|
|
range_map_(new RangeMap<uint64_t, unsigned int>()),
|
2011-01-13 20:05:33 +01:00
|
|
|
infos_(NULL),
|
|
|
|
|
info_count_(0) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpMemoryInfoList::~MinidumpMemoryInfoList() {
|
|
|
|
|
delete range_map_;
|
|
|
|
|
delete infos_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool MinidumpMemoryInfoList::Read(uint32_t expected_size) {
|
2011-01-13 20:05:33 +01:00
|
|
|
// Invalidate cached data.
|
|
|
|
|
delete infos_;
|
|
|
|
|
infos_ = NULL;
|
|
|
|
|
range_map_->Clear();
|
|
|
|
|
info_count_ = 0;
|
|
|
|
|
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
|
|
|
|
MDRawMemoryInfoList header;
|
|
|
|
|
if (expected_size < sizeof(MDRawMemoryInfoList)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " <<
|
|
|
|
|
expected_size << " < " << sizeof(MDRawMemoryInfoList);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!minidump_->ReadBytes(&header, sizeof(header))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryInfoList could not read header";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&header.size_of_header);
|
|
|
|
|
Swap(&header.size_of_entry);
|
|
|
|
|
Swap(&header.number_of_entries);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sanity check that the header is the expected size.
|
2013-06-04 18:51:01 +02:00
|
|
|
// TODO(ted): could possibly handle this more gracefully, assuming
|
2011-01-13 20:05:33 +01:00
|
|
|
// that future versions of the structs would be backwards-compatible.
|
|
|
|
|
if (header.size_of_header != sizeof(MDRawMemoryInfoList)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " <<
|
|
|
|
|
header.size_of_header << " != " <<
|
|
|
|
|
sizeof(MDRawMemoryInfoList);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sanity check that the entries are the expected size.
|
|
|
|
|
if (header.size_of_entry != sizeof(MDRawMemoryInfo)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryInfoList entry size mismatch, " <<
|
|
|
|
|
header.size_of_entry << " != " <<
|
|
|
|
|
sizeof(MDRawMemoryInfo);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (header.number_of_entries >
|
2013-03-06 15:04:42 +01:00
|
|
|
numeric_limits<uint32_t>::max() / sizeof(MDRawMemoryInfo)) {
|
2011-01-13 20:05:33 +01:00
|
|
|
BPLOG(ERROR) << "MinidumpMemoryInfoList info count " <<
|
|
|
|
|
header.number_of_entries <<
|
|
|
|
|
" would cause multiplication overflow";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (expected_size != sizeof(MDRawMemoryInfoList) +
|
|
|
|
|
header.number_of_entries * sizeof(MDRawMemoryInfo)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryInfoList size mismatch, " << expected_size <<
|
|
|
|
|
" != " << sizeof(MDRawMemoryInfoList) +
|
|
|
|
|
header.number_of_entries * sizeof(MDRawMemoryInfo);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-04 18:51:01 +02:00
|
|
|
// Check for data loss when converting header.number_of_entries from
|
|
|
|
|
// uint64_t into MinidumpMemoryInfos::size_type (uint32_t)
|
|
|
|
|
MinidumpMemoryInfos::size_type header_number_of_entries =
|
|
|
|
|
static_cast<unsigned int>(header.number_of_entries);
|
|
|
|
|
if (static_cast<uint64_t>(header_number_of_entries) !=
|
|
|
|
|
header.number_of_entries) {
|
|
|
|
|
BPLOG(ERROR) << "Data loss detected when converting "
|
|
|
|
|
"the header's number_of_entries";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-13 20:05:33 +01:00
|
|
|
if (header.number_of_entries != 0) {
|
|
|
|
|
scoped_ptr<MinidumpMemoryInfos> infos(
|
2013-06-04 18:51:01 +02:00
|
|
|
new MinidumpMemoryInfos(header_number_of_entries,
|
2011-01-13 20:05:33 +01:00
|
|
|
MinidumpMemoryInfo(minidump_)));
|
|
|
|
|
|
|
|
|
|
for (unsigned int index = 0;
|
|
|
|
|
index < header.number_of_entries;
|
|
|
|
|
++index) {
|
|
|
|
|
MinidumpMemoryInfo* info = &(*infos)[index];
|
|
|
|
|
|
|
|
|
|
// Assume that the file offset is correct after the last read.
|
|
|
|
|
if (!info->Read()) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryInfoList cannot read info " <<
|
|
|
|
|
index << "/" << header.number_of_entries;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint64_t base_address = info->GetBase();
|
2013-06-04 18:51:01 +02:00
|
|
|
uint64_t region_size = info->GetSize();
|
2011-01-13 20:05:33 +01:00
|
|
|
|
|
|
|
|
if (!range_map_->StoreRange(base_address, region_size, index)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryInfoList could not store"
|
|
|
|
|
" memory region " <<
|
|
|
|
|
index << "/" << header.number_of_entries << ", " <<
|
|
|
|
|
HexString(base_address) << "+" <<
|
|
|
|
|
HexString(region_size);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
infos_ = infos.release();
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-04 18:51:01 +02:00
|
|
|
info_count_ = header_number_of_entries;
|
2011-01-13 20:05:33 +01:00
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoAtIndex(
|
|
|
|
|
unsigned int index) const {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for GetMemoryInfoAtIndex";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index >= info_count_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryInfoList index out of range: " <<
|
|
|
|
|
index << "/" << info_count_;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &(*infos_)[index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoForAddress(
|
2013-03-06 15:04:42 +01:00
|
|
|
uint64_t address) const {
|
2011-01-13 20:05:33 +01:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for"
|
|
|
|
|
" GetMemoryInfoForAddress";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int info_index;
|
|
|
|
|
if (!range_map_->RetrieveRange(address, &info_index, NULL, NULL)) {
|
|
|
|
|
BPLOG(INFO) << "MinidumpMemoryInfoList has no memory info at " <<
|
|
|
|
|
HexString(address);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GetMemoryInfoAtIndex(info_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MinidumpMemoryInfoList::Print() {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryInfoList cannot print invalid data";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("MinidumpMemoryInfoList\n");
|
|
|
|
|
printf(" info_count = %d\n", info_count_);
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
|
|
for (unsigned int info_index = 0;
|
|
|
|
|
info_index < info_count_;
|
|
|
|
|
++info_index) {
|
|
|
|
|
printf("info[%d]\n", info_index);
|
|
|
|
|
(*infos_)[info_index].Print();
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Add support for Linux memory mapping stream and remove ELF header usage
when checking exploitability rating.
Linux minidumps do not support MD_MEMORY_INFO_LIST_STREAM, meaning the
processor cannot retrieve its memory mappings. However, it has its own
stream, MD_LINUX_MAPS, which contains memory mappings specific to Linux
(it contains the contents of /proc/self/maps). This CL allows the minidump
to gather information from the memory mappings for Linux minidumps.
In addition, exploitability rating for Linux dumps now use memory mappings
instead of checking the ELF headers of binaries. The basis for the change
is that checking the ELF headers requires the minidumps to store the memory
from the ELF headers, while the memory mapping data is already present,
meaning the size of a minidump will be unchanged.
As a result, of removing ELF header analysis, two unit tests have been removed.
Arguably, the cases that those unit tests check do not merit a high
exploitability rating and do not warrant a solid conclusion that was given
earlier.
R=ivanpe@chromium.org
Review URL: https://codereview.chromium.org/1251593007
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1476 4c0a9323-5329-0410-9bdc-e9ce6186880e
2015-07-28 02:53:44 +02:00
|
|
|
//
|
|
|
|
|
// MinidumpLinuxMaps
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
MinidumpLinuxMaps::MinidumpLinuxMaps(Minidump *minidump)
|
|
|
|
|
: MinidumpObject(minidump) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MinidumpLinuxMaps::Print() {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpLinuxMaps cannot print invalid data";
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-08-11 18:05:48 +02:00
|
|
|
std::cout << region_.line << std::endl;
|
Add support for Linux memory mapping stream and remove ELF header usage
when checking exploitability rating.
Linux minidumps do not support MD_MEMORY_INFO_LIST_STREAM, meaning the
processor cannot retrieve its memory mappings. However, it has its own
stream, MD_LINUX_MAPS, which contains memory mappings specific to Linux
(it contains the contents of /proc/self/maps). This CL allows the minidump
to gather information from the memory mappings for Linux minidumps.
In addition, exploitability rating for Linux dumps now use memory mappings
instead of checking the ELF headers of binaries. The basis for the change
is that checking the ELF headers requires the minidumps to store the memory
from the ELF headers, while the memory mapping data is already present,
meaning the size of a minidump will be unchanged.
As a result, of removing ELF header analysis, two unit tests have been removed.
Arguably, the cases that those unit tests check do not merit a high
exploitability rating and do not warrant a solid conclusion that was given
earlier.
R=ivanpe@chromium.org
Review URL: https://codereview.chromium.org/1251593007
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1476 4c0a9323-5329-0410-9bdc-e9ce6186880e
2015-07-28 02:53:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpLinuxMapsList
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
MinidumpLinuxMapsList::MinidumpLinuxMapsList(Minidump *minidump)
|
|
|
|
|
: MinidumpStream(minidump),
|
|
|
|
|
maps_(NULL),
|
|
|
|
|
maps_count_(0) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MinidumpLinuxMapsList::~MinidumpLinuxMapsList() {
|
2015-07-31 17:26:39 +02:00
|
|
|
if (maps_) {
|
|
|
|
|
for (unsigned int i = 0; i < maps_->size(); i++) {
|
|
|
|
|
delete (*maps_)[i];
|
|
|
|
|
}
|
|
|
|
|
delete maps_;
|
Add support for Linux memory mapping stream and remove ELF header usage
when checking exploitability rating.
Linux minidumps do not support MD_MEMORY_INFO_LIST_STREAM, meaning the
processor cannot retrieve its memory mappings. However, it has its own
stream, MD_LINUX_MAPS, which contains memory mappings specific to Linux
(it contains the contents of /proc/self/maps). This CL allows the minidump
to gather information from the memory mappings for Linux minidumps.
In addition, exploitability rating for Linux dumps now use memory mappings
instead of checking the ELF headers of binaries. The basis for the change
is that checking the ELF headers requires the minidumps to store the memory
from the ELF headers, while the memory mapping data is already present,
meaning the size of a minidump will be unchanged.
As a result, of removing ELF header analysis, two unit tests have been removed.
Arguably, the cases that those unit tests check do not merit a high
exploitability rating and do not warrant a solid conclusion that was given
earlier.
R=ivanpe@chromium.org
Review URL: https://codereview.chromium.org/1251593007
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1476 4c0a9323-5329-0410-9bdc-e9ce6186880e
2015-07-28 02:53:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MinidumpLinuxMaps *MinidumpLinuxMapsList::GetLinuxMapsForAddress(
|
|
|
|
|
uint64_t address) const {
|
2015-07-31 17:26:39 +02:00
|
|
|
if (!valid_ || (maps_ == NULL)) {
|
Add support for Linux memory mapping stream and remove ELF header usage
when checking exploitability rating.
Linux minidumps do not support MD_MEMORY_INFO_LIST_STREAM, meaning the
processor cannot retrieve its memory mappings. However, it has its own
stream, MD_LINUX_MAPS, which contains memory mappings specific to Linux
(it contains the contents of /proc/self/maps). This CL allows the minidump
to gather information from the memory mappings for Linux minidumps.
In addition, exploitability rating for Linux dumps now use memory mappings
instead of checking the ELF headers of binaries. The basis for the change
is that checking the ELF headers requires the minidumps to store the memory
from the ELF headers, while the memory mapping data is already present,
meaning the size of a minidump will be unchanged.
As a result, of removing ELF header analysis, two unit tests have been removed.
Arguably, the cases that those unit tests check do not merit a high
exploitability rating and do not warrant a solid conclusion that was given
earlier.
R=ivanpe@chromium.org
Review URL: https://codereview.chromium.org/1251593007
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1476 4c0a9323-5329-0410-9bdc-e9ce6186880e
2015-07-28 02:53:44 +02:00
|
|
|
BPLOG(ERROR) << "Invalid MinidumpLinuxMapsList for GetLinuxMapsForAddress";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Search every memory mapping.
|
|
|
|
|
for (unsigned int index = 0; index < maps_count_; index++) {
|
|
|
|
|
// Check if address is within bounds of the current memory region.
|
|
|
|
|
if ((*maps_)[index]->GetBase() <= address &&
|
|
|
|
|
(*maps_)[index]->GetBase() + (*maps_)[index]->GetSize() > address) {
|
|
|
|
|
return (*maps_)[index];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No mapping encloses the memory address.
|
|
|
|
|
BPLOG(ERROR) << "MinidumpLinuxMapsList has no mapping at "
|
|
|
|
|
<< HexString(address);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MinidumpLinuxMaps *MinidumpLinuxMapsList::GetLinuxMapsAtIndex(
|
|
|
|
|
unsigned int index) const {
|
2015-07-31 17:26:39 +02:00
|
|
|
if (!valid_ || (maps_ == NULL)) {
|
Add support for Linux memory mapping stream and remove ELF header usage
when checking exploitability rating.
Linux minidumps do not support MD_MEMORY_INFO_LIST_STREAM, meaning the
processor cannot retrieve its memory mappings. However, it has its own
stream, MD_LINUX_MAPS, which contains memory mappings specific to Linux
(it contains the contents of /proc/self/maps). This CL allows the minidump
to gather information from the memory mappings for Linux minidumps.
In addition, exploitability rating for Linux dumps now use memory mappings
instead of checking the ELF headers of binaries. The basis for the change
is that checking the ELF headers requires the minidumps to store the memory
from the ELF headers, while the memory mapping data is already present,
meaning the size of a minidump will be unchanged.
As a result, of removing ELF header analysis, two unit tests have been removed.
Arguably, the cases that those unit tests check do not merit a high
exploitability rating and do not warrant a solid conclusion that was given
earlier.
R=ivanpe@chromium.org
Review URL: https://codereview.chromium.org/1251593007
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1476 4c0a9323-5329-0410-9bdc-e9ce6186880e
2015-07-28 02:53:44 +02:00
|
|
|
BPLOG(ERROR) << "Invalid MinidumpLinuxMapsList for GetLinuxMapsAtIndex";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Index out of bounds.
|
2015-07-31 17:26:39 +02:00
|
|
|
if (index >= maps_count_ || (maps_ == NULL)) {
|
Add support for Linux memory mapping stream and remove ELF header usage
when checking exploitability rating.
Linux minidumps do not support MD_MEMORY_INFO_LIST_STREAM, meaning the
processor cannot retrieve its memory mappings. However, it has its own
stream, MD_LINUX_MAPS, which contains memory mappings specific to Linux
(it contains the contents of /proc/self/maps). This CL allows the minidump
to gather information from the memory mappings for Linux minidumps.
In addition, exploitability rating for Linux dumps now use memory mappings
instead of checking the ELF headers of binaries. The basis for the change
is that checking the ELF headers requires the minidumps to store the memory
from the ELF headers, while the memory mapping data is already present,
meaning the size of a minidump will be unchanged.
As a result, of removing ELF header analysis, two unit tests have been removed.
Arguably, the cases that those unit tests check do not merit a high
exploitability rating and do not warrant a solid conclusion that was given
earlier.
R=ivanpe@chromium.org
Review URL: https://codereview.chromium.org/1251593007
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1476 4c0a9323-5329-0410-9bdc-e9ce6186880e
2015-07-28 02:53:44 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpLinuxMapsList index of out range: "
|
|
|
|
|
<< index
|
|
|
|
|
<< "/"
|
|
|
|
|
<< maps_count_;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return (*maps_)[index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MinidumpLinuxMapsList::Read(uint32_t expected_size) {
|
|
|
|
|
// Invalidate cached data.
|
2015-07-31 17:26:39 +02:00
|
|
|
if (maps_) {
|
|
|
|
|
for (unsigned int i = 0; i < maps_->size(); i++) {
|
|
|
|
|
delete (*maps_)[i];
|
|
|
|
|
}
|
|
|
|
|
delete maps_;
|
|
|
|
|
}
|
Add support for Linux memory mapping stream and remove ELF header usage
when checking exploitability rating.
Linux minidumps do not support MD_MEMORY_INFO_LIST_STREAM, meaning the
processor cannot retrieve its memory mappings. However, it has its own
stream, MD_LINUX_MAPS, which contains memory mappings specific to Linux
(it contains the contents of /proc/self/maps). This CL allows the minidump
to gather information from the memory mappings for Linux minidumps.
In addition, exploitability rating for Linux dumps now use memory mappings
instead of checking the ELF headers of binaries. The basis for the change
is that checking the ELF headers requires the minidumps to store the memory
from the ELF headers, while the memory mapping data is already present,
meaning the size of a minidump will be unchanged.
As a result, of removing ELF header analysis, two unit tests have been removed.
Arguably, the cases that those unit tests check do not merit a high
exploitability rating and do not warrant a solid conclusion that was given
earlier.
R=ivanpe@chromium.org
Review URL: https://codereview.chromium.org/1251593007
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1476 4c0a9323-5329-0410-9bdc-e9ce6186880e
2015-07-28 02:53:44 +02:00
|
|
|
maps_ = NULL;
|
|
|
|
|
maps_count_ = 0;
|
|
|
|
|
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
|
|
|
|
// Load and check expected stream length.
|
|
|
|
|
uint32_t length = 0;
|
|
|
|
|
if (!minidump_->SeekToStreamType(MD_LINUX_MAPS, &length)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpLinuxMapsList stream type not found";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (expected_size != length) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpLinuxMapsList size mismatch: "
|
|
|
|
|
<< expected_size
|
|
|
|
|
<< " != "
|
|
|
|
|
<< length;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a vector to read stream data. The vector needs to have
|
|
|
|
|
// at least enough capacity to read all the data.
|
|
|
|
|
vector<char> mapping_bytes(length);
|
|
|
|
|
if (!minidump_->ReadBytes(&mapping_bytes[0], length)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpLinuxMapsList failed to read bytes";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
string map_string(mapping_bytes.begin(), mapping_bytes.end());
|
|
|
|
|
vector<MappedMemoryRegion> all_regions;
|
|
|
|
|
|
|
|
|
|
// Parse string into mapping data.
|
|
|
|
|
if (!ParseProcMaps(map_string, &all_regions)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scoped_ptr<MinidumpLinuxMappings> maps(new MinidumpLinuxMappings());
|
|
|
|
|
|
|
|
|
|
// Push mapping data into wrapper classes.
|
|
|
|
|
for (size_t i = 0; i < all_regions.size(); i++) {
|
|
|
|
|
scoped_ptr<MinidumpLinuxMaps> ele(new MinidumpLinuxMaps(minidump_));
|
|
|
|
|
ele->region_ = all_regions[i];
|
|
|
|
|
ele->valid_ = true;
|
|
|
|
|
maps->push_back(ele.release());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set instance variables.
|
|
|
|
|
maps_ = maps.release();
|
|
|
|
|
maps_count_ = maps_->size();
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MinidumpLinuxMapsList::Print() {
|
2015-07-31 17:26:39 +02:00
|
|
|
if (!valid_ || (maps_ == NULL)) {
|
Add support for Linux memory mapping stream and remove ELF header usage
when checking exploitability rating.
Linux minidumps do not support MD_MEMORY_INFO_LIST_STREAM, meaning the
processor cannot retrieve its memory mappings. However, it has its own
stream, MD_LINUX_MAPS, which contains memory mappings specific to Linux
(it contains the contents of /proc/self/maps). This CL allows the minidump
to gather information from the memory mappings for Linux minidumps.
In addition, exploitability rating for Linux dumps now use memory mappings
instead of checking the ELF headers of binaries. The basis for the change
is that checking the ELF headers requires the minidumps to store the memory
from the ELF headers, while the memory mapping data is already present,
meaning the size of a minidump will be unchanged.
As a result, of removing ELF header analysis, two unit tests have been removed.
Arguably, the cases that those unit tests check do not merit a high
exploitability rating and do not warrant a solid conclusion that was given
earlier.
R=ivanpe@chromium.org
Review URL: https://codereview.chromium.org/1251593007
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1476 4c0a9323-5329-0410-9bdc-e9ce6186880e
2015-07-28 02:53:44 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpLinuxMapsList cannot print valid data";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < maps_->size(); i++) {
|
|
|
|
|
(*maps_)[i]->Print();
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-01-13 20:05:33 +01:00
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
//
|
|
|
|
|
// Minidump
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t Minidump::max_streams_ = 128;
|
2007-05-31 17:54:06 +02:00
|
|
|
unsigned int Minidump::max_string_length_ = 1024;
|
|
|
|
|
|
|
|
|
|
|
2006-09-08 18:41:17 +02:00
|
|
|
Minidump::Minidump(const string& path)
|
2006-09-07 17:56:38 +02:00
|
|
|
: header_(),
|
|
|
|
|
directory_(NULL),
|
2006-11-27 22:42:07 +01:00
|
|
|
stream_map_(new MinidumpStreamMap()),
|
2006-09-08 18:41:17 +02:00
|
|
|
path_(path),
|
2009-12-09 02:24:37 +01:00
|
|
|
stream_(NULL),
|
2006-09-07 17:56:38 +02:00
|
|
|
swap_(false),
|
|
|
|
|
valid_(false) {
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
2009-12-09 02:24:37 +01:00
|
|
|
Minidump::Minidump(istream& stream)
|
|
|
|
|
: header_(),
|
|
|
|
|
directory_(NULL),
|
|
|
|
|
stream_map_(new MinidumpStreamMap()),
|
|
|
|
|
path_(),
|
|
|
|
|
stream_(&stream),
|
|
|
|
|
swap_(false),
|
|
|
|
|
valid_(false) {
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
Minidump::~Minidump() {
|
2009-12-09 02:24:37 +01:00
|
|
|
if (stream_) {
|
|
|
|
|
BPLOG(INFO) << "Minidump closing minidump";
|
|
|
|
|
}
|
|
|
|
|
if (!path_.empty()) {
|
|
|
|
|
delete stream_;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
delete directory_;
|
|
|
|
|
delete stream_map_;
|
2006-09-08 18:41:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool Minidump::Open() {
|
2009-12-09 02:24:37 +01:00
|
|
|
if (stream_ != NULL) {
|
|
|
|
|
BPLOG(INFO) << "Minidump reopening minidump " << path_;
|
2007-05-17 20:34:37 +02:00
|
|
|
|
2006-09-08 18:41:17 +02:00
|
|
|
// The file is already open. Seek to the beginning, which is the position
|
|
|
|
|
// the file would be at if it were opened anew.
|
|
|
|
|
return SeekSet(0);
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-09 02:24:37 +01:00
|
|
|
stream_ = new ifstream(path_.c_str(), std::ios::in | std::ios::binary);
|
|
|
|
|
if (!stream_ || !stream_->good()) {
|
2007-05-17 20:34:37 +02:00
|
|
|
string error_string;
|
|
|
|
|
int error_code = ErrnoString(&error_string);
|
|
|
|
|
BPLOG(ERROR) << "Minidump could not open minidump " << path_ <<
|
|
|
|
|
", error " << error_code << ": " << error_string;
|
2006-09-08 18:41:17 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-08 18:41:17 +02:00
|
|
|
|
2009-12-09 02:24:37 +01:00
|
|
|
BPLOG(INFO) << "Minidump opened minidump " << path_;
|
2006-09-08 18:41:17 +02:00
|
|
|
return true;
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool Minidump::GetContextCPUFlagsFromSystemInfo(uint32_t *context_cpu_flags) {
|
2012-12-08 04:18:52 +01:00
|
|
|
// Initialize output parameters
|
|
|
|
|
*context_cpu_flags = 0;
|
|
|
|
|
|
|
|
|
|
// Save the current stream position
|
|
|
|
|
off_t saved_position = Tell();
|
|
|
|
|
if (saved_position == -1) {
|
|
|
|
|
// Failed to save the current stream position.
|
|
|
|
|
// Returns true because the current position of the stream is preserved.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MDRawSystemInfo* system_info =
|
|
|
|
|
GetSystemInfo() ? GetSystemInfo()->system_info() : NULL;
|
|
|
|
|
|
|
|
|
|
if (system_info != NULL) {
|
|
|
|
|
switch (system_info->processor_architecture) {
|
|
|
|
|
case MD_CPU_ARCHITECTURE_X86:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_X86;
|
|
|
|
|
break;
|
|
|
|
|
case MD_CPU_ARCHITECTURE_MIPS:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_MIPS;
|
|
|
|
|
break;
|
|
|
|
|
case MD_CPU_ARCHITECTURE_ALPHA:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_ALPHA;
|
|
|
|
|
break;
|
|
|
|
|
case MD_CPU_ARCHITECTURE_PPC:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_PPC;
|
|
|
|
|
break;
|
2013-04-13 01:24:02 +02:00
|
|
|
case MD_CPU_ARCHITECTURE_PPC64:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_PPC64;
|
|
|
|
|
break;
|
2012-12-08 04:18:52 +01:00
|
|
|
case MD_CPU_ARCHITECTURE_SHX:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_SHX;
|
|
|
|
|
break;
|
|
|
|
|
case MD_CPU_ARCHITECTURE_ARM:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_ARM;
|
|
|
|
|
break;
|
2013-11-23 02:45:20 +01:00
|
|
|
case MD_CPU_ARCHITECTURE_ARM64:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_ARM64;
|
|
|
|
|
break;
|
2012-12-08 04:18:52 +01:00
|
|
|
case MD_CPU_ARCHITECTURE_IA64:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_IA64;
|
|
|
|
|
break;
|
|
|
|
|
case MD_CPU_ARCHITECTURE_ALPHA64:
|
|
|
|
|
*context_cpu_flags = 0;
|
|
|
|
|
break;
|
|
|
|
|
case MD_CPU_ARCHITECTURE_MSIL:
|
|
|
|
|
*context_cpu_flags = 0;
|
|
|
|
|
break;
|
|
|
|
|
case MD_CPU_ARCHITECTURE_AMD64:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_AMD64;
|
|
|
|
|
break;
|
|
|
|
|
case MD_CPU_ARCHITECTURE_X86_WIN64:
|
|
|
|
|
*context_cpu_flags = 0;
|
|
|
|
|
break;
|
|
|
|
|
case MD_CPU_ARCHITECTURE_SPARC:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_SPARC;
|
|
|
|
|
break;
|
|
|
|
|
case MD_CPU_ARCHITECTURE_UNKNOWN:
|
|
|
|
|
*context_cpu_flags = 0;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
*context_cpu_flags = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Restore position and return
|
|
|
|
|
return SeekSet(saved_position);
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
bool Minidump::Read() {
|
|
|
|
|
// Invalidate cached data.
|
|
|
|
|
delete directory_;
|
|
|
|
|
directory_ = NULL;
|
2006-11-27 22:42:07 +01:00
|
|
|
stream_map_->clear();
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!Open()) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump cannot open minidump";
|
2006-09-08 18:41:17 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-08 18:41:17 +02:00
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!ReadBytes(&header_, sizeof(MDRawHeader))) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump cannot read header";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
if (header_.signature != MD_HEADER_SIGNATURE) {
|
|
|
|
|
// The file may be byte-swapped. Under the present architecture, these
|
|
|
|
|
// classes don't know or need to know what CPU (or endianness) the
|
|
|
|
|
// minidump was produced on in order to parse it. Use the signature as
|
|
|
|
|
// a byte order marker.
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t signature_swapped = header_.signature;
|
2006-09-06 04:56:44 +02:00
|
|
|
Swap(&signature_swapped);
|
|
|
|
|
if (signature_swapped != MD_HEADER_SIGNATURE) {
|
|
|
|
|
// This isn't a minidump or a byte-swapped minidump.
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "Minidump header signature mismatch: (" <<
|
|
|
|
|
HexString(header_.signature) << ", " <<
|
|
|
|
|
HexString(signature_swapped) << ") != " <<
|
|
|
|
|
HexString(MD_HEADER_SIGNATURE);
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
swap_ = true;
|
|
|
|
|
} else {
|
|
|
|
|
// The file is not byte-swapped. Set swap_ false (it may have been true
|
|
|
|
|
// if the object is being reused?)
|
|
|
|
|
swap_ = false;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(INFO) << "Minidump " << (swap_ ? "" : "not ") <<
|
|
|
|
|
"byte-swapping minidump";
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
if (swap_) {
|
|
|
|
|
Swap(&header_.signature);
|
|
|
|
|
Swap(&header_.version);
|
|
|
|
|
Swap(&header_.stream_count);
|
|
|
|
|
Swap(&header_.stream_directory_rva);
|
|
|
|
|
Swap(&header_.checksum);
|
|
|
|
|
Swap(&header_.time_date_stamp);
|
|
|
|
|
Swap(&header_.flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Version check. The high 16 bits of header_.version contain something
|
|
|
|
|
// else "implementation specific."
|
|
|
|
|
if ((header_.version & 0x0000ffff) != MD_HEADER_VERSION) {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "Minidump version mismatch: " <<
|
|
|
|
|
HexString(header_.version & 0x0000ffff) << " != " <<
|
|
|
|
|
HexString(MD_HEADER_VERSION);
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!SeekSet(header_.stream_directory_rva)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump cannot seek to stream directory";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-31 17:54:06 +02:00
|
|
|
if (header_.stream_count > max_streams_) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump stream count " << header_.stream_count <<
|
|
|
|
|
" exceeds maximum " << max_streams_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (header_.stream_count != 0) {
|
2006-11-27 22:42:07 +01:00
|
|
|
scoped_ptr<MinidumpDirectoryEntries> directory(
|
|
|
|
|
new MinidumpDirectoryEntries(header_.stream_count));
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
// Read the entire array in one fell swoop, instead of reading one entry
|
|
|
|
|
// at a time in the loop.
|
|
|
|
|
if (!ReadBytes(&(*directory)[0],
|
2007-05-17 20:34:37 +02:00
|
|
|
sizeof(MDRawDirectory) * header_.stream_count)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump cannot read stream directory";
|
2006-11-27 22:42:07 +01:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
for (unsigned int stream_index = 0;
|
|
|
|
|
stream_index < header_.stream_count;
|
|
|
|
|
++stream_index) {
|
|
|
|
|
MDRawDirectory* directory_entry = &(*directory)[stream_index];
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
if (swap_) {
|
|
|
|
|
Swap(&directory_entry->stream_type);
|
|
|
|
|
Swap(&directory_entry->location);
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
// Initialize the stream_map_ map, which speeds locating a stream by
|
|
|
|
|
// type.
|
|
|
|
|
unsigned int stream_type = directory_entry->stream_type;
|
|
|
|
|
switch (stream_type) {
|
|
|
|
|
case MD_THREAD_LIST_STREAM:
|
|
|
|
|
case MD_MODULE_LIST_STREAM:
|
|
|
|
|
case MD_MEMORY_LIST_STREAM:
|
|
|
|
|
case MD_EXCEPTION_STREAM:
|
|
|
|
|
case MD_SYSTEM_INFO_STREAM:
|
|
|
|
|
case MD_MISC_INFO_STREAM:
|
2007-02-14 20:51:05 +01:00
|
|
|
case MD_BREAKPAD_INFO_STREAM: {
|
2006-11-27 22:42:07 +01:00
|
|
|
if (stream_map_->find(stream_type) != stream_map_->end()) {
|
|
|
|
|
// Another stream with this type was already found. A minidump
|
|
|
|
|
// file should contain at most one of each of these stream types.
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(ERROR) << "Minidump found multiple streams of type " <<
|
|
|
|
|
stream_type << ", but can only deal with one";
|
2006-11-27 22:42:07 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Fall through to default
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
default: {
|
|
|
|
|
// Overwrites for stream types other than those above, but it's
|
|
|
|
|
// expected to be the user's burden in that case.
|
|
|
|
|
(*stream_map_)[stream_type].stream_index = stream_index;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
directory_ = directory.release();
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpThreadList* Minidump::GetThreadList() {
|
|
|
|
|
MinidumpThreadList* thread_list;
|
|
|
|
|
return GetStream(&thread_list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpModuleList* Minidump::GetModuleList() {
|
|
|
|
|
MinidumpModuleList* module_list;
|
|
|
|
|
return GetStream(&module_list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpMemoryList* Minidump::GetMemoryList() {
|
|
|
|
|
MinidumpMemoryList* memory_list;
|
|
|
|
|
return GetStream(&memory_list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpException* Minidump::GetException() {
|
|
|
|
|
MinidumpException* exception;
|
|
|
|
|
return GetStream(&exception);
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-02 18:43:57 +01:00
|
|
|
MinidumpAssertion* Minidump::GetAssertion() {
|
|
|
|
|
MinidumpAssertion* assertion;
|
|
|
|
|
return GetStream(&assertion);
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
MinidumpSystemInfo* Minidump::GetSystemInfo() {
|
|
|
|
|
MinidumpSystemInfo* system_info;
|
|
|
|
|
return GetStream(&system_info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpMiscInfo* Minidump::GetMiscInfo() {
|
|
|
|
|
MinidumpMiscInfo* misc_info;
|
|
|
|
|
return GetStream(&misc_info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-02-14 20:51:05 +01:00
|
|
|
MinidumpBreakpadInfo* Minidump::GetBreakpadInfo() {
|
|
|
|
|
MinidumpBreakpadInfo* breakpad_info;
|
|
|
|
|
return GetStream(&breakpad_info);
|
2006-11-07 00:00:19 +01:00
|
|
|
}
|
|
|
|
|
|
2011-01-13 20:05:33 +01:00
|
|
|
MinidumpMemoryInfoList* Minidump::GetMemoryInfoList() {
|
|
|
|
|
MinidumpMemoryInfoList* memory_info_list;
|
|
|
|
|
return GetStream(&memory_info_list);
|
|
|
|
|
}
|
|
|
|
|
|
Add support for Linux memory mapping stream and remove ELF header usage
when checking exploitability rating.
Linux minidumps do not support MD_MEMORY_INFO_LIST_STREAM, meaning the
processor cannot retrieve its memory mappings. However, it has its own
stream, MD_LINUX_MAPS, which contains memory mappings specific to Linux
(it contains the contents of /proc/self/maps). This CL allows the minidump
to gather information from the memory mappings for Linux minidumps.
In addition, exploitability rating for Linux dumps now use memory mappings
instead of checking the ELF headers of binaries. The basis for the change
is that checking the ELF headers requires the minidumps to store the memory
from the ELF headers, while the memory mapping data is already present,
meaning the size of a minidump will be unchanged.
As a result, of removing ELF header analysis, two unit tests have been removed.
Arguably, the cases that those unit tests check do not merit a high
exploitability rating and do not warrant a solid conclusion that was given
earlier.
R=ivanpe@chromium.org
Review URL: https://codereview.chromium.org/1251593007
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1476 4c0a9323-5329-0410-9bdc-e9ce6186880e
2015-07-28 02:53:44 +02:00
|
|
|
MinidumpLinuxMapsList *Minidump::GetLinuxMapsList() {
|
|
|
|
|
MinidumpLinuxMapsList *linux_maps_list;
|
|
|
|
|
return GetStream(&linux_maps_list);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-11 12:57:30 +02:00
|
|
|
static const char* get_stream_name(uint32_t stream_type) {
|
|
|
|
|
switch (stream_type) {
|
|
|
|
|
case MD_UNUSED_STREAM:
|
|
|
|
|
return "MD_UNUSED_STREAM";
|
|
|
|
|
case MD_RESERVED_STREAM_0:
|
|
|
|
|
return "MD_RESERVED_STREAM_0";
|
|
|
|
|
case MD_RESERVED_STREAM_1:
|
|
|
|
|
return "MD_RESERVED_STREAM_1";
|
|
|
|
|
case MD_THREAD_LIST_STREAM:
|
|
|
|
|
return "MD_THREAD_LIST_STREAM";
|
|
|
|
|
case MD_MODULE_LIST_STREAM:
|
|
|
|
|
return "MD_MODULE_LIST_STREAM";
|
|
|
|
|
case MD_MEMORY_LIST_STREAM:
|
|
|
|
|
return "MD_MEMORY_LIST_STREAM";
|
|
|
|
|
case MD_EXCEPTION_STREAM:
|
|
|
|
|
return "MD_EXCEPTION_STREAM";
|
|
|
|
|
case MD_SYSTEM_INFO_STREAM:
|
|
|
|
|
return "MD_SYSTEM_INFO_STREAM";
|
|
|
|
|
case MD_THREAD_EX_LIST_STREAM:
|
|
|
|
|
return "MD_THREAD_EX_LIST_STREAM";
|
|
|
|
|
case MD_MEMORY_64_LIST_STREAM:
|
|
|
|
|
return "MD_MEMORY_64_LIST_STREAM";
|
|
|
|
|
case MD_COMMENT_STREAM_A:
|
|
|
|
|
return "MD_COMMENT_STREAM_A";
|
|
|
|
|
case MD_COMMENT_STREAM_W:
|
|
|
|
|
return "MD_COMMENT_STREAM_W";
|
|
|
|
|
case MD_HANDLE_DATA_STREAM:
|
|
|
|
|
return "MD_HANDLE_DATA_STREAM";
|
|
|
|
|
case MD_FUNCTION_TABLE_STREAM:
|
|
|
|
|
return "MD_FUNCTION_TABLE_STREAM";
|
|
|
|
|
case MD_UNLOADED_MODULE_LIST_STREAM:
|
|
|
|
|
return "MD_UNLOADED_MODULE_LIST_STREAM";
|
|
|
|
|
case MD_MISC_INFO_STREAM:
|
|
|
|
|
return "MD_MISC_INFO_STREAM";
|
|
|
|
|
case MD_MEMORY_INFO_LIST_STREAM:
|
|
|
|
|
return "MD_MEMORY_INFO_LIST_STREAM";
|
|
|
|
|
case MD_THREAD_INFO_LIST_STREAM:
|
|
|
|
|
return "MD_THREAD_INFO_LIST_STREAM";
|
|
|
|
|
case MD_HANDLE_OPERATION_LIST_STREAM:
|
|
|
|
|
return "MD_HANDLE_OPERATION_LIST_STREAM";
|
|
|
|
|
case MD_LAST_RESERVED_STREAM:
|
|
|
|
|
return "MD_LAST_RESERVED_STREAM";
|
|
|
|
|
case MD_BREAKPAD_INFO_STREAM:
|
|
|
|
|
return "MD_BREAKPAD_INFO_STREAM";
|
|
|
|
|
case MD_ASSERTION_INFO_STREAM:
|
|
|
|
|
return "MD_ASSERTION_INFO_STREAM";
|
|
|
|
|
case MD_LINUX_CPU_INFO:
|
|
|
|
|
return "MD_LINUX_CPU_INFO";
|
|
|
|
|
case MD_LINUX_PROC_STATUS:
|
|
|
|
|
return "MD_LINUX_PROC_STATUS";
|
|
|
|
|
case MD_LINUX_LSB_RELEASE:
|
|
|
|
|
return "MD_LINUX_LSB_RELEASE";
|
|
|
|
|
case MD_LINUX_CMD_LINE:
|
|
|
|
|
return "MD_LINUX_CMD_LINE";
|
|
|
|
|
case MD_LINUX_ENVIRON:
|
|
|
|
|
return "MD_LINUX_ENVIRON";
|
|
|
|
|
case MD_LINUX_AUXV:
|
|
|
|
|
return "MD_LINUX_AUXV";
|
|
|
|
|
case MD_LINUX_MAPS:
|
|
|
|
|
return "MD_LINUX_MAPS";
|
|
|
|
|
case MD_LINUX_DSO_DEBUG:
|
|
|
|
|
return "MD_LINUX_DSO_DEBUG";
|
|
|
|
|
default:
|
|
|
|
|
return "unknown";
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-11-07 00:00:19 +01:00
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
void Minidump::Print() {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump cannot print invalid data";
|
2006-09-06 04:56:44 +02:00
|
|
|
return;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
printf("MDRawHeader\n");
|
|
|
|
|
printf(" signature = 0x%x\n", header_.signature);
|
|
|
|
|
printf(" version = 0x%x\n", header_.version);
|
|
|
|
|
printf(" stream_count = %d\n", header_.stream_count);
|
|
|
|
|
printf(" stream_directory_rva = 0x%x\n", header_.stream_directory_rva);
|
|
|
|
|
printf(" checksum = 0x%x\n", header_.checksum);
|
2014-06-17 20:03:31 +02:00
|
|
|
printf(" time_date_stamp = 0x%x %s\n",
|
|
|
|
|
header_.time_date_stamp,
|
|
|
|
|
TimeTToUTCString(header_.time_date_stamp).c_str());
|
2008-02-25 20:32:00 +01:00
|
|
|
printf(" flags = 0x%" PRIx64 "\n", header_.flags);
|
2006-09-06 04:56:44 +02:00
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
|
|
for (unsigned int stream_index = 0;
|
|
|
|
|
stream_index < header_.stream_count;
|
|
|
|
|
++stream_index) {
|
|
|
|
|
MDRawDirectory* directory_entry = &(*directory_)[stream_index];
|
|
|
|
|
|
|
|
|
|
printf("mDirectory[%d]\n", stream_index);
|
|
|
|
|
printf("MDRawDirectory\n");
|
2014-07-11 12:57:30 +02:00
|
|
|
printf(" stream_type = 0x%x (%s)\n", directory_entry->stream_type,
|
|
|
|
|
get_stream_name(directory_entry->stream_type));
|
2006-09-06 04:56:44 +02:00
|
|
|
printf(" location.data_size = %d\n",
|
|
|
|
|
directory_entry->location.data_size);
|
|
|
|
|
printf(" location.rva = 0x%x\n", directory_entry->location.rva);
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("Streams:\n");
|
|
|
|
|
for (MinidumpStreamMap::const_iterator iterator = stream_map_->begin();
|
|
|
|
|
iterator != stream_map_->end();
|
|
|
|
|
++iterator) {
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t stream_type = iterator->first;
|
2006-09-06 04:56:44 +02:00
|
|
|
MinidumpStreamInfo info = iterator->second;
|
2014-07-11 12:57:30 +02:00
|
|
|
printf(" stream type 0x%x (%s) at index %d\n", stream_type,
|
|
|
|
|
get_stream_name(stream_type),
|
|
|
|
|
info.stream_index);
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const MDRawDirectory* Minidump::GetDirectoryEntryAtIndex(unsigned int index)
|
|
|
|
|
const {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid Minidump for GetDirectoryEntryAtIndex";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index >= header_.stream_count) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump stream directory index out of range: " <<
|
|
|
|
|
index << "/" << header_.stream_count;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
return &(*directory_)[index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool Minidump::ReadBytes(void* bytes, size_t count) {
|
|
|
|
|
// Can't check valid_ because Read needs to call this method before
|
2009-12-09 02:24:37 +01:00
|
|
|
// validity can be determined.
|
|
|
|
|
if (!stream_) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
stream_->read(static_cast<char*>(bytes), count);
|
2013-06-04 18:51:01 +02:00
|
|
|
std::streamsize bytes_read = stream_->gcount();
|
|
|
|
|
if (bytes_read == -1) {
|
|
|
|
|
string error_string;
|
|
|
|
|
int error_code = ErrnoString(&error_string);
|
|
|
|
|
BPLOG(ERROR) << "ReadBytes: error " << error_code << ": " << error_string;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert to size_t and check for data loss
|
|
|
|
|
size_t bytes_read_converted = static_cast<size_t>(bytes_read);
|
|
|
|
|
if (static_cast<std::streamsize>(bytes_read_converted) != bytes_read) {
|
|
|
|
|
BPLOG(ERROR) << "ReadBytes: conversion data loss detected when converting "
|
|
|
|
|
<< bytes_read << " to " << bytes_read_converted;
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2013-06-04 18:51:01 +02:00
|
|
|
|
|
|
|
|
if (bytes_read_converted != count) {
|
|
|
|
|
BPLOG(ERROR) << "ReadBytes: read " << bytes_read_converted << "/" << count;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool Minidump::SeekSet(off_t offset) {
|
|
|
|
|
// Can't check valid_ because Read needs to call this method before
|
2009-12-09 02:24:37 +01:00
|
|
|
// validity can be determined.
|
|
|
|
|
if (!stream_) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
stream_->seekg(offset, std::ios_base::beg);
|
|
|
|
|
if (!stream_->good()) {
|
|
|
|
|
string error_string;
|
|
|
|
|
int error_code = ErrnoString(&error_string);
|
|
|
|
|
BPLOG(ERROR) << "SeekSet: error " << error_code << ": " << error_string;
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-09 02:24:37 +01:00
|
|
|
off_t Minidump::Tell() {
|
|
|
|
|
if (!valid_ || !stream_) {
|
|
|
|
|
return (off_t)-1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-04 18:51:01 +02:00
|
|
|
// Check for conversion data loss
|
|
|
|
|
std::streamoff std_streamoff = stream_->tellg();
|
|
|
|
|
off_t rv = static_cast<off_t>(std_streamoff);
|
|
|
|
|
if (static_cast<std::streamoff>(rv) == std_streamoff) {
|
|
|
|
|
return rv;
|
|
|
|
|
} else {
|
|
|
|
|
BPLOG(ERROR) << "Data loss detected";
|
|
|
|
|
return (off_t)-1;
|
|
|
|
|
}
|
2009-12-09 02:24:37 +01:00
|
|
|
}
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
string* Minidump::ReadString(off_t offset) {
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid Minidump for ReadString";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
|
|
|
|
if (!SeekSet(offset)) {
|
2007-05-31 17:54:06 +02:00
|
|
|
BPLOG(ERROR) << "ReadString could not seek to string at offset " << offset;
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t bytes;
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!ReadBytes(&bytes, sizeof(bytes))) {
|
2007-05-31 17:54:06 +02:00
|
|
|
BPLOG(ERROR) << "ReadString could not read string size at offset " <<
|
|
|
|
|
offset;
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
if (swap_)
|
|
|
|
|
Swap(&bytes);
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (bytes % 2 != 0) {
|
2007-05-31 17:54:06 +02:00
|
|
|
BPLOG(ERROR) << "ReadString found odd-sized " << bytes <<
|
|
|
|
|
"-byte string at offset " << offset;
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
unsigned int utf16_words = bytes / 2;
|
|
|
|
|
|
2007-05-31 17:54:06 +02:00
|
|
|
if (utf16_words > max_string_length_) {
|
|
|
|
|
BPLOG(ERROR) << "ReadString string length " << utf16_words <<
|
|
|
|
|
" exceeds maximum " << max_string_length_ <<
|
|
|
|
|
" at offset " << offset;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
vector<uint16_t> string_utf16(utf16_words);
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-11-27 22:42:07 +01:00
|
|
|
if (utf16_words) {
|
|
|
|
|
if (!ReadBytes(&string_utf16[0], bytes)) {
|
2007-05-31 17:54:06 +02:00
|
|
|
BPLOG(ERROR) << "ReadString could not read " << bytes <<
|
|
|
|
|
"-byte string at offset " << offset;
|
2006-11-27 22:42:07 +01:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
return UTF16ToUTF8(string_utf16, swap_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
bool Minidump::SeekToStreamType(uint32_t stream_type,
|
|
|
|
|
uint32_t* stream_length) {
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG_IF(ERROR, !stream_length) << "Minidump::SeekToStreamType requires "
|
|
|
|
|
"|stream_length|";
|
|
|
|
|
assert(stream_length);
|
|
|
|
|
*stream_length = 0;
|
|
|
|
|
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid Mindump for SeekToStreamType";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
MinidumpStreamMap::const_iterator iterator = stream_map_->find(stream_type);
|
|
|
|
|
if (iterator == stream_map_->end()) {
|
|
|
|
|
// This stream type didn't exist in the directory.
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(INFO) << "SeekToStreamType: type " << stream_type << " not present";
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MinidumpStreamInfo info = iterator->second;
|
2007-05-17 20:34:37 +02:00
|
|
|
if (info.stream_index >= header_.stream_count) {
|
|
|
|
|
BPLOG(ERROR) << "SeekToStreamType: type " << stream_type <<
|
|
|
|
|
" out of range: " <<
|
|
|
|
|
info.stream_index << "/" << header_.stream_count;
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
MDRawDirectory* directory_entry = &(*directory_)[info.stream_index];
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!SeekSet(directory_entry->location.rva)) {
|
|
|
|
|
BPLOG(ERROR) << "SeekToStreamType could not seek to stream type " <<
|
|
|
|
|
stream_type;
|
2006-09-06 04:56:44 +02:00
|
|
|
return false;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
*stream_length = directory_entry->location.data_size;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
T* Minidump::GetStream(T** stream) {
|
|
|
|
|
// stream is a garbage parameter that's present only to account for C++'s
|
|
|
|
|
// inability to overload a method based solely on its return type.
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
const uint32_t stream_type = T::kStreamType;
|
2007-05-17 20:34:37 +02:00
|
|
|
|
|
|
|
|
BPLOG_IF(ERROR, !stream) << "Minidump::GetStream type " << stream_type <<
|
|
|
|
|
" requires |stream|";
|
|
|
|
|
assert(stream);
|
2006-09-06 04:56:44 +02:00
|
|
|
*stream = NULL;
|
|
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid Minidump for GetStream type " << stream_type;
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
MinidumpStreamMap::iterator iterator = stream_map_->find(stream_type);
|
|
|
|
|
if (iterator == stream_map_->end()) {
|
|
|
|
|
// This stream type didn't exist in the directory.
|
2007-05-17 20:34:37 +02:00
|
|
|
BPLOG(INFO) << "GetStream: type " << stream_type << " not present";
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get a pointer so that the stored stream field can be altered.
|
|
|
|
|
MinidumpStreamInfo* info = &iterator->second;
|
|
|
|
|
|
|
|
|
|
if (info->stream) {
|
|
|
|
|
// This cast is safe because info.stream is only populated by this
|
|
|
|
|
// method, and there is a direct correlation between T and stream_type.
|
|
|
|
|
*stream = static_cast<T*>(info->stream);
|
|
|
|
|
return *stream;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-06 15:04:42 +01:00
|
|
|
uint32_t stream_length;
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!SeekToStreamType(stream_type, &stream_length)) {
|
|
|
|
|
BPLOG(ERROR) << "GetStream could not seek to stream type " << stream_type;
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-10-23 22:25:42 +02:00
|
|
|
scoped_ptr<T> new_stream(new T(this));
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2007-05-17 20:34:37 +02:00
|
|
|
if (!new_stream->Read(stream_length)) {
|
|
|
|
|
BPLOG(ERROR) << "GetStream could not read stream type " << stream_type;
|
2006-09-06 04:56:44 +02:00
|
|
|
return NULL;
|
2007-05-17 20:34:37 +02:00
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
*stream = new_stream.release();
|
|
|
|
|
info->stream = *stream;
|
|
|
|
|
return *stream;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-02-14 20:51:05 +01:00
|
|
|
} // namespace google_breakpad
|