| // 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. |
| |
| #include "minidump/minidump_context_writer.h" |
| |
| #include <windows.h> |
| #include <dbghelp.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "nb/cpp14oncpp11.h" |
| #include "snapshot/cpu_context.h" |
| #include "util/file/file_writer.h" |
| #include "util/stdlib/aligned_allocator.h" |
| |
| namespace crashpad { |
| |
| namespace { |
| |
| // Sanity-check complex structures to ensure interoperability. |
| static_assert(sizeof(MinidumpContextX86) == 716, "MinidumpContextX86 size"); |
| static_assert(sizeof(MinidumpContextAMD64) == 1232, |
| "MinidumpContextAMD64 size"); |
| |
| // These structures can also be checked against definitions in the Windows SDK. |
| #if defined(OS_WIN) |
| #if defined(ARCH_CPU_X86_FAMILY) |
| static_assert(sizeof(MinidumpContextX86) == sizeof(WOW64_CONTEXT), |
| "WOW64_CONTEXT size"); |
| #if defined(ARCH_CPU_X86) |
| static_assert(sizeof(MinidumpContextX86) == sizeof(CONTEXT), "CONTEXT size"); |
| #elif defined(ARCH_CPU_X86_64) |
| static_assert(sizeof(MinidumpContextAMD64) == sizeof(CONTEXT), "CONTEXT size"); |
| #endif |
| #endif // ARCH_CPU_X86_FAMILY |
| #endif // OS_WIN |
| |
| } // namespace |
| |
| MinidumpContextWriter::~MinidumpContextWriter() { |
| } |
| |
| // static |
| std::unique_ptr<MinidumpContextWriter> |
| MinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) { |
| std::unique_ptr<MinidumpContextWriter> context; |
| |
| switch (context_snapshot->architecture) { |
| case kCPUArchitectureX86: { |
| MinidumpContextX86Writer* context_x86 = new MinidumpContextX86Writer(); |
| context.reset(context_x86); |
| context_x86->InitializeFromSnapshot(context_snapshot->x86); |
| break; |
| } |
| |
| case kCPUArchitectureX86_64: { |
| MinidumpContextAMD64Writer* context_amd64 = |
| new MinidumpContextAMD64Writer(); |
| context.reset(context_amd64); |
| context_amd64->InitializeFromSnapshot(context_snapshot->x86_64); |
| break; |
| } |
| |
| case kCPUArchitectureARM: { |
| context = std::make_unique<MinidumpContextARMWriter>(); |
| reinterpret_cast<MinidumpContextARMWriter*>(context.get()) |
| ->InitializeFromSnapshot(context_snapshot->arm); |
| break; |
| } |
| |
| case kCPUArchitectureARM64: { |
| context = std::make_unique<MinidumpContextARM64Writer>(); |
| reinterpret_cast<MinidumpContextARM64Writer*>(context.get()) |
| ->InitializeFromSnapshot(context_snapshot->arm64); |
| break; |
| } |
| |
| case kCPUArchitectureMIPSEL: { |
| context = std::make_unique<MinidumpContextMIPSWriter>(); |
| reinterpret_cast<MinidumpContextMIPSWriter*>(context.get()) |
| ->InitializeFromSnapshot(context_snapshot->mipsel); |
| break; |
| } |
| |
| case kCPUArchitectureMIPS64EL: { |
| context = std::make_unique<MinidumpContextMIPS64Writer>(); |
| reinterpret_cast<MinidumpContextMIPS64Writer*>(context.get()) |
| ->InitializeFromSnapshot(context_snapshot->mips64); |
| break; |
| } |
| |
| default: { |
| LOG(ERROR) << "unknown context architecture " |
| << context_snapshot->architecture; |
| break; |
| } |
| } |
| |
| return context; |
| } |
| |
| size_t MinidumpContextWriter::SizeOfObject() { |
| DCHECK_GE(state(), kStateFrozen); |
| |
| return ContextSize(); |
| } |
| |
| MinidumpContextX86Writer::MinidumpContextX86Writer() |
| : MinidumpContextWriter(), context_() { |
| context_.context_flags = kMinidumpContextX86; |
| } |
| |
| MinidumpContextX86Writer::~MinidumpContextX86Writer() { |
| } |
| |
| |
| void MinidumpContextX86Writer::InitializeFromSnapshot( |
| const CPUContextX86* context_snapshot) { |
| DCHECK_EQ(state(), kStateMutable); |
| DCHECK_EQ(context_.context_flags, kMinidumpContextX86); |
| |
| context_.context_flags = kMinidumpContextX86All; |
| |
| context_.dr0 = context_snapshot->dr0; |
| context_.dr1 = context_snapshot->dr1; |
| context_.dr2 = context_snapshot->dr2; |
| context_.dr3 = context_snapshot->dr3; |
| context_.dr6 = context_snapshot->dr6; |
| context_.dr7 = context_snapshot->dr7; |
| |
| // The contents of context_.fsave effectively alias everything in |
| // context_.fxsave that’s related to x87 FPU state. context_.fsave doesn’t |
| // carry state specific to SSE (or later), such as mxcsr and the xmm |
| // registers. |
| CPUContextX86::FxsaveToFsave(context_snapshot->fxsave, &context_.fsave); |
| |
| context_.gs = context_snapshot->gs; |
| context_.fs = context_snapshot->fs; |
| context_.es = context_snapshot->es; |
| context_.ds = context_snapshot->ds; |
| context_.edi = context_snapshot->edi; |
| context_.esi = context_snapshot->esi; |
| context_.ebx = context_snapshot->ebx; |
| context_.edx = context_snapshot->edx; |
| context_.ecx = context_snapshot->ecx; |
| context_.eax = context_snapshot->eax; |
| context_.ebp = context_snapshot->ebp; |
| context_.eip = context_snapshot->eip; |
| context_.cs = context_snapshot->cs; |
| context_.eflags = context_snapshot->eflags; |
| context_.esp = context_snapshot->esp; |
| context_.ss = context_snapshot->ss; |
| |
| // This is effectively a memcpy() of a big structure. |
| context_.fxsave = context_snapshot->fxsave; |
| } |
| |
| bool MinidumpContextX86Writer::WriteObject(FileWriterInterface* file_writer) { |
| DCHECK_EQ(state(), kStateWritable); |
| |
| return file_writer->Write(&context_, sizeof(context_)); |
| } |
| |
| size_t MinidumpContextX86Writer::ContextSize() const { |
| DCHECK_GE(state(), kStateFrozen); |
| |
| return sizeof(context_); |
| } |
| |
| static_assert(alignof(MinidumpContextAMD64) >= 16, |
| "MinidumpContextAMD64 alignment"); |
| static_assert(alignof(MinidumpContextAMD64Writer) >= |
| alignof(MinidumpContextAMD64), |
| "MinidumpContextAMD64Writer alignment"); |
| |
| MinidumpContextAMD64Writer::MinidumpContextAMD64Writer() |
| : MinidumpContextWriter(), context_() { |
| context_.context_flags = kMinidumpContextAMD64; |
| } |
| |
| MinidumpContextAMD64Writer::~MinidumpContextAMD64Writer() { |
| } |
| |
| // static |
| void* MinidumpContextAMD64Writer::operator new(size_t size) { |
| // MinidumpContextAMD64 requests an alignment of 16, which can be larger than |
| // what standard new provides. This may trigger MSVC warning C4316. As a |
| // workaround to this language deficiency, provide a custom allocation |
| // function to allocate a block meeting the alignment requirement. |
| return AlignedAllocate(alignof(MinidumpContextAMD64Writer), size); |
| } |
| |
| // static |
| void MinidumpContextAMD64Writer::operator delete(void* pointer) { |
| return AlignedFree(pointer); |
| } |
| |
| void MinidumpContextAMD64Writer::InitializeFromSnapshot( |
| const CPUContextX86_64* context_snapshot) { |
| DCHECK_EQ(state(), kStateMutable); |
| DCHECK_EQ(context_.context_flags, kMinidumpContextAMD64); |
| |
| context_.context_flags = kMinidumpContextAMD64All; |
| |
| context_.mx_csr = context_snapshot->fxsave.mxcsr; |
| context_.cs = context_snapshot->cs; |
| context_.fs = context_snapshot->fs; |
| context_.gs = context_snapshot->gs; |
| // The top 32 bits of rflags are reserved/unused. |
| context_.eflags = static_cast<uint32_t>(context_snapshot->rflags); |
| context_.dr0 = context_snapshot->dr0; |
| context_.dr1 = context_snapshot->dr1; |
| context_.dr2 = context_snapshot->dr2; |
| context_.dr3 = context_snapshot->dr3; |
| context_.dr6 = context_snapshot->dr6; |
| context_.dr7 = context_snapshot->dr7; |
| context_.rax = context_snapshot->rax; |
| context_.rcx = context_snapshot->rcx; |
| context_.rdx = context_snapshot->rdx; |
| context_.rbx = context_snapshot->rbx; |
| context_.rsp = context_snapshot->rsp; |
| context_.rbp = context_snapshot->rbp; |
| context_.rsi = context_snapshot->rsi; |
| context_.rdi = context_snapshot->rdi; |
| context_.r8 = context_snapshot->r8; |
| context_.r9 = context_snapshot->r9; |
| context_.r10 = context_snapshot->r10; |
| context_.r11 = context_snapshot->r11; |
| context_.r12 = context_snapshot->r12; |
| context_.r13 = context_snapshot->r13; |
| context_.r14 = context_snapshot->r14; |
| context_.r15 = context_snapshot->r15; |
| context_.rip = context_snapshot->rip; |
| |
| // This is effectively a memcpy() of a big structure. |
| context_.fxsave = context_snapshot->fxsave; |
| } |
| |
| size_t MinidumpContextAMD64Writer::Alignment() { |
| DCHECK_GE(state(), kStateFrozen); |
| |
| // Match the alignment of MinidumpContextAMD64. |
| return 16; |
| } |
| |
| bool MinidumpContextAMD64Writer::WriteObject(FileWriterInterface* file_writer) { |
| DCHECK_EQ(state(), kStateWritable); |
| |
| return file_writer->Write(&context_, sizeof(context_)); |
| } |
| |
| size_t MinidumpContextAMD64Writer::ContextSize() const { |
| DCHECK_GE(state(), kStateFrozen); |
| |
| return sizeof(context_); |
| } |
| |
| MinidumpContextARMWriter::MinidumpContextARMWriter() |
| : MinidumpContextWriter(), context_() { |
| context_.context_flags = kMinidumpContextARM; |
| } |
| |
| MinidumpContextARMWriter::~MinidumpContextARMWriter() = default; |
| |
| void MinidumpContextARMWriter::InitializeFromSnapshot( |
| const CPUContextARM* context_snapshot) { |
| DCHECK_EQ(state(), kStateMutable); |
| DCHECK_EQ(context_.context_flags, kMinidumpContextARM); |
| |
| context_.context_flags = kMinidumpContextARMAll; |
| |
| static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs), |
| "GPRS size mismatch"); |
| memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); |
| context_.fp = context_snapshot->fp; |
| context_.ip = context_snapshot->ip; |
| context_.sp = context_snapshot->sp; |
| context_.lr = context_snapshot->lr; |
| context_.pc = context_snapshot->pc; |
| context_.cpsr = context_snapshot->cpsr; |
| |
| context_.fpscr = context_snapshot->vfp_regs.fpscr; |
| static_assert(sizeof(context_.vfp) == sizeof(context_snapshot->vfp_regs.vfp), |
| "VFP size mismatch"); |
| memcpy(context_.vfp, context_snapshot->vfp_regs.vfp, sizeof(context_.vfp)); |
| |
| memset(context_.extra, 0, sizeof(context_.extra)); |
| } |
| |
| bool MinidumpContextARMWriter::WriteObject(FileWriterInterface* file_writer) { |
| DCHECK_EQ(state(), kStateWritable); |
| return file_writer->Write(&context_, sizeof(context_)); |
| } |
| |
| size_t MinidumpContextARMWriter::ContextSize() const { |
| DCHECK_GE(state(), kStateFrozen); |
| return sizeof(context_); |
| } |
| |
| MinidumpContextARM64Writer::MinidumpContextARM64Writer() |
| : MinidumpContextWriter(), context_() { |
| context_.context_flags = kMinidumpContextARM64; |
| } |
| |
| MinidumpContextARM64Writer::~MinidumpContextARM64Writer() = default; |
| |
| void MinidumpContextARM64Writer::InitializeFromSnapshot( |
| const CPUContextARM64* context_snapshot) { |
| DCHECK_EQ(state(), kStateMutable); |
| DCHECK_EQ(context_.context_flags, kMinidumpContextARM64); |
| |
| context_.context_flags = kMinidumpContextARM64Full; |
| |
| static_assert( |
| sizeof(context_.regs) == sizeof(context_snapshot->regs) - |
| 2 * sizeof(context_snapshot->regs[0]), |
| "GPRs size mismatch"); |
| memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); |
| context_.fp = context_snapshot->regs[29]; |
| context_.lr = context_snapshot->regs[30]; |
| context_.sp = context_snapshot->sp; |
| context_.pc = context_snapshot->pc; |
| context_.cpsr = context_snapshot->spsr; |
| |
| static_assert(sizeof(context_.fpsimd) == sizeof(context_snapshot->fpsimd), |
| "FPSIMD size mismatch"); |
| memcpy(context_.fpsimd, context_snapshot->fpsimd, sizeof(context_.fpsimd)); |
| context_.fpcr = context_snapshot->fpcr; |
| context_.fpsr = context_snapshot->fpsr; |
| |
| memset(context_.bcr, 0, sizeof(context_.bcr)); |
| memset(context_.bvr, 0, sizeof(context_.bvr)); |
| memset(context_.wcr, 0, sizeof(context_.wcr)); |
| memset(context_.wvr, 0, sizeof(context_.wvr)); |
| } |
| |
| bool MinidumpContextARM64Writer::WriteObject(FileWriterInterface* file_writer) { |
| DCHECK_EQ(state(), kStateWritable); |
| return file_writer->Write(&context_, sizeof(context_)); |
| } |
| |
| size_t MinidumpContextARM64Writer::ContextSize() const { |
| DCHECK_GE(state(), kStateFrozen); |
| return sizeof(context_); |
| } |
| |
| MinidumpContextMIPSWriter::MinidumpContextMIPSWriter() |
| : MinidumpContextWriter(), context_() { |
| context_.context_flags = kMinidumpContextMIPS; |
| } |
| |
| MinidumpContextMIPSWriter::~MinidumpContextMIPSWriter() = default; |
| |
| void MinidumpContextMIPSWriter::InitializeFromSnapshot( |
| const CPUContextMIPS* context_snapshot) { |
| DCHECK_EQ(state(), kStateMutable); |
| DCHECK_EQ(context_.context_flags, kMinidumpContextMIPS); |
| |
| context_.context_flags = kMinidumpContextMIPSAll; |
| |
| static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs), |
| "GPRs size mismatch"); |
| memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); |
| context_.mdhi = context_snapshot->mdhi; |
| context_.mdlo = context_snapshot->mdlo; |
| context_.epc = context_snapshot->cp0_epc; |
| context_.badvaddr = context_snapshot->cp0_badvaddr; |
| context_.status = context_snapshot->cp0_status; |
| context_.cause = context_snapshot->cp0_cause; |
| |
| static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs), |
| "FPRs size mismatch"); |
| memcpy(&context_.fpregs, &context_snapshot->fpregs, sizeof(context_.fpregs)); |
| context_.fpcsr = context_snapshot->fpcsr; |
| context_.fir = context_snapshot->fir; |
| |
| for (size_t index = 0; index < 3; ++index) { |
| context_.hi[index] = context_snapshot->hi[index]; |
| context_.lo[index] = context_snapshot->lo[index]; |
| } |
| context_.dsp_control = context_snapshot->dsp_control; |
| } |
| |
| bool MinidumpContextMIPSWriter::WriteObject(FileWriterInterface* file_writer) { |
| DCHECK_EQ(state(), kStateWritable); |
| return file_writer->Write(&context_, sizeof(context_)); |
| } |
| |
| size_t MinidumpContextMIPSWriter::ContextSize() const { |
| DCHECK_GE(state(), kStateFrozen); |
| return sizeof(context_); |
| } |
| |
| MinidumpContextMIPS64Writer::MinidumpContextMIPS64Writer() |
| : MinidumpContextWriter(), context_() { |
| context_.context_flags = kMinidumpContextMIPS64; |
| } |
| |
| MinidumpContextMIPS64Writer::~MinidumpContextMIPS64Writer() = default; |
| |
| void MinidumpContextMIPS64Writer::InitializeFromSnapshot( |
| const CPUContextMIPS64* context_snapshot) { |
| DCHECK_EQ(state(), kStateMutable); |
| DCHECK_EQ(context_.context_flags, kMinidumpContextMIPS64); |
| |
| context_.context_flags = kMinidumpContextMIPS64All; |
| |
| static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs), |
| "GPRs size mismatch"); |
| memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); |
| context_.mdhi = context_snapshot->mdhi; |
| context_.mdlo = context_snapshot->mdlo; |
| context_.epc = context_snapshot->cp0_epc; |
| context_.badvaddr = context_snapshot->cp0_badvaddr; |
| context_.status = context_snapshot->cp0_status; |
| context_.cause = context_snapshot->cp0_cause; |
| |
| static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs), |
| "FPRs size mismatch"); |
| memcpy(context_.fpregs.dregs, |
| context_snapshot->fpregs.dregs, |
| sizeof(context_.fpregs.dregs)); |
| context_.fpcsr = context_snapshot->fpcsr; |
| context_.fir = context_snapshot->fir; |
| |
| for (size_t index = 0; index < 3; ++index) { |
| context_.hi[index] = context_snapshot->hi[index]; |
| context_.lo[index] = context_snapshot->lo[index]; |
| } |
| context_.dsp_control = context_snapshot->dsp_control; |
| } |
| |
| bool MinidumpContextMIPS64Writer::WriteObject( |
| FileWriterInterface* file_writer) { |
| DCHECK_EQ(state(), kStateWritable); |
| return file_writer->Write(&context_, sizeof(context_)); |
| } |
| |
| size_t MinidumpContextMIPS64Writer::ContextSize() const { |
| DCHECK_GE(state(), kStateFrozen); |
| return sizeof(context_); |
| } |
| |
| } // namespace crashpad |