2022-09-07 16:34:05 +02:00
|
|
|
// Copyright 2010 Google LLC
|
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.
|
2022-09-07 16:34:05 +02:00
|
|
|
// * Neither the name of Google LLC nor the names of its
|
2006-09-20 23:16:16 +02:00
|
|
|
// 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
|
|
|
|
|
|
Add #include <config.h> to the beginning of all cc files
Added
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
to the beginning of all source files that didn't have it.
This ensures that configuration options are respected in all source
files. In particular, it ensures that the defines needed to fix Large
File System issues are set before including system headers.
More generally, it ensures consistency between the source files, and
avoids the possibility of ODR violations between source files that were
including config.h and source files that were not.
Process:
Ran
find . \( -name third_party -prune \) -o \( -name '.git*' -prune \) -o \( \( -name '*.cc' -o -name '*.c' \) -exec sed -i '0,/^#include/ s/^#include/#ifdef HAVE_CONFIG_H\n#include <config.h> \/\/ Must come first\n#endif\n\n#include/' {} + \)
and then manually fixed up src/common/linux/guid_creator.cc,
src/tools/solaris/dump_syms/testdata/dump_syms_regtest.cc,
src/tools/windows/dump_syms/testdata/dump_syms_regtest.cc,
src/common/stabs_reader.h, and src/common/linux/breakpad_getcontext.h.
BUG=google-breakpad:877
Fixed: google-breakpad:877
TEST=./configure && make && make check
TEST=Did the find/sed in ChromeOS's copy, ensured emerge-hana google-breakpad
worked and had fewer LFS violations.
TEST=Did the find/sed in Chrome's copy, ensured compiling hana, windows, linux, and
eve still worked (since Chrome doesn't used config.h)
Change-Id: I16cededbba0ea0c28e919b13243e35300999e799
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/4289676
Reviewed-by: Mike Frysinger <vapier@chromium.org>
2023-02-24 20:14:53 +01:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include <config.h> // Must come first
|
|
|
|
|
#endif
|
|
|
|
|
|
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>
|
2023-01-20 22:19:37 +01:00
|
|
|
#include <inttypes.h>
|
2013-03-06 15:06:52 +01:00
|
|
|
#include <stddef.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>
|
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
|
|
|
|
2016-02-17 12:20:58 +01:00
|
|
|
#include <algorithm>
|
2009-12-09 02:24:37 +01:00
|
|
|
#include <fstream>
|
2007-05-21 23:02:04 +02:00
|
|
|
#include <limits>
|
2017-09-27 23:13:47 +02:00
|
|
|
#include <utility>
|
2006-09-06 04:56:44 +02:00
|
|
|
|
2006-09-20 18:20:15 +02:00
|
|
|
#include "processor/range_map-inl.h"
|
2006-10-25 23:25:41 +02:00
|
|
|
|
2019-11-05 03:34:20 +01:00
|
|
|
#include "common/macros.h"
|
2013-01-17 16:53:56 +01:00
|
|
|
#include "common/scoped_ptr.h"
|
2016-04-05 21:45:30 +02:00
|
|
|
#include "common/stdio_wrapper.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"
|
2018-08-01 19:48:27 +02:00
|
|
|
#include "processor/convert_old_arm64_context.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;
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
namespace {
|
|
|
|
|
|
2023-03-29 00:47:20 +02:00
|
|
|
// Limit arrived at by adding up possible states in Intel Ch. 13.5 X-SAVE
|
|
|
|
|
// MANAGED STATE
|
|
|
|
|
// (~ 3680 bytes) plus some extra for the future.
|
|
|
|
|
const uint32_t kMaxXSaveAreaSize = 16384;
|
|
|
|
|
|
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
|
2016-11-15 14:06:22 +01:00
|
|
|
// https://bugs.chromium.org/p/google-breakpad/issues/detail?id=550 is fixed.
|
2018-06-25 21:46:32 +02:00
|
|
|
bool IsContextSizeUnique(uint32_t context_size) {
|
2013-11-23 02:45:20 +01:00
|
|
|
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++;
|
2018-08-01 19:48:27 +02:00
|
|
|
if (context_size == sizeof(MDRawContextARM64))
|
|
|
|
|
num_matching_contexts++;
|
2018-07-31 22:30:11 +02:00
|
|
|
if (context_size == sizeof(MDRawContextARM64_Old))
|
2013-11-23 02:45:20 +01:00
|
|
|
num_matching_contexts++;
|
|
|
|
|
if (context_size == sizeof(MDRawContextMIPS))
|
|
|
|
|
num_matching_contexts++;
|
2022-09-09 09:53:29 +02:00
|
|
|
if (context_size == sizeof(MDRawContextRISCV))
|
|
|
|
|
num_matching_contexts++;
|
|
|
|
|
if (context_size == sizeof(MDRawContextRISCV64))
|
|
|
|
|
num_matching_contexts++;
|
2013-11-23 02:45:20 +01:00
|
|
|
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).
|
2018-06-25 21:46:32 +02:00
|
|
|
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.
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
inline void Swap(uint16_t* value) {
|
|
|
|
|
*value = (*value >> 8) | (*value << 8);
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
inline void Swap(uint64_t* value) {
|
2013-03-06 15:04:42 +01:00
|
|
|
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.
|
2018-06-25 21:46:32 +02:00
|
|
|
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().
|
2018-06-25 21:46:32 +02:00
|
|
|
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
|
2018-06-25 21:46:32 +02:00
|
|
|
inline void Swap(int32_t* value) {
|
2013-08-02 20:15:57 +02:00
|
|
|
Swap(reinterpret_cast<uint32_t*>(value));
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
inline void Swap(MDLocationDescriptor* location_descriptor) {
|
2006-09-06 04:56:44 +02:00
|
|
|
Swap(&location_descriptor->data_size);
|
|
|
|
|
Swap(&location_descriptor->rva);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
inline void Swap(MDMemoryDescriptor* memory_descriptor) {
|
2006-09-06 04:56:44 +02:00
|
|
|
Swap(&memory_descriptor->start_of_memory_range);
|
|
|
|
|
Swap(&memory_descriptor->memory);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
inline void Swap(MDGUID* guid) {
|
2006-09-06 04:56:44 +02:00
|
|
|
Swap(&guid->data1);
|
|
|
|
|
Swap(&guid->data2);
|
|
|
|
|
Swap(&guid->data3);
|
|
|
|
|
// Don't swap guid->data4[] because it contains 8-bit quantities.
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
inline void Swap(MDSystemTime* system_time) {
|
2013-08-02 20:15:57 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
inline void Swap(MDXStateFeature* xstate_feature) {
|
2016-08-19 19:29:36 +02:00
|
|
|
Swap(&xstate_feature->offset);
|
|
|
|
|
Swap(&xstate_feature->size);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
inline void Swap(MDXStateConfigFeatureMscInfo* xstate_feature_info) {
|
2016-08-19 19:29:36 +02:00
|
|
|
Swap(&xstate_feature_info->size_of_info);
|
|
|
|
|
Swap(&xstate_feature_info->context_size);
|
|
|
|
|
Swap(&xstate_feature_info->enabled_features);
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < MD_MAXIMUM_XSTATE_FEATURES; i++) {
|
|
|
|
|
Swap(&xstate_feature_info->features[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
inline void Swap(MDRawSimpleStringDictionaryEntry* entry) {
|
2017-09-27 23:13:47 +02:00
|
|
|
Swap(&entry->key);
|
|
|
|
|
Swap(&entry->value);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-26 00:56:36 +01:00
|
|
|
inline void Swap(MDRawCrashpadAnnotation* annotation) {
|
|
|
|
|
Swap(&annotation->name);
|
|
|
|
|
Swap(&annotation->type);
|
|
|
|
|
Swap(&annotation->value);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
inline void Swap(uint16_t* data, size_t size_in_bytes) {
|
2013-08-02 20:15:57 +02:00
|
|
|
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.
|
2018-06-25 21:46:32 +02:00
|
|
|
string* UTF16ToUTF8(const vector<uint16_t>& in, 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.
|
2018-06-25 21:46:32 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
inline void Swap(MDTimeZoneInformation* time_zone) {
|
2013-08-02 20:15:57 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
void ConvertUTF16BufferToUTF8String(const uint16_t* utf16_data,
|
|
|
|
|
size_t max_length_in_bytes,
|
|
|
|
|
string* utf8_result,
|
|
|
|
|
bool swap) {
|
2013-08-02 20:15:57 +02:00
|
|
|
// 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,
|
|
|
|
|
};
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
void PrintValueOrInvalid(bool valid,
|
|
|
|
|
NumberFormat number_format,
|
|
|
|
|
uint32_t value) {
|
2014-06-03 21:35:41 +02:00
|
|
|
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.
|
2018-06-25 21:46:32 +02:00
|
|
|
string TimeTToUTCString(time_t tt) {
|
2014-06-17 20:03:31 +02:00
|
|
|
struct tm timestruct;
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
gmtime_s(×truct, &tt);
|
|
|
|
|
#else
|
|
|
|
|
gmtime_r(&tt, ×truct);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
char timestr[20];
|
2017-08-28 09:26:23 +02:00
|
|
|
size_t rv = strftime(timestr, 20, "%Y-%m-%d %H:%M:%S", ×truct);
|
2014-06-17 20:03:31 +02:00
|
|
|
if (rv == 0) {
|
|
|
|
|
return string();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string(timestr);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
string MDGUIDToString(const MDGUID& uuid) {
|
2017-09-27 23:13:47 +02:00
|
|
|
char buf[37];
|
|
|
|
|
snprintf(buf, sizeof(buf), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
|
|
|
|
uuid.data1,
|
|
|
|
|
uuid.data2,
|
|
|
|
|
uuid.data3,
|
|
|
|
|
uuid.data4[0],
|
|
|
|
|
uuid.data4[1],
|
|
|
|
|
uuid.data4[2],
|
|
|
|
|
uuid.data4[3],
|
|
|
|
|
uuid.data4[4],
|
|
|
|
|
uuid.data4[5],
|
|
|
|
|
uuid.data4[6],
|
|
|
|
|
uuid.data4[7]);
|
|
|
|
|
return std::string(buf);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 23:31:04 +02:00
|
|
|
bool IsDevAshmem(const string& filename) {
|
|
|
|
|
const string kDevAshmem("/dev/ashmem/");
|
|
|
|
|
return filename.compare(0, kDevAshmem.length(), kDevAshmem) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 21:46:32 +02:00
|
|
|
} // namespace
|
2017-09-27 23:13:47 +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;
|
|
|
|
|
}
|
2018-07-31 22:30:11 +02:00
|
|
|
if (!IsContextSizeUnique(sizeof(MDRawContextARM64_Old))) {
|
|
|
|
|
BPLOG(ERROR) << "sizeof(MDRawContextARM64_Old) cannot match the size of any "
|
2013-11-23 02:45:20 +01:00
|
|
|
<< "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.
|
2021-11-02 21:14:52 +01:00
|
|
|
|
|
|
|
|
uint32_t sysinfo_cpu_type = 0;
|
|
|
|
|
if (!minidump_->GetContextCPUFlagsFromSystemInfo(&sysinfo_cpu_type)) {
|
|
|
|
|
BPLOG(ERROR) << "Failed to preserve the current stream position";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (expected_size == sizeof(MDRawContextAMD64) ||
|
|
|
|
|
(sysinfo_cpu_type == MD_CONTEXT_AMD64 &&
|
|
|
|
|
expected_size >= sizeof(MDRawContextAMD64))) {
|
2007-10-31 20:20:31 +01:00
|
|
|
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
|
|
|
|
2021-11-02 21:14:52 +01:00
|
|
|
// Context may include xsave registers and so be larger than
|
|
|
|
|
// sizeof(MDRawContextAMD64). For now we skip this extended data.
|
|
|
|
|
if (expected_size > sizeof(MDRawContextAMD64)) {
|
|
|
|
|
size_t bytes_left = expected_size - sizeof(MDRawContextAMD64);
|
2023-03-29 00:47:20 +02:00
|
|
|
if (bytes_left > kMaxXSaveAreaSize) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext oversized xstate area";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-11-02 21:14:52 +01:00
|
|
|
std::vector<uint8_t> xstate(bytes_left);
|
|
|
|
|
if (!minidump_->ReadBytes(xstate.data(),
|
|
|
|
|
bytes_left)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not skip amd64 xstate";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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) {
|
2021-11-02 21:14:52 +01:00
|
|
|
context_amd64->context_flags |= sysinfo_cpu_type;
|
2012-12-08 04:18:52 +01:00
|
|
|
}
|
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.
|
2016-11-15 14:06:22 +01:00
|
|
|
// https://bugs.chromium.org/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.
|
2016-11-15 14:06:22 +01:00
|
|
|
// https://bugs.chromium.org/p/google-breakpad/issues/detail?id=550
|
2013-11-23 02:45:20 +01:00
|
|
|
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());
|
2018-07-31 22:30:11 +02:00
|
|
|
} else if (expected_size == sizeof(MDRawContextARM64_Old)) {
|
|
|
|
|
// |context_flags| of MDRawContextARM64_Old is 64 bits, but other MDRawContext
|
2013-11-23 02:45:20 +01:00
|
|
|
// 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);
|
|
|
|
|
|
2018-07-31 22:30:11 +02:00
|
|
|
scoped_ptr<MDRawContextARM64_Old> context_arm64(new MDRawContextARM64_Old());
|
2013-11-23 02:45:20 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-31 22:30:11 +02:00
|
|
|
if (cpu_type != MD_CONTEXT_ARM64_OLD) {
|
2013-11-23 02:45:20 +01:00
|
|
|
// TODO: Fall through to switch below.
|
2016-11-15 14:06:22 +01:00
|
|
|
// https://bugs.chromium.org/p/google-breakpad/issues/detail?id=550
|
2013-11-23 02:45:20 +01:00
|
|
|
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,
|
2018-07-31 22:30:11 +02:00
|
|
|
sizeof(MDRawContextARM64_Old) - flags_size)) {
|
2013-11-23 02:45:20 +01:00
|
|
|
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) {
|
2018-08-01 19:48:27 +02:00
|
|
|
Normalize128(&context_arm64->float_save.regs[fpr_index],
|
|
|
|
|
minidump_->is_big_endian());
|
2013-11-23 02:45:20 +01:00
|
|
|
Swap(&context_arm64->float_save.regs[fpr_index]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-12-17 23:21:40 +01:00
|
|
|
|
2018-08-01 19:48:27 +02:00
|
|
|
scoped_ptr<MDRawContextARM64> new_context(new MDRawContextARM64());
|
|
|
|
|
ConvertOldARM64Context(*context_arm64.get(), new_context.get());
|
2018-08-06 19:27:13 +02:00
|
|
|
SetContextFlags(new_context->context_flags);
|
2018-08-01 19:48:27 +02:00
|
|
|
SetContextARM64(new_context.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
|
|
|
|
2021-11-02 21:14:52 +01:00
|
|
|
// Fixup if we were not provided a cpu type.
|
2012-12-08 04:18:52 +01:00
|
|
|
if (cpu_type == 0) {
|
2021-11-02 21:14:52 +01:00
|
|
|
cpu_type = sysinfo_cpu_type;
|
|
|
|
|
context_flags |= cpu_type;
|
2012-12-08 04:18:52 +01:00
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-01 19:48:27 +02:00
|
|
|
case MD_CONTEXT_ARM64: {
|
|
|
|
|
if (expected_size != sizeof(MDRawContextARM64)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext arm64 size mismatch, " <<
|
|
|
|
|
expected_size << " != " << sizeof(MDRawContextARM64);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scoped_ptr<MDRawContextARM64> context_arm64(new MDRawContextARM64());
|
|
|
|
|
|
|
|
|
|
// 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(*context_arm64) - 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 arm 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) {
|
|
|
|
|
Normalize128(&context_arm64->float_save.regs[fpr_index],
|
|
|
|
|
minidump_->is_big_endian());
|
|
|
|
|
Swap(&context_arm64->float_save.regs[fpr_index]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SetContextARM64(context_arm64.release());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-07 00:58:39 +01:00
|
|
|
case MD_CONTEXT_MIPS:
|
|
|
|
|
case MD_CONTEXT_MIPS64: {
|
2013-09-11 13:37:04 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-09 09:53:29 +02:00
|
|
|
case MD_CONTEXT_RISCV: {
|
|
|
|
|
if (expected_size != sizeof(MDRawContextRISCV)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext RISCV size mismatch, "
|
|
|
|
|
<< expected_size
|
|
|
|
|
<< " != "
|
|
|
|
|
<< sizeof(MDRawContextRISCV);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scoped_ptr<MDRawContextRISCV> context_riscv(new MDRawContextRISCV());
|
|
|
|
|
|
|
|
|
|
// 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_riscv->context_flags = context_flags;
|
|
|
|
|
|
|
|
|
|
size_t flags_size = sizeof(context_riscv->context_flags);
|
|
|
|
|
uint8_t* context_after_flags =
|
|
|
|
|
reinterpret_cast<uint8_t*>(context_riscv.get()) + flags_size;
|
|
|
|
|
if (!minidump_->ReadBytes(context_after_flags,
|
|
|
|
|
sizeof(MDRawContextRISCV) - flags_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not read RISCV 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 RISCV does not match system info";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&context_riscv->pc);
|
|
|
|
|
Swap(&context_riscv->ra);
|
|
|
|
|
Swap(&context_riscv->sp);
|
|
|
|
|
Swap(&context_riscv->gp);
|
|
|
|
|
Swap(&context_riscv->tp);
|
|
|
|
|
Swap(&context_riscv->t0);
|
|
|
|
|
Swap(&context_riscv->t1);
|
|
|
|
|
Swap(&context_riscv->t2);
|
|
|
|
|
Swap(&context_riscv->s0);
|
|
|
|
|
Swap(&context_riscv->s1);
|
|
|
|
|
Swap(&context_riscv->a0);
|
|
|
|
|
Swap(&context_riscv->a1);
|
|
|
|
|
Swap(&context_riscv->a2);
|
|
|
|
|
Swap(&context_riscv->a3);
|
|
|
|
|
Swap(&context_riscv->a4);
|
|
|
|
|
Swap(&context_riscv->a5);
|
|
|
|
|
Swap(&context_riscv->a6);
|
|
|
|
|
Swap(&context_riscv->a7);
|
|
|
|
|
Swap(&context_riscv->s2);
|
|
|
|
|
Swap(&context_riscv->s3);
|
|
|
|
|
Swap(&context_riscv->s4);
|
|
|
|
|
Swap(&context_riscv->s5);
|
|
|
|
|
Swap(&context_riscv->s6);
|
|
|
|
|
Swap(&context_riscv->s7);
|
|
|
|
|
Swap(&context_riscv->s8);
|
|
|
|
|
Swap(&context_riscv->s9);
|
|
|
|
|
Swap(&context_riscv->s10);
|
|
|
|
|
Swap(&context_riscv->s11);
|
|
|
|
|
Swap(&context_riscv->t3);
|
|
|
|
|
Swap(&context_riscv->t4);
|
|
|
|
|
Swap(&context_riscv->t5);
|
|
|
|
|
Swap(&context_riscv->t6);
|
|
|
|
|
|
2023-05-23 00:51:47 +02:00
|
|
|
for (int fpr_index = 0; fpr_index < MD_CONTEXT_RISCV_FPR_COUNT;
|
2022-09-09 09:53:29 +02:00
|
|
|
++fpr_index) {
|
2023-05-23 00:51:47 +02:00
|
|
|
Swap(&context_riscv->fpregs[fpr_index]);
|
2022-09-09 09:53:29 +02:00
|
|
|
}
|
2023-05-23 00:51:47 +02:00
|
|
|
Swap(&context_riscv->fcsr);
|
2022-09-09 09:53:29 +02:00
|
|
|
}
|
|
|
|
|
SetContextRISCV(context_riscv.release());
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case MD_CONTEXT_RISCV64: {
|
|
|
|
|
if (expected_size != sizeof(MDRawContextRISCV64)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext RISCV64 size mismatch, "
|
|
|
|
|
<< expected_size
|
|
|
|
|
<< " != "
|
|
|
|
|
<< sizeof(MDRawContextRISCV64);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scoped_ptr<MDRawContextRISCV64> context_riscv64(
|
|
|
|
|
new MDRawContextRISCV64());
|
|
|
|
|
|
|
|
|
|
// 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_riscv64->context_flags = context_flags;
|
|
|
|
|
|
|
|
|
|
size_t flags_size = sizeof(context_riscv64->context_flags);
|
|
|
|
|
uint8_t* context_after_flags =
|
|
|
|
|
reinterpret_cast<uint8_t*>(context_riscv64.get()) + flags_size;
|
|
|
|
|
if (!minidump_->ReadBytes(context_after_flags,
|
|
|
|
|
sizeof(MDRawContextRISCV64) - flags_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpContext could not read RISCV 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 RISCV does not match system info";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&context_riscv64->pc);
|
|
|
|
|
Swap(&context_riscv64->ra);
|
|
|
|
|
Swap(&context_riscv64->sp);
|
|
|
|
|
Swap(&context_riscv64->gp);
|
|
|
|
|
Swap(&context_riscv64->tp);
|
|
|
|
|
Swap(&context_riscv64->t0);
|
|
|
|
|
Swap(&context_riscv64->t1);
|
|
|
|
|
Swap(&context_riscv64->t2);
|
|
|
|
|
Swap(&context_riscv64->s0);
|
|
|
|
|
Swap(&context_riscv64->s1);
|
|
|
|
|
Swap(&context_riscv64->a0);
|
|
|
|
|
Swap(&context_riscv64->a1);
|
|
|
|
|
Swap(&context_riscv64->a2);
|
|
|
|
|
Swap(&context_riscv64->a3);
|
|
|
|
|
Swap(&context_riscv64->a4);
|
|
|
|
|
Swap(&context_riscv64->a5);
|
|
|
|
|
Swap(&context_riscv64->a6);
|
|
|
|
|
Swap(&context_riscv64->a7);
|
|
|
|
|
Swap(&context_riscv64->s2);
|
|
|
|
|
Swap(&context_riscv64->s3);
|
|
|
|
|
Swap(&context_riscv64->s4);
|
|
|
|
|
Swap(&context_riscv64->s5);
|
|
|
|
|
Swap(&context_riscv64->s6);
|
|
|
|
|
Swap(&context_riscv64->s7);
|
|
|
|
|
Swap(&context_riscv64->s8);
|
|
|
|
|
Swap(&context_riscv64->s9);
|
|
|
|
|
Swap(&context_riscv64->s10);
|
|
|
|
|
Swap(&context_riscv64->s11);
|
|
|
|
|
Swap(&context_riscv64->t3);
|
|
|
|
|
Swap(&context_riscv64->t4);
|
|
|
|
|
Swap(&context_riscv64->t5);
|
|
|
|
|
Swap(&context_riscv64->t6);
|
|
|
|
|
|
2023-05-23 00:51:47 +02:00
|
|
|
for (int fpr_index = 0; fpr_index < MD_CONTEXT_RISCV_FPR_COUNT;
|
2022-09-09 09:53:29 +02:00
|
|
|
++fpr_index) {
|
2023-05-23 00:51:47 +02:00
|
|
|
Swap(&context_riscv64->fpregs[fpr_index]);
|
2022-09-09 09:53:29 +02:00
|
|
|
}
|
2023-05-23 00:51:47 +02:00
|
|
|
Swap(&context_riscv64->fcsr);
|
2022-09-09 09:53:29 +02:00
|
|
|
}
|
|
|
|
|
SetContextRISCV64(context_riscv64.release());
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
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
|
|
|
|
2018-08-01 19:48:27 +02:00
|
|
|
case MD_CONTEXT_ARM64:
|
|
|
|
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM64)
|
|
|
|
|
return_value = true;
|
|
|
|
|
break;
|
|
|
|
|
|
2018-07-31 22:30:11 +02:00
|
|
|
case MD_CONTEXT_ARM64_OLD:
|
|
|
|
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM64_OLD)
|
2013-11-23 02:45:20 +01:00
|
|
|
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;
|
2016-02-07 00:58:39 +01:00
|
|
|
|
|
|
|
|
case MD_CONTEXT_MIPS64:
|
|
|
|
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS64)
|
|
|
|
|
return_value = true;
|
|
|
|
|
break;
|
2022-09-09 09:53:29 +02:00
|
|
|
|
|
|
|
|
case MD_CONTEXT_RISCV:
|
|
|
|
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_RISCV)
|
|
|
|
|
return_value = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MD_CONTEXT_RISCV64:
|
|
|
|
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_RISCV64)
|
|
|
|
|
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
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
2017-07-11 21:26:50 +02:00
|
|
|
uint32_t MinidumpMemoryRegion::max_bytes_ = 64 * 1024 * 1024; // 64MB
|
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) {
|
2017-03-25 02:35:26 +01:00
|
|
|
hexdump_width_ = minidump_ ? minidump_->HexdumpMode() : 0;
|
2016-10-26 02:12:09 +02:00
|
|
|
hexdump_ = hexdump_width_ != 0;
|
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) {
|
2016-10-26 02:12:09 +02:00
|
|
|
if (hexdump_) {
|
|
|
|
|
// Pretty hexdump view.
|
|
|
|
|
for (unsigned int byte_index = 0;
|
|
|
|
|
byte_index < descriptor_->memory.data_size;
|
|
|
|
|
byte_index += hexdump_width_) {
|
|
|
|
|
// In case the memory won't fill a whole line.
|
|
|
|
|
unsigned int num_bytes = std::min(
|
|
|
|
|
descriptor_->memory.data_size - byte_index, hexdump_width_);
|
|
|
|
|
|
|
|
|
|
// Display the leading address.
|
|
|
|
|
printf("%08x ", byte_index);
|
|
|
|
|
|
|
|
|
|
// Show the bytes in hex.
|
|
|
|
|
for (unsigned int i = 0; i < hexdump_width_; ++i) {
|
|
|
|
|
if (i < num_bytes) {
|
|
|
|
|
// Show the single byte of memory in hex.
|
|
|
|
|
printf("%02x ", memory[byte_index + i]);
|
|
|
|
|
} else {
|
|
|
|
|
// If this line doesn't fill up, pad it out.
|
|
|
|
|
printf(" ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Insert a space every 8 bytes to make it more readable.
|
|
|
|
|
if (((i + 1) % 8) == 0) {
|
|
|
|
|
printf(" ");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Decode the line as ASCII.
|
|
|
|
|
printf("|");
|
|
|
|
|
for (unsigned int i = 0; i < hexdump_width_; ++i) {
|
|
|
|
|
if (i < num_bytes) {
|
|
|
|
|
uint8_t byte = memory[byte_index + i];
|
|
|
|
|
printf("%c", isprint(byte) ? byte : '.');
|
|
|
|
|
} else {
|
|
|
|
|
// If this line doesn't fill up, pad it out.
|
|
|
|
|
printf(" ");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
printf("|\n");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Ugly raw string view.
|
|
|
|
|
printf("0x");
|
|
|
|
|
for (unsigned int i = 0;
|
|
|
|
|
i < descriptor_->memory.data_size;
|
|
|
|
|
i++) {
|
|
|
|
|
printf("%02x", memory[i]);
|
|
|
|
|
}
|
|
|
|
|
printf("\n");
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
printf("No memory\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-10-26 02:12:09 +02:00
|
|
|
void MinidumpMemoryRegion::SetPrintMode(bool hexdump,
|
|
|
|
|
unsigned int hexdump_width) {
|
|
|
|
|
// Require the width to be a multiple of 8 bytes.
|
|
|
|
|
if (hexdump_width == 0 || (hexdump_width % 8) != 0) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMemoryRegion print hexdump_width must be "
|
|
|
|
|
"multiple of 8, not " << hexdump_width;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hexdump_ = hexdump;
|
|
|
|
|
hexdump_width_ = hexdump_width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
//
|
|
|
|
|
// 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_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-06-24 00:55:43 +02: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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-07 03:01:15 +02:00
|
|
|
//
|
|
|
|
|
// MinidumpThreadName
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
MinidumpThreadName::MinidumpThreadName(Minidump* minidump)
|
|
|
|
|
: MinidumpObject(minidump),
|
|
|
|
|
thread_name_valid_(false),
|
|
|
|
|
thread_name_(),
|
|
|
|
|
name_(NULL) {}
|
|
|
|
|
|
|
|
|
|
MinidumpThreadName::~MinidumpThreadName() {
|
|
|
|
|
delete name_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MinidumpThreadName::Read() {
|
|
|
|
|
// Invalidate cached data.
|
|
|
|
|
delete name_;
|
|
|
|
|
name_ = NULL;
|
|
|
|
|
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
|
|
|
|
if (!minidump_->ReadBytes(&thread_name_, sizeof(thread_name_))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadName cannot read thread name";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&thread_name_.thread_id);
|
|
|
|
|
Swap(&thread_name_.thread_name_rva);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_name_valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MinidumpThreadName::ReadAuxiliaryData() {
|
|
|
|
|
if (!thread_name_valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpThreadName for ReadAuxiliaryData";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// On 32-bit systems, check that the RVA64 is within range (off_t is 32 bits).
|
|
|
|
|
if (thread_name_.thread_name_rva > numeric_limits<off_t>::max()) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadName RVA64 out of range";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read the thread name.
|
|
|
|
|
const off_t thread_name_rva_offset =
|
|
|
|
|
static_cast<off_t>(thread_name_.thread_name_rva);
|
|
|
|
|
name_ = minidump_->ReadString(thread_name_rva_offset);
|
|
|
|
|
if (!name_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadName could not read name";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// At this point, we have enough info for the thread name to be valid.
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MinidumpThreadName::GetThreadID(uint32_t* thread_id) const {
|
|
|
|
|
BPLOG_IF(ERROR, !thread_id) << "MinidumpThreadName::GetThreadID requires "
|
|
|
|
|
"|thread_id|";
|
|
|
|
|
assert(thread_id);
|
|
|
|
|
*thread_id = 0;
|
|
|
|
|
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpThreadName for GetThreadID";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*thread_id = thread_name_.thread_id;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string MinidumpThreadName::GetThreadName() const {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpThreadName for GetThreadName";
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return *name_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MinidumpThreadName::Print() {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadName cannot print invalid data";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("MDRawThreadName\n");
|
|
|
|
|
printf(" thread_id = 0x%x\n", thread_name_.thread_id);
|
|
|
|
|
printf(" thread_name_rva = 0x%" PRIx64 "\n",
|
|
|
|
|
thread_name_.thread_name_rva);
|
|
|
|
|
printf(" thread_name = \"%s\"\n", GetThreadName().c_str());
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpThreadNameList
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
MinidumpThreadNameList::MinidumpThreadNameList(Minidump* minidump)
|
|
|
|
|
: MinidumpStream(minidump), thread_names_(NULL), thread_name_count_(0) {}
|
|
|
|
|
|
|
|
|
|
MinidumpThreadNameList::~MinidumpThreadNameList() {
|
|
|
|
|
delete thread_names_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MinidumpThreadNameList::Read(uint32_t expected_size) {
|
|
|
|
|
// Invalidate cached data.
|
|
|
|
|
delete thread_names_;
|
|
|
|
|
thread_names_ = NULL;
|
|
|
|
|
thread_name_count_ = 0;
|
|
|
|
|
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
|
|
|
|
uint32_t thread_name_count;
|
|
|
|
|
if (expected_size < sizeof(thread_name_count)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadNameList count size mismatch, "
|
|
|
|
|
<< expected_size << " < " << sizeof(thread_name_count);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!minidump_->ReadBytes(&thread_name_count, sizeof(thread_name_count))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadNameList cannot read thread name count";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap())
|
|
|
|
|
Swap(&thread_name_count);
|
|
|
|
|
|
|
|
|
|
if (thread_name_count >
|
|
|
|
|
numeric_limits<uint32_t>::max() / sizeof(MDRawThreadName)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadNameList thread name count "
|
|
|
|
|
<< thread_name_count << " would cause multiplication overflow";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (expected_size !=
|
|
|
|
|
sizeof(thread_name_count) + thread_name_count * sizeof(MDRawThreadName)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadNameList size mismatch, " << expected_size
|
|
|
|
|
<< " != "
|
|
|
|
|
<< sizeof(thread_name_count) +
|
|
|
|
|
thread_name_count * sizeof(MDRawThreadName);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (thread_name_count > MinidumpThreadList::max_threads()) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadNameList count " << thread_name_count
|
|
|
|
|
<< " exceeds maximum " << MinidumpThreadList::max_threads();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (thread_name_count != 0) {
|
|
|
|
|
scoped_ptr<MinidumpThreadNames> thread_names(new MinidumpThreadNames(
|
|
|
|
|
thread_name_count, MinidumpThreadName(minidump_)));
|
|
|
|
|
|
|
|
|
|
for (unsigned int thread_name_index = 0;
|
|
|
|
|
thread_name_index < thread_name_count; ++thread_name_index) {
|
|
|
|
|
MinidumpThreadName* thread_name = &(*thread_names)[thread_name_index];
|
|
|
|
|
|
|
|
|
|
// Assume that the file offset is correct after the last read.
|
|
|
|
|
if (!thread_name->Read()) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadNameList cannot read thread name "
|
|
|
|
|
<< thread_name_index << "/" << thread_name_count;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (unsigned int thread_name_index = 0;
|
|
|
|
|
thread_name_index < thread_name_count; ++thread_name_index) {
|
|
|
|
|
MinidumpThreadName* thread_name = &(*thread_names)[thread_name_index];
|
|
|
|
|
|
|
|
|
|
if (!thread_name->ReadAuxiliaryData() && !thread_name->valid()) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadNameList cannot read thread name "
|
|
|
|
|
<< thread_name_index << "/" << thread_name_count;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_names_ = thread_names.release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_name_count_ = thread_name_count;
|
|
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MinidumpThreadName* MinidumpThreadNameList::GetThreadNameAtIndex(
|
|
|
|
|
unsigned int index) const {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpThreadNameList for GetThreadNameAtIndex";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index >= thread_name_count_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadNameList index out of range: " << index
|
|
|
|
|
<< "/" << thread_name_count_;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &(*thread_names_)[index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MinidumpThreadNameList::Print() {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpThreadNameList cannot print invalid data";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("MinidumpThreadNameList\n");
|
|
|
|
|
printf(" thread_name_count = %d\n", thread_name_count_);
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
|
|
for (unsigned int thread_name_index = 0;
|
|
|
|
|
thread_name_index < thread_name_count_; ++thread_name_index) {
|
|
|
|
|
printf("thread_name[%d]\n", thread_name_index);
|
|
|
|
|
|
|
|
|
|
(*thread_names_)[thread_name_index].Print();
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// 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 "";
|
|
|
|
|
|
2020-06-24 00:55:43 +02: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
|
|
|
|
2020-06-24 00:55:43 +02: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;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-10 15:00:02 +01:00
|
|
|
case MD_OS_ANDROID:
|
2019-07-12 03:34:48 +02:00
|
|
|
case MD_OS_FUCHSIA:
|
2016-02-10 15:00:02 +01:00
|
|
|
case MD_OS_LINUX: {
|
|
|
|
|
// If ELF CodeView data is present, return the debug id.
|
|
|
|
|
if (cv_record_ && cv_record_signature_ == MD_CVINFOELF_SIGNATURE) {
|
|
|
|
|
const MDCVInfoELF* cv_record_elf =
|
|
|
|
|
reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0]);
|
|
|
|
|
assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE);
|
|
|
|
|
|
|
|
|
|
for (unsigned int build_id_index = 0;
|
|
|
|
|
build_id_index < (cv_record_->size() - MDCVInfoELF_minsize);
|
|
|
|
|
++build_id_index) {
|
|
|
|
|
char hexbyte[3];
|
|
|
|
|
snprintf(hexbyte, sizeof(hexbyte), "%02x",
|
|
|
|
|
cv_record_elf->build_id[build_id_index]);
|
|
|
|
|
identifier += hexbyte;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// Otherwise fall through to the case below.
|
2018-01-31 17:38:18 +01:00
|
|
|
BP_FALLTHROUGH;
|
2016-02-10 15:00:02 +01:00
|
|
|
}
|
|
|
|
|
|
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:
|
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);
|
2016-02-10 15:00:02 +01:00
|
|
|
} else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) {
|
|
|
|
|
// It's actually an MDCVInfoELF structure.
|
2016-04-06 01:37:13 +02:00
|
|
|
assert(reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0])->
|
|
|
|
|
cv_signature == MD_CVINFOELF_SIGNATURE);
|
2016-02-10 15:00:02 +01:00
|
|
|
|
|
|
|
|
// For MDCVInfoELF, the debug file is the code file.
|
|
|
|
|
file = *name_;
|
2006-12-05 23:52:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 =
|
2020-06-24 00:55:43 +02:00
|
|
|
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.
|
|
|
|
|
|
2017-08-28 09:26:23 +02:00
|
|
|
size_t 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) {
|
2017-08-28 09:26:23 +02:00
|
|
|
size_t utf16_words = bytes / 2;
|
2006-12-05 23:52:28 +01:00
|
|
|
|
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));
|
2017-03-04 01:09:37 +01:00
|
|
|
if (new_file.get() != nullptr) {
|
|
|
|
|
file = *new_file;
|
|
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-10 15:00:02 +01:00
|
|
|
static string guid_and_age_to_debug_id(const MDGUID& guid,
|
|
|
|
|
uint32_t age) {
|
|
|
|
|
char identifier_string[41];
|
|
|
|
|
snprintf(identifier_string, sizeof(identifier_string),
|
|
|
|
|
"%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x",
|
|
|
|
|
guid.data1,
|
|
|
|
|
guid.data2,
|
|
|
|
|
guid.data3,
|
|
|
|
|
guid.data4[0],
|
|
|
|
|
guid.data4[1],
|
|
|
|
|
guid.data4[2],
|
|
|
|
|
guid.data4[3],
|
|
|
|
|
guid.data4[4],
|
|
|
|
|
guid.data4[5],
|
|
|
|
|
guid.data4[6],
|
|
|
|
|
guid.data4[7],
|
|
|
|
|
age);
|
|
|
|
|
return identifier_string;
|
|
|
|
|
}
|
2006-12-05 23:52:28 +01:00
|
|
|
|
|
|
|
|
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.
|
2016-02-10 15:00:02 +01:00
|
|
|
identifier = guid_and_age_to_debug_id(cv_record_70->signature,
|
|
|
|
|
cv_record_70->age);
|
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;
|
2016-02-10 15:00:02 +01:00
|
|
|
} else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) {
|
|
|
|
|
// It's actually an MDCVInfoELF structure.
|
|
|
|
|
const MDCVInfoELF* cv_record_elf =
|
|
|
|
|
reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0]);
|
|
|
|
|
assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE);
|
|
|
|
|
|
|
|
|
|
// For backwards-compatibility, stuff as many bytes as will fit into
|
|
|
|
|
// a MDGUID and use the MS symbol server format as MDCVInfoPDB70 does
|
|
|
|
|
// with age = 0. Historically Breakpad would do this during dump
|
|
|
|
|
// writing to fit the build id data into a MDCVInfoPDB70 struct.
|
|
|
|
|
// The full build id is available by calling code_identifier.
|
|
|
|
|
MDGUID guid = {0};
|
|
|
|
|
memcpy(&guid, &cv_record_elf->build_id,
|
2016-02-17 12:20:58 +01:00
|
|
|
std::min(cv_record_->size() - MDCVInfoELF_minsize,
|
|
|
|
|
sizeof(MDGUID)));
|
2016-02-10 15:00:02 +01:00
|
|
|
identifier = guid_and_age_to_debug_id(guid, 0);
|
2006-12-05 23:52:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-06-20 20:14:47 +02:00
|
|
|
CodeModule* MinidumpModule::Copy() const {
|
2006-12-05 23:52:28 +01:00
|
|
|
return new BasicCodeModule(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-06-20 20:14:47 +02:00
|
|
|
uint64_t MinidumpModule::shrink_down_delta() const {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MinidumpModule::SetShrinkDownDelta(uint64_t shrink_down_delta) {
|
|
|
|
|
// Not implemented
|
|
|
|
|
assert(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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) {
|
2017-01-30 20:46:33 +01:00
|
|
|
// Now that the structure type is known, recheck the size,
|
|
|
|
|
// ensuring at least one byte for the null terminator.
|
|
|
|
|
if (MDCVInfoPDB70_minsize + 1 > 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) {
|
2017-01-30 20:46:33 +01:00
|
|
|
// Now that the structure type is known, recheck the size,
|
|
|
|
|
// ensuring at least one byte for the null terminator.
|
|
|
|
|
if (MDCVInfoPDB20_minsize + 1 > 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
|
|
|
}
|
2016-02-10 15:00:02 +01:00
|
|
|
} else if (signature == MD_CVINFOELF_SIGNATURE) {
|
|
|
|
|
// Now that the structure type is known, recheck the size.
|
|
|
|
|
if (MDCVInfoELF_minsize > module_.cv_record.data_size) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpModule CodeViewELF record size mismatch, " <<
|
|
|
|
|
MDCVInfoELF_minsize << " > " <<
|
|
|
|
|
module_.cv_record.data_size;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2018-08-28 11:00:49 +02:00
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
MDCVInfoELF* cv_record_elf =
|
|
|
|
|
reinterpret_cast<MDCVInfoELF*>(&(*cv_record)[0]);
|
|
|
|
|
Swap(&cv_record_elf->cv_signature);
|
|
|
|
|
}
|
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));
|
2017-08-28 09:26:23 +02:00
|
|
|
size_t dataBytes = module_.misc_record.data_size -
|
|
|
|
|
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;
|
2020-06-24 00:55:43 +02:00
|
|
|
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);
|
2017-09-27 23:13:47 +02:00
|
|
|
printf(" (cv_record).signature = %s\n",
|
|
|
|
|
MDGUIDToString(cv_record_70->signature).c_str());
|
2006-09-06 04:56:44 +02:00
|
|
|
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);
|
2016-02-10 15:00:02 +01:00
|
|
|
} else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) {
|
|
|
|
|
const MDCVInfoELF* cv_record_elf =
|
|
|
|
|
reinterpret_cast<const MDCVInfoELF*>(cv_record);
|
|
|
|
|
assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE);
|
|
|
|
|
|
|
|
|
|
printf(" (cv_record).cv_signature = 0x%x\n",
|
|
|
|
|
cv_record_elf->cv_signature);
|
|
|
|
|
printf(" (cv_record).build_id = ");
|
|
|
|
|
for (unsigned int build_id_index = 0;
|
|
|
|
|
build_id_index < (cv_record_size - MDCVInfoELF_minsize);
|
|
|
|
|
++build_id_index) {
|
|
|
|
|
printf("%02x", cv_record_elf->build_id[build_id_index]);
|
|
|
|
|
}
|
|
|
|
|
printf("\n");
|
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
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
2017-09-01 16:39:49 +02:00
|
|
|
uint32_t MinidumpModuleList::max_modules_ = 2048;
|
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) {
|
2019-06-11 20:48:14 +02:00
|
|
|
MDOSPlatform platform;
|
2020-02-21 03:56:04 +01:00
|
|
|
if (minidump_->GetPlatform(&platform) &&
|
|
|
|
|
(platform == MD_OS_ANDROID || platform == MD_OS_LINUX)) {
|
|
|
|
|
range_map_->SetMergeStrategy(MergeRangeStrategy::kTruncateLower);
|
2019-06-11 20:48:14 +02:00
|
|
|
}
|
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_) {
|
2017-09-01 16:39:49 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpModuleList count " << module_count <<
|
2007-05-31 17:54:06 +02:00
|
|
|
" 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
|
|
|
|
2018-06-25 23:31:04 +02:00
|
|
|
for (uint32_t module_index = 0; module_index < module_count;
|
2006-11-27 22:42:07 +01:00
|
|
|
++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;
|
2018-06-25 23:31:04 +02:00
|
|
|
for (uint32_t module_index = 0; module_index < module_count;
|
2006-12-05 23:52:28 +01:00
|
|
|
++module_index) {
|
2018-06-25 23:31:04 +02:00
|
|
|
MinidumpModule& module = (*modules)[module_index];
|
2006-12-05 23:52:28 +01:00
|
|
|
|
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.
|
2018-06-25 23:31:04 +02:00
|
|
|
if (!module.ReadAuxiliaryData() && !module.valid()) {
|
2008-04-04 23:41:50 +02:00
|
|
|
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
|
|
|
|
2018-06-25 23:31:04 +02:00
|
|
|
uint64_t base_address = module.base_address();
|
|
|
|
|
uint64_t module_size = module.size();
|
2013-03-06 15:04:42 +01:00
|
|
|
if (base_address == static_cast<uint64_t>(-1)) {
|
2018-06-25 23:31:04 +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
|
|
|
|
2018-06-26 20:34:34 +02:00
|
|
|
// Some minidumps have additional modules in the list that are duplicates.
|
|
|
|
|
// Ignore them. See https://crbug.com/838322
|
|
|
|
|
uint32_t existing_module_index;
|
|
|
|
|
if (range_map_->RetrieveRange(base_address, &existing_module_index,
|
|
|
|
|
nullptr, nullptr, nullptr) &&
|
|
|
|
|
existing_module_index < module_count) {
|
|
|
|
|
const MinidumpModule& existing_module =
|
|
|
|
|
(*modules)[existing_module_index];
|
|
|
|
|
if (existing_module.base_address() == module.base_address() &&
|
|
|
|
|
existing_module.size() == module.size() &&
|
|
|
|
|
existing_module.code_file() == module.code_file() &&
|
|
|
|
|
existing_module.code_identifier() == module.code_identifier()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-26 20:34:13 +02:00
|
|
|
const bool is_android = minidump_->IsAndroid();
|
|
|
|
|
if (!StoreRange(module, base_address, module_index, module_count,
|
|
|
|
|
is_android)) {
|
|
|
|
|
if (!is_android || base_address >= last_end_address) {
|
2018-06-25 23:31:04 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpModuleList could not store module "
|
|
|
|
|
<< module_index << "/" << module_count << ", "
|
|
|
|
|
<< module.code_file() << ", " << HexString(base_address)
|
|
|
|
|
<< "+" << HexString(module_size);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.
|
2018-06-26 20:34:13 +02:00
|
|
|
assert(is_android);
|
2018-06-25 23:31:04 +02:00
|
|
|
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;
|
2014-12-20 01:47:07 +01:00
|
|
|
}
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 23:31:04 +02:00
|
|
|
bool MinidumpModuleList::StoreRange(const MinidumpModule& module,
|
|
|
|
|
uint64_t base_address,
|
|
|
|
|
uint32_t module_index,
|
2018-06-26 20:34:13 +02:00
|
|
|
uint32_t module_count,
|
|
|
|
|
bool is_android) {
|
2018-06-25 23:31:04 +02:00
|
|
|
if (range_map_->StoreRange(base_address, module.size(), module_index))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// 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
|
2018-06-26 20:34:13 +02:00
|
|
|
if (is_android && IsDevAshmem(module.code_file())) {
|
2018-06-25 23:31:04 +02:00
|
|
|
BPLOG(INFO) << "MinidumpModuleList ignoring overlapping module "
|
|
|
|
|
<< module_index << "/" << module_count << ", "
|
|
|
|
|
<< module.code_file() << ", " << HexString(base_address) << "+"
|
|
|
|
|
<< HexString(module.size());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
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;
|
2016-06-06 07:41:10 +02:00
|
|
|
if (!range_map_->RetrieveRange(address, &module_index, NULL /* base */,
|
|
|
|
|
NULL /* delta */, NULL /* size */)) {
|
2007-05-17 20:34:37 +02:00
|
|
|
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;
|
2016-06-20 20:14:47 +02:00
|
|
|
if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index,
|
2016-06-06 07:41:10 +02:00
|
|
|
NULL /* base */, NULL /* delta */,
|
|
|
|
|
NULL /* size */)) {
|
2007-05-17 20:34:37 +02:00
|
|
|
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 {
|
2019-06-11 20:48:14 +02:00
|
|
|
return new BasicCodeModules(this, range_map_->GetMergeStrategy());
|
2006-12-05 23:52:28 +01:00
|
|
|
}
|
|
|
|
|
|
2016-06-20 20:14:47 +02:00
|
|
|
vector<linked_ptr<const CodeModule> >
|
|
|
|
|
MinidumpModuleList::GetShrunkRangeModules() const {
|
|
|
|
|
return vector<linked_ptr<const CodeModule> >();
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2016-06-06 07:41:10 +02:00
|
|
|
if (!range_map_->RetrieveRange(address, ®ion_index, NULL /* base */,
|
|
|
|
|
NULL /* delta */, NULL /* size */)) {
|
2007-05-17 20:34:37 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-06-24 00:55:43 +02: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;
|
|
|
|
|
|
2019-07-12 03:34:48 +02:00
|
|
|
case MD_OS_FUCHSIA:
|
|
|
|
|
os = "fuchsia";
|
|
|
|
|
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;
|
|
|
|
|
|
2018-08-01 19:48:27 +02:00
|
|
|
case MD_CPU_ARCHITECTURE_ARM64:
|
2018-07-31 22:30:11 +02:00
|
|
|
case MD_CPU_ARCHITECTURE_ARM64_OLD:
|
2013-11-23 02:45:20 +01:00
|
|
|
cpu = "arm64";
|
|
|
|
|
break;
|
|
|
|
|
|
2022-09-09 09:53:29 +02:00
|
|
|
case MD_CPU_ARCHITECTURE_RISCV:
|
|
|
|
|
cpu = "riscv";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MD_CPU_ARCHITECTURE_RISCV64:
|
|
|
|
|
cpu = "riscv64";
|
|
|
|
|
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");
|
2021-03-12 02:56:26 +01:00
|
|
|
printf(" processor_architecture = 0x%x (%s)\n",
|
|
|
|
|
system_info_.processor_architecture, GetCPU().c_str());
|
2006-09-06 04:56:44 +02:00
|
|
|
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);
|
2021-03-12 02:56:26 +01:00
|
|
|
printf(" platform_id = 0x%x (%s)\n",
|
|
|
|
|
system_info_.platform_id, GetOS().c_str());
|
2006-09-06 04:56:44 +02:00
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-12-16 21:10:33 +01:00
|
|
|
//
|
|
|
|
|
// MinidumpUnloadedModule
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpUnloadedModule::MinidumpUnloadedModule(Minidump* minidump)
|
|
|
|
|
: MinidumpObject(minidump),
|
|
|
|
|
module_valid_(false),
|
|
|
|
|
unloaded_module_(),
|
|
|
|
|
name_(NULL) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MinidumpUnloadedModule::~MinidumpUnloadedModule() {
|
|
|
|
|
delete name_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string MinidumpUnloadedModule::code_file() const {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpUnloadedModule for code_file";
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return *name_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string MinidumpUnloadedModule::code_identifier() const {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpUnloadedModule for code_identifier";
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 00:55:43 +02:00
|
|
|
MinidumpSystemInfo* minidump_system_info = minidump_->GetSystemInfo();
|
2016-12-16 21:10:33 +01:00
|
|
|
if (!minidump_system_info) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModule code_identifier requires "
|
|
|
|
|
"MinidumpSystemInfo";
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 00:55:43 +02:00
|
|
|
const MDRawSystemInfo* raw_system_info = minidump_system_info->system_info();
|
2016-12-16 21:10:33 +01:00
|
|
|
if (!raw_system_info) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModule code_identifier requires "
|
|
|
|
|
<< "MDRawSystemInfo";
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string identifier;
|
|
|
|
|
|
|
|
|
|
switch (raw_system_info->platform_id) {
|
|
|
|
|
case MD_OS_WIN32_NT:
|
|
|
|
|
case MD_OS_WIN32_WINDOWS: {
|
|
|
|
|
// Use the same format that the MS symbol server uses in filesystem
|
|
|
|
|
// hierarchies.
|
|
|
|
|
char identifier_string[17];
|
|
|
|
|
snprintf(identifier_string, sizeof(identifier_string), "%08X%x",
|
|
|
|
|
unloaded_module_.time_date_stamp,
|
|
|
|
|
unloaded_module_.size_of_image);
|
|
|
|
|
identifier = identifier_string;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case MD_OS_ANDROID:
|
|
|
|
|
case MD_OS_LINUX:
|
|
|
|
|
case MD_OS_MAC_OS_X:
|
|
|
|
|
case MD_OS_IOS:
|
|
|
|
|
case MD_OS_SOLARIS:
|
|
|
|
|
case MD_OS_NACL:
|
|
|
|
|
case MD_OS_PS3: {
|
|
|
|
|
// 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.
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModule code_identifier requires known "
|
|
|
|
|
<< "platform, found "
|
|
|
|
|
<< HexString(raw_system_info->platform_id);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return identifier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string MinidumpUnloadedModule::debug_file() const {
|
|
|
|
|
return ""; // No debug info provided with unloaded modules
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string MinidumpUnloadedModule::debug_identifier() const {
|
|
|
|
|
return ""; // No debug info provided with unloaded modules
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string MinidumpUnloadedModule::version() const {
|
|
|
|
|
return ""; // No version info provided with unloaded modules
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CodeModule* MinidumpUnloadedModule::Copy() const {
|
|
|
|
|
return new BasicCodeModule(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t MinidumpUnloadedModule::shrink_down_delta() const {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MinidumpUnloadedModule::SetShrinkDownDelta(uint64_t shrink_down_delta) {
|
|
|
|
|
// Not implemented
|
|
|
|
|
assert(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MinidumpUnloadedModule::Read(uint32_t expected_size) {
|
|
|
|
|
|
|
|
|
|
delete name_;
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
|
|
|
|
if (expected_size < sizeof(unloaded_module_)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModule expected size is less than size "
|
|
|
|
|
<< "of struct " << expected_size << " < "
|
|
|
|
|
<< sizeof(unloaded_module_);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!minidump_->ReadBytes(&unloaded_module_, sizeof(unloaded_module_))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModule cannot read module";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (expected_size > sizeof(unloaded_module_)) {
|
|
|
|
|
uint32_t module_bytes_remaining = expected_size - sizeof(unloaded_module_);
|
|
|
|
|
off_t pos = minidump_->Tell();
|
|
|
|
|
if (!minidump_->SeekSet(pos + module_bytes_remaining)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModule unable to seek to end of module";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&unloaded_module_.base_of_image);
|
|
|
|
|
Swap(&unloaded_module_.size_of_image);
|
|
|
|
|
Swap(&unloaded_module_.checksum);
|
|
|
|
|
Swap(&unloaded_module_.time_date_stamp);
|
|
|
|
|
Swap(&unloaded_module_.module_name_rva);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for base + size overflow or undersize.
|
|
|
|
|
if (unloaded_module_.size_of_image == 0 ||
|
|
|
|
|
unloaded_module_.size_of_image >
|
|
|
|
|
numeric_limits<uint64_t>::max() - unloaded_module_.base_of_image) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModule has a module problem, " <<
|
|
|
|
|
HexString(unloaded_module_.base_of_image) << "+" <<
|
|
|
|
|
HexString(unloaded_module_.size_of_image);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
module_valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MinidumpUnloadedModule::ReadAuxiliaryData() {
|
|
|
|
|
if (!module_valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpUnloadedModule for ReadAuxiliaryData";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Each module must have a name.
|
|
|
|
|
name_ = minidump_->ReadString(unloaded_module_.module_name_rva);
|
|
|
|
|
if (!name_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModule could not read name";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// At this point, we have enough info for the module to be valid.
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// MinidumpUnloadedModuleList
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
2017-09-01 16:39:49 +02:00
|
|
|
uint32_t MinidumpUnloadedModuleList::max_modules_ = 2048;
|
2016-12-16 21:10:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpUnloadedModuleList::MinidumpUnloadedModuleList(Minidump* minidump)
|
|
|
|
|
: MinidumpStream(minidump),
|
|
|
|
|
range_map_(new RangeMap<uint64_t, unsigned int>()),
|
|
|
|
|
unloaded_modules_(NULL),
|
|
|
|
|
module_count_(0) {
|
2019-06-11 20:48:14 +02:00
|
|
|
range_map_->SetMergeStrategy(MergeRangeStrategy::kTruncateLower);
|
2016-12-16 21:10:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MinidumpUnloadedModuleList::~MinidumpUnloadedModuleList() {
|
|
|
|
|
delete range_map_;
|
|
|
|
|
delete unloaded_modules_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool MinidumpUnloadedModuleList::Read(uint32_t expected_size) {
|
|
|
|
|
range_map_->Clear();
|
|
|
|
|
delete unloaded_modules_;
|
|
|
|
|
unloaded_modules_ = NULL;
|
|
|
|
|
module_count_ = 0;
|
|
|
|
|
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
|
|
|
|
uint32_t size_of_header;
|
|
|
|
|
if (!minidump_->ReadBytes(&size_of_header, sizeof(size_of_header))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read header size";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t size_of_entry;
|
|
|
|
|
if (!minidump_->ReadBytes(&size_of_entry, sizeof(size_of_entry))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read entry size";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t number_of_entries;
|
|
|
|
|
if (!minidump_->ReadBytes(&number_of_entries, sizeof(number_of_entries))) {
|
|
|
|
|
BPLOG(ERROR) <<
|
|
|
|
|
"MinidumpUnloadedModuleList could not read number of entries";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&size_of_header);
|
|
|
|
|
Swap(&size_of_entry);
|
|
|
|
|
Swap(&number_of_entries);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t header_bytes_remaining = size_of_header - sizeof(size_of_header) -
|
|
|
|
|
sizeof(size_of_entry) - sizeof(number_of_entries);
|
|
|
|
|
if (header_bytes_remaining) {
|
|
|
|
|
off_t pos = minidump_->Tell();
|
|
|
|
|
if (!minidump_->SeekSet(pos + header_bytes_remaining)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read header sized "
|
|
|
|
|
<< size_of_header;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (expected_size != size_of_header + (size_of_entry * number_of_entries)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModuleList expected_size mismatch " <<
|
|
|
|
|
expected_size << " != " << size_of_header << " + (" <<
|
|
|
|
|
size_of_entry << " * " << number_of_entries << ")";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (number_of_entries > max_modules_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModuleList count " <<
|
|
|
|
|
number_of_entries << " exceeds maximum " << max_modules_;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (number_of_entries != 0) {
|
|
|
|
|
scoped_ptr<MinidumpUnloadedModules> modules(
|
|
|
|
|
new MinidumpUnloadedModules(number_of_entries,
|
|
|
|
|
MinidumpUnloadedModule(minidump_)));
|
|
|
|
|
|
|
|
|
|
for (unsigned int module_index = 0;
|
|
|
|
|
module_index < number_of_entries;
|
|
|
|
|
++module_index) {
|
|
|
|
|
MinidumpUnloadedModule* module = &(*modules)[module_index];
|
|
|
|
|
|
|
|
|
|
if (!module->Read(size_of_entry)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read module " <<
|
|
|
|
|
module_index << "/" << number_of_entries;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (unsigned int module_index = 0;
|
|
|
|
|
module_index < number_of_entries;
|
|
|
|
|
++module_index) {
|
|
|
|
|
MinidumpUnloadedModule* module = &(*modules)[module_index];
|
|
|
|
|
|
|
|
|
|
if (!module->ReadAuxiliaryData()) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read required "
|
|
|
|
|
"module auxiliary data for module " <<
|
|
|
|
|
module_index << "/" << number_of_entries;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t base_address = module->base_address();
|
|
|
|
|
uint64_t module_size = module->size();
|
|
|
|
|
|
|
|
|
|
// Ignore any failures for conflicting address ranges
|
|
|
|
|
range_map_->StoreRange(base_address, module_size, module_index);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
unloaded_modules_ = modules.release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module_count_ = number_of_entries;
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MinidumpUnloadedModule* MinidumpUnloadedModuleList::GetModuleForAddress(
|
|
|
|
|
uint64_t address) const {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR)
|
|
|
|
|
<< "Invalid MinidumpUnloadedModuleList for GetModuleForAddress";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int module_index;
|
|
|
|
|
if (!range_map_->RetrieveRange(address, &module_index, NULL /* base */,
|
|
|
|
|
NULL /* delta */, NULL /* size */)) {
|
|
|
|
|
BPLOG(INFO) << "MinidumpUnloadedModuleList has no module at "
|
|
|
|
|
<< HexString(address);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GetModuleAtIndex(module_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MinidumpUnloadedModule*
|
|
|
|
|
MinidumpUnloadedModuleList::GetMainModule() const {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MinidumpUnloadedModule*
|
|
|
|
|
MinidumpUnloadedModuleList::GetModuleAtSequence(unsigned int sequence) const {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR)
|
|
|
|
|
<< "Invalid MinidumpUnloadedModuleList for GetModuleAtSequence";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sequence >= module_count_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModuleList sequence out of range: "
|
|
|
|
|
<< sequence << "/" << module_count_;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int module_index;
|
|
|
|
|
if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index,
|
|
|
|
|
NULL /* base */, NULL /* delta */,
|
|
|
|
|
NULL /* size */)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModuleList has no module at sequence "
|
|
|
|
|
<< sequence;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GetModuleAtIndex(module_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MinidumpUnloadedModule*
|
|
|
|
|
MinidumpUnloadedModuleList::GetModuleAtIndex(
|
|
|
|
|
unsigned int index) const {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid MinidumpUnloadedModuleList for GetModuleAtIndex";
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index >= module_count_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpUnloadedModuleList index out of range: "
|
|
|
|
|
<< index << "/" << module_count_;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &(*unloaded_modules_)[index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const CodeModules* MinidumpUnloadedModuleList::Copy() const {
|
2019-06-11 20:48:14 +02:00
|
|
|
return new BasicCodeModules(this, range_map_->GetMergeStrategy());
|
2016-12-16 21:10:33 +01:00
|
|
|
}
|
|
|
|
|
|
2017-02-02 01:26:39 +01:00
|
|
|
vector<linked_ptr<const CodeModule>>
|
2016-12-16 21:10:33 +01:00
|
|
|
MinidumpUnloadedModuleList::GetShrunkRangeModules() const {
|
|
|
|
|
return vector<linked_ptr<const CodeModule> >();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
//
|
|
|
|
|
// 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;
|
|
|
|
|
|
2016-08-19 19:29:36 +02:00
|
|
|
size_t padding = 0;
|
2006-09-06 04:56:44 +02:00
|
|
|
if (expected_size != MD_MISCINFO_SIZE &&
|
2013-08-02 20:15:57 +02:00
|
|
|
expected_size != MD_MISCINFO2_SIZE &&
|
|
|
|
|
expected_size != MD_MISCINFO3_SIZE &&
|
2016-08-19 19:29:36 +02:00
|
|
|
expected_size != MD_MISCINFO4_SIZE &&
|
|
|
|
|
expected_size != MD_MISCINFO5_SIZE) {
|
|
|
|
|
if (expected_size > MD_MISCINFO5_SIZE) {
|
|
|
|
|
// Only read the part of the misc info structure we know how to handle
|
|
|
|
|
BPLOG(INFO) << "MinidumpMiscInfo size larger than expected "
|
|
|
|
|
<< expected_size << ", skipping over the unknown part";
|
|
|
|
|
padding = expected_size - MD_MISCINFO5_SIZE;
|
|
|
|
|
expected_size = MD_MISCINFO5_SIZE;
|
|
|
|
|
} else {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << expected_size
|
|
|
|
|
<< " != " << MD_MISCINFO_SIZE << ", " << MD_MISCINFO2_SIZE
|
|
|
|
|
<< ", " << MD_MISCINFO3_SIZE << ", " << MD_MISCINFO4_SIZE
|
|
|
|
|
<< ", " << MD_MISCINFO5_SIZE << ")";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
2016-08-19 19:29:36 +02:00
|
|
|
if (padding != 0) {
|
|
|
|
|
off_t saved_position = minidump_->Tell();
|
|
|
|
|
if (saved_position == -1) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpMiscInfo could not tell the current position";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-28 09:26:23 +02:00
|
|
|
if (!minidump_->SeekSet(saved_position + static_cast<off_t>(padding))) {
|
2016-08-19 19:29:36 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpMiscInfo could not seek past the miscellaneous "
|
|
|
|
|
<< "info structure";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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).
|
|
|
|
|
}
|
2016-08-19 19:29:36 +02:00
|
|
|
if (misc_info_.size_of_info > MD_MISCINFO4_SIZE) {
|
|
|
|
|
// Swap version 5 fields
|
|
|
|
|
Swap(&misc_info_.xstate_data);
|
|
|
|
|
Swap(&misc_info_.process_cookie);
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
}
|
|
|
|
|
|
2016-08-19 19:29:36 +02:00
|
|
|
if (expected_size + padding != 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
|
|
|
}
|
2016-08-19 19:29:36 +02:00
|
|
|
if (misc_info_.size_of_info > MD_MISCINFO4_SIZE) {
|
|
|
|
|
// Print version 5 fields
|
|
|
|
|
if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_COOKIE) {
|
|
|
|
|
printf(" xstate_data.size_of_info = %d\n",
|
|
|
|
|
misc_info_.xstate_data.size_of_info);
|
|
|
|
|
printf(" xstate_data.context_size = %d\n",
|
|
|
|
|
misc_info_.xstate_data.context_size);
|
|
|
|
|
printf(" xstate_data.enabled_features = 0x%" PRIx64 "\n",
|
|
|
|
|
misc_info_.xstate_data.enabled_features);
|
|
|
|
|
for (size_t i = 0; i < MD_MAXIMUM_XSTATE_FEATURES; i++) {
|
2016-10-11 12:42:43 +02:00
|
|
|
if ((misc_info_.xstate_data.enabled_features >> i) & 1) {
|
2016-08-19 19:29:36 +02:00
|
|
|
printf(" xstate_data.features[%02zu] = { %d, %d }\n", i,
|
|
|
|
|
misc_info_.xstate_data.features[i].offset,
|
|
|
|
|
misc_info_.xstate_data.features[i].size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (misc_info_.xstate_data.enabled_features == 0) {
|
|
|
|
|
printf(" xstate_data.features[] = (empty)\n");
|
|
|
|
|
}
|
|
|
|
|
printf(" process_cookie = %d\n",
|
|
|
|
|
misc_info_.process_cookie);
|
|
|
|
|
} else {
|
|
|
|
|
printf(" xstate_data.size_of_info = (invalid)\n");
|
|
|
|
|
printf(" xstate_data.context_size = (invalid)\n");
|
|
|
|
|
printf(" xstate_data.enabled_features = (invalid)\n");
|
|
|
|
|
printf(" xstate_data.features[] = (invalid)\n");
|
|
|
|
|
printf(" process_cookie = (invalid)\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-06-24 00:55:43 +02: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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-06-24 00:55:43 +02: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();
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-28 09:26:23 +02:00
|
|
|
info_count_ = static_cast<uint32_t>(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;
|
2016-06-06 07:41:10 +02:00
|
|
|
if (!range_map_->RetrieveRange(address, &info_index, NULL /* base */,
|
|
|
|
|
NULL /* delta */, NULL /* size */)) {
|
2011-01-13 20:05:33 +01:00
|
|
|
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
|
|
|
|
|
//
|
|
|
|
|
|
2020-06-24 00:55:43 +02:00
|
|
|
MinidumpLinuxMaps::MinidumpLinuxMaps(Minidump* minidump)
|
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
|
|
|
: MinidumpObject(minidump) {
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-12 02:21:44 +02:00
|
|
|
void MinidumpLinuxMaps::Print() const {
|
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
|
|
|
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
|
|
|
|
|
//
|
|
|
|
|
|
2020-06-24 00:55:43 +02:00
|
|
|
MinidumpLinuxMapsList::MinidumpLinuxMapsList(Minidump* minidump)
|
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
|
|
|
: 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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 00:55:43 +02:00
|
|
|
const MinidumpLinuxMaps* MinidumpLinuxMapsList::GetLinuxMapsForAddress(
|
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
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 00:55:43 +02:00
|
|
|
const MinidumpLinuxMaps* MinidumpLinuxMapsList::GetLinuxMapsAtIndex(
|
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
|
|
|
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();
|
2017-08-28 09:26:23 +02:00
|
|
|
maps_count_ = static_cast<uint32_t>(maps_->size());
|
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
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-12 02:21:44 +02:00
|
|
|
void MinidumpLinuxMapsList::Print() 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) << "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
|
|
|
|
2017-09-27 23:13:47 +02:00
|
|
|
//
|
|
|
|
|
// MinidumpCrashpadInfo
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MinidumpCrashpadInfo::MinidumpCrashpadInfo(Minidump* minidump)
|
|
|
|
|
: MinidumpStream(minidump),
|
|
|
|
|
crashpad_info_(),
|
|
|
|
|
module_crashpad_info_links_(),
|
|
|
|
|
module_crashpad_info_(),
|
|
|
|
|
module_crashpad_info_list_annotations_(),
|
|
|
|
|
module_crashpad_info_simple_annotations_(),
|
2023-01-26 00:56:36 +01:00
|
|
|
module_crashpad_info_annotation_objects_(),
|
2017-09-27 23:13:47 +02:00
|
|
|
simple_annotations_() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool MinidumpCrashpadInfo::Read(uint32_t expected_size) {
|
|
|
|
|
valid_ = false;
|
|
|
|
|
|
2023-01-20 19:30:35 +01:00
|
|
|
// Support old minidumps that do not implement newer crashpad_info_
|
|
|
|
|
// fields, currently limited to the address mask.
|
|
|
|
|
static_assert(sizeof(crashpad_info_) == 64,
|
|
|
|
|
"Updated ::Read for new crashpad_info field.");
|
|
|
|
|
|
|
|
|
|
constexpr size_t crashpad_info_min_size =
|
|
|
|
|
offsetof(decltype(crashpad_info_), reserved);
|
|
|
|
|
if (expected_size < crashpad_info_min_size) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpCrashpadInfo size mismatch, " << expected_size
|
|
|
|
|
<< " < " << crashpad_info_min_size;
|
2017-09-27 23:13:47 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-20 19:30:35 +01:00
|
|
|
if (!minidump_->ReadBytes(&crashpad_info_, crashpad_info_min_size)) {
|
2017-09-27 23:13:47 +02:00
|
|
|
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad info";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-01-20 19:30:35 +01:00
|
|
|
expected_size -= crashpad_info_min_size;
|
|
|
|
|
|
|
|
|
|
// Read `reserved` if available.
|
|
|
|
|
size_t crashpad_reserved_size = sizeof(crashpad_info_.reserved);
|
|
|
|
|
if (expected_size >= crashpad_reserved_size) {
|
|
|
|
|
if (!minidump_->ReadBytes(
|
|
|
|
|
&crashpad_info_.reserved,
|
|
|
|
|
crashpad_reserved_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read reserved";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
expected_size -= crashpad_reserved_size;
|
|
|
|
|
} else {
|
|
|
|
|
crashpad_info_.reserved = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read `address_mask` if available.
|
|
|
|
|
size_t crashpad_address_mask_size = sizeof(crashpad_info_.address_mask);
|
|
|
|
|
if (expected_size >= crashpad_address_mask_size) {
|
|
|
|
|
if (!minidump_->ReadBytes(
|
|
|
|
|
&crashpad_info_.address_mask,
|
|
|
|
|
crashpad_address_mask_size)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read address mask";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
expected_size -= crashpad_address_mask_size;
|
|
|
|
|
} else {
|
|
|
|
|
crashpad_info_.address_mask = 0;
|
|
|
|
|
}
|
2017-09-27 23:13:47 +02:00
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&crashpad_info_.version);
|
|
|
|
|
Swap(&crashpad_info_.report_id);
|
|
|
|
|
Swap(&crashpad_info_.client_id);
|
|
|
|
|
Swap(&crashpad_info_.simple_annotations);
|
|
|
|
|
Swap(&crashpad_info_.module_list);
|
2023-01-20 19:30:35 +01:00
|
|
|
Swap(&crashpad_info_.reserved);
|
|
|
|
|
Swap(&crashpad_info_.address_mask);
|
2017-09-27 23:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (crashpad_info_.simple_annotations.data_size) {
|
|
|
|
|
if (!minidump_->ReadSimpleStringDictionary(
|
|
|
|
|
crashpad_info_.simple_annotations.rva,
|
|
|
|
|
&simple_annotations_)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read simple_annotations";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (crashpad_info_.module_list.data_size) {
|
|
|
|
|
if (!minidump_->SeekSet(crashpad_info_.module_list.rva)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot seek to module_list";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t count;
|
|
|
|
|
if (!minidump_->ReadBytes(&count, sizeof(count))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read module_list count";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scoped_array<MDRawModuleCrashpadInfoLink> module_crashpad_info_links(
|
|
|
|
|
new MDRawModuleCrashpadInfoLink[count]);
|
|
|
|
|
|
|
|
|
|
// Read the entire array in one fell swoop, instead of reading one entry
|
|
|
|
|
// at a time in the loop.
|
|
|
|
|
if (!minidump_->ReadBytes(
|
|
|
|
|
&module_crashpad_info_links[0],
|
|
|
|
|
sizeof(MDRawModuleCrashpadInfoLink) * count)) {
|
|
|
|
|
BPLOG(ERROR)
|
|
|
|
|
<< "MinidumpCrashpadInfo could not read Crashpad module links";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint32_t index = 0; index < count; ++index) {
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&module_crashpad_info_links[index].minidump_module_list_index);
|
|
|
|
|
Swap(&module_crashpad_info_links[index].location);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!minidump_->SeekSet(module_crashpad_info_links[index].location.rva)) {
|
|
|
|
|
BPLOG(ERROR)
|
|
|
|
|
<< "MinidumpCrashpadInfo cannot seek to Crashpad module info";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MDRawModuleCrashpadInfo module_crashpad_info;
|
|
|
|
|
if (!minidump_->ReadBytes(&module_crashpad_info,
|
|
|
|
|
sizeof(module_crashpad_info))) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad module info";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (minidump_->swap()) {
|
|
|
|
|
Swap(&module_crashpad_info.version);
|
|
|
|
|
Swap(&module_crashpad_info.list_annotations);
|
|
|
|
|
Swap(&module_crashpad_info.simple_annotations);
|
2023-01-26 00:56:36 +01:00
|
|
|
Swap(&module_crashpad_info.annotation_objects);
|
2017-09-27 23:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> list_annotations;
|
|
|
|
|
if (module_crashpad_info.list_annotations.data_size) {
|
|
|
|
|
if (!minidump_->ReadStringList(
|
|
|
|
|
module_crashpad_info.list_annotations.rva,
|
|
|
|
|
&list_annotations)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad module "
|
|
|
|
|
"info list annotations";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::map<std::string, std::string> simple_annotations;
|
|
|
|
|
if (module_crashpad_info.simple_annotations.data_size) {
|
|
|
|
|
if (!minidump_->ReadSimpleStringDictionary(
|
|
|
|
|
module_crashpad_info.simple_annotations.rva,
|
|
|
|
|
&simple_annotations)) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad module "
|
|
|
|
|
"info simple annotations";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-26 00:56:36 +01:00
|
|
|
std::vector<MinidumpCrashpadInfo::AnnotationObject> annotation_objects;
|
|
|
|
|
if (module_crashpad_info.annotation_objects.data_size) {
|
|
|
|
|
if (!minidump_->ReadCrashpadAnnotationsList(
|
|
|
|
|
module_crashpad_info.annotation_objects.rva,
|
|
|
|
|
&annotation_objects)) {
|
|
|
|
|
BPLOG(ERROR)
|
|
|
|
|
<< "MinidumpCrashpadInfo cannot read Crashpad annotations list";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-27 23:13:47 +02:00
|
|
|
module_crashpad_info_links_.push_back(
|
|
|
|
|
module_crashpad_info_links[index].minidump_module_list_index);
|
|
|
|
|
module_crashpad_info_.push_back(module_crashpad_info);
|
|
|
|
|
module_crashpad_info_list_annotations_.push_back(list_annotations);
|
|
|
|
|
module_crashpad_info_simple_annotations_.push_back(simple_annotations);
|
2023-01-26 00:56:36 +01:00
|
|
|
module_crashpad_info_annotation_objects_.push_back(annotation_objects);
|
2017-09-27 23:13:47 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
valid_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MinidumpCrashpadInfo::Print() {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot print invalid data";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("MDRawCrashpadInfo\n");
|
|
|
|
|
printf(" version = %d\n", crashpad_info_.version);
|
|
|
|
|
printf(" report_id = %s\n",
|
|
|
|
|
MDGUIDToString(crashpad_info_.report_id).c_str());
|
|
|
|
|
printf(" client_id = %s\n",
|
|
|
|
|
MDGUIDToString(crashpad_info_.client_id).c_str());
|
2021-08-26 06:20:02 +02:00
|
|
|
for (const auto& annot : simple_annotations_) {
|
|
|
|
|
printf(" simple_annotations[\"%s\"] = %s\n", annot.first.c_str(),
|
|
|
|
|
annot.second.c_str());
|
2017-09-27 23:13:47 +02:00
|
|
|
}
|
|
|
|
|
for (uint32_t module_index = 0;
|
|
|
|
|
module_index < module_crashpad_info_links_.size();
|
|
|
|
|
++module_index) {
|
|
|
|
|
printf(" module_list[%d].minidump_module_list_index = %d\n",
|
|
|
|
|
module_index, module_crashpad_info_links_[module_index]);
|
|
|
|
|
printf(" module_list[%d].version = %d\n",
|
|
|
|
|
module_index, module_crashpad_info_[module_index].version);
|
2021-08-26 06:20:02 +02:00
|
|
|
const auto& list_annots =
|
|
|
|
|
module_crashpad_info_list_annotations_[module_index];
|
|
|
|
|
for (uint32_t annotation_index = 0; annotation_index < list_annots.size();
|
2017-09-27 23:13:47 +02:00
|
|
|
++annotation_index) {
|
2021-08-26 06:20:02 +02:00
|
|
|
printf(" module_list[%d].list_annotations[%d] = %s\n", module_index,
|
|
|
|
|
annotation_index, list_annots[annotation_index].c_str());
|
2017-09-27 23:13:47 +02:00
|
|
|
}
|
2021-08-26 06:20:02 +02:00
|
|
|
const auto& simple_annots =
|
|
|
|
|
module_crashpad_info_simple_annotations_[module_index];
|
|
|
|
|
for (const auto& annot : simple_annots) {
|
2017-09-27 23:13:47 +02:00
|
|
|
printf(" module_list[%d].simple_annotations[\"%s\"] = %s\n",
|
2021-08-26 06:20:02 +02:00
|
|
|
module_index, annot.first.c_str(), annot.second.c_str());
|
2017-09-27 23:13:47 +02:00
|
|
|
}
|
2023-02-16 19:51:13 +01:00
|
|
|
const auto& crashpad_annots =
|
|
|
|
|
module_crashpad_info_annotation_objects_[module_index];
|
|
|
|
|
for (const AnnotationObject& annot : crashpad_annots) {
|
|
|
|
|
std::string str_value;
|
|
|
|
|
if (annot.type == 1) {
|
|
|
|
|
// Value represents a C-style string.
|
|
|
|
|
for (const uint8_t& v : annot.value) {
|
|
|
|
|
str_value.append(1, static_cast<char>(v));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Value represents something else.
|
|
|
|
|
char buffer[3];
|
|
|
|
|
for (const uint8_t& v : annot.value) {
|
2023-05-26 17:52:25 +02:00
|
|
|
snprintf(buffer, sizeof(buffer), "%02X", v);
|
2023-02-16 19:51:13 +01:00
|
|
|
str_value.append(buffer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
printf(
|
|
|
|
|
" module_list[%d].crashpad_annotations[\"%s\"] (type = %u) = %s\n",
|
|
|
|
|
module_index, annot.name.c_str(), annot.type, str_value.c_str());
|
|
|
|
|
}
|
2023-01-20 22:19:37 +01:00
|
|
|
printf(" address_mask = %" PRIu64 "\n", crashpad_info_.address_mask);
|
2017-09-27 23:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
2016-10-26 02:12:09 +02:00
|
|
|
Minidump::Minidump(const string& path, bool hexdump, unsigned int hexdump_width)
|
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),
|
2018-08-01 19:48:27 +02:00
|
|
|
is_big_endian_(false),
|
2016-10-26 02:12:09 +02:00
|
|
|
valid_(false),
|
|
|
|
|
hexdump_(hexdump),
|
|
|
|
|
hexdump_width_(hexdump_width) {
|
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),
|
2018-08-01 19:48:27 +02:00
|
|
|
is_big_endian_(false),
|
2017-03-25 02:35:26 +01:00
|
|
|
valid_(false),
|
|
|
|
|
hexdump_(false),
|
|
|
|
|
hexdump_width_(0) {
|
2009-12-09 02:24:37 +01:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
2020-06-24 00:55:43 +02: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;
|
2016-02-07 00:58:39 +01:00
|
|
|
case MD_CPU_ARCHITECTURE_MIPS64:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_MIPS64;
|
|
|
|
|
break;
|
2012-12-08 04:18:52 +01:00
|
|
|
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;
|
2018-08-01 19:48:27 +02:00
|
|
|
case MD_CPU_ARCHITECTURE_ARM64:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_ARM64;
|
|
|
|
|
break;
|
2018-07-31 22:30:11 +02:00
|
|
|
case MD_CPU_ARCHITECTURE_ARM64_OLD:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_ARM64_OLD;
|
2013-11-23 02:45:20 +01:00
|
|
|
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;
|
2022-09-09 09:53:29 +02:00
|
|
|
case MD_CPU_ARCHITECTURE_RISCV:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_RISCV;
|
|
|
|
|
break;
|
|
|
|
|
case MD_CPU_ARCHITECTURE_RISCV64:
|
|
|
|
|
*context_cpu_flags = MD_CONTEXT_RISCV64;
|
|
|
|
|
break;
|
2012-12-08 04:18:52 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-01 19:48:27 +02:00
|
|
|
#if defined(__BIG_ENDIAN__) || \
|
|
|
|
|
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
|
|
|
|
is_big_endian_ = !swap_;
|
|
|
|
|
#else
|
|
|
|
|
is_big_endian_ = swap_;
|
|
|
|
|
#endif
|
|
|
|
|
|
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:
|
2022-06-07 03:01:15 +02:00
|
|
|
case MD_THREAD_NAME_LIST_STREAM:
|
2006-11-27 22:42:07 +01:00
|
|
|
case MD_MODULE_LIST_STREAM:
|
|
|
|
|
case MD_MEMORY_LIST_STREAM:
|
|
|
|
|
case MD_EXCEPTION_STREAM:
|
|
|
|
|
case MD_SYSTEM_INFO_STREAM:
|
|
|
|
|
case MD_MISC_INFO_STREAM:
|
2017-09-27 23:13:47 +02:00
|
|
|
case MD_BREAKPAD_INFO_STREAM:
|
|
|
|
|
case MD_CRASHPAD_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;
|
|
|
|
|
}
|
2018-01-31 17:38:18 +01:00
|
|
|
BP_FALLTHROUGH;
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-07 03:01:15 +02:00
|
|
|
MinidumpThreadNameList* Minidump::GetThreadNameList() {
|
|
|
|
|
MinidumpThreadNameList* thread_name_list;
|
|
|
|
|
return GetStream(&thread_name_list);
|
|
|
|
|
}
|
2006-09-06 04:56:44 +02:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-12-16 21:10:33 +01:00
|
|
|
MinidumpUnloadedModuleList* Minidump::GetUnloadedModuleList() {
|
|
|
|
|
MinidumpUnloadedModuleList* unloaded_module_list;
|
|
|
|
|
return GetStream(&unloaded_module_list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-09-06 04:56:44 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 00:55:43 +02:00
|
|
|
MinidumpLinuxMapsList* Minidump::GetLinuxMapsList() {
|
|
|
|
|
MinidumpLinuxMapsList* linux_maps_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
|
|
|
return GetStream(&linux_maps_list);
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-20 20:14:47 +02:00
|
|
|
bool Minidump::IsAndroid() {
|
2019-06-11 20:48:14 +02:00
|
|
|
MDOSPlatform platform;
|
|
|
|
|
return GetPlatform(&platform) && platform == MD_OS_ANDROID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Minidump::GetPlatform(MDOSPlatform* platform) {
|
2016-06-20 20:14:47 +02:00
|
|
|
// Save the current stream position
|
|
|
|
|
off_t saved_position = Tell();
|
|
|
|
|
if (saved_position == -1) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const MDRawSystemInfo* system_info =
|
|
|
|
|
GetSystemInfo() ? GetSystemInfo()->system_info() : NULL;
|
|
|
|
|
|
|
|
|
|
// Restore position and return
|
|
|
|
|
if (!SeekSet(saved_position)) {
|
|
|
|
|
BPLOG(ERROR) << "Couldn't seek back to saved position";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 20:48:14 +02:00
|
|
|
if (!system_info) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
*platform = static_cast<MDOSPlatform>(system_info->platform_id);
|
|
|
|
|
return true;
|
2016-06-20 20:14:47 +02:00
|
|
|
}
|
|
|
|
|
|
2017-09-27 23:13:47 +02:00
|
|
|
MinidumpCrashpadInfo* Minidump::GetCrashpadInfo() {
|
|
|
|
|
MinidumpCrashpadInfo* crashpad_info;
|
|
|
|
|
return GetStream(&crashpad_info);
|
|
|
|
|
}
|
|
|
|
|
|
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";
|
2022-06-07 03:01:15 +02:00
|
|
|
case MD_THREAD_NAME_LIST_STREAM:
|
|
|
|
|
return "MD_THREAD_NAME_LIST_STREAM";
|
2014-07-11 12:57:30 +02:00
|
|
|
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";
|
2016-04-13 18:15:15 +02:00
|
|
|
case MD_TOKEN_STREAM:
|
|
|
|
|
return "MD_TOKEN_STREAM";
|
|
|
|
|
case MD_JAVASCRIPT_DATA_STREAM:
|
|
|
|
|
return "MD_JAVASCRIPT_DATA_STREAM";
|
|
|
|
|
case MD_SYSTEM_MEMORY_INFO_STREAM:
|
|
|
|
|
return "MD_SYSTEM_MEMORY_INFO_STREAM";
|
|
|
|
|
case MD_PROCESS_VM_COUNTERS_STREAM:
|
|
|
|
|
return "MD_PROCESS_VM_COUNTERS_STREAM";
|
2014-07-11 12:57:30 +02:00
|
|
|
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";
|
2017-09-27 23:13:47 +02:00
|
|
|
case MD_CRASHPAD_INFO_STREAM:
|
|
|
|
|
return "MD_CRASHPAD_INFO_STREAM";
|
2014-07-11 12:57:30 +02:00
|
|
|
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;
|
2016-06-20 20:14:47 +02:00
|
|
|
const 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_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-09-27 23:13:47 +02:00
|
|
|
bool Minidump::ReadUTF8String(off_t offset, string* string_utf8) {
|
|
|
|
|
if (!valid_) {
|
|
|
|
|
BPLOG(ERROR) << "Invalid Minidump for ReadString";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!SeekSet(offset)) {
|
|
|
|
|
BPLOG(ERROR) << "ReadUTF8String could not seek to string at offset "
|
|
|
|
|
<< offset;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t bytes;
|
|
|
|
|
if (!ReadBytes(&bytes, sizeof(bytes))) {
|
|
|
|
|
BPLOG(ERROR) << "ReadUTF8String could not read string size at offset " <<
|
|
|
|
|
offset;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (swap_) {
|
|
|
|
|
Swap(&bytes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bytes > max_string_length_) {
|
|
|
|
|
BPLOG(ERROR) << "ReadUTF8String string length " << bytes <<
|
|
|
|
|
" exceeds maximum " << max_string_length_ <<
|
|
|
|
|
" at offset " << offset;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string_utf8->resize(bytes);
|
|
|
|
|
|
|
|
|
|
if (!ReadBytes(&(*string_utf8)[0], bytes)) {
|
|
|
|
|
BPLOG(ERROR) << "ReadUTF8String could not read " << bytes <<
|
|
|
|
|
"-byte string at offset " << offset;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool Minidump::ReadStringList(
|
|
|
|
|
off_t offset,
|
|
|
|
|
std::vector<std::string>* string_list) {
|
|
|
|
|
string_list->clear();
|
|
|
|
|
|
|
|
|
|
if (!SeekSet(offset)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump cannot seek to string_list";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t count;
|
|
|
|
|
if (!ReadBytes(&count, sizeof(count))) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump cannot read string_list count";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (swap_) {
|
|
|
|
|
Swap(&count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scoped_array<MDRVA> rvas(new MDRVA[count]);
|
|
|
|
|
|
|
|
|
|
// Read the entire array in one fell swoop, instead of reading one entry
|
|
|
|
|
// at a time in the loop.
|
|
|
|
|
if (!ReadBytes(&rvas[0], sizeof(MDRVA) * count)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump could not read string_list";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint32_t index = 0; index < count; ++index) {
|
|
|
|
|
if (swap()) {
|
|
|
|
|
Swap(&rvas[index]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string entry;
|
|
|
|
|
if (!ReadUTF8String(rvas[index], &entry)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump could not read string_list entry";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string_list->push_back(entry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool Minidump::ReadSimpleStringDictionary(
|
|
|
|
|
off_t offset,
|
|
|
|
|
std::map<std::string, std::string>* simple_string_dictionary) {
|
|
|
|
|
simple_string_dictionary->clear();
|
|
|
|
|
|
|
|
|
|
if (!SeekSet(offset)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump cannot seek to simple_string_dictionary";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t count;
|
|
|
|
|
if (!ReadBytes(&count, sizeof(count))) {
|
|
|
|
|
BPLOG(ERROR)
|
|
|
|
|
<< "Minidump cannot read simple_string_dictionary count";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (swap()) {
|
|
|
|
|
Swap(&count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scoped_array<MDRawSimpleStringDictionaryEntry> entries(
|
|
|
|
|
new MDRawSimpleStringDictionaryEntry[count]);
|
|
|
|
|
|
|
|
|
|
// Read the entire array in one fell swoop, instead of reading one entry
|
|
|
|
|
// at a time in the loop.
|
|
|
|
|
if (!ReadBytes(
|
|
|
|
|
&entries[0],
|
|
|
|
|
sizeof(MDRawSimpleStringDictionaryEntry) * count)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump could not read simple_string_dictionary";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint32_t index = 0; index < count; ++index) {
|
|
|
|
|
if (swap()) {
|
|
|
|
|
Swap(&entries[index]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string key;
|
|
|
|
|
if (!ReadUTF8String(entries[index].key, &key)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump could not read simple_string_dictionary key";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string value;
|
|
|
|
|
if (!ReadUTF8String(entries[index].value, &value)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump could not read simple_string_dictionary value";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (simple_string_dictionary->find(key) !=
|
|
|
|
|
simple_string_dictionary->end()) {
|
|
|
|
|
BPLOG(ERROR)
|
|
|
|
|
<< "Minidump: discarding duplicate simple_string_dictionary value "
|
|
|
|
|
<< value << " for key " << key;
|
|
|
|
|
} else {
|
|
|
|
|
simple_string_dictionary->insert(std::make_pair(key, value));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-26 00:56:36 +01:00
|
|
|
bool Minidump::ReadCrashpadAnnotationsList(
|
|
|
|
|
off_t offset,
|
|
|
|
|
std::vector<MinidumpCrashpadInfo::AnnotationObject>* annotations_list) {
|
|
|
|
|
annotations_list->clear();
|
|
|
|
|
|
|
|
|
|
if (!SeekSet(offset)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump cannot seek to annotations_list";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t count;
|
|
|
|
|
if (!ReadBytes(&count, sizeof(count))) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump cannot read annotations_list count";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (swap_) {
|
|
|
|
|
Swap(&count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scoped_array<MDRawCrashpadAnnotation> objects(
|
|
|
|
|
new MDRawCrashpadAnnotation[count]);
|
|
|
|
|
|
|
|
|
|
// Read the entire array in one fell swoop, instead of reading one entry
|
|
|
|
|
// at a time in the loop.
|
|
|
|
|
if (!ReadBytes(&objects[0], sizeof(MDRawCrashpadAnnotation) * count)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump could not read annotations_list";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint32_t index = 0; index < count; ++index) {
|
|
|
|
|
MDRawCrashpadAnnotation annotation = objects[index];
|
|
|
|
|
|
|
|
|
|
if (swap_) {
|
|
|
|
|
Swap(&annotation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string name;
|
|
|
|
|
if (!ReadUTF8String(annotation.name, &name)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump could not read annotation name";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!SeekSet(annotation.value)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump cannot seek to annotations value";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t value_length;
|
|
|
|
|
if (!ReadBytes(&value_length, sizeof(value_length))) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump could not read annotation value length";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<uint8_t> value_data(value_length);
|
|
|
|
|
if (!ReadBytes(value_data.data(), value_length)) {
|
|
|
|
|
BPLOG(ERROR) << "Minidump could not read annotation value";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MinidumpCrashpadInfo::AnnotationObject object = {annotation.type, name,
|
|
|
|
|
value_data};
|
|
|
|
|
annotations_list->push_back(object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-09-27 23:13:47 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-20 20:14:47 +02:00
|
|
|
const 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
|