mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-27 07:14:10 +08:00
Centrally define CPUContextX86::Fsave and fsave↔︎fxsave conversions
As I was finishing d98a4de718d9, it became evident that fsave proliferation was becoming a problem. Especially considering tests, there was much duplicated conversion code. This ties everything up together in a central location. test::BytesToHexString() is a new function to ease testing of byte arrays like x87 registers, without having to loop over each byte. Some static_asserts are added to verify that complex structures that need to maintain interoperability don’t grow or shrink. This is used to check the size of the fxsave and fsave structures, as well as the MinidumpCPUContext* structures. BUG=crashpad:162 Change-Id: I1a1be18096ee9be250cbfb2e006adfd08eba8753 Reviewed-on: https://chromium-review.googlesource.com/444004 Reviewed-by: Scott Graham <scottmg@chromium.org>
This commit is contained in:
parent
0c322ecc3f
commit
546e64cd0b
@ -92,7 +92,8 @@ enum MinidumpContextX86Flags : uint32_t {
|
||||
//! \brief Indicates the validity of floating-point state
|
||||
//! (`CONTEXT_FLOATING_POINT`).
|
||||
//!
|
||||
//! The `float_save` field is valid.
|
||||
//! The `fsave` field is valid. The `float_save` field is included in this
|
||||
//! definition, but its members have no practical use asdie from `fsave`.
|
||||
kMinidumpContextX86FloatingPoint = kMinidumpContextX86 | 0x00000008,
|
||||
|
||||
//! \brief Indicates the validity of debug registers
|
||||
@ -130,8 +131,9 @@ enum MinidumpContextX86Flags : uint32_t {
|
||||
//! \brief A 32-bit x86 CPU context (register state) carried in a minidump file.
|
||||
//!
|
||||
//! This is analogous to the `CONTEXT` structure on Windows when targeting
|
||||
//! 32-bit x86. This structure is used instead of `CONTEXT` to make it available
|
||||
//! when targeting other architectures.
|
||||
//! 32-bit x86, and the `WOW64_CONTEXT` structure when targeting an x86-family
|
||||
//! CPU, either 32- or 64-bit. This structure is used instead of `CONTEXT` or
|
||||
//! `WOW64_CONTEXT` to make it available when targeting other architectures.
|
||||
//!
|
||||
//! \note This structure doesn’t carry `dr4` or `dr5`, which are obsolete and
|
||||
//! normally alias `dr6` and `dr7`, respectively. See Intel Software
|
||||
@ -152,16 +154,12 @@ struct MinidumpContextX86 {
|
||||
uint32_t dr6;
|
||||
uint32_t dr7;
|
||||
|
||||
struct {
|
||||
uint32_t control_word;
|
||||
uint32_t status_word;
|
||||
uint32_t tag_word;
|
||||
uint32_t error_offset;
|
||||
uint32_t error_selector;
|
||||
uint32_t data_offset;
|
||||
uint32_t data_selector;
|
||||
uint8_t register_area[80];
|
||||
uint32_t spare_0;
|
||||
// CPUContextX86::Fsave has identical layout to what the x86 CONTEXT
|
||||
// structure places here.
|
||||
CPUContextX86::Fsave fsave;
|
||||
union {
|
||||
uint32_t spare_0; // As in the native x86 CONTEXT structure since Windows 8
|
||||
uint32_t cr0_npx_state; // As in WOW64_CONTEXT and older SDKs’ x86 CONTEXT
|
||||
} float_save;
|
||||
|
||||
uint32_t gs;
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include "minidump/minidump_context_writer.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -24,6 +26,28 @@
|
||||
|
||||
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() {
|
||||
}
|
||||
|
||||
@ -89,32 +113,11 @@ void MinidumpContextX86Writer::InitializeFromSnapshot(
|
||||
context_.dr6 = context_snapshot->dr6;
|
||||
context_.dr7 = context_snapshot->dr7;
|
||||
|
||||
// The contents of context_.float_save effectively alias everything in
|
||||
// context_.fxsave that’s related to x87 FPU state. context_.float_save
|
||||
// doesn’t carry state specific to SSE (or later), such as mxcsr and the xmm
|
||||
// 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.
|
||||
context_.float_save.control_word = context_snapshot->fxsave.fcw;
|
||||
context_.float_save.status_word = context_snapshot->fxsave.fsw;
|
||||
context_.float_save.tag_word =
|
||||
CPUContextX86::FxsaveToFsaveTagWord(context_snapshot->fxsave.fsw,
|
||||
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.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;
|
||||
|
||||
CPUContextX86::X87Register* context_float_save_st =
|
||||
reinterpret_cast<CPUContextX86::X87Register*>(
|
||||
context_.float_save.register_area);
|
||||
for (size_t index = 0;
|
||||
index < arraysize(context_snapshot->fxsave.st_mm);
|
||||
++index) {
|
||||
memcpy(&context_float_save_st[index],
|
||||
&context_snapshot->fxsave.st_mm[index].st,
|
||||
sizeof(context_snapshot->fxsave.st_mm[index].st));
|
||||
}
|
||||
CPUContextX86::FxsaveToFsave(context_snapshot->fxsave, &context_.fsave);
|
||||
|
||||
context_.gs = context_snapshot->gs;
|
||||
context_.fs = context_snapshot->fs;
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "snapshot/cpu_context.h"
|
||||
#include "snapshot/test/test_cpu_context.h"
|
||||
#include "test/hex_string.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
@ -56,6 +57,7 @@ void InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed) {
|
||||
context->ss = value++ & 0xffff;
|
||||
|
||||
InitializeCPUContextX86Fxsave(&context->fxsave, &value);
|
||||
CPUContextX86::FxsaveToFsave(context->fxsave, &context->fsave);
|
||||
|
||||
context->dr0 = value++;
|
||||
context->dr1 = value++;
|
||||
@ -65,31 +67,6 @@ void InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed) {
|
||||
context->dr6 = value++;
|
||||
context->dr7 = value++;
|
||||
|
||||
// Copy the values that are aliased between the fxsave area
|
||||
// (context->extended_registers) and the floating-point save area
|
||||
// (context->float_save).
|
||||
context->float_save.control_word = context->fxsave.fcw;
|
||||
context->float_save.status_word = context->fxsave.fsw;
|
||||
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.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;
|
||||
st_mm_index < arraysize(context->fxsave.st_mm);
|
||||
++st_mm_index) {
|
||||
for (size_t byte = 0;
|
||||
byte < arraysize(context->fxsave.st_mm[st_mm_index].st);
|
||||
++byte) {
|
||||
size_t st_index =
|
||||
st_mm_index * arraysize(context->fxsave.st_mm[st_mm_index].st) + byte;
|
||||
context->float_save.register_area[st_index] =
|
||||
context->fxsave.st_mm[st_mm_index].st[byte];
|
||||
}
|
||||
}
|
||||
|
||||
// Set this field last, because it has no analogue in CPUContextX86.
|
||||
context->float_save.spare_0 = value++;
|
||||
}
|
||||
@ -189,37 +166,31 @@ void ExpectMinidumpContextFxsave(const FxsaveType* expected,
|
||||
st_mm_index < arraysize(expected->st_mm);
|
||||
++st_mm_index) {
|
||||
SCOPED_TRACE(base::StringPrintf("st_mm_index %" PRIuS, st_mm_index));
|
||||
for (size_t byte = 0;
|
||||
byte < arraysize(expected->st_mm[st_mm_index].st);
|
||||
++byte) {
|
||||
EXPECT_EQ(expected->st_mm[st_mm_index].st[byte],
|
||||
observed->st_mm[st_mm_index].st[byte]) << "byte " << byte;
|
||||
}
|
||||
for (size_t byte = 0;
|
||||
byte < arraysize(expected->st_mm[st_mm_index].st_reserved);
|
||||
++byte) {
|
||||
EXPECT_EQ(expected->st_mm[st_mm_index].st_reserved[byte],
|
||||
observed->st_mm[st_mm_index].st_reserved[byte])
|
||||
<< "byte " << byte;
|
||||
}
|
||||
EXPECT_EQ(BytesToHexString(expected->st_mm[st_mm_index].st,
|
||||
arraysize(expected->st_mm[st_mm_index].st)),
|
||||
BytesToHexString(observed->st_mm[st_mm_index].st,
|
||||
arraysize(observed->st_mm[st_mm_index].st)));
|
||||
EXPECT_EQ(
|
||||
BytesToHexString(expected->st_mm[st_mm_index].st_reserved,
|
||||
arraysize(expected->st_mm[st_mm_index].st_reserved)),
|
||||
BytesToHexString(observed->st_mm[st_mm_index].st_reserved,
|
||||
arraysize(observed->st_mm[st_mm_index].st_reserved)));
|
||||
}
|
||||
for (size_t xmm_index = 0;
|
||||
xmm_index < arraysize(expected->xmm);
|
||||
++xmm_index) {
|
||||
SCOPED_TRACE(base::StringPrintf("xmm_index %" PRIuS, xmm_index));
|
||||
for (size_t byte = 0; byte < arraysize(expected->xmm[xmm_index]); ++byte) {
|
||||
EXPECT_EQ(expected->xmm[xmm_index][byte], observed->xmm[xmm_index][byte])
|
||||
<< "byte " << byte;
|
||||
}
|
||||
}
|
||||
for (size_t byte = 0; byte < arraysize(expected->reserved_4); ++byte) {
|
||||
EXPECT_EQ(expected->reserved_4[byte], observed->reserved_4[byte])
|
||||
<< "byte " << byte;
|
||||
}
|
||||
for (size_t byte = 0; byte < arraysize(expected->available); ++byte) {
|
||||
EXPECT_EQ(expected->available[byte], observed->available[byte])
|
||||
<< "byte " << byte;
|
||||
EXPECT_EQ(BytesToHexString(expected->xmm[xmm_index],
|
||||
arraysize(expected->xmm[xmm_index])),
|
||||
BytesToHexString(observed->xmm[xmm_index],
|
||||
arraysize(observed->xmm[xmm_index])))
|
||||
<< "xmm_index " << xmm_index;
|
||||
}
|
||||
EXPECT_EQ(
|
||||
BytesToHexString(expected->reserved_4, arraysize(expected->reserved_4)),
|
||||
BytesToHexString(observed->reserved_4, arraysize(observed->reserved_4)));
|
||||
EXPECT_EQ(
|
||||
BytesToHexString(expected->available, arraysize(expected->available)),
|
||||
BytesToHexString(observed->available, arraysize(observed->available)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -237,22 +208,19 @@ void ExpectMinidumpContextX86(
|
||||
EXPECT_EQ(expected.dr6, observed->dr6);
|
||||
EXPECT_EQ(expected.dr7, observed->dr7);
|
||||
|
||||
EXPECT_EQ(expected.float_save.control_word,
|
||||
observed->float_save.control_word);
|
||||
EXPECT_EQ(expected.float_save.status_word, observed->float_save.status_word);
|
||||
EXPECT_EQ(expected.float_save.tag_word, observed->float_save.tag_word);
|
||||
EXPECT_EQ(expected.float_save.error_offset,
|
||||
observed->float_save.error_offset);
|
||||
EXPECT_EQ(expected.float_save.error_selector,
|
||||
observed->float_save.error_selector);
|
||||
EXPECT_EQ(expected.float_save.data_offset, observed->float_save.data_offset);
|
||||
EXPECT_EQ(expected.float_save.data_selector,
|
||||
observed->float_save.data_selector);
|
||||
for (size_t index = 0;
|
||||
index < arraysize(expected.float_save.register_area);
|
||||
++index) {
|
||||
EXPECT_EQ(expected.float_save.register_area[index],
|
||||
observed->float_save.register_area[index]) << "index " << index;
|
||||
EXPECT_EQ(expected.fsave.fcw, observed->fsave.fcw);
|
||||
EXPECT_EQ(expected.fsave.fsw, observed->fsave.fsw);
|
||||
EXPECT_EQ(expected.fsave.ftw, observed->fsave.ftw);
|
||||
EXPECT_EQ(expected.fsave.fpu_ip, observed->fsave.fpu_ip);
|
||||
EXPECT_EQ(expected.fsave.fpu_cs, observed->fsave.fpu_cs);
|
||||
EXPECT_EQ(expected.fsave.fpu_dp, observed->fsave.fpu_dp);
|
||||
EXPECT_EQ(expected.fsave.fpu_ds, observed->fsave.fpu_ds);
|
||||
for (size_t index = 0; index < arraysize(expected.fsave.st); ++index) {
|
||||
EXPECT_EQ(BytesToHexString(expected.fsave.st[index],
|
||||
arraysize(expected.fsave.st[index])),
|
||||
BytesToHexString(observed->fsave.st[index],
|
||||
arraysize(observed->fsave.st[index])))
|
||||
<< "index " << index;
|
||||
}
|
||||
if (snapshot) {
|
||||
EXPECT_EQ(0u, observed->float_save.spare_0);
|
||||
|
@ -14,13 +14,23 @@
|
||||
|
||||
#include "snapshot/cpu_context.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "util/misc/implicit_cast.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
// Sanity-check complex structures to ensure interoperability.
|
||||
static_assert(sizeof(CPUContextX86::Fsave) == 108, "CPUContextX86::Fsave size");
|
||||
static_assert(sizeof(CPUContextX86::Fxsave) == 512,
|
||||
"CPUContextX86::Fxsave size");
|
||||
static_assert(sizeof(CPUContextX86_64::Fxsave) == 512,
|
||||
"CPUContextX86_64::Fxsave size");
|
||||
|
||||
enum {
|
||||
kX87TagValid = 0,
|
||||
kX87TagZero,
|
||||
@ -30,6 +40,53 @@ enum {
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void CPUContextX86::FxsaveToFsave(const Fxsave& fxsave, Fsave* fsave) {
|
||||
fsave->fcw = fxsave.fcw;
|
||||
fsave->reserved_1 = 0;
|
||||
fsave->fsw = fxsave.fsw;
|
||||
fsave->reserved_2 = 0;
|
||||
fsave->ftw = FxsaveToFsaveTagWord(fxsave.fsw, fxsave.ftw, fxsave.st_mm);
|
||||
fsave->reserved_3 = 0;
|
||||
fsave->fpu_ip = fxsave.fpu_ip;
|
||||
fsave->fpu_cs = fxsave.fpu_cs;
|
||||
fsave->fop = fxsave.fop;
|
||||
fsave->fpu_dp = fxsave.fpu_dp;
|
||||
fsave->fpu_ds = fxsave.fpu_ds;
|
||||
fsave->reserved_4 = 0;
|
||||
static_assert(arraysize(fsave->st) == arraysize(fxsave.st_mm),
|
||||
"FPU stack registers must be equivalent");
|
||||
for (size_t index = 0; index < arraysize(fsave->st); ++index) {
|
||||
memcpy(fsave->st[index], fxsave.st_mm[index].st, sizeof(fsave->st[index]));
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void CPUContextX86::FsaveToFxsave(const Fsave& fsave, Fxsave* fxsave) {
|
||||
fxsave->fcw = fsave.fcw;
|
||||
fxsave->fsw = fsave.fsw;
|
||||
fxsave->ftw = FsaveToFxsaveTagWord(fsave.ftw);
|
||||
fxsave->reserved_1 = 0;
|
||||
fxsave->fop = fsave.fop;
|
||||
fxsave->fpu_ip = fsave.fpu_ip;
|
||||
fxsave->fpu_cs = fsave.fpu_cs;
|
||||
fxsave->reserved_2 = 0;
|
||||
fxsave->fpu_dp = fsave.fpu_dp;
|
||||
fxsave->fpu_ds = fsave.fpu_ds;
|
||||
fxsave->reserved_3 = 0;
|
||||
fxsave->mxcsr = 0;
|
||||
fxsave->mxcsr_mask = 0;
|
||||
static_assert(arraysize(fxsave->st_mm) == arraysize(fsave.st),
|
||||
"FPU stack registers must be equivalent");
|
||||
for (size_t index = 0; index < arraysize(fsave.st); ++index) {
|
||||
memcpy(fxsave->st_mm[index].st, fsave.st[index], sizeof(fsave.st[index]));
|
||||
memset(fxsave->st_mm[index].st_reserved,
|
||||
0,
|
||||
sizeof(fxsave->st_mm[index].st_reserved));
|
||||
}
|
||||
memset(fxsave->xmm, 0, sizeof(*fxsave) - offsetof(Fxsave, xmm));
|
||||
}
|
||||
|
||||
// static
|
||||
uint16_t CPUContextX86::FxsaveToFsaveTagWord(
|
||||
uint16_t fsw,
|
||||
|
@ -25,6 +25,22 @@ namespace crashpad {
|
||||
struct CPUContextX86 {
|
||||
using X87Register = uint8_t[10];
|
||||
|
||||
struct Fsave {
|
||||
uint16_t fcw; // FPU control word
|
||||
uint16_t reserved_1;
|
||||
uint16_t fsw; // FPU status word
|
||||
uint16_t reserved_2;
|
||||
uint16_t ftw; // full FPU tag word
|
||||
uint16_t reserved_3;
|
||||
uint32_t fpu_ip; // FPU instruction pointer offset
|
||||
uint16_t fpu_cs; // FPU instruction pointer segment selector
|
||||
uint16_t fop; // FPU opcode
|
||||
uint32_t fpu_dp; // FPU data pointer offset
|
||||
uint16_t fpu_ds; // FPU data pointer segment selector
|
||||
uint16_t reserved_4;
|
||||
X87Register st[8];
|
||||
};
|
||||
|
||||
union X87OrMMXRegister {
|
||||
struct {
|
||||
X87Register st;
|
||||
@ -58,14 +74,49 @@ struct CPUContextX86 {
|
||||
uint8_t available[48];
|
||||
};
|
||||
|
||||
//! \brief Converts an `fxsave` area to an `fsave` area.
|
||||
//!
|
||||
//! `fsave` state is restricted to the x87 FPU, while `fxsave` state includes
|
||||
//! state related to the x87 FPU as well as state specific to SSE.
|
||||
//!
|
||||
//! As the `fxsave` format is a superset of the `fsave` format, this operation
|
||||
//! fully populates the `fsave` area. `fsave` uses the full 16-bit form for
|
||||
//! the x87 floating-point tag word, so FxsaveToFsaveTagWord() is used to
|
||||
//! derive Fsave::ftw from the abridged 8-bit form used by `fxsave`. Reserved
|
||||
//! fields in \a fsave are set to `0`.
|
||||
//!
|
||||
//! \param[in] fxsave The `fxsave` area to convert.
|
||||
//! \param[out] fsave The `fsave` area to populate.
|
||||
//!
|
||||
//! \sa FsaveToFxsave()
|
||||
static void FxsaveToFsave(const Fxsave& fxsave, Fsave* fsave);
|
||||
|
||||
//! \brief Converts an `fsave` area to an `fxsave` area.
|
||||
//!
|
||||
//! `fsave` state is restricted to the x87 FPU, while `fxsave` state includes
|
||||
//! state related to the x87 FPU as well as state specific to SSE.
|
||||
//!
|
||||
//! As the `fsave` format is a subset of the `fxsave` format, this operation
|
||||
//! cannot fully populate the `fxsave` area. Fields in \a fxsave that have no
|
||||
//! equivalent in \a fsave are set to `0`, including Fxsave::mxcsr,
|
||||
//! Fxsave::mxcsr_mask, Fxsave::xmm, and Fxsave::available.
|
||||
//! FsaveToFxsaveTagWord() is used to derive Fxsave::ftw from the full 16-bit
|
||||
//! form used by `fsave`. Reserved fields in \a fxsave are set to `0`.
|
||||
//!
|
||||
//! \param[in] fsave The `fsave` area to convert.
|
||||
//! \param[out] fxsave The `fxsave` area to populate.
|
||||
//!
|
||||
//! \sa FxsaveToFsave()
|
||||
static void FsaveToFxsave(const Fsave& fsave, Fxsave* fxsave);
|
||||
|
||||
//! \brief Converts x87 floating-point tag words from `fxsave` (abridged,
|
||||
//! 8-bit) to `fsave` (full, 16-bit) form.
|
||||
//!
|
||||
//! `fxsave` stores the x87 floating-point tag word in abridged 8-bit form,
|
||||
//! and `fsave` stores it in full 16-bit form. Some users, notably
|
||||
//! MinidumpContextX86::float_save::tag_word, require the full 16-bit form,
|
||||
//! where most other contemporary code uses `fxsave` and thus the abridged
|
||||
//! 8-bit form found in CPUContextX86::Fxsave::ftw.
|
||||
//! CPUContextX86::Fsave::ftw, require the full 16-bit form, where most other
|
||||
//! contemporary code uses `fxsave` and thus the abridged 8-bit form found in
|
||||
//! CPUContextX86::Fxsave::ftw.
|
||||
//!
|
||||
//! This function converts an abridged tag word to the full version by using
|
||||
//! the abridged tag word and the contents of the registers it describes. See
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/hex_string.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
@ -48,37 +49,188 @@ enum FractionValue {
|
||||
//! \param[in] j_bit The value to use for the “J bit” (“integer bit”).
|
||||
//! \param[in] fraction_value If kFractionAllZero, the fraction will be zeroed
|
||||
//! out. If kFractionNormal, the fraction will not be all zeroes.
|
||||
void SetX87Register(CPUContextX86::X87OrMMXRegister* st_mm,
|
||||
void SetX87Register(CPUContextX86::X87Register* st,
|
||||
ExponentValue exponent_value,
|
||||
bool j_bit,
|
||||
FractionValue fraction_value) {
|
||||
switch (exponent_value) {
|
||||
case kExponentAllZero:
|
||||
st_mm->st[9] = 0x80;
|
||||
st_mm->st[8] = 0;
|
||||
(*st)[9] = 0x80;
|
||||
(*st)[8] = 0;
|
||||
break;
|
||||
case kExponentAllOne:
|
||||
st_mm->st[9] = 0x7f;
|
||||
st_mm->st[8] = 0xff;
|
||||
(*st)[9] = 0x7f;
|
||||
(*st)[8] = 0xff;
|
||||
break;
|
||||
case kExponentNormal:
|
||||
st_mm->st[9] = 0x55;
|
||||
st_mm->st[8] = 0x55;
|
||||
(*st)[9] = 0x55;
|
||||
(*st)[8] = 0x55;
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t fraction_pattern = fraction_value == kFractionAllZero ? 0 : 0x55;
|
||||
memset(&st_mm->st[0], fraction_pattern, 8);
|
||||
memset(st, fraction_pattern, 8);
|
||||
|
||||
if (j_bit) {
|
||||
st_mm->st[7] |= 0x80;
|
||||
(*st)[7] |= 0x80;
|
||||
} else {
|
||||
st_mm->st[7] &= ~0x80;
|
||||
(*st)[7] &= ~0x80;
|
||||
}
|
||||
}
|
||||
|
||||
//! \brief Initializes an x87 register to a known bit pattern.
|
||||
//!
|
||||
//! This behaves as SetX87Register() but also clears the reserved portion of the
|
||||
//! field as used in the `fxsave` format.
|
||||
void SetX87OrMMXRegister(CPUContextX86::X87OrMMXRegister* st_mm,
|
||||
ExponentValue exponent_value,
|
||||
bool j_bit,
|
||||
FractionValue fraction_value) {
|
||||
SetX87Register(&st_mm->st, exponent_value, j_bit, fraction_value);
|
||||
memset(st_mm->st_reserved, 0, sizeof(st_mm->st_reserved));
|
||||
}
|
||||
|
||||
TEST(CPUContextX86, FxsaveToFsave) {
|
||||
// Establish a somewhat plausible fxsave state. Use nonzero values for
|
||||
// reserved fields and things that aren’t present in fsave.
|
||||
CPUContextX86::Fxsave fxsave;
|
||||
fxsave.fcw = 0x027f; // mask exceptions, 53-bit precision, round to nearest
|
||||
fxsave.fsw = 1 << 11; // top = 1: logical 0-7 maps to physical 1-7, 0
|
||||
fxsave.ftw = 0x1f; // physical 5-7 (logical 4-6) empty
|
||||
fxsave.reserved_1 = 0x5a;
|
||||
fxsave.fop = 0x1fe; // fsin
|
||||
fxsave.fpu_ip = 0x76543210;
|
||||
fxsave.fpu_cs = 0x0007;
|
||||
fxsave.reserved_2 = 0x5a5a;
|
||||
fxsave.fpu_dp = 0xfedcba98;
|
||||
fxsave.fpu_ds = 0x000f;
|
||||
fxsave.reserved_3 = 0x5a5a;
|
||||
fxsave.mxcsr = 0x1f80;
|
||||
fxsave.mxcsr_mask = 0xffff;
|
||||
SetX87Register(
|
||||
&fxsave.st_mm[0].st, kExponentNormal, true, kFractionAllZero); // valid
|
||||
SetX87Register(
|
||||
&fxsave.st_mm[1].st, kExponentAllZero, false, kFractionAllZero); // zero
|
||||
SetX87Register(
|
||||
&fxsave.st_mm[2].st, kExponentAllOne, true, kFractionAllZero); // spec.
|
||||
SetX87Register(
|
||||
&fxsave.st_mm[3].st, kExponentAllOne, true, kFractionNormal); // spec.
|
||||
SetX87Register(
|
||||
&fxsave.st_mm[4].st, kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87Register(
|
||||
&fxsave.st_mm[5].st, kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87Register(
|
||||
&fxsave.st_mm[6].st, kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87Register(
|
||||
&fxsave.st_mm[7].st, kExponentNormal, true, kFractionNormal); // valid
|
||||
for (size_t index = 0; index < arraysize(fxsave.st_mm); ++index) {
|
||||
memset(&fxsave.st_mm[index].st_reserved,
|
||||
0x5a,
|
||||
sizeof(fxsave.st_mm[index].st_reserved));
|
||||
}
|
||||
memset(&fxsave.xmm, 0x5a, sizeof(fxsave) - offsetof(decltype(fxsave), xmm));
|
||||
|
||||
CPUContextX86::Fsave fsave;
|
||||
CPUContextX86::FxsaveToFsave(fxsave, &fsave);
|
||||
|
||||
// Everything should have come over from fxsave. Reserved fields should be
|
||||
// zero.
|
||||
EXPECT_EQ(fxsave.fcw, fsave.fcw);
|
||||
EXPECT_EQ(0, fsave.reserved_1);
|
||||
EXPECT_EQ(fxsave.fsw, fsave.fsw);
|
||||
EXPECT_EQ(0, fsave.reserved_2);
|
||||
EXPECT_EQ(0xfe90, fsave.ftw); // FxsaveToFsaveTagWord
|
||||
EXPECT_EQ(0, fsave.reserved_3);
|
||||
EXPECT_EQ(fxsave.fpu_ip, fsave.fpu_ip);
|
||||
EXPECT_EQ(fxsave.fpu_cs, fsave.fpu_cs);
|
||||
EXPECT_EQ(fxsave.fop, fsave.fop);
|
||||
EXPECT_EQ(fxsave.fpu_dp, fsave.fpu_dp);
|
||||
EXPECT_EQ(fxsave.fpu_ds, fsave.fpu_ds);
|
||||
EXPECT_EQ(0, fsave.reserved_4);
|
||||
for (size_t index = 0; index < arraysize(fsave.st); ++index) {
|
||||
EXPECT_EQ(BytesToHexString(fxsave.st_mm[index].st,
|
||||
arraysize(fxsave.st_mm[index].st)),
|
||||
BytesToHexString(fsave.st[index], arraysize(fsave.st[index])))
|
||||
<< "index " << index;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CPUContextX86, FsaveToFxsave) {
|
||||
// Establish a somewhat plausible fsave state. Use nonzero values for
|
||||
// reserved fields.
|
||||
CPUContextX86::Fsave fsave;
|
||||
fsave.fcw = 0x0300; // unmask exceptions, 64-bit precision, round to nearest
|
||||
fsave.reserved_1 = 0xa5a5;
|
||||
fsave.fsw = 2 << 11; // top = 2: logical 0-7 maps to physical 2-7, 0-1
|
||||
fsave.reserved_2 = 0xa5a5;
|
||||
fsave.ftw = 0xa9ff; // physical 0-3 (logical 6-7, 0-1) empty; physical 4
|
||||
// (logical 2) zero; physical 5-7 (logical 3-5) special
|
||||
fsave.reserved_3 = 0xa5a5;
|
||||
fsave.fpu_ip = 0x456789ab;
|
||||
fsave.fpu_cs = 0x1013;
|
||||
fsave.fop = 0x01ee; // fldz
|
||||
fsave.fpu_dp = 0x0123cdef;
|
||||
fsave.fpu_ds = 0x2017;
|
||||
fsave.reserved_4 = 0xa5a5;
|
||||
SetX87Register(&fsave.st[0], kExponentAllZero, false, kFractionNormal);
|
||||
SetX87Register(&fsave.st[1], kExponentAllZero, true, kFractionNormal);
|
||||
SetX87Register(
|
||||
&fsave.st[2], kExponentAllZero, false, kFractionAllZero); // zero
|
||||
SetX87Register(
|
||||
&fsave.st[3], kExponentAllZero, true, kFractionAllZero); // spec.
|
||||
SetX87Register(
|
||||
&fsave.st[4], kExponentAllZero, false, kFractionNormal); // spec.
|
||||
SetX87Register(
|
||||
&fsave.st[5], kExponentAllZero, true, kFractionNormal); // spec.
|
||||
SetX87Register(&fsave.st[6], kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87Register(&fsave.st[7], kExponentAllZero, true, kFractionAllZero);
|
||||
|
||||
CPUContextX86::Fxsave fxsave;
|
||||
CPUContextX86::FsaveToFxsave(fsave, &fxsave);
|
||||
|
||||
// Everything in fsave should have come over from there. Fields not present in
|
||||
// fsave and reserved fields should be zero.
|
||||
EXPECT_EQ(fsave.fcw, fxsave.fcw);
|
||||
EXPECT_EQ(fsave.fsw, fxsave.fsw);
|
||||
EXPECT_EQ(0xf0, fxsave.ftw); // FsaveToFxsaveTagWord
|
||||
EXPECT_EQ(0, fxsave.reserved_1);
|
||||
EXPECT_EQ(fsave.fop, fxsave.fop);
|
||||
EXPECT_EQ(fsave.fpu_ip, fxsave.fpu_ip);
|
||||
EXPECT_EQ(fsave.fpu_cs, fxsave.fpu_cs);
|
||||
EXPECT_EQ(0, fxsave.reserved_2);
|
||||
EXPECT_EQ(fsave.fpu_dp, fxsave.fpu_dp);
|
||||
EXPECT_EQ(fsave.fpu_ds, fxsave.fpu_ds);
|
||||
EXPECT_EQ(0, fxsave.reserved_3);
|
||||
EXPECT_EQ(0u, fxsave.mxcsr);
|
||||
EXPECT_EQ(0u, fxsave.mxcsr_mask);
|
||||
for (size_t index = 0; index < arraysize(fxsave.st_mm); ++index) {
|
||||
EXPECT_EQ(BytesToHexString(fsave.st[index], arraysize(fsave.st[index])),
|
||||
BytesToHexString(fxsave.st_mm[index].st,
|
||||
arraysize(fxsave.st_mm[index].st)))
|
||||
<< "index " << index;
|
||||
EXPECT_EQ(std::string(arraysize(fxsave.st_mm[index].st_reserved) * 2, '0'),
|
||||
BytesToHexString(fxsave.st_mm[index].st_reserved,
|
||||
arraysize(fxsave.st_mm[index].st_reserved)))
|
||||
<< "index " << index;
|
||||
}
|
||||
size_t unused_len = sizeof(fxsave) - offsetof(decltype(fxsave), xmm);
|
||||
EXPECT_EQ(std::string(unused_len * 2, '0'),
|
||||
BytesToHexString(fxsave.xmm, unused_len));
|
||||
|
||||
// Since the fsave format is a subset of the fxsave format, fsave-fxsave-fsave
|
||||
// should round-trip cleanly.
|
||||
CPUContextX86::Fsave fsave_2;
|
||||
CPUContextX86::FxsaveToFsave(fxsave, &fsave_2);
|
||||
|
||||
// Clear the reserved fields in the original fsave structure, since they’re
|
||||
// expected to be clear in the copy.
|
||||
fsave.reserved_1 = 0;
|
||||
fsave.reserved_2 = 0;
|
||||
fsave.reserved_3 = 0;
|
||||
fsave.reserved_4 = 0;
|
||||
EXPECT_EQ(0, memcmp(&fsave, &fsave_2, sizeof(fsave)));
|
||||
}
|
||||
|
||||
TEST(CPUContextX86, FxsaveToFsaveTagWord) {
|
||||
// The fsave tag word uses bit pattern 00 for valid, 01 for zero, 10 for
|
||||
// “special”, and 11 for empty. Like the fxsave tag word, it is arranged by
|
||||
@ -93,40 +245,52 @@ TEST(CPUContextX86, FxsaveToFsaveTagWord) {
|
||||
uint16_t fsw = 0 << 11; // top = 0: logical 0-7 maps to physical 0-7
|
||||
uint8_t fxsave_tag = 0x0f; // physical 4-7 (logical 4-7) empty
|
||||
CPUContextX86::X87OrMMXRegister st_mm[8];
|
||||
SetX87Register(&st_mm[0], kExponentNormal, false, kFractionNormal); // spec.
|
||||
SetX87Register(&st_mm[1], kExponentNormal, true, kFractionNormal); // valid
|
||||
SetX87Register(&st_mm[2], kExponentNormal, false, kFractionAllZero); // spec.
|
||||
SetX87Register(&st_mm[3], kExponentNormal, true, kFractionAllZero); // valid
|
||||
SetX87Register(&st_mm[4], kExponentNormal, false, kFractionNormal);
|
||||
SetX87Register(&st_mm[5], kExponentNormal, true, kFractionNormal);
|
||||
SetX87Register(&st_mm[6], kExponentNormal, false, kFractionAllZero);
|
||||
SetX87Register(&st_mm[7], kExponentNormal, true, kFractionAllZero);
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[0], kExponentNormal, false, kFractionNormal); // spec.
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[1], kExponentNormal, true, kFractionNormal); // valid
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[2], kExponentNormal, false, kFractionAllZero); // spec.
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[3], kExponentNormal, true, kFractionAllZero); // valid
|
||||
SetX87OrMMXRegister(&st_mm[4], kExponentNormal, false, kFractionNormal);
|
||||
SetX87OrMMXRegister(&st_mm[5], kExponentNormal, true, kFractionNormal);
|
||||
SetX87OrMMXRegister(&st_mm[6], kExponentNormal, false, kFractionAllZero);
|
||||
SetX87OrMMXRegister(&st_mm[7], kExponentNormal, true, kFractionAllZero);
|
||||
EXPECT_EQ(0xff22,
|
||||
CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
|
||||
|
||||
fsw = 2 << 11; // top = 2: logical 0-7 maps to physical 2-7, 0-1
|
||||
fxsave_tag = 0xf0; // physical 0-3 (logical 6-7, 0-1) empty
|
||||
SetX87Register(&st_mm[0], kExponentAllZero, false, kFractionNormal);
|
||||
SetX87Register(&st_mm[1], kExponentAllZero, true, kFractionNormal);
|
||||
SetX87Register(&st_mm[2], kExponentAllZero, false, kFractionAllZero); // zero
|
||||
SetX87Register(&st_mm[3], kExponentAllZero, true, kFractionAllZero); // spec.
|
||||
SetX87Register(&st_mm[4], kExponentAllZero, false, kFractionNormal); // spec.
|
||||
SetX87Register(&st_mm[5], kExponentAllZero, true, kFractionNormal); // spec.
|
||||
SetX87Register(&st_mm[6], kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87Register(&st_mm[7], kExponentAllZero, true, kFractionAllZero);
|
||||
SetX87OrMMXRegister(&st_mm[0], kExponentAllZero, false, kFractionNormal);
|
||||
SetX87OrMMXRegister(&st_mm[1], kExponentAllZero, true, kFractionNormal);
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[2], kExponentAllZero, false, kFractionAllZero); // zero
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[3], kExponentAllZero, true, kFractionAllZero); // spec.
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[4], kExponentAllZero, false, kFractionNormal); // spec.
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[5], kExponentAllZero, true, kFractionNormal); // spec.
|
||||
SetX87OrMMXRegister(&st_mm[6], kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87OrMMXRegister(&st_mm[7], kExponentAllZero, true, kFractionAllZero);
|
||||
EXPECT_EQ(0xa9ff,
|
||||
CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
|
||||
|
||||
fsw = 5 << 11; // top = 5: logical 0-7 maps to physical 5-7, 0-4
|
||||
fxsave_tag = 0x5a; // physical 0, 2, 5, and 7 (logical 5, 0, 2, and 3) empty
|
||||
SetX87Register(&st_mm[0], kExponentAllOne, false, kFractionNormal);
|
||||
SetX87Register(&st_mm[1], kExponentAllOne, true, kFractionNormal); // spec.
|
||||
SetX87Register(&st_mm[2], kExponentAllOne, false, kFractionAllZero);
|
||||
SetX87Register(&st_mm[3], kExponentAllOne, true, kFractionAllZero);
|
||||
SetX87Register(&st_mm[4], kExponentAllOne, false, kFractionNormal); // spec.
|
||||
SetX87Register(&st_mm[5], kExponentAllOne, true, kFractionNormal);
|
||||
SetX87Register(&st_mm[6], kExponentAllOne, false, kFractionAllZero); // spec.
|
||||
SetX87Register(&st_mm[7], kExponentAllOne, true, kFractionAllZero); // spec.
|
||||
SetX87OrMMXRegister(&st_mm[0], kExponentAllOne, false, kFractionNormal);
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[1], kExponentAllOne, true, kFractionNormal); // spec.
|
||||
SetX87OrMMXRegister(&st_mm[2], kExponentAllOne, false, kFractionAllZero);
|
||||
SetX87OrMMXRegister(&st_mm[3], kExponentAllOne, true, kFractionAllZero);
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[4], kExponentAllOne, false, kFractionNormal); // spec.
|
||||
SetX87OrMMXRegister(&st_mm[5], kExponentAllOne, true, kFractionNormal);
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[6], kExponentAllOne, false, kFractionAllZero); // spec.
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[7], kExponentAllOne, true, kFractionAllZero); // spec.
|
||||
EXPECT_EQ(0xeebb,
|
||||
CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
|
||||
|
||||
@ -134,14 +298,19 @@ TEST(CPUContextX86, FxsaveToFsaveTagWord) {
|
||||
// register file.
|
||||
fsw = 1 << 11; // top = 1: logical 0-7 maps to physical 1-7, 0
|
||||
fxsave_tag = 0x1f; // physical 5-7 (logical 4-6) empty
|
||||
SetX87Register(&st_mm[0], kExponentNormal, true, kFractionAllZero); // valid
|
||||
SetX87Register(&st_mm[1], kExponentAllZero, false, kFractionAllZero); // zero
|
||||
SetX87Register(&st_mm[2], kExponentAllOne, true, kFractionAllZero); // spec.
|
||||
SetX87Register(&st_mm[3], kExponentAllOne, true, kFractionNormal); // spec.
|
||||
SetX87Register(&st_mm[4], kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87Register(&st_mm[5], kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87Register(&st_mm[6], kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87Register(&st_mm[7], kExponentNormal, true, kFractionNormal); // valid
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[0], kExponentNormal, true, kFractionAllZero); // valid
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[1], kExponentAllZero, false, kFractionAllZero); // zero
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[2], kExponentAllOne, true, kFractionAllZero); // spec.
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[3], kExponentAllOne, true, kFractionNormal); // spec.
|
||||
SetX87OrMMXRegister(&st_mm[4], kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87OrMMXRegister(&st_mm[5], kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87OrMMXRegister(&st_mm[6], kExponentAllZero, false, kFractionAllZero);
|
||||
SetX87OrMMXRegister(
|
||||
&st_mm[7], kExponentNormal, true, kFractionNormal); // valid
|
||||
EXPECT_EQ(0xfe90,
|
||||
CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
|
||||
|
||||
@ -149,7 +318,7 @@ TEST(CPUContextX86, FxsaveToFsaveTagWord) {
|
||||
fsw = 0 << 11; // top = 0: logical 0-7 maps to physical 0-7
|
||||
fxsave_tag = 0xff; // nothing empty
|
||||
for (size_t index = 0; index < arraysize(st_mm); ++index) {
|
||||
SetX87Register(&st_mm[index], kExponentNormal, true, kFractionAllZero);
|
||||
SetX87OrMMXRegister(&st_mm[index], kExponentNormal, true, kFractionAllZero);
|
||||
}
|
||||
EXPECT_EQ(0, CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
|
||||
|
||||
@ -168,6 +337,7 @@ TEST(CPUContextX86, FsaveToFxsaveTagWord) {
|
||||
EXPECT_EQ(0xf0, CPUContextX86::FsaveToFxsaveTagWord(0xa9ff));
|
||||
EXPECT_EQ(0x5a, CPUContextX86::FsaveToFxsaveTagWord(0xeebb));
|
||||
EXPECT_EQ(0x1f, CPUContextX86::FsaveToFxsaveTagWord(0xfe90));
|
||||
EXPECT_EQ(0xff, CPUContextX86::FsaveToFxsaveTagWord(0x0000));
|
||||
EXPECT_EQ(0x00, CPUContextX86::FsaveToFxsaveTagWord(0xffff));
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,16 @@ namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
// Validation for casts used with CPUContextX86::FsaveToFxsave().
|
||||
static_assert(sizeof(CPUContextX86::Fsave) ==
|
||||
offsetof(WOW64_FLOATING_SAVE_AREA, Cr0NpxState),
|
||||
"WoW64 fsave types must be equivalent");
|
||||
#if defined(ARCH_CPU_X86)
|
||||
static_assert(sizeof(CPUContextX86::Fsave) ==
|
||||
offsetof(FLOATING_SAVE_AREA, Spare0),
|
||||
"fsave types must be equivalent");
|
||||
#endif // ARCH_CPU_X86
|
||||
|
||||
template <typename T>
|
||||
bool HasContextPart(const T& context, uint32_t bits) {
|
||||
return (context.ContextFlags & bits) == bits;
|
||||
@ -90,36 +100,26 @@ void CommonInitializeX86Context(const T& context, CPUContextX86* out) {
|
||||
out->dr1 = context.Dr1;
|
||||
out->dr2 = context.Dr2;
|
||||
out->dr3 = context.Dr3;
|
||||
|
||||
// DR4 and DR5 are obsolete synonyms for DR6 and DR7, see
|
||||
// https://en.wikipedia.org/wiki/X86_debug_register.
|
||||
out->dr4 = context.Dr6;
|
||||
out->dr5 = context.Dr7;
|
||||
|
||||
out->dr6 = context.Dr6;
|
||||
out->dr7 = context.Dr7;
|
||||
}
|
||||
|
||||
if (HasContextPart(context, WOW64_CONTEXT_EXTENDED_REGISTERS)) {
|
||||
static_assert(sizeof(out->fxsave) == sizeof(context.ExtendedRegisters),
|
||||
"types must be equivalent");
|
||||
"fxsave types must be equivalent");
|
||||
memcpy(&out->fxsave, &context.ExtendedRegisters, sizeof(out->fxsave));
|
||||
} else if (HasContextPart(context, WOW64_CONTEXT_FLOATING_POINT)) {
|
||||
out->fxsave.fcw = static_cast<uint16_t>(context.FloatSave.ControlWord);
|
||||
out->fxsave.fsw = static_cast<uint16_t>(context.FloatSave.StatusWord);
|
||||
out->fxsave.ftw = CPUContextX86::FsaveToFxsaveTagWord(
|
||||
static_cast<uint16_t>(context.FloatSave.TagWord));
|
||||
out->fxsave.fop = context.FloatSave.ErrorSelector >> 16;
|
||||
out->fxsave.fpu_ip = context.FloatSave.ErrorOffset;
|
||||
out->fxsave.fpu_cs = static_cast<uint16_t>(context.FloatSave.ErrorSelector);
|
||||
out->fxsave.fpu_dp = context.FloatSave.DataOffset;
|
||||
out->fxsave.fpu_ds = static_cast<uint16_t>(context.FloatSave.DataSelector);
|
||||
const CPUContextX86::X87Register* context_floatsave_st =
|
||||
reinterpret_cast<const CPUContextX86::X87Register*>(
|
||||
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));
|
||||
}
|
||||
// The static_assert that validates this cast can’t be here because it
|
||||
// relies on field names that vary based on the template parameter.
|
||||
CPUContextX86::FsaveToFxsave(
|
||||
*reinterpret_cast<const CPUContextX86::Fsave*>(&context.FloatSave),
|
||||
&out->fxsave);
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,10 +174,12 @@ void InitializeX64Context(const CONTEXT& context, CPUContextX86_64* out) {
|
||||
out->dr1 = context.Dr1;
|
||||
out->dr2 = context.Dr2;
|
||||
out->dr3 = context.Dr3;
|
||||
|
||||
// DR4 and DR5 are obsolete synonyms for DR6 and DR7, see
|
||||
// https://en.wikipedia.org/wiki/X86_debug_register.
|
||||
out->dr4 = context.Dr6;
|
||||
out->dr5 = context.Dr7;
|
||||
|
||||
out->dr6 = context.Dr6;
|
||||
out->dr7 = context.Dr7;
|
||||
}
|
||||
@ -185,7 +187,7 @@ void InitializeX64Context(const CONTEXT& context, CPUContextX86_64* out) {
|
||||
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));
|
||||
memcpy(&out->fxsave, &context.FltSave, sizeof(out->fxsave));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "base/macros.h"
|
||||
#include "build/build_config.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/hex_string.h"
|
||||
#include "snapshot/cpu_context.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -84,18 +85,16 @@ void TestInitializeX86Context_FsaveWithoutFxsave() {
|
||||
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]);
|
||||
}
|
||||
EXPECT_EQ(
|
||||
std::string(arraysize(cpu_context_x86.fxsave.st_mm[st_mm].st) * 2,
|
||||
'0'),
|
||||
BytesToHexString(cpu_context_x86.fxsave.st_mm[st_mm].st,
|
||||
arraysize(cpu_context_x86.fxsave.st_mm[st_mm].st)))
|
||||
<< "st_mm " << st_mm;
|
||||
}
|
||||
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("0000000000000080ff7f",
|
||||
BytesToHexString(cpu_context_x86.fxsave.st_mm[7].st,
|
||||
arraysize(cpu_context_x86.fxsave.st_mm[7].st)));
|
||||
|
||||
EXPECT_EQ(3u, cpu_context_x86.dr0);
|
||||
}
|
||||
|
35
test/hex_string.cc
Normal file
35
test/hex_string.cc
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2017 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 "test/hex_string.h"
|
||||
|
||||
#include "base/strings/stringprintf.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
|
||||
std::string BytesToHexString(const void* bytes, size_t length) {
|
||||
const unsigned char* bytes_c = reinterpret_cast<const unsigned char*>(bytes);
|
||||
|
||||
std::string hex_string;
|
||||
hex_string.reserve(length * 2);
|
||||
for (size_t index = 0; index < length; ++index) {
|
||||
hex_string.append(base::StringPrintf("%02x", bytes_c[index]));
|
||||
}
|
||||
|
||||
return hex_string;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
40
test/hex_string.h
Normal file
40
test/hex_string.h
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2017 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_TEST_HEX_STRING_H_
|
||||
#define CRASHPAD_TEST_HEX_STRING_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
|
||||
//! \brief Returns a hexadecimal string corresponding to \a bytes and \a length.
|
||||
//!
|
||||
//! Example usage:
|
||||
//! \code
|
||||
//! uint8_t expected[10];
|
||||
//! uint8_t observed[10];
|
||||
//! // …
|
||||
//! EXPECT_EQ(BytesToHexString(expected, arraysize(expected)),
|
||||
//! BytesToHexString(observed, arraysize(observed)));
|
||||
//! \endcode
|
||||
std::string BytesToHexString(const void* bytes, size_t length);
|
||||
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_TEST_HEX_STRING_H_
|
34
test/hex_string_test.cc
Normal file
34
test/hex_string_test.cc
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2017 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 "test/hex_string.h"
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
TEST(HexString, HexString) {
|
||||
EXPECT_EQ("", BytesToHexString(nullptr, 0));
|
||||
|
||||
const char kBytes[] = "Abc123xyz \x0a\x7f\xf0\x9f\x92\xa9_";
|
||||
EXPECT_EQ("41626331323378797a200a7ff09f92a95f00",
|
||||
BytesToHexString(kBytes, arraysize(kBytes)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -35,6 +35,8 @@
|
||||
'file.cc',
|
||||
'file.h',
|
||||
'gtest_death_check.h',
|
||||
'hex_string.cc',
|
||||
'hex_string.h',
|
||||
'mac/dyld.h',
|
||||
'mac/mach_errors.cc',
|
||||
'mac/mach_errors.h',
|
||||
|
@ -34,6 +34,7 @@
|
||||
'..',
|
||||
],
|
||||
'sources': [
|
||||
'hex_string_test.cc',
|
||||
'mac/mach_multiprocess_test.cc',
|
||||
'multiprocess_exec_test.cc',
|
||||
'multiprocess_posix_test.cc',
|
||||
|
Loading…
x
Reference in New Issue
Block a user