crashpad/minidump/minidump_context_writer.h
Alex Gough 9e0051aba6 Write compacted xsave contexts in minidumps
Adds new structures and offsets for minidump extended contexts. This
information will be captured from threads in a later CL so this CL
does not yet write different dumps, except in testing.

Minidump format for extended compacted contexts has been determined by
experiment. Offsets for where to write various parts of the context
are hardcoded to 0x550 as this matches values seen in Windows. Offsets
for misc_info_5 match those seen in working minidumps that can be opened
in windbg. Our hope is that while these could change in future, CPU
and OS vendors are unlikely to change them.

See doc[0] for a discussion of these fields and offsets in the minidump.

See "MANAGING STATE USING THE XSAVE FEATURE SET" Chapter 13 in the
Intel SDM[1]. Many of the offsets and sizes of the extended features
are provided by cpu specific values. We can access these in Windows
using the SDK, and transfer these to the saved extended context
which in turn is understandable by windbg.

Further information is available from AMD Ch. 18 "Shadow Stacks"[2].

    [0] https://docs.google.com/document/d/1Dn8n97r5B7kxYouvujNnPIYd_7QeVHpahSRmB92Qn6g/edit#heading=h.hivqj2jg39y
    [1] https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html.
    [2] https://www.amd.com/system/files/TechDocs/24593.pdf

Bug: 1250098
Change-Id: Ia9041acc379c6d38329ee99737a2a0a77f7a1ee0
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3536964
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
Commit-Queue: Alex Gough <ajgo@chromium.org>
2022-05-17 01:45:56 +00:00

375 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_
#define CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_
#include <sys/types.h>
#include <memory>
#include "minidump/minidump_context.h"
#include "minidump/minidump_writable.h"
namespace crashpad {
struct CPUContext;
struct CPUContextX86;
struct CPUContextX86_64;
class MinidumpMiscInfoWriter;
//! \brief The base class for writers of CPU context structures in minidump
//! files.
class MinidumpContextWriter : public internal::MinidumpWritable {
public:
MinidumpContextWriter(const MinidumpContextWriter&) = delete;
MinidumpContextWriter& operator=(const MinidumpContextWriter&) = delete;
~MinidumpContextWriter() override;
//! \brief Creates a MinidumpContextWriter based on \a context_snapshot.
//!
//! \param[in] context_snapshot The context snapshot to use as source data.
//!
//! \return A MinidumpContextWriter subclass, such as MinidumpContextWriterX86
//! or MinidumpContextWriterAMD64, appropriate to the CPU type of \a
//! context_snapshot. The returned object is initialized using the source
//! data in \a context_snapshot. If \a context_snapshot is an unknown CPU
//! types context, logs a message and returns `nullptr`.
static std::unique_ptr<MinidumpContextWriter> CreateFromSnapshot(
const CPUContext* context_snapshot);
//! \brief Returns the size of the context structure that this object will
//! write.
//!
//! \note This method will force this to #kStateFrozen, if it is not already.
size_t FreezeAndGetSizeOfObject();
protected:
MinidumpContextWriter() : MinidumpWritable() {}
//! \brief Returns the size of the context structure that this object will
//! write.
//!
//! \note This method will only be called in #kStateFrozen or a subsequent
//! state.
virtual size_t ContextSize() const = 0;
// MinidumpWritable:
size_t SizeOfObject() final;
};
//! \brief The writer for a MinidumpContextX86 structure in a minidump file.
class MinidumpContextX86Writer final : public MinidumpContextWriter {
public:
MinidumpContextX86Writer();
MinidumpContextX86Writer(const MinidumpContextX86Writer&) = delete;
MinidumpContextX86Writer& operator=(const MinidumpContextX86Writer&) = delete;
~MinidumpContextX86Writer() override;
//! \brief Initializes the MinidumpContextX86 based on \a context_snapshot.
//!
//! \param[in] context_snapshot The context snapshot to use as source data.
//!
//! \note Valid in #kStateMutable. No mutation of context() may be done before
//! calling this method, and it is not normally necessary to alter
//! context() after calling this method.
void InitializeFromSnapshot(const CPUContextX86* context_snapshot);
//! \brief Returns a pointer to the context structure that this object will
//! write.
//!
//! \attention This returns a non-`const` pointer to this objects private
//! data so that a caller can populate the context structure directly.
//! This is done because providing setter interfaces to each field in the
//! context structure would be unwieldy and cumbersome. Care must be taken
//! to populate the context structure correctly. The context structure
//! must only be modified while this object is in the #kStateMutable
//! state.
MinidumpContextX86* context() { return &context_; }
protected:
// MinidumpWritable:
bool WriteObject(FileWriterInterface* file_writer) override;
// MinidumpContextWriter:
size_t ContextSize() const override;
private:
MinidumpContextX86 context_;
};
//! \brief Wraps an xsave feature that knows where and how big it is.
class MinidumpXSaveFeatureAMD64 {
public:
virtual ~MinidumpXSaveFeatureAMD64() = default;
// Number of bytes that will be written. May need to vary by CPUID (see
// Intel 13.5).
virtual size_t Size() const = 0;
// Intel 13.4.2 XCOMP_BV.
virtual uint8_t XCompBVBit() const = 0;
// Write data to dst. Does not write padding.
virtual bool Copy(void* dst) const = 0;
};
//! \brief XSAVE_CET_U_FORMAT
class MinidumpXSaveAMD64CetU final : public MinidumpXSaveFeatureAMD64 {
public:
MinidumpXSaveAMD64CetU() {}
~MinidumpXSaveAMD64CetU() {}
MinidumpXSaveAMD64CetU(const MinidumpXSaveAMD64CetU&) = delete;
MinidumpXSaveAMD64CetU& operator=(const MinidumpXSaveAMD64CetU&) = delete;
size_t Size() const override { return sizeof(cet_u_); }
uint8_t XCompBVBit() const override { return XSTATE_CET_U; }
bool Copy(void* dst) const override;
bool InitializeFromSnapshot(const CPUContextX86_64* context_snapshot);
private:
MinidumpAMD64XSaveFormatCetU cet_u_;
};
//! \brief The writer for a MinidumpContextAMD64 structure in a minidump file.
class MinidumpContextAMD64Writer final : public MinidumpContextWriter {
public:
MinidumpContextAMD64Writer();
MinidumpContextAMD64Writer(const MinidumpContextAMD64Writer&) = delete;
MinidumpContextAMD64Writer& operator=(const MinidumpContextAMD64Writer&) =
delete;
~MinidumpContextAMD64Writer() override;
// Ensure proper alignment of heap-allocated objects. This should not be
// necessary in C++17.
static void* operator new(size_t size);
static void operator delete(void* ptr);
// Prevent unaligned heap-allocated arrays. Provisions could be made to allow
// these if necessary, but there is currently no use for them.
static void* operator new[](size_t size) = delete;
static void operator delete[](void* ptr) = delete;
//! \brief Initializes the MinidumpContextAMD64 based on \a context_snapshot.
//!
//! \param[in] context_snapshot The context snapshot to use as source data.
//!
//! \note Valid in #kStateMutable. No mutation of context() may be done before
//! calling this method, and it is not normally necessary to alter
//! context() after calling this method.
void InitializeFromSnapshot(const CPUContextX86_64* context_snapshot);
//! \brief Returns a pointer to the context structure that this object will
//! write.
//!
//! \attention This returns a non-`const` pointer to this objects private
//! data so that a caller can populate the context structure directly.
//! This is done because providing setter interfaces to each field in the
//! context structure would be unwieldy and cumbersome. Care must be taken
//! to populate the context structure correctly. The context structure
//! must only be modified while this object is in the #kStateMutable
//! state.
MinidumpContextAMD64* context() { return &context_; }
protected:
// MinidumpWritable:
size_t Alignment() override;
bool WriteObject(FileWriterInterface* file_writer) override;
// MinidumpContextWriter:
size_t ContextSize() const override;
private:
MinidumpContextAMD64 context_;
// These should be in order of XCompBVBit().
std::vector<std::unique_ptr<MinidumpXSaveFeatureAMD64>> xsave_entries_;
};
//! \brief The writer for a MinidumpContextARM structure in a minidump file.
class MinidumpContextARMWriter final : public MinidumpContextWriter {
public:
MinidumpContextARMWriter();
MinidumpContextARMWriter(const MinidumpContextARMWriter&) = delete;
MinidumpContextARMWriter& operator=(const MinidumpContextARMWriter&) = delete;
~MinidumpContextARMWriter() override;
//! \brief Initializes the MinidumpContextARM based on \a context_snapshot.
//!
//! \param[in] context_snapshot The context snapshot to use as source data.
//!
//! \note Valid in #kStateMutable. No mutation of context() may be done before
//! calling this method, and it is not normally necessary to alter
//! context() after calling this method.
void InitializeFromSnapshot(const CPUContextARM* context_snapshot);
//! \brief Returns a pointer to the context structure that this object will
//! write.
//!
//! \attention This returns a non-`const` pointer to this objects private
//! data so that a caller can populate the context structure directly.
//! This is done because providing setter interfaces to each field in the
//! context structure would be unwieldy and cumbersome. Care must be taken
//! to populate the context structure correctly. The context structure
//! must only be modified while this object is in the #kStateMutable
//! state.
MinidumpContextARM* context() { return &context_; }
protected:
// MinidumpWritable:
bool WriteObject(FileWriterInterface* file_writer) override;
// MinidumpContextWriter:
size_t ContextSize() const override;
private:
MinidumpContextARM context_;
};
//! \brief The writer for a MinidumpContextARM64 structure in a minidump file.
class MinidumpContextARM64Writer final : public MinidumpContextWriter {
public:
MinidumpContextARM64Writer();
MinidumpContextARM64Writer(const MinidumpContextARM64Writer&) = delete;
MinidumpContextARM64Writer& operator=(const MinidumpContextARM64Writer&) =
delete;
~MinidumpContextARM64Writer() override;
//! \brief Initializes the MinidumpContextARM64 based on \a context_snapshot.
//!
//! \param[in] context_snapshot The context snapshot to use as source data.
//!
//! \note Valid in #kStateMutable. No mutation of context() may be done before
//! calling this method, and it is not normally necessary to alter
//! context() after calling this method.
void InitializeFromSnapshot(const CPUContextARM64* context_snapshot);
//! \brief Returns a pointer to the context structure that this object will
//! write.
//!
//! \attention This returns a non-`const` pointer to this objects private
//! data so that a caller can populate the context structure directly.
//! This is done because providing setter interfaces to each field in the
//! context structure would be unwieldy and cumbersome. Care must be taken
//! to populate the context structure correctly. The context structure
//! must only be modified while this object is in the #kStateMutable
//! state.
MinidumpContextARM64* context() { return &context_; }
protected:
// MinidumpWritable:
bool WriteObject(FileWriterInterface* file_writer) override;
// MinidumpContextWriter:
size_t ContextSize() const override;
private:
MinidumpContextARM64 context_;
};
//! \brief The writer for a MinidumpContextMIPS structure in a minidump file.
class MinidumpContextMIPSWriter final : public MinidumpContextWriter {
public:
MinidumpContextMIPSWriter();
MinidumpContextMIPSWriter(const MinidumpContextMIPSWriter&) = delete;
MinidumpContextMIPSWriter& operator=(const MinidumpContextMIPSWriter&) =
delete;
~MinidumpContextMIPSWriter() override;
//! \brief Initializes the MinidumpContextMIPS based on \a context_snapshot.
//!
//! \param[in] context_snapshot The context snapshot to use as source data.
//!
//! \note Valid in #kStateMutable. No mutation of context() may be done before
//! calling this method, and it is not normally necessary to alter
//! context() after calling this method.
void InitializeFromSnapshot(const CPUContextMIPS* context_snapshot);
//! \brief Returns a pointer to the context structure that this object will
//! write.
//!
//! \attention This returns a non-`const` pointer to this objects private
//! data so that a caller can populate the context structure directly.
//! This is done because providing setter interfaces to each field in the
//! context structure would be unwieldy and cumbersome. Care must be taken
//! to populate the context structure correctly. The context structure
//! must only be modified while this object is in the #kStateMutable
//! state.
MinidumpContextMIPS* context() { return &context_; }
protected:
// MinidumpWritable:
bool WriteObject(FileWriterInterface* file_writer) override;
// MinidumpContextWriter:
size_t ContextSize() const override;
private:
MinidumpContextMIPS context_;
};
//! \brief The writer for a MinidumpContextMIPS64 structure in a minidump file.
class MinidumpContextMIPS64Writer final : public MinidumpContextWriter {
public:
MinidumpContextMIPS64Writer();
MinidumpContextMIPS64Writer(const MinidumpContextMIPS64Writer&) = delete;
MinidumpContextMIPS64Writer& operator=(const MinidumpContextMIPS64Writer&) =
delete;
~MinidumpContextMIPS64Writer() override;
//! \brief Initializes the MinidumpContextMIPS based on \a context_snapshot.
//!
//! \param[in] context_snapshot The context snapshot to use as source data.
//!
//! \note Valid in #kStateMutable. No mutation of context() may be done before
//! calling this method, and it is not normally necessary to alter
//! context() after calling this method.
void InitializeFromSnapshot(const CPUContextMIPS64* context_snapshot);
//! \brief Returns a pointer to the context structure that this object will
//! write.
//!
//! \attention This returns a non-`const` pointer to this objects private
//! data so that a caller can populate the context structure directly.
//! This is done because providing setter interfaces to each field in the
//! context structure would be unwieldy and cumbersome. Care must be taken
//! to populate the context structure correctly. The context structure
//! must only be modified while this object is in the #kStateMutable
//! state.
MinidumpContextMIPS64* context() { return &context_; }
protected:
// MinidumpWritable:
bool WriteObject(FileWriterInterface* file_writer) override;
// MinidumpContextWriter:
size_t ContextSize() const override;
private:
MinidumpContextMIPS64 context_;
};
} // namespace crashpad
#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_