[lldb] Reland "Use translated full ftag values"

Translate between abridged and full ftag values in order to expose
the latter in the gdb-remote protocol while the former are used by
FXSAVE/XSAVE...  This matches the gdb behavior.

The Shell/Register tests now rely on the new behavior, and therefore
are run on non-Darwin systems only.  The Python (API) test relies
on the legacy behavior, and is run on Darwin only.

Differential Revision: https://reviews.llvm.org/D91504
This commit is contained in:
Michał Górny 2020-11-15 18:36:22 +01:00
parent 9b7b8de6d1
commit 8aea95f3cb
13 changed files with 223 additions and 37 deletions

View File

@ -451,10 +451,16 @@ NativeRegisterContextFreeBSD_x86_64::ReadRegister(const RegisterInfo *reg_info,
switch (set) {
case GPRegSet:
case FPRegSet:
case DBRegSet:
reg_value.SetBytes(GetOffsetRegSetData(set, reg_info->byte_offset),
reg_info->byte_size, endian::InlHostByteOrder());
case DBRegSet: {
void *data = GetOffsetRegSetData(set, reg_info->byte_offset);
FXSAVE *fpr = reinterpret_cast<FXSAVE *>(m_fpr.data());
if (data == &fpr->ftag) // ftag
reg_value.SetUInt16(
AbridgedToFullTagWord(fpr->ftag, fpr->fstat, fpr->stmm));
else
reg_value.SetBytes(data, reg_info->byte_size, endian::InlHostByteOrder());
break;
}
case YMMRegSet: {
llvm::Optional<YMMSplitPtr> ymm_reg = GetYMMSplitReg(reg);
if (!ymm_reg) {
@ -511,10 +517,15 @@ Status NativeRegisterContextFreeBSD_x86_64::WriteRegister(
switch (set) {
case GPRegSet:
case FPRegSet:
case DBRegSet:
::memcpy(GetOffsetRegSetData(set, reg_info->byte_offset),
reg_value.GetBytes(), reg_value.GetByteSize());
case DBRegSet: {
void *data = GetOffsetRegSetData(set, reg_info->byte_offset);
FXSAVE *fpr = reinterpret_cast<FXSAVE *>(m_fpr.data());
if (data == &fpr->ftag) // ftag
fpr->ftag = FullToAbridgedTagWord(reg_value.GetAsUInt16());
else
::memcpy(data, reg_value.GetBytes(), reg_value.GetByteSize());
break;
}
case YMMRegSet: {
llvm::Optional<YMMSplitPtr> ymm_reg = GetYMMSplitReg(reg);
if (!ymm_reg) {

View File

@ -530,6 +530,13 @@ NativeRegisterContextLinux_x86_64::ReadRegister(const RegisterInfo *reg_info,
assert((reg_info->byte_offset - m_fctrl_offset_in_userarea) < sizeof(FPR));
uint8_t *src = (uint8_t *)m_xstate.get() + reg_info->byte_offset -
m_fctrl_offset_in_userarea;
if (src == reinterpret_cast<uint8_t *>(&m_xstate->fxsave.ftag)) {
reg_value.SetUInt16(AbridgedToFullTagWord(
m_xstate->fxsave.ftag, m_xstate->fxsave.fstat, m_xstate->fxsave.stmm));
return error;
}
switch (reg_info->byte_size) {
case 1:
reg_value.SetUInt8(*(uint8_t *)src);
@ -639,23 +646,28 @@ Status NativeRegisterContextLinux_x86_64::WriteRegister(
sizeof(FPR));
uint8_t *dst = (uint8_t *)m_xstate.get() + reg_info->byte_offset -
m_fctrl_offset_in_userarea;
switch (reg_info->byte_size) {
case 1:
*(uint8_t *)dst = reg_value.GetAsUInt8();
break;
case 2:
*(uint16_t *)dst = reg_value.GetAsUInt16();
break;
case 4:
*(uint32_t *)dst = reg_value.GetAsUInt32();
break;
case 8:
*(uint64_t *)dst = reg_value.GetAsUInt64();
break;
default:
assert(false && "Unhandled data size.");
return Status("unhandled register data size %" PRIu32,
reg_info->byte_size);
if (dst == reinterpret_cast<uint8_t *>(&m_xstate->fxsave.ftag))
m_xstate->fxsave.ftag = FullToAbridgedTagWord(reg_value.GetAsUInt16());
else {
switch (reg_info->byte_size) {
case 1:
*(uint8_t *)dst = reg_value.GetAsUInt8();
break;
case 2:
*(uint16_t *)dst = reg_value.GetAsUInt16();
break;
case 4:
*(uint32_t *)dst = reg_value.GetAsUInt32();
break;
case 8:
*(uint64_t *)dst = reg_value.GetAsUInt64();
break;
default:
assert(false && "Unhandled data size.");
return Status("unhandled register data size %" PRIu32,
reg_info->byte_size);
}
}
}

View File

@ -607,9 +607,13 @@ NativeRegisterContextNetBSD_x86_64::ReadRegister(const RegisterInfo *reg_info,
case lldb_fstat_x86_64:
reg_value = (uint16_t)m_xstate.xs_fxsave.fx_sw;
break;
case lldb_ftag_x86_64:
reg_value = (uint16_t)m_xstate.xs_fxsave.fx_tw;
case lldb_ftag_x86_64: {
llvm::ArrayRef<MMSReg> st_regs{
reinterpret_cast<MMSReg *>(m_xstate.xs_fxsave.fx_87_ac), 8};
reg_value = (uint16_t)AbridgedToFullTagWord(
m_xstate.xs_fxsave.fx_tw, m_xstate.xs_fxsave.fx_sw, st_regs);
break;
}
case lldb_fop_x86_64:
reg_value = (uint64_t)m_xstate.xs_fxsave.fx_opcode;
break;
@ -905,7 +909,7 @@ Status NativeRegisterContextNetBSD_x86_64::WriteRegister(
m_xstate.xs_fxsave.fx_sw = reg_value.GetAsUInt16();
break;
case lldb_ftag_x86_64:
m_xstate.xs_fxsave.fx_tw = reg_value.GetAsUInt16();
m_xstate.xs_fxsave.fx_tw = FullToAbridgedTagWord(reg_value.GetAsUInt16());
break;
case lldb_fop_x86_64:
m_xstate.xs_fxsave.fx_opcode = reg_value.GetAsUInt16();

View File

@ -12,6 +12,7 @@ add_lldb_library(lldbPluginProcessUtility
NativeRegisterContextRegisterInfo.cpp
NativeRegisterContextWatchpoint_x86.cpp
NetBSDSignals.cpp
RegisterContext_x86.cpp
RegisterContextDarwin_arm.cpp
RegisterContextDarwin_arm64.cpp
RegisterContextDarwin_i386.cpp

View File

@ -0,0 +1,58 @@
//===-- RegisterContext_x86.cpp ---------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "RegisterContext_x86.h"
using namespace lldb_private;
// Convert the 8-bit abridged FPU Tag Word (as found in FXSAVE) to the full
// 16-bit FPU Tag Word (as found in FSAVE, and used by gdb protocol). This
// requires knowing the values of the ST(i) registers and the FPU Status Word.
uint16_t lldb_private::AbridgedToFullTagWord(uint8_t abridged_tw, uint16_t sw,
llvm::ArrayRef<MMSReg> st_regs) {
// Tag word is using internal FPU register numbering rather than ST(i).
// Mapping to ST(i): i = FPU regno - TOP (Status Word, bits 11:13).
// Here we start with FPU reg 7 and go down.
int st = 7 - ((sw >> 11) & 7);
uint16_t tw = 0;
for (uint8_t mask = 0x80; mask != 0; mask >>= 1) {
tw <<= 2;
if (abridged_tw & mask) {
// The register is non-empty, so we need to check the value of ST(i).
uint16_t exp =
st_regs[st].comp.sign_exp & 0x7fff; // Discard the sign bit.
if (exp == 0) {
if (st_regs[st].comp.mantissa == 0)
tw |= 1; // Zero
else
tw |= 2; // Denormal
} else if (exp == 0x7fff)
tw |= 2; // Infinity or NaN
// 0 if normal number
} else
tw |= 3; // Empty register
// Rotate ST down.
st = (st - 1) & 7;
}
return tw;
}
// Convert the 16-bit FPU Tag Word to the abridged 8-bit value, to be written
// into FXSAVE.
uint8_t lldb_private::FullToAbridgedTagWord(uint16_t tw) {
uint8_t abridged_tw = 0;
for (uint16_t mask = 0xc000; mask != 0; mask >>= 2) {
abridged_tw <<= 1;
// full TW uses 11 for empty registers, aTW uses 0
if ((tw & mask) != mask)
abridged_tw |= 1;
}
return abridged_tw;
}

View File

@ -12,6 +12,7 @@
#include <cstddef>
#include <cstdint>
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitmaskEnum.h"
#include "llvm/Support/Compiler.h"
@ -239,10 +240,23 @@ enum {
// Generic floating-point registers
LLVM_PACKED_START
struct MMSRegComp {
uint64_t mantissa;
uint16_t sign_exp;
};
struct MMSReg {
uint8_t bytes[10];
union {
uint8_t bytes[10];
MMSRegComp comp;
};
uint8_t pad[6];
};
LLVM_PACKED_END
static_assert(sizeof(MMSRegComp) == 10, "MMSRegComp is not 10 bytes of size");
static_assert(sizeof(MMSReg) == 16, "MMSReg is not 16 bytes of size");
struct XMMReg {
uint8_t bytes[16]; // 128-bits for each XMM register
@ -369,6 +383,10 @@ inline void YMMToXState(const YMMReg& input, void* xmm_bytes, void* ymmh_bytes)
::memcpy(ymmh_bytes, input.bytes + sizeof(XMMReg), sizeof(YMMHReg));
}
uint16_t AbridgedToFullTagWord(uint8_t abridged_tw, uint16_t sw,
llvm::ArrayRef<MMSReg> st_regs);
uint8_t FullToAbridgedTagWord(uint16_t tw);
} // namespace lldb_private
#endif

View File

@ -190,6 +190,11 @@ class RegisterCommandsTestCase(TestBase):
' = ',
new_value])
# This test relies on ftag containing the 'abridged' value. Linux
# and *BSD targets have been ported to report the full value instead
# consistently with GDB. They are covered by the new-style
# lldb/test/Shell/Register/x86*-fp-read.test.
@skipUnlessDarwin
def fp_special_purpose_register_read(self):
exe = self.getBuildArtifact("a.out")

View File

@ -1,4 +1,6 @@
# XFAIL: system-windows
# Darwin uses the legacy behavior of reporting abridged ftag value,
# it is covered by TestRegisters.py::fp_special_purpose_register_read.
# XFAIL: system-darwin
# REQUIRES: native && target-x86_64
# RUN: %clangxx_host -g %p/Inputs/x86-fp-read.cpp -o %t
@ -15,9 +17,7 @@ print &zero
register read --all
# CHECK-DAG: fctrl = 0x037b
# CHECK-DAG: fstat = 0x8084
# TODO: the following value is incorrect, it's a bug in the way
# FXSAVE/XSAVE is interpreted
# CHECK-DAG: ftag = 0x007f
# CHECK-DAG: ftag = 0xea58
# CHECK-DAG: fop = 0x0033
# CHECK-DAG: fip = [[FDIV]]
# CHECK-DAG: fdp = [[ZERO]]

View File

@ -1,4 +1,6 @@
# XFAIL: system-windows
# Darwin uses the legacy behavior of reporting abridged ftag value,
# it is covered by TestRegisters.py::fp_special_purpose_register_read.
# XFAIL: system-darwin
# REQUIRES: native && target-x86_64
# RUN: %clangxx_host %p/Inputs/x86-fp-write.cpp -o %t
@ -8,8 +10,7 @@ process launch
register write fctrl 0x037b
register write fstat 0x8884
# note: this needs to enable all registers for writes to be effective
# TODO: fix it to use proper ftag values instead of 'abridged'
register write ftag 0x00ff
register write ftag 0x2a58
register write fop 0x0033
# the exact addresses do not matter, we want just to verify FXSAVE
# note: fxrstor64 apparently truncates this to 48 bits, and sign extends

View File

@ -1,4 +1,7 @@
# XFAIL: system-windows
# Darwin uses the legacy behavior of reporting abridged ftag value,
# it is covered by TestRegisters.py::fp_special_purpose_register_read.
# XFAIL: system-darwin
# REQUIRES: native && target-x86
# RUN: %clangxx_host -g %p/Inputs/x86-fp-read.cpp -o %t
# RUN: %lldb -b -s %s %t | FileCheck %s
@ -14,9 +17,7 @@ print &zero
register read --all
# CHECK-DAG: fctrl = 0x037b
# CHECK-DAG: fstat = 0x8084
# TODO: the following value is incorrect, it's a bug in the way
# FXSAVE/XSAVE is interpreted
# CHECK-DAG: ftag = 0x007f
# CHECK-DAG: ftag = 0xea58
# CHECK-DAG: fop = 0x0033
# CHECK-DAG: fioff = [[FDIV]]
# CHECK-DAG: fooff = [[ZERO]]

View File

@ -1,4 +1,6 @@
# XFAIL: system-windows
# Darwin uses the legacy behavior of reporting abridged ftag value,
# it is covered by TestRegisters.py::fp_special_purpose_register_read.
# XFAIL: system-darwin
# REQUIRES: native && (target-x86 || target-x86_64)
# RUN: %clangxx_host %p/Inputs/x86-fp-write.cpp -o %t
@ -8,8 +10,7 @@ process launch
register write fctrl 0x037b
register write fstat 0x8884
# note: this needs to enable all registers for writes to be effective
# TODO: fix it to use proper ftag values instead of 'abridged'
register write ftag 0x00ff
register write ftag 0x2a58
register write fop 0x0033
# the exact addresses do not matter, we want just to verify FXSAVE
# note: segment registers are not supported on all CPUs

View File

@ -1,4 +1,5 @@
add_lldb_unittest(ProcessUtilityTests
RegisterContextTest.cpp
RegisterContextFreeBSDTest.cpp
LinuxProcMapsTest.cpp

View File

@ -0,0 +1,73 @@
//===-- RegisterContextTest.cpp -------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "gtest/gtest.h"
#include "Plugins/Process/Utility/RegisterContext_x86.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/FormatVariadic.h"
#include <array>
using namespace lldb_private;
struct TagWordTestVector {
uint16_t sw;
uint16_t tw;
uint8_t tw_abridged;
int st_reg_num;
};
constexpr MMSReg st_from_comp(uint64_t mantissa, uint16_t sign_exp) {
MMSReg ret = {};
ret.comp.mantissa = mantissa;
ret.comp.sign_exp = sign_exp;
return ret;
}
const std::array<MMSReg, 8> st_regs = {
st_from_comp(0x8000000000000000, 0x4000), // +2.0
st_from_comp(0x3f00000000000000, 0x0000), // 1.654785e-4932
st_from_comp(0x0000000000000000, 0x0000), // +0
st_from_comp(0x0000000000000000, 0x8000), // -0
st_from_comp(0x8000000000000000, 0x7fff), // +inf
st_from_comp(0x8000000000000000, 0xffff), // -inf
st_from_comp(0xc000000000000000, 0xffff), // nan
st_from_comp(0x8000000000000000, 0xc000), // -2.0
};
const std::array<TagWordTestVector, 8> tag_word_test_vectors{
TagWordTestVector{0x3800, 0x3fff, 0x80, 1},
TagWordTestVector{0x3000, 0x2fff, 0xc0, 2},
TagWordTestVector{0x2800, 0x27ff, 0xe0, 3},
TagWordTestVector{0x2000, 0x25ff, 0xf0, 4},
TagWordTestVector{0x1800, 0x25bf, 0xf8, 5},
TagWordTestVector{0x1000, 0x25af, 0xfc, 6},
TagWordTestVector{0x0800, 0x25ab, 0xfe, 7},
TagWordTestVector{0x0000, 0x25a8, 0xff, 8},
};
TEST(RegisterContext_x86Test, AbridgedToFullTagWord) {
for (const auto &x : llvm::enumerate(tag_word_test_vectors)) {
SCOPED_TRACE(llvm::formatv("tag_word_test_vectors[{0}]", x.index()));
std::array<MMSReg, 8> test_regs;
for (int i = 0; i < x.value().st_reg_num; ++i)
test_regs[i] = st_regs[x.value().st_reg_num - i - 1];
EXPECT_EQ(
AbridgedToFullTagWord(x.value().tw_abridged, x.value().sw, test_regs),
x.value().tw);
}
}
TEST(RegisterContext_x86Test, FullToAbridgedTagWord) {
for (const auto &x : llvm::enumerate(tag_word_test_vectors)) {
SCOPED_TRACE(llvm::formatv("tag_word_test_vectors[{0}]", x.index()));
EXPECT_EQ(FullToAbridgedTagWord(x.value().tw), x.value().tw_abridged);
}
}