From c9244d58df1d27baf91b6cad117f2b0061da152d Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 30 Jan 2018 12:41:09 -0800 Subject: [PATCH] Add ARM family minidump support Bug: crashpad:30 Change-Id: I6784d42ba6c525c4e0b16dfdbbb4949c83e32fea Reviewed-on: https://chromium-review.googlesource.com/888541 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- minidump/minidump_context.h | 96 ++++++++++++ minidump/minidump_context_writer.cc | 99 ++++++++++++ minidump/minidump_context_writer.h | 80 ++++++++++ minidump/minidump_context_writer_test.cc | 116 ++++++++------ minidump/test/minidump_context_test_util.cc | 103 ++++++++++++ minidump/test/minidump_context_test_util.h | 9 ++ snapshot/test/test_cpu_context.cc | 165 +++++++++++++------- snapshot/test/test_cpu_context.h | 40 ++--- 8 files changed, 587 insertions(+), 121 deletions(-) diff --git a/minidump/minidump_context.h b/minidump/minidump_context.h index 1226b654..a7328cad 100644 --- a/minidump/minidump_context.h +++ b/minidump/minidump_context.h @@ -336,6 +336,102 @@ struct alignas(16) MinidumpContextAMD64 { //! \} }; +//! \brief 32-bit ARM-specifc flags for MinidumpContextARM::context_flags. +enum MinidumpContextARMFlags : uint32_t { + //! \brief Identifies the context structure as 32-bit ARM. + kMinidumpContextARM = 0x40000000, + + //! \brief Indicates the validity of integer regsiters. + //! + //! Regsiters `r0`-`r15` and `cpsr` are valid. + kMinidumpContextARMInteger = kMinidumpContextARM | 0x00000002, + + //! \brief Inidicates the validity of VFP regsiters. + //! + //! Registers `d0`-`d31` and `fpscr` are valid. + kMinidumpContextARMVFP = kMinidumpContextARM | 0x00000004, + + //! \brief Indicates the validity of all registers. + kMinidumpContextARMAll = kMinidumpContextARMInteger | kMinidumpContextARMVFP, +}; + +//! \brief A 32-bit ARM CPU context (register state) carried in a minidump file. +struct MinidumpContextARM { + //! \brief A bitfield composed of values of #MinidumpContextFlags and + //! #MinidumpContextARMFlags. + //! + //! This field identifies the context structure as a 32-bit ARM CPU context, + //! and indicates which other fields in the structure are valid. + uint32_t context_flags; + + //! \brief General-purpose registers `r0`-`r15`. + uint32_t regs[11]; + uint32_t fp; // r11 + uint32_t ip; // r12 + uint32_t sp; // r13 + uint32_t lr; // r14 + uint32_t pc; // r15 + + //! \brief Current program status register. + uint32_t cpsr; + + //! \brief Floating-point status and control register. + uint32_t fpscr; + + //! \brief VFP registers `d0`-`d31`. + uint64_t vfp[32]; + + //! \brief This space is unused. It is included for compatibility with + //! breakpad (which also doesn't use it). + uint32_t extra[8]; +}; + +//! \brief 64-bit ARM-specifc flags for MinidumpContextARM64::context_flags. +enum MinidumpContextARM64Flags : uint32_t { + //! \brief Identifies the context structure as 64-bit ARM. + kMinidumpContextARM64 = 0x80000000, + + //! \brief Indicates the validty of integer registers. + //! + //! Registers `x0`-`x31`, `pc`, and `cpsr`. + kMinidumpContextARM64Integer = kMinidumpContextARM64 | 0x00000002, + + //! \brief Indicates the validity of fpsimd registers. + //! + //! Registers `v0`-`v31`, `fpsr`, and `fpcr` are valid. + kMinidumpContextARM64Fpsimd = kMinidumpContextARM64 | 0x00000004, + + //! \brief Indicates the validity of all registers. + kMinidumpContextARM64All = + kMinidumpContextARM64Integer | kMinidumpContextARM64Fpsimd, +}; + +//! \brief A 64-bit ARM CPU context (register state) carried in a minidump file. +struct MinidumpContextARM64 { + uint64_t context_flags; + + //! \brief General-purpose registers `x0`-`x30`. + uint64_t regs[31]; + + //! \brief Stack pointer or `x31`. + uint64_t sp; + + //! \brief Program counter. + uint64_t pc; + + //! \brief Current program status register. + uint32_t cpsr; + + //! \brief Floating-point status register. + uint32_t fpsr; + + //! \brief Floating-point control register. + uint32_t fpcr; + + //! \brief NEON registers `v0`-`v31`. + uint128_struct fpsimd[32]; +}; + } // namespace crashpad #endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_ diff --git a/minidump/minidump_context_writer.cc b/minidump/minidump_context_writer.cc index 218d7775..fbf291ea 100644 --- a/minidump/minidump_context_writer.cc +++ b/minidump/minidump_context_writer.cc @@ -73,6 +73,19 @@ MinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) { break; } + case kCPUArchitectureARM: { + context = std::make_unique(); + reinterpret_cast(context.get()) + ->InitializeFromSnapshot(context_snapshot->arm); + break; + } + + case kCPUArchitectureARM64: { + context = std::make_unique(); + reinterpret_cast(context.get()) + ->InitializeFromSnapshot(context_snapshot->arm64); + } + default: { LOG(ERROR) << "unknown context architecture " << context_snapshot->architecture; @@ -239,4 +252,90 @@ size_t MinidumpContextAMD64Writer::ContextSize() const { 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 = kMinidumpContextARM64All; + + static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs), + "GPRs size mismatch"); + memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); + context_.sp = context_snapshot->sp; + context_.pc = context_snapshot->pc; + + if (context_snapshot->pstate > + std::numeric_limits::max()) { + LOG(WARNING) << "pstate truncation"; + } + context_.cpsr = + static_cast(context_snapshot->pstate); + + context_.fpsr = context_snapshot->fpsr; + context_.fpcr = context_snapshot->fpcr; + static_assert(sizeof(context_.fpsimd) == sizeof(context_snapshot->fpsimd), + "FPSIMD size mismatch"); + memcpy(context_.fpsimd, context_snapshot->fpsimd, sizeof(context_.fpsimd)); +} + +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_); +} + } // namespace crashpad diff --git a/minidump/minidump_context_writer.h b/minidump/minidump_context_writer.h index 25d717e5..fb3b1513 100644 --- a/minidump/minidump_context_writer.h +++ b/minidump/minidump_context_writer.h @@ -155,6 +155,86 @@ class MinidumpContextAMD64Writer final : public MinidumpContextWriter { DISALLOW_COPY_AND_ASSIGN(MinidumpContextAMD64Writer); }; +//! \brief The writer for a MinidumpContextARM structure in a minidump file. +class MinidumpContextARMWriter final : public MinidumpContextWriter { + public: + MinidumpContextARMWriter(); + ~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 object’s 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_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpContextARMWriter); +}; + +//! \brief The writer for a MinidumpContextARM64 structure in a minidump file. +class MinidumpContextARM64Writer final : public MinidumpContextWriter { + public: + MinidumpContextARM64Writer(); + ~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 object’s 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_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpContextARM64Writer); +}; + } // namespace crashpad #endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_ diff --git a/minidump/minidump_context_writer_test.cc b/minidump/minidump_context_writer_test.cc index 82b4db75..0122683c 100644 --- a/minidump/minidump_context_writer_test.cc +++ b/minidump/minidump_context_writer_test.cc @@ -28,6 +28,20 @@ namespace crashpad { namespace test { namespace { +template +void EmptyContextTest(void (*expect_context)(uint32_t, const Context*, bool)) { + Writer context_writer; + StringFile string_file; + EXPECT_TRUE(context_writer.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), sizeof(Context)); + + const Context* observed = + MinidumpWritableAtRVA(string_file.string(), 0); + ASSERT_TRUE(observed); + + expect_context(0, observed, false); +} + TEST(MinidumpContextWriter, MinidumpContextX86Writer) { StringFile string_file; @@ -36,16 +50,8 @@ TEST(MinidumpContextWriter, MinidumpContextX86Writer) { // context. SCOPED_TRACE("zero"); - MinidumpContextX86Writer context_writer; - - EXPECT_TRUE(context_writer.WriteEverything(&string_file)); - ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextX86)); - - const MinidumpContextX86* observed = - MinidumpWritableAtRVA(string_file.string(), 0); - ASSERT_TRUE(observed); - - ExpectMinidumpContextX86(0, observed, false); + EmptyContextTest( + ExpectMinidumpContextX86); } { @@ -85,16 +91,8 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) { // context. SCOPED_TRACE("zero"); - MinidumpContextAMD64Writer context_writer; - - EXPECT_TRUE(context_writer.WriteEverything(&string_file)); - ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextAMD64)); - - const MinidumpContextAMD64* observed = - MinidumpWritableAtRVA(string_file.string(), 0); - ASSERT_TRUE(observed); - - ExpectMinidumpContextAMD64(0, observed, false); + EmptyContextTest( + ExpectMinidumpContextAMD64); } { @@ -117,48 +115,72 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) { } } -TEST(MinidumpContextWriter, CreateFromSnapshot_X86) { - constexpr uint32_t kSeed = 32; - - CPUContextX86 context_snapshot_x86; - CPUContext context_snapshot; - context_snapshot.x86 = &context_snapshot_x86; - InitializeCPUContextX86(&context_snapshot, kSeed); - +template +void FromSnapshotTest(const CPUContext& snapshot_context, + void (*expect_context)(uint32_t, const Context*, bool), + uint32_t seed) { std::unique_ptr context_writer = - MinidumpContextWriter::CreateFromSnapshot(&context_snapshot); + MinidumpContextWriter::CreateFromSnapshot(&snapshot_context); ASSERT_TRUE(context_writer); StringFile string_file; ASSERT_TRUE(context_writer->WriteEverything(&string_file)); - const MinidumpContextX86* observed = - MinidumpWritableAtRVA(string_file.string(), 0); + const Context* observed = + MinidumpWritableAtRVA(string_file.string(), 0); ASSERT_TRUE(observed); - ExpectMinidumpContextX86(kSeed, observed, true); + expect_context(seed, observed, true); } -TEST(MinidumpContextWriter, CreateFromSnapshot_AMD64) { +TEST(MinidumpContextWriter, X86_FromSnapshot) { + constexpr uint32_t kSeed = 32; + CPUContextX86 context_x86; + CPUContext context; + context.x86 = &context_x86; + InitializeCPUContextX86(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextX86, kSeed); +} + +TEST(MinidumpContextWriter, AMD64_FromSnapshot) { constexpr uint32_t kSeed = 64; + CPUContextX86_64 context_x86_64; + CPUContext context; + context.x86_64 = &context_x86_64; + InitializeCPUContextX86_64(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextAMD64, kSeed); +} - CPUContextX86_64 context_snapshot_x86_64; - CPUContext context_snapshot; - context_snapshot.x86_64 = &context_snapshot_x86_64; - InitializeCPUContextX86_64(&context_snapshot, kSeed); +TEST(MinidumpContextWriter, ARM_Zeros) { + EmptyContextTest( + ExpectMinidumpContextARM); +} - std::unique_ptr context_writer = - MinidumpContextWriter::CreateFromSnapshot(&context_snapshot); - ASSERT_TRUE(context_writer); +TEST(MinidumpContextWRiter, ARM64_Zeros) { + EmptyContextTest( + ExpectMinidumpContextARM64); +} - StringFile string_file; - ASSERT_TRUE(context_writer->WriteEverything(&string_file)); +TEST(MinidumpContextWriter, ARM_FromSnapshot) { + constexpr uint32_t kSeed = 32; + CPUContextARM context_arm; + CPUContext context; + context.arm = &context_arm; + InitializeCPUContextARM(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextARM, kSeed); +} - const MinidumpContextAMD64* observed = - MinidumpWritableAtRVA(string_file.string(), 0); - ASSERT_TRUE(observed); - - ExpectMinidumpContextAMD64(kSeed, observed, true); +TEST(MinidumpContextWriter, ARM64_FromSnapshot) { + constexpr uint32_t kSeed = 64; + CPUContextARM64 context_arm64; + CPUContext context; + context.arm64 = &context_arm64; + InitializeCPUContextARM64(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextARM64, kSeed); } } // namespace diff --git a/minidump/test/minidump_context_test_util.cc b/minidump/test/minidump_context_test_util.cc index cd8e5227..0dc3a971 100644 --- a/minidump/test/minidump_context_test_util.cc +++ b/minidump/test/minidump_context_test_util.cc @@ -140,6 +140,61 @@ void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context, context->last_exception_from_rip = value++; } +void InitializeMinidumpContextARM(MinidumpContextARM* context, uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextARM; + return; + } + + context->context_flags = kMinidumpContextARMAll; + + uint32_t value = seed; + + for (size_t index = 0; index < arraysize(context->regs); ++index) { + context->regs[index] = value++; + } + context->fp = value++; + context->ip = value++; + context->ip = value++; + context->sp = value++; + context->lr = value++; + context->pc = value++; + context->cpsr = value++; + + for (size_t index = 0; index < arraysize(context->vfp); ++index) { + context->vfp[index] = value++; + } + context->fpscr = value++; +} + +void InitializeMinidumpContextARM64(MinidumpContextARM64* context, + uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextARM64; + return; + } + + context->context_flags = kMinidumpContextARM64All; + + uint32_t value = seed; + + for (size_t index = 0; index < arraysize(context->regs); ++index) { + context->regs[index] = value++; + } + context->sp = value++; + context->pc = value++; + context->cpsr = value++; + + for (size_t index = 0; index < arraysize(context->fpsimd); ++index) { + context->fpsimd[index].lo = value++; + context->fpsimd[index].hi = value++; + } + context->fpsr = value++; + context->fpcr = value++; +} + namespace { // Using gtest assertions, compares |expected| to |observed|. This is @@ -350,5 +405,53 @@ void ExpectMinidumpContextAMD64( } } +void ExpectMinidumpContextARM(uint32_t expect_seed, + const MinidumpContextARM* observed, + bool snapshot) { + MinidumpContextARM expected; + InitializeMinidumpContextARM(&expected, expect_seed); + + EXPECT_EQ(observed->context_flags, expected.context_flags); + + for (size_t index = 0; index < arraysize(expected.regs); ++index) { + EXPECT_EQ(observed->regs[index], expected.regs[index]); + } + EXPECT_EQ(observed->fp, expected.fp); + EXPECT_EQ(observed->ip, expected.ip); + EXPECT_EQ(observed->sp, expected.sp); + EXPECT_EQ(observed->lr, expected.lr); + EXPECT_EQ(observed->pc, expected.pc); + EXPECT_EQ(observed->cpsr, expected.cpsr); + + EXPECT_EQ(observed->fpscr, expected.fpscr); + for (size_t index = 0; index < arraysize(expected.vfp); ++index) { + EXPECT_EQ(observed->vfp[index], expected.vfp[index]); + } + for (size_t index = 0; index < arraysize(expected.extra); ++index) { + EXPECT_EQ(observed->extra[index], snapshot ? 0 : expected.extra[index]); + } +} + +void ExpectMinidumpContextARM64(uint32_t expect_seed, + const MinidumpContextARM64* observed, + bool snapshot) { + MinidumpContextARM64 expected; + InitializeMinidumpContextARM64(&expected, expect_seed); + + EXPECT_EQ(observed->context_flags, expected.context_flags); + + for (size_t index = 0; index < arraysize(expected.regs); ++index) { + EXPECT_EQ(observed->regs[index], expected.regs[index]); + } + EXPECT_EQ(observed->cpsr, expected.cpsr); + + EXPECT_EQ(observed->fpsr, expected.fpsr); + EXPECT_EQ(observed->fpcr, expected.fpcr); + for (size_t index = 0; index < arraysize(expected.fpsimd); ++index) { + EXPECT_EQ(observed->fpsimd[index].lo, expected.fpsimd[index].lo); + EXPECT_EQ(observed->fpsimd[index].hi, expected.fpsimd[index].hi); + } +} + } // namespace test } // namespace crashpad diff --git a/minidump/test/minidump_context_test_util.h b/minidump/test/minidump_context_test_util.h index 14f57430..64f79cde 100644 --- a/minidump/test/minidump_context_test_util.h +++ b/minidump/test/minidump_context_test_util.h @@ -41,6 +41,9 @@ namespace test { void InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed); void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context, uint32_t seed); +void InitializeMinidumpContextARM(MinidumpContextARM* context, uint32_t seed); +void InitializeMinidumpContextARM64(MinidumpContextARM64* context, + uint32_t seed); //! \} //! \brief Verifies, via gtest assertions, that a context structure contains @@ -67,6 +70,12 @@ void ExpectMinidumpContextX86( uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot); void ExpectMinidumpContextAMD64( uint32_t expect_seed, const MinidumpContextAMD64* observed, bool snapshot); +void ExpectMinidumpContextARM(uint32_t expect_seed, + const MinidumpContextARM* observed, + bool snapshot); +void ExpectMinidumpContextARM64(uint32_t expect_seed, + const MinidumpContextARM64* observed, + bool snapshot); //! \} } // namespace test diff --git a/snapshot/test/test_cpu_context.cc b/snapshot/test/test_cpu_context.cc index a7506b87..09a5e799 100644 --- a/snapshot/test/test_cpu_context.cc +++ b/snapshot/test/test_cpu_context.cc @@ -22,6 +22,68 @@ namespace crashpad { namespace test { +namespace { + +// This is templatized because the CPUContextX86::Fxsave and +// CPUContextX86_64::Fxsave are nearly identical but have different sizes for +// the members |xmm|, |reserved_4|, and |available|. +template +void InitializeCPUContextFxsave(FxsaveType* fxsave, uint32_t* seed) { + uint32_t value = *seed; + + fxsave->fcw = static_cast(value++); + fxsave->fsw = static_cast(value++); + fxsave->ftw = static_cast(value++); + fxsave->reserved_1 = static_cast(value++); + fxsave->fop = static_cast(value++); + fxsave->fpu_ip = value++; + fxsave->fpu_cs = static_cast(value++); + fxsave->reserved_2 = static_cast(value++); + fxsave->fpu_dp = value++; + fxsave->fpu_ds = static_cast(value++); + fxsave->reserved_3 = static_cast(value++); + fxsave->mxcsr = value++; + fxsave->mxcsr_mask = value++; + for (size_t st_mm_index = 0; st_mm_index < arraysize(fxsave->st_mm); + ++st_mm_index) { + for (size_t byte = 0; byte < arraysize(fxsave->st_mm[st_mm_index].st); + ++byte) { + fxsave->st_mm[st_mm_index].st[byte] = static_cast(value++); + } + for (size_t byte = 0; + byte < arraysize(fxsave->st_mm[st_mm_index].st_reserved); + ++byte) { + fxsave->st_mm[st_mm_index].st_reserved[byte] = + static_cast(value); + } + } + for (size_t xmm_index = 0; xmm_index < arraysize(fxsave->xmm); ++xmm_index) { + for (size_t byte = 0; byte < arraysize(fxsave->xmm[xmm_index]); ++byte) { + fxsave->xmm[xmm_index][byte] = static_cast(value++); + } + } + for (size_t byte = 0; byte < arraysize(fxsave->reserved_4); ++byte) { + fxsave->reserved_4[byte] = static_cast(value++); + } + for (size_t byte = 0; byte < arraysize(fxsave->available); ++byte) { + fxsave->available[byte] = static_cast(value++); + } + + *seed = value; +} + +} // namespace + +void InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave, + uint32_t* seed) { + return InitializeCPUContextFxsave(fxsave, seed); +} + +void InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave, + uint32_t* seed) { + return InitializeCPUContextFxsave(fxsave, seed); +} + void InitializeCPUContextX86(CPUContext* context, uint32_t seed) { context->architecture = kCPUArchitectureX86; @@ -101,68 +163,61 @@ void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed) { context->x86_64->dr7 = value++; } -namespace { +void InitializeCPUContextARM(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureARM; + CPUContextARM* arm = context->arm; -// This is templatized because the CPUContextX86::Fxsave and -// CPUContextX86_64::Fxsave are nearly identical but have different sizes for -// the members |xmm|, |reserved_4|, and |available|. -template -void InitializeCPUContextFxsave(FxsaveType* fxsave, uint32_t* seed) { - uint32_t value = *seed; - - fxsave->fcw = static_cast(value++); - fxsave->fsw = static_cast(value++); - fxsave->ftw = static_cast(value++); - fxsave->reserved_1 = static_cast(value++); - fxsave->fop = static_cast(value++); - fxsave->fpu_ip = value++; - fxsave->fpu_cs = static_cast(value++); - fxsave->reserved_2 = static_cast(value++); - fxsave->fpu_dp = value++; - fxsave->fpu_ds = static_cast(value++); - fxsave->reserved_3 = static_cast(value++); - fxsave->mxcsr = value++; - fxsave->mxcsr_mask = value++; - for (size_t st_mm_index = 0; - st_mm_index < arraysize(fxsave->st_mm); - ++st_mm_index) { - for (size_t byte = 0; - byte < arraysize(fxsave->st_mm[st_mm_index].st); - ++byte) { - fxsave->st_mm[st_mm_index].st[byte] = static_cast(value++); - } - for (size_t byte = 0; - byte < arraysize(fxsave->st_mm[st_mm_index].st_reserved); - ++byte) { - fxsave->st_mm[st_mm_index].st_reserved[byte] = - static_cast(value); - } - } - for (size_t xmm_index = 0; xmm_index < arraysize(fxsave->xmm); ++xmm_index) { - for (size_t byte = 0; byte < arraysize(fxsave->xmm[xmm_index]); ++byte) { - fxsave->xmm[xmm_index][byte] = static_cast(value++); - } - } - for (size_t byte = 0; byte < arraysize(fxsave->reserved_4); ++byte) { - fxsave->reserved_4[byte] = static_cast(value++); - } - for (size_t byte = 0; byte < arraysize(fxsave->available); ++byte) { - fxsave->available[byte] = static_cast(value++); + if (seed == 0) { + memset(arm, 0, sizeof(*arm)); + return; } - *seed = value; + uint32_t value = seed; + + for (size_t index = 0; index < arraysize(arm->regs); ++index) { + arm->regs[index] = value++; + } + arm->fp = value++; + arm->ip = value++; + arm->ip = value++; + arm->sp = value++; + arm->lr = value++; + arm->pc = value++; + arm->cpsr = value++; + + for (size_t index = 0; index < arraysize(arm->vfp_regs.vfp); ++index) { + arm->vfp_regs.vfp[index] = value++; + } + arm->vfp_regs.fpscr = value++; + + arm->have_fpa_regs = false; + arm->have_vfp_regs = true; } -} // namespace +void InitializeCPUContextARM64(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureARM64; + CPUContextARM64* arm64 = context->arm64; -void InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave, - uint32_t* seed) { - return InitializeCPUContextFxsave(fxsave, seed); -} + if (seed == 0) { + memset(arm64, 0, sizeof(*arm64)); + return; + } -void InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave, - uint32_t* seed) { - return InitializeCPUContextFxsave(fxsave, seed); + uint32_t value = seed; + + for (size_t index = 0; index < arraysize(arm64->regs); ++index) { + arm64->regs[index] = value++; + } + arm64->sp = value++; + arm64->pc = value++; + arm64->pstate = value++; + + for (size_t index = 0; index < arraysize(arm64->fpsimd); ++index) { + arm64->fpsimd[index].lo = value++; + arm64->fpsimd[index].hi = value++; + } + arm64->fpsr = value++; + arm64->fpcr = value++; } } // namespace test diff --git a/snapshot/test/test_cpu_context.h b/snapshot/test/test_cpu_context.h index 0de2497a..0b4b5376 100644 --- a/snapshot/test/test_cpu_context.h +++ b/snapshot/test/test_cpu_context.h @@ -22,6 +22,25 @@ namespace crashpad { namespace test { +//! \brief Initializes an `fxsave` context substructure for testing. +//! +//! \param[out] fxsave The structure to initialize. +//! \param[in,out] seed The seed value. Initializing two `fxsave` structures of +//! the same type with identical seed values should produce identical +//! structures. Initialization with a different seed value should produce +//! a different `fxsave` structure. If \a seed is `0`, \a fxsave is zeroed +//! out entirely. If \a seed is nonzero, \a fxsave will be populated +//! entirely with nonzero values. \a seed will be updated by this function +//! to allow the caller to perform subsequent initialization of the context +//! structure containing \a fxsave. +//! +//! \{ +void InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave, + uint32_t* seed); +void InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave, + uint32_t* seed); +//! \} + //! \brief Initializes a context structure for testing. //! //! Initialization is compatible with the initialization used by minidump @@ -40,25 +59,8 @@ namespace test { //! \{ void InitializeCPUContextX86(CPUContext* context, uint32_t seed); void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed); -//! \} - -//! \brief Initializes an `fxsave` context substructure for testing. -//! -//! \param[out] fxsave The structure to initialize. -//! \param[in,out] seed The seed value. Initializing two `fxsave` structures of -//! the same type with identical seed values should produce identical -//! structures. Initialization with a different seed value should produce -//! a different `fxsave` structure. If \a seed is `0`, \a fxsave is zeroed -//! out entirely. If \a seed is nonzero, \a fxsave will be populated -//! entirely with nonzero values. \a seed will be updated by this function -//! to allow the caller to perform subsequent initialization of the context -//! structure containing \a fxsave. -//! -//! \{ -void InitializeCPUContextX86Fxsave( - CPUContextX86::Fxsave* fxsave, uint32_t* seed); -void InitializeCPUContextX86_64Fxsave( - CPUContextX86_64::Fxsave* fxsave, uint32_t* seed); +void InitializeCPUContextARM(CPUContext* context, uint32_t seed); +void InitializeCPUContextARM64(CPUContext* context, uint32_t seed); //! \} } // namespace test