blob: 4e6e3efc30f8c495f3b46be13e40270a894fe616 [file] [log] [blame]
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "syzygy/minidump/minidump.h"
#include "base/logging.h"
#include "base/files/file_util.h"
namespace minidump {
namespace internal {
size_t DefaultHeaderParser::Parse(const MINIDUMP_MEMORY_LIST& header) {
return header.NumberOfMemoryRanges;
}
size_t DefaultHeaderParser::Parse(const MINIDUMP_MODULE_LIST& header) {
return header.NumberOfModules;
}
size_t DefaultHeaderParser::Parse(const MINIDUMP_THREAD_LIST& header) {
return header.NumberOfThreads;
}
size_t DefaultHeaderParser::Parse(const MINIDUMP_THREAD_EX_LIST& header) {
return header.NumberOfThreads;
}
} // namespace internal
Minidump::Minidump() {
}
Minidump::~Minidump() {
}
Minidump::TypedMemoryList Minidump::GetMemoryList() const {
return TypedMemoryList(*this, MemoryListStream);
}
Minidump::TypedModuleList Minidump::GetModuleList() const {
return TypedModuleList(*this, ModuleListStream);
}
Minidump::TypedThreadList Minidump::GetThreadList() const {
return TypedThreadList(*this, ThreadListStream);
}
Minidump::TypedThreadExList Minidump::GetThreadExList() const {
return TypedThreadExList(*this, ThreadExListStream);
}
bool Minidump::ReadDirectory() {
// Read the header and validate the signature.
MINIDUMP_HEADER header = {};
if (!ReadBytes(0, sizeof(header), &header))
return false;
if (header.Signature != MINIDUMP_SIGNATURE || header.NumberOfStreams == 0) {
return false;
}
directory_.resize(header.NumberOfStreams);
if (!ReadBytes(header.StreamDirectoryRva,
header.NumberOfStreams * sizeof(directory_[0]),
&directory_.at(0))) {
return false;
}
return true;
}
bool FileMinidump::Open(const base::FilePath& path) {
file_.reset(base::OpenFile(path, "rb"));
if (!file_)
return false;
return ReadDirectory();
}
Minidump::Stream Minidump::GetStreamFor(
const MINIDUMP_LOCATION_DESCRIPTOR& location) const {
return Stream(this, location.Rva, location.DataSize, kNoStreamId);
}
Minidump::Stream Minidump::GetStream(size_t stream_id) const {
DCHECK_GT(directory_.size(), stream_id);
const MINIDUMP_DIRECTORY& dir_entry = directory_[stream_id];
return Stream(this, dir_entry.Location.Rva, dir_entry.Location.DataSize,
stream_id);
}
Minidump::Stream Minidump::FindNextStream(const Stream* prev,
size_t stream_type) const {
size_t start = prev ? prev->stream_id() + 1 : 0;
for (size_t id = start; id < directory_.size(); ++id) {
if (directory_[id].StreamType == stream_type)
return GetStream(id);
}
// Not found, return an invalid stream.
return Stream();
}
bool FileMinidump::ReadBytes(size_t offset,
size_t data_size,
void* data) const {
DCHECK_LE(offset, static_cast<size_t>(std::numeric_limits<long>::max()));
if (fseek(file_.get(), static_cast<long>(offset), SEEK_SET) != 0)
return false;
if (fread(data, 1, data_size, file_.get()) != data_size)
return false;
return true;
}
BufferMinidump::BufferMinidump() : buf_(nullptr), buf_len_(0) {
}
bool BufferMinidump::Initialize(const uint8_t* buf, size_t buf_len) {
DCHECK(buf);
buf_ = buf;
buf_len_ = buf_len;
return ReadDirectory();
}
bool BufferMinidump::ReadBytes(size_t offset,
size_t data_size,
void* data) const {
// Bounds check the request.
if (offset >= buf_len_ || offset + data_size > buf_len_ ||
offset + data_size < offset) { // Test for overflow.
return false;
}
::memcpy(data, buf_ + offset, data_size);
return true;
}
Minidump::Stream::Stream()
: minidump_(nullptr),
current_offset_(0),
remaining_length_(0),
stream_id_(0) {
}
Minidump::Stream::Stream(
const Minidump* minidump, size_t offset, size_t length, size_t stream_id)
: minidump_(minidump),
current_offset_(offset),
remaining_length_(length),
stream_id_(stream_id) {
DCHECK_NE(static_cast<Minidump*>(nullptr), minidump);
}
bool Minidump::Stream::ReadAndAdvanceBytes(size_t data_len, void* data) {
return ReadBytes(data_len, data) && AdvanceBytes(data_len);
}
bool Minidump::Stream::ReadAndAdvanceBytes(size_t data_len, std::string* data) {
DCHECK(minidump_ != nullptr);
DCHECK(data != nullptr);
data->resize(data_len);
bool success = ReadAndAdvanceBytes(data_len, &data->at(0));
if (!success)
data->resize(0);
return success;
}
bool Minidump::Stream::ReadAndAdvanceString(std::wstring* data) {
DCHECK(minidump_ != nullptr);
DCHECK(data != nullptr);
ULONG32 size_bytes = 0U;
if (!ReadAndAdvanceElement(&size_bytes))
return false;
// Increment to account for (consume) null-terminating character.
size_bytes += sizeof(wchar_t);
if (size_bytes % sizeof(wchar_t))
return false;
size_t num_characters = size_bytes / sizeof(wchar_t);
std::wstring buffer;
buffer.resize(num_characters);
if (!ReadAndAdvanceBytes(size_bytes, &buffer.at(0)))
return false;
// Drop the extra null-terminating character.
buffer.resize(num_characters - 1);
buffer.swap(*data);
return true;
}
bool Minidump::Stream::ReadBytes(size_t data_len, void* data) {
DCHECK(minidump_ != nullptr);
if (data_len > remaining_length_)
return false;
if (!minidump_->ReadBytes(current_offset_, data_len, data))
return false;
return true;
}
bool Minidump::Stream::AdvanceBytes(size_t data_len) {
if (data_len > remaining_length_)
return false;
current_offset_ += data_len;
remaining_length_ -= data_len;
return true;
}
} // namespace minidump