Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h @@ -77,7 +77,8 @@ private: // Private member types. - enum FPRType { eFPRTypeNotValid = 0, eFPRTypeFXSAVE, eFPRTypeXSAVE }; + enum XStateType { eXStateTypeNotValid = 0, eXStateTypeFXSAVE, eXStateTypeXSAVE }; + enum RegSet { gpr, fpu, avx, mpx }; // Info about register ranges. struct RegInfo { @@ -106,8 +107,8 @@ }; // Private member variables. - mutable FPRType m_fpr_type; - FPR m_fpr; + mutable XStateType m_xstate_type; + FPR m_fpr; // Extended States Area, named FPR for historical reasons. IOVEC m_iovec; YMM m_ymm_set; MPX m_mpx_set; @@ -116,16 +117,20 @@ uint32_t m_fctrl_offset_in_userarea; // Private member methods. + bool HasFXSAVE() const; + + bool HasXSAVE() const; + + bool IsCPUFeatureAvailable(RegSet feature_code) const; + bool IsRegisterSetAvailable(uint32_t set_index) const; bool IsGPR(uint32_t reg_index) const; - FPRType GetFPRType() const; + XStateType GetXStateType() const; bool IsFPR(uint32_t reg_index) const; - bool IsFPR(uint32_t reg_index, FPRType fpr_type) const; - bool CopyXSTATEtoYMM(uint32_t reg_index, lldb::ByteOrder byte_order); bool CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order); Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp @@ -20,6 +20,8 @@ #include "Plugins/Process/Utility/RegisterContextLinux_i386.h" #include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" +#include + using namespace lldb_private; using namespace lldb_private::process_linux; @@ -218,6 +220,23 @@ #define NT_PRXFPREG 0x46e62b7f #endif +// ---------------------------------------------------------------------------- +// Required MPX define. +// ---------------------------------------------------------------------------- + +// Support MPX extensions also if compiled with compiler without MPX support. +#ifndef bit_MPX +#define bit_MPX 0x4000 +#endif + +// ---------------------------------------------------------------------------- +// XCR0 extended register sets masks. +// ---------------------------------------------------------------------------- +#define mask_XSTATE_AVX (1ULL << 2) +#define mask_XSTATE_BNDREGS (1ULL << 3) +#define mask_XSTATE_BNDCFG (1ULL << 4) +#define mask_XSTATE_MPX (mask_XSTATE_BNDREGS | mask_XSTATE_BNDCFG) + NativeRegisterContextLinux * NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( const ArchSpec &target_arch, NativeThreadProtocol &native_thread, @@ -249,7 +268,7 @@ uint32_t concrete_frame_idx) : NativeRegisterContextLinux(native_thread, concrete_frame_idx, CreateRegisterInfoInterface(target_arch)), - m_fpr_type(eFPRTypeNotValid), m_fpr(), m_iovec(), m_ymm_set(), + m_xstate_type(eXStateTypeNotValid), m_fpr(), m_iovec(), m_ymm_set(), m_mpx_set(), m_reg_info(), m_gpr_x86_64() { // Set up data about ranges of valid registers. switch (target_arch.GetMachine()) { @@ -379,7 +398,7 @@ return error; } - if (IsFPR(reg, GetFPRType())) { + if (IsFPR(reg) || IsAVX(reg) || IsMPX(reg)) { error = ReadFPR(); if (error.Fail()) return error; @@ -428,7 +447,7 @@ reg_info->byte_size, byte_order); if (reg >= m_reg_info.first_ymm && reg <= m_reg_info.last_ymm) { // Concatenate ymm using the register halves in xmm.bytes and ymmh.bytes - if (GetFPRType() == eFPRTypeXSAVE && CopyXSTATEtoYMM(reg, byte_order)) + if (CopyXSTATEtoYMM(reg, byte_order)) reg_value.SetBytes(m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes, reg_info->byte_size, byte_order); else { @@ -437,7 +456,7 @@ } } if (reg >= m_reg_info.first_mpxr && reg <= m_reg_info.last_mpxr) { - if (GetFPRType() == eFPRTypeXSAVE && CopyXSTATEtoMPX(reg)) + if (CopyXSTATEtoMPX(reg)) reg_value.SetBytes(m_mpx_set.mpxr[reg - m_reg_info.first_mpxr].bytes, reg_info->byte_size, byte_order); else { @@ -446,7 +465,7 @@ } } if (reg >= m_reg_info.first_mpxc && reg <= m_reg_info.last_mpxc) { - if (GetFPRType() == eFPRTypeXSAVE && CopyXSTATEtoMPX(reg)) + if (CopyXSTATEtoMPX(reg)) reg_value.SetBytes(m_mpx_set.mpxc[reg - m_reg_info.first_mpxc].bytes, reg_info->byte_size, byte_order); else { @@ -517,7 +536,7 @@ if (IsGPR(reg_index)) return WriteRegisterRaw(reg_index, reg_value); - if (IsFPR(reg_index, GetFPRType())) { + if (IsFPR(reg_index) || IsAVX(reg_index) || IsMPX(reg_index)) { if (reg_info->encoding == lldb::eEncodingVector) { if (reg_index >= m_reg_info.first_st && reg_index <= m_reg_info.last_st) ::memcpy( @@ -536,9 +555,6 @@ if (reg_index >= m_reg_info.first_ymm && reg_index <= m_reg_info.last_ymm) { - if (GetFPRType() != eFPRTypeXSAVE) - return Error("target processor does not support AVX"); - // Store ymm register content, and split into the register halves in // xmm.bytes and ymmh.bytes ::memcpy(m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes, @@ -549,9 +565,6 @@ if (reg_index >= m_reg_info.first_mpxr && reg_index <= m_reg_info.last_mpxr) { - if (GetFPRType() != eFPRTypeXSAVE) - return Error("target processor does not support MPX"); - ::memcpy(m_mpx_set.mpxr[reg_index - m_reg_info.first_mpxr].bytes, reg_value.GetBytes(), reg_value.GetByteSize()); if (!CopyMPXtoXSTATE(reg_index)) @@ -560,9 +573,6 @@ if (reg_index >= m_reg_info.first_mpxc && reg_index <= m_reg_info.last_mpxc) { - if (GetFPRType() != eFPRTypeXSAVE) - return Error("target processor does not support MPX"); - ::memcpy(m_mpx_set.mpxc[reg_index - m_reg_info.first_mpxc].bytes, reg_value.GetBytes(), reg_value.GetByteSize()); if (!CopyMPXtoXSTATE(reg_index)) @@ -654,31 +664,37 @@ ::memcpy(dst, &m_gpr_x86_64, GetRegisterInfoInterface().GetGPRSize()); dst += GetRegisterInfoInterface().GetGPRSize(); - if (GetFPRType() == eFPRTypeFXSAVE) + if (GetXStateType() == eXStateTypeFXSAVE) ::memcpy(dst, &m_fpr.xstate.fxsave, sizeof(m_fpr.xstate.fxsave)); - else if (GetFPRType() == eFPRTypeXSAVE) { + else if (GetXStateType() == eXStateTypeXSAVE) { lldb::ByteOrder byte_order = GetByteOrder(); - // Assemble the YMM register content from the register halves. - for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm; - ++reg) { - if (!CopyXSTATEtoYMM(reg, byte_order)) { - error.SetErrorStringWithFormat("NativeRegisterContextLinux_x86_64::%s " - "CopyXSTATEtoYMM() failed for reg num " - "%" PRIu32, - __FUNCTION__, reg); - return error; + if (IsCPUFeatureAvailable(avx)) { + // Assemble the YMM register content from the register halves. + for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm; + ++reg) { + if (!CopyXSTATEtoYMM(reg, byte_order)) { + error.SetErrorStringWithFormat( + "NativeRegisterContextLinux_x86_64::%s " + "CopyXSTATEtoYMM() failed for reg num " + "%" PRIu32, + __FUNCTION__, reg); + return error; + } } } - for (uint32_t reg = m_reg_info.first_mpxr; reg <= m_reg_info.last_mpxc; - ++reg) { - if (!CopyXSTATEtoMPX(reg)) { - error.SetErrorStringWithFormat("NativeRegisterContextLinux_x86_64::%s " - "CopyXSTATEtoMPX() failed for reg num " - "%" PRIu32, - __FUNCTION__, reg); - return error; + if (IsCPUFeatureAvailable(mpx)) { + for (uint32_t reg = m_reg_info.first_mpxr; reg <= m_reg_info.last_mpxc; + ++reg) { + if (!CopyXSTATEtoMPX(reg)) { + error.SetErrorStringWithFormat( + "NativeRegisterContextLinux_x86_64::%s " + "CopyXSTATEtoMPX() failed for reg num " + "%" PRIu32, + __FUNCTION__, reg); + return error; + } } } // Copy the extended register state including the assembled ymm registers. @@ -740,38 +756,44 @@ return error; src += GetRegisterInfoInterface().GetGPRSize(); - if (GetFPRType() == eFPRTypeFXSAVE) + if (GetXStateType() == eXStateTypeFXSAVE) ::memcpy(&m_fpr.xstate.fxsave, src, sizeof(m_fpr.xstate.fxsave)); - else if (GetFPRType() == eFPRTypeXSAVE) + else if (GetXStateType() == eXStateTypeXSAVE) ::memcpy(&m_fpr.xstate.xsave, src, sizeof(m_fpr.xstate.xsave)); error = WriteFPR(); if (error.Fail()) return error; - if (GetFPRType() == eFPRTypeXSAVE) { + if (GetXStateType() == eXStateTypeXSAVE) { lldb::ByteOrder byte_order = GetByteOrder(); - // Parse the YMM register content from the register halves. - for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm; - ++reg) { - if (!CopyYMMtoXSTATE(reg, byte_order)) { - error.SetErrorStringWithFormat("NativeRegisterContextLinux_x86_64::%s " - "CopyYMMtoXSTATE() failed for reg num " - "%" PRIu32, - __FUNCTION__, reg); - return error; + if (IsCPUFeatureAvailable(avx)) { + // Parse the YMM register content from the register halves. + for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm; + ++reg) { + if (!CopyYMMtoXSTATE(reg, byte_order)) { + error.SetErrorStringWithFormat( + "NativeRegisterContextLinux_x86_64::%s " + "CopyYMMtoXSTATE() failed for reg num " + "%" PRIu32, + __FUNCTION__, reg); + return error; + } } } - for (uint32_t reg = m_reg_info.first_mpxr; reg <= m_reg_info.last_mpxc; - ++reg) { - if (!CopyMPXtoXSTATE(reg)) { - error.SetErrorStringWithFormat("NativeRegisterContextLinux_x86_64::%s " - "CopyMPXtoXSTATE() failed for reg num " - "%" PRIu32, - __FUNCTION__, reg); - return error; + if (IsCPUFeatureAvailable(mpx)) { + for (uint32_t reg = m_reg_info.first_mpxr; reg <= m_reg_info.last_mpxc; + ++reg) { + if (!CopyMPXtoXSTATE(reg)) { + error.SetErrorStringWithFormat( + "NativeRegisterContextLinux_x86_64::%s " + "CopyMPXtoXSTATE() failed for reg num " + "%" PRIu32, + __FUNCTION__, reg); + return error; + } } } } @@ -779,16 +801,75 @@ return error; } +bool NativeRegisterContextLinux_x86_64::HasFXSAVE() const { + unsigned int rax, rbx, rcx, rdx; + + // Check if FXSAVE is enabled. + if (!__get_cpuid(1, &rax, &rbx, &rcx, &rdx)) + return false; + if ((rdx & bit_FXSAVE) == bit_FXSAVE) { + m_xstate_type = eXStateTypeFXSAVE; + if (const_cast(this)->ReadFPR().Fail()) + return false; + return true; + } + return false; +} + +bool NativeRegisterContextLinux_x86_64::HasXSAVE() const { + unsigned int rax, rbx, rcx, rdx; + + // Check if XSAVE is enabled. + if (!__get_cpuid(1, &rax, &rbx, &rcx, &rdx)) + return false; + if ((rcx & bit_OSXSAVE) == bit_OSXSAVE) { + m_xstate_type = eXStateTypeXSAVE; + if (const_cast(this)->ReadFPR().Fail()) + return false; + return true; + } + return false; +} + +bool NativeRegisterContextLinux_x86_64::IsCPUFeatureAvailable( + RegSet feature_code) const { + unsigned int rax, rbx, rcx, rdx; + + // Check if XSAVE is enabled. + if (!HasXSAVE()) + return false; + + __get_cpuid(1, &rax, &rbx, &rcx, &rdx); + switch (feature_code) { + case avx: // Check if CPU has AVX and if there is kernel support, by reading in the XCR0 area of XSAVE. + if (((rcx & bit_AVX) != 0) && ((m_fpr.xstate.xsave.i387.xcr0 & mask_XSTATE_AVX) == mask_XSTATE_AVX)) + return true; + case mpx: // Check if CPU has MPX and if there is kernel support, by reading in the XCR0 area of XSAVE. + if (__get_cpuid_max(0, NULL) > 7) { + __cpuid_count(7, 0, rax, rbx, rcx, rdx); + if (((rbx & bit_MPX) != 0) && ((m_fpr.xstate.xsave.i387.xcr0 & mask_XSTATE_MPX) == mask_XSTATE_MPX)) + return true; + } + default: + return false; + } +} + bool NativeRegisterContextLinux_x86_64::IsRegisterSetAvailable( uint32_t set_index) const { - // Note: Extended register sets are assumed to be at the end of g_reg_sets. uint32_t num_sets = k_num_register_sets - k_num_extended_register_sets; - if (GetFPRType() == eFPRTypeXSAVE) { - // AVX is the first extended register set. - num_sets += 2; + switch (set_index) { + case gpr: + case fpu: + return (set_index < num_sets); + case avx: + return IsCPUFeatureAvailable(avx); + case mpx: + return IsCPUFeatureAvailable(mpx); + default: + return false; } - return (set_index < num_sets); } bool NativeRegisterContextLinux_x86_64::IsGPR(uint32_t reg_index) const { @@ -796,32 +877,15 @@ return reg_index <= m_reg_info.last_gpr; } -NativeRegisterContextLinux_x86_64::FPRType -NativeRegisterContextLinux_x86_64::GetFPRType() const { - Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (m_fpr_type == eFPRTypeNotValid) { - // TODO: Use assembly to call cpuid on the inferior and query ebx or ecx. - - // Try and see if AVX register retrieval works. - m_fpr_type = eFPRTypeXSAVE; - if (const_cast(this) - ->ReadFPR() - .Fail()) { - // Fall back to general floating point with no AVX support. - m_fpr_type = eFPRTypeFXSAVE; - - // Check if FXSAVE area can be read. - if (const_cast(this) - ->ReadFPR() - .Fail()) { - if (log) - log->Printf("NativeRegisterContextLinux_x86_64::%s ptrace APIs " - "failed to read XSAVE/FXSAVE area", - __FUNCTION__); - } - } +NativeRegisterContextLinux_x86_64::XStateType +NativeRegisterContextLinux_x86_64::GetXStateType() const { + if (m_xstate_type == eXStateTypeNotValid) { + if (HasXSAVE()) + m_xstate_type = eXStateTypeXSAVE; + else if (HasFXSAVE()) + m_xstate_type = eXStateTypeFXSAVE; } - return m_fpr_type; + return m_xstate_type; } bool NativeRegisterContextLinux_x86_64::IsFPR(uint32_t reg_index) const { @@ -829,21 +893,12 @@ reg_index <= m_reg_info.last_fpr); } -bool NativeRegisterContextLinux_x86_64::IsFPR(uint32_t reg_index, - FPRType fpr_type) const { - bool generic_fpr = IsFPR(reg_index); - - if (fpr_type == eFPRTypeXSAVE) - return generic_fpr || IsAVX(reg_index) || IsMPX(reg_index); - return generic_fpr; -} - Error NativeRegisterContextLinux_x86_64::WriteFPR() { - const FPRType fpr_type = GetFPRType(); + const XStateType fpr_type = GetXStateType(); const lldb_private::ArchSpec &target_arch = GetRegisterInfoInterface().GetTargetArchitecture(); switch (fpr_type) { - case FPRType::eFPRTypeFXSAVE: + case XStateType::eXStateTypeFXSAVE: // For 32-bit inferiors on x86_32/x86_64 architectures, // FXSAVE area can be written using PTRACE_SETREGSET ptrace api // For 64-bit inferiors on x86_64 architectures, @@ -858,7 +913,7 @@ assert(false && "Unhandled target architecture."); break; } - case FPRType::eFPRTypeXSAVE: + case XStateType::eXStateTypeXSAVE: return WriteRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_X86_XSTATE); default: @@ -867,6 +922,8 @@ } bool NativeRegisterContextLinux_x86_64::IsAVX(uint32_t reg_index) const { + if (!IsCPUFeatureAvailable(avx)) + return false; return (m_reg_info.first_ymm <= reg_index && reg_index <= m_reg_info.last_ymm); } @@ -926,11 +983,11 @@ } void *NativeRegisterContextLinux_x86_64::GetFPRBuffer() { - const FPRType fpr_type = GetFPRType(); - switch (fpr_type) { - case FPRType::eFPRTypeFXSAVE: + const XStateType xstate_type = GetXStateType(); + switch (xstate_type) { + case XStateType::eXStateTypeFXSAVE: return &m_fpr.xstate.fxsave; - case FPRType::eFPRTypeXSAVE: + case XStateType::eXStateTypeXSAVE: return &m_iovec; default: return nullptr; @@ -938,11 +995,11 @@ } size_t NativeRegisterContextLinux_x86_64::GetFPRSize() { - const FPRType fpr_type = GetFPRType(); - switch (fpr_type) { - case FPRType::eFPRTypeFXSAVE: + const XStateType xstate_type = GetXStateType(); + switch (xstate_type) { + case XStateType::eXStateTypeFXSAVE: return sizeof(m_fpr.xstate.fxsave); - case FPRType::eFPRTypeXSAVE: + case XStateType::eXStateTypeXSAVE: return sizeof(m_iovec); default: return 0; @@ -950,11 +1007,11 @@ } Error NativeRegisterContextLinux_x86_64::ReadFPR() { - const FPRType fpr_type = GetFPRType(); + const XStateType xstate_type = GetXStateType(); const lldb_private::ArchSpec &target_arch = GetRegisterInfoInterface().GetTargetArchitecture(); - switch (fpr_type) { - case FPRType::eFPRTypeFXSAVE: + switch (xstate_type) { + case XStateType::eXStateTypeFXSAVE: // For 32-bit inferiors on x86_32/x86_64 architectures, // FXSAVE area can be read using PTRACE_GETREGSET ptrace api // For 64-bit inferiors on x86_64 architectures, @@ -968,7 +1025,7 @@ assert(false && "Unhandled target architecture."); break; } - case FPRType::eFPRTypeXSAVE: + case XStateType::eXStateTypeXSAVE: return ReadRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_X86_XSTATE); default: return Error("Unrecognized FPR type"); @@ -976,8 +1033,10 @@ } bool NativeRegisterContextLinux_x86_64::IsMPX(uint32_t reg_index) const { - return (m_reg_info.first_mpxr <= reg_index && - reg_index <= m_reg_info.last_mpxc); + if (!IsCPUFeatureAvailable(mpx)) + return false; + return (m_reg_info.first_mpxr <= reg_index && + reg_index <= m_reg_info.last_mpxc); } bool NativeRegisterContextLinux_x86_64::CopyXSTATEtoMPX(uint32_t reg) { Index: source/Plugins/Process/Utility/RegisterContext_x86.h =================================================================== --- source/Plugins/Process/Utility/RegisterContext_x86.h +++ source/Plugins/Process/Utility/RegisterContext_x86.h @@ -277,7 +277,9 @@ uint32_t mxcsrmask; // MXCSR Mask MMSReg stmm[8]; // 8*16 bytes for each FP-reg = 128 bytes XMMReg xmm[16]; // 16*16 bytes for each XMM-reg = 256 bytes - uint32_t padding[24]; + uint8_t padding1[48]; + uint64_t xcr0; + uint8_t padding2[40]; }; //---------------------------------------------------------------------------