mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
win: support native x86 CONTEXT structures with x87 but no SSE context
When no SSE (fxsave) context is available but x87 (fsave) context is, use the x87 context. This also embeds the x87 FPU opcode from the fxsave fop field in bits 16-26 of the fsave error_selector field, true to the layout of the fsave structure. See Intel SDM volume 1 (253665-061) 8.1.10 and figure 8-9. BUG=crashpad:161 TEST=crashpad_snapshot_test CPUContextX86.*:CPUContextWin.* Change-Id: I0bf7ed995c152f124166eaa20104d228d3468f76 Reviewed-on: https://chromium-review.googlesource.com/442144 Reviewed-by: Scott Graham <scottmg@chromium.org>
This commit is contained in:
parent
c1b305244a
commit
d98a4de718
@ -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<CPUContextX86::X87Register*>(
|
||||
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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -24,16 +24,43 @@ namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
bool HasContextPart(const T& context, uint32_t bits) {
|
||||
return (context.ContextFlags & bits) == bits;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
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<uint16_t>(context.SegCs);
|
||||
@ -42,7 +69,7 @@ void CommonInitializeX86Context(const T& context, CPUContextX86* out) {
|
||||
out->ss = static_cast<uint16_t>(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<uint16_t>(context.SegDs);
|
||||
out->es = static_cast<uint16_t>(context.SegEs);
|
||||
out->fs = static_cast<uint16_t>(context.SegFs);
|
||||
out->gs = static_cast<uint16_t>(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<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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#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 <typename T>
|
||||
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 <typename T>
|
||||
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<CONTEXT>();
|
||||
#else // ARCH_CPU_X86
|
||||
TestInitializeX86Context<WOW64_CONTEXT>();
|
||||
#endif // ARCH_CPU_X86
|
||||
}
|
||||
|
||||
#endif // ARCH_CPU_X86_64
|
||||
TEST(CPUContextWin, InitializeX86Context_FsaveWithoutFxsave) {
|
||||
#if defined(ARCH_CPU_X86)
|
||||
TestInitializeX86Context_FsaveWithoutFxsave<CONTEXT>();
|
||||
#else // ARCH_CPU_X86
|
||||
TestInitializeX86Context_FsaveWithoutFxsave<WOW64_CONTEXT>();
|
||||
#endif // ARCH_CPU_X86
|
||||
}
|
||||
|
||||
#endif // ARCH_CPU_X86_FAMILY
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
|
Loading…
x
Reference in New Issue
Block a user