diff --git a/minidump/minidump_context_writer.cc b/minidump/minidump_context_writer.cc index 4d3bf8a4..cc18c34a 100644 --- a/minidump/minidump_context_writer.cc +++ b/minidump/minidump_context_writer.cc @@ -100,14 +100,18 @@ void MinidumpContextX86Writer::InitializeFromSnapshot( context_snapshot->fxsave.ftw, context_snapshot->fxsave.st_mm); context_.float_save.error_offset = context_snapshot->fxsave.fpu_ip; - context_.float_save.error_selector = context_snapshot->fxsave.fpu_cs; + context_.float_save.error_selector = + (context_snapshot->fxsave.fop << 16) | context_snapshot->fxsave.fpu_cs; context_.float_save.data_offset = context_snapshot->fxsave.fpu_dp; context_.float_save.data_selector = context_snapshot->fxsave.fpu_ds; - for (size_t index = 0, offset = 0; + CPUContextX86::X87Register* context_float_save_st = + reinterpret_cast( + context_.float_save.register_area); + for (size_t index = 0; index < arraysize(context_snapshot->fxsave.st_mm); - offset += sizeof(context_snapshot->fxsave.st_mm[index].st), ++index) { - memcpy(&context_.float_save.register_area[offset], + ++index) { + memcpy(&context_float_save_st[index], &context_snapshot->fxsave.st_mm[index].st, sizeof(context_snapshot->fxsave.st_mm[index].st)); } diff --git a/minidump/test/minidump_context_test_util.cc b/minidump/test/minidump_context_test_util.cc index 730c2b79..a4d1d26d 100644 --- a/minidump/test/minidump_context_test_util.cc +++ b/minidump/test/minidump_context_test_util.cc @@ -73,7 +73,8 @@ void InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed) { context->float_save.tag_word = CPUContextX86::FxsaveToFsaveTagWord( context->fxsave.fsw, context->fxsave.ftw, context->fxsave.st_mm); context->float_save.error_offset = context->fxsave.fpu_ip; - context->float_save.error_selector = context->fxsave.fpu_cs; + context->float_save.error_selector = + (context->fxsave.fop << 16) | context->fxsave.fpu_cs; context->float_save.data_offset = context->fxsave.fpu_dp; context->float_save.data_selector = context->fxsave.fpu_ds; for (size_t st_mm_index = 0; diff --git a/snapshot/cpu_context.cc b/snapshot/cpu_context.cc index c11a031b..c846fd1f 100644 --- a/snapshot/cpu_context.cc +++ b/snapshot/cpu_context.cc @@ -19,18 +19,22 @@ namespace crashpad { +namespace { + +enum { + kX87TagValid = 0, + kX87TagZero, + kX87TagSpecial, + kX87TagEmpty, +}; + +} // namespace + // static uint16_t CPUContextX86::FxsaveToFsaveTagWord( uint16_t fsw, uint8_t fxsave_tag, const CPUContextX86::X87OrMMXRegister st_mm[8]) { - enum { - kX87TagValid = 0, - kX87TagZero, - kX87TagSpecial, - kX87TagEmpty, - }; - // The x87 tag word (in both abridged and full form) identifies physical // registers, but |st_mm| is arranged in logical stack order. In order to map // physical tag word bits to the logical stack registers they correspond to, @@ -85,6 +89,17 @@ uint16_t CPUContextX86::FxsaveToFsaveTagWord( return fsave_tag; } +// static +uint8_t CPUContextX86::FsaveToFxsaveTagWord(uint16_t fsave_tag) { + uint8_t fxsave_tag = 0; + for (int physical_index = 0; physical_index < 8; ++physical_index) { + const uint8_t fsave_bits = (fsave_tag >> (physical_index * 2)) & 0x3; + const bool fxsave_bit = fsave_bits != kX87TagEmpty; + fxsave_tag |= fxsave_bit << physical_index; + } + return fxsave_tag; +} + uint64_t CPUContext::InstructionPointer() const { switch (architecture) { case kCPUArchitectureX86: diff --git a/snapshot/cpu_context.h b/snapshot/cpu_context.h index 67b298e8..e1989141 100644 --- a/snapshot/cpu_context.h +++ b/snapshot/cpu_context.h @@ -75,6 +75,8 @@ struct CPUContextX86 { //! Manual, Volume 2: System Programming (24593-3.24), “FXSAVE Format for x87 //! Tag Word”. //! + //! \sa FsaveToFxsaveTagWord() + //! //! \param[in] fsw The FPU status word, used to map logical \a st_mm registers //! to their physical counterparts. This can be taken from //! CPUContextX86::Fxsave::fsw. @@ -87,6 +89,16 @@ struct CPUContextX86 { static uint16_t FxsaveToFsaveTagWord( uint16_t fsw, uint8_t fxsave_tag, const X87OrMMXRegister st_mm[8]); + //! \breif Converts x87 floating-point tag words from `fsave` (full, 16-bit) + //! to `fxsave` (abridged, 8-bit) form. + //! + //! This function performs the inverse operation of FxsaveToFsaveTagWord(). + //! + //! \param[in] fsave_tag The full FPU tag word. + //! + //! \return The abridged FPU tag word. + static uint8_t FsaveToFxsaveTagWord(uint16_t fsave_tag); + // Integer registers. uint32_t eax; uint32_t ebx; diff --git a/snapshot/cpu_context_test.cc b/snapshot/cpu_context_test.cc index 808ba615..042b14e7 100644 --- a/snapshot/cpu_context_test.cc +++ b/snapshot/cpu_context_test.cc @@ -161,6 +161,16 @@ TEST(CPUContextX86, FxsaveToFsaveTagWord) { CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm)); } +TEST(CPUContextX86, FsaveToFxsaveTagWord) { + // The register sets that these x87 tag words might apply to are given in the + // FxsaveToFsaveTagWord test above. + EXPECT_EQ(0x0f, CPUContextX86::FsaveToFxsaveTagWord(0xff22)); + EXPECT_EQ(0xf0, CPUContextX86::FsaveToFxsaveTagWord(0xa9ff)); + EXPECT_EQ(0x5a, CPUContextX86::FsaveToFxsaveTagWord(0xeebb)); + EXPECT_EQ(0x1f, CPUContextX86::FsaveToFxsaveTagWord(0xfe90)); + EXPECT_EQ(0x00, CPUContextX86::FsaveToFxsaveTagWord(0xffff)); +} + } // namespace } // namespace test } // namespace crashpad diff --git a/snapshot/win/cpu_context_win.cc b/snapshot/win/cpu_context_win.cc index 2450ef25..900fbda8 100644 --- a/snapshot/win/cpu_context_win.cc +++ b/snapshot/win/cpu_context_win.cc @@ -24,16 +24,43 @@ namespace crashpad { namespace { +template +bool HasContextPart(const T& context, uint32_t bits) { + return (context.ContextFlags & bits) == bits; +} + template void CommonInitializeX86Context(const T& context, CPUContextX86* out) { - LOG_IF(ERROR, !(context.ContextFlags & WOW64_CONTEXT_i386)) - << "non-x86 context"; + // This function assumes that the WOW64_CONTEXT_* and x86 CONTEXT_* values + // for ContextFlags are identical. This can be tested when targeting 32-bit + // x86. +#if defined(ARCH_CPU_X86) + static_assert(sizeof(CONTEXT) == sizeof(WOW64_CONTEXT), + "type mismatch: CONTEXT"); +#define ASSERT_WOW64_EQUIVALENT(x) \ + do { \ + static_assert(x == WOW64_##x, "value mismatch: " #x); \ + } while (false) + ASSERT_WOW64_EQUIVALENT(CONTEXT_i386); + ASSERT_WOW64_EQUIVALENT(CONTEXT_i486); + ASSERT_WOW64_EQUIVALENT(CONTEXT_CONTROL); + ASSERT_WOW64_EQUIVALENT(CONTEXT_INTEGER); + ASSERT_WOW64_EQUIVALENT(CONTEXT_SEGMENTS); + ASSERT_WOW64_EQUIVALENT(CONTEXT_FLOATING_POINT); + ASSERT_WOW64_EQUIVALENT(CONTEXT_DEBUG_REGISTERS); + ASSERT_WOW64_EQUIVALENT(CONTEXT_EXTENDED_REGISTERS); + ASSERT_WOW64_EQUIVALENT(CONTEXT_FULL); + ASSERT_WOW64_EQUIVALENT(CONTEXT_ALL); + ASSERT_WOW64_EQUIVALENT(CONTEXT_XSTATE); +#undef ASSERT_WOW64_EQUIVALENT +#endif // ARCH_CPU_X86 + memset(out, 0, sizeof(*out)); - // We assume in this function that the WOW64_CONTEXT_* and x86 CONTEXT_* - // values for ContextFlags are identical. + LOG_IF(ERROR, !HasContextPart(context, WOW64_CONTEXT_i386)) + << "non-x86 context"; - if (context.ContextFlags & WOW64_CONTEXT_CONTROL) { + if (HasContextPart(context, WOW64_CONTEXT_CONTROL)) { out->ebp = context.Ebp; out->eip = context.Eip; out->cs = static_cast(context.SegCs); @@ -42,7 +69,7 @@ void CommonInitializeX86Context(const T& context, CPUContextX86* out) { out->ss = static_cast(context.SegSs); } - if (context.ContextFlags & WOW64_CONTEXT_INTEGER) { + if (HasContextPart(context, WOW64_CONTEXT_INTEGER)) { out->eax = context.Eax; out->ebx = context.Ebx; out->ecx = context.Ecx; @@ -51,14 +78,14 @@ void CommonInitializeX86Context(const T& context, CPUContextX86* out) { out->esi = context.Esi; } - if (context.ContextFlags & WOW64_CONTEXT_SEGMENTS) { + if (HasContextPart(context, WOW64_CONTEXT_SEGMENTS)) { out->ds = static_cast(context.SegDs); out->es = static_cast(context.SegEs); out->fs = static_cast(context.SegFs); out->gs = static_cast(context.SegGs); } - if (context.ContextFlags & WOW64_CONTEXT_DEBUG_REGISTERS) { + if (HasContextPart(context, WOW64_CONTEXT_DEBUG_REGISTERS)) { out->dr0 = context.Dr0; out->dr1 = context.Dr1; out->dr2 = context.Dr2; @@ -71,12 +98,28 @@ void CommonInitializeX86Context(const T& context, CPUContextX86* out) { out->dr7 = context.Dr7; } - if (context.ContextFlags & WOW64_CONTEXT_EXTENDED_REGISTERS) { + if (HasContextPart(context, WOW64_CONTEXT_EXTENDED_REGISTERS)) { static_assert(sizeof(out->fxsave) == sizeof(context.ExtendedRegisters), "types must be equivalent"); memcpy(&out->fxsave, &context.ExtendedRegisters, sizeof(out->fxsave)); - } else if (context.ContextFlags & WOW64_CONTEXT_FLOATING_POINT) { - CHECK(false) << "TODO(scottmg): extract x87 data"; + } else if (HasContextPart(context, WOW64_CONTEXT_FLOATING_POINT)) { + out->fxsave.fcw = static_cast(context.FloatSave.ControlWord); + out->fxsave.fsw = static_cast(context.FloatSave.StatusWord); + out->fxsave.ftw = CPUContextX86::FsaveToFxsaveTagWord( + static_cast(context.FloatSave.TagWord)); + out->fxsave.fop = context.FloatSave.ErrorSelector >> 16; + out->fxsave.fpu_ip = context.FloatSave.ErrorOffset; + out->fxsave.fpu_cs = static_cast(context.FloatSave.ErrorSelector); + out->fxsave.fpu_dp = context.FloatSave.DataOffset; + out->fxsave.fpu_ds = static_cast(context.FloatSave.DataSelector); + const CPUContextX86::X87Register* context_floatsave_st = + reinterpret_cast( + context.FloatSave.RegisterArea); + for (size_t index = 0; index < arraysize(out->fxsave.st_mm); ++index) { + memcpy(out->fxsave.st_mm[index].st, + context_floatsave_st[index], + sizeof(out->fxsave.st_mm[index].st)); + } } } @@ -91,9 +134,9 @@ void InitializeX86Context(const WOW64_CONTEXT& context, CPUContextX86* out) { void InitializeX64Context(const CONTEXT& context, CPUContextX86_64* out) { memset(out, 0, sizeof(*out)); - LOG_IF(ERROR, !(context.ContextFlags & CONTEXT_AMD64)) << "non-x64 context"; + LOG_IF(ERROR, !HasContextPart(context, CONTEXT_AMD64)) << "non-x64 context"; - if (context.ContextFlags & CONTEXT_CONTROL) { + if (HasContextPart(context, CONTEXT_CONTROL)) { out->cs = context.SegCs; out->rflags = context.EFlags; out->rip = context.Rip; @@ -101,7 +144,7 @@ void InitializeX64Context(const CONTEXT& context, CPUContextX86_64* out) { // SegSs ignored. } - if (context.ContextFlags & CONTEXT_INTEGER) { + if (HasContextPart(context, CONTEXT_INTEGER)) { out->rax = context.Rax; out->rbx = context.Rbx; out->rcx = context.Rcx; @@ -119,14 +162,14 @@ void InitializeX64Context(const CONTEXT& context, CPUContextX86_64* out) { out->r15 = context.R15; } - if (context.ContextFlags & CONTEXT_SEGMENTS) { + if (HasContextPart(context, CONTEXT_SEGMENTS)) { out->fs = context.SegFs; out->gs = context.SegGs; // SegDs ignored. // SegEs ignored. } - if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS) { + if (HasContextPart(context, CONTEXT_DEBUG_REGISTERS)) { out->dr0 = context.Dr0; out->dr1 = context.Dr1; out->dr2 = context.Dr2; @@ -139,7 +182,7 @@ void InitializeX64Context(const CONTEXT& context, CPUContextX86_64* out) { out->dr7 = context.Dr7; } - if (context.ContextFlags & CONTEXT_FLOATING_POINT) { + if (HasContextPart(context, CONTEXT_FLOATING_POINT)) { static_assert(sizeof(out->fxsave) == sizeof(context.FltSave), "types must be equivalent"); memcpy(&out->fxsave, &context.FltSave.ControlWord, sizeof(out->fxsave)); diff --git a/snapshot/win/cpu_context_win_test.cc b/snapshot/win/cpu_context_win_test.cc index 0c7484b7..1f8bfe98 100644 --- a/snapshot/win/cpu_context_win_test.cc +++ b/snapshot/win/cpu_context_win_test.cc @@ -16,6 +16,7 @@ #include +#include "base/macros.h" #include "build/build_config.h" #include "gtest/gtest.h" #include "snapshot/cpu_context.h" @@ -24,6 +25,84 @@ namespace crashpad { namespace test { namespace { +template +void TestInitializeX86Context() { + T context = {0}; + context.ContextFlags = WOW64_CONTEXT_INTEGER | + WOW64_CONTEXT_DEBUG_REGISTERS | + WOW64_CONTEXT_EXTENDED_REGISTERS; + context.Eax = 1; + context.Dr0 = 3; + context.ExtendedRegisters[4] = 2; // FTW + + // Test the simple case, where everything in the CPUContextX86 argument is set + // directly from the supplied thread, float, and debug state parameters. + { + CPUContextX86 cpu_context_x86 = {}; + InitializeX86Context(context, &cpu_context_x86); + EXPECT_EQ(1u, cpu_context_x86.eax); + EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw); + EXPECT_EQ(3u, cpu_context_x86.dr0); + } +} + +template +void TestInitializeX86Context_FsaveWithoutFxsave() { + T context = {0}; + context.ContextFlags = WOW64_CONTEXT_INTEGER | + WOW64_CONTEXT_FLOATING_POINT | + WOW64_CONTEXT_DEBUG_REGISTERS; + context.Eax = 1; + + // In fields that are wider than they need to be, set the high bits to ensure + // that they’re masked off appropriately in the output. + context.FloatSave.ControlWord = 0xffff027f; + context.FloatSave.StatusWord = 0xffff0004; + context.FloatSave.TagWord = 0xffffa9ff; + context.FloatSave.ErrorOffset = 0x01234567; + context.FloatSave.ErrorSelector = 0x0bad0003; + context.FloatSave.DataOffset = 0x89abcdef; + context.FloatSave.DataSelector = 0xffff0007; + context.FloatSave.RegisterArea[77] = 0x80; + context.FloatSave.RegisterArea[78] = 0xff; + context.FloatSave.RegisterArea[79] = 0x7f; + + context.Dr0 = 3; + + { + CPUContextX86 cpu_context_x86 = {}; + InitializeX86Context(context, &cpu_context_x86); + + EXPECT_EQ(1u, cpu_context_x86.eax); + + EXPECT_EQ(0x027f, cpu_context_x86.fxsave.fcw); + EXPECT_EQ(0x0004, cpu_context_x86.fxsave.fsw); + EXPECT_EQ(0x00f0, cpu_context_x86.fxsave.ftw); + EXPECT_EQ(0x0bad, cpu_context_x86.fxsave.fop); + EXPECT_EQ(0x01234567, cpu_context_x86.fxsave.fpu_ip); + EXPECT_EQ(0x0003, cpu_context_x86.fxsave.fpu_cs); + EXPECT_EQ(0x89abcdef, cpu_context_x86.fxsave.fpu_dp); + EXPECT_EQ(0x0007, cpu_context_x86.fxsave.fpu_ds); + for (size_t st_mm = 0; st_mm < 7; ++st_mm) { + for (size_t byte = 0; + byte < arraysize(cpu_context_x86.fxsave.st_mm[st_mm].st); + ++byte) { + EXPECT_EQ(0x00, cpu_context_x86.fxsave.st_mm[st_mm].st[byte]); + } + } + for (size_t byte = 0; byte < 7; ++byte) { + EXPECT_EQ(0x00, cpu_context_x86.fxsave.st_mm[7].st[byte]); + } + EXPECT_EQ(0x80, cpu_context_x86.fxsave.st_mm[7].st[7]); + EXPECT_EQ(0xff, cpu_context_x86.fxsave.st_mm[7].st[8]); + EXPECT_EQ(0x7f, cpu_context_x86.fxsave.st_mm[7].st[9]); + + EXPECT_EQ(3u, cpu_context_x86.dr0); + } +} + +#if defined(ARCH_CPU_X86_FAMILY) + #if defined(ARCH_CPU_X86_64) TEST(CPUContextWin, InitializeX64Context) { @@ -45,28 +124,25 @@ TEST(CPUContextWin, InitializeX64Context) { } } -#else +#endif // ARCH_CPU_X86_64 TEST(CPUContextWin, InitializeX86Context) { - CONTEXT context = {0}; - context.ContextFlags = - CONTEXT_INTEGER | CONTEXT_EXTENDED_REGISTERS | CONTEXT_DEBUG_REGISTERS; - context.Eax = 1; - context.ExtendedRegisters[4] = 2; // FTW. - context.Dr0 = 3; - - // Test the simple case, where everything in the CPUContextX86 argument is - // set directly from the supplied thread, float, and debug state parameters. - { - CPUContextX86 cpu_context_x86 = {}; - InitializeX86Context(context, &cpu_context_x86); - EXPECT_EQ(1u, cpu_context_x86.eax); - EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw); - EXPECT_EQ(3u, cpu_context_x86.dr0); - } +#if defined(ARCH_CPU_X86) + TestInitializeX86Context(); +#else // ARCH_CPU_X86 + TestInitializeX86Context(); +#endif // ARCH_CPU_X86 } -#endif // ARCH_CPU_X86_64 +TEST(CPUContextWin, InitializeX86Context_FsaveWithoutFxsave) { +#if defined(ARCH_CPU_X86) + TestInitializeX86Context_FsaveWithoutFxsave(); +#else // ARCH_CPU_X86 + TestInitializeX86Context_FsaveWithoutFxsave(); +#endif // ARCH_CPU_X86 +} + +#endif // ARCH_CPU_X86_FAMILY } // namespace } // namespace test