Index: lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt =================================================================== --- lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt +++ lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt @@ -26,6 +26,7 @@ GDBRemoteCommunicationServerLLGS.cpp GDBRemoteCommunicationServerPlatform.cpp GDBRemoteRegisterContext.cpp + GDBRemoteRegisterContext_x86_64.cpp ProcessGDBRemote.cpp ProcessGDBRemoteLog.cpp ThreadGDBRemote.cpp Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h @@ -40,6 +40,10 @@ void HardcodeARMRegisters(bool from_scratch); bool UpdateARM64SVERegistersInfos(uint64_t vg); + void Finalize(const lldb_private::ArchSpec &arch); + + void PreFinalize_x86_64(); + void PostFinalize_x86_64(); }; class GDBRemoteRegisterContext : public RegisterContext { Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp @@ -1064,3 +1064,28 @@ } } } + +void GDBRemoteDynamicRegisterInfo::Finalize(const ArchSpec &arch) { + if (m_finalized) + return; + + switch (arch.GetMachine()) { + case llvm::Triple::x86_64: + PreFinalize_x86_64(); + break; + + default: + break; + } + + DynamicRegisterInfo::Finalize(arch); + + switch (arch.GetMachine()) { + case llvm::Triple::x86_64: + PostFinalize_x86_64(); + break; + + default: + break; + } +} Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext_x86_64.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext_x86_64.cpp @@ -0,0 +1,193 @@ +//===-- GDBRemoteRegisterContext.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 "GDBRemoteRegisterContext.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +enum PartialGPRRegKind { + eRegKind32, + eRegKind16, + eRegKind8h, + eRegKind8l, + + eRegKindCount +}; + +struct PartialGPRReg { + const char *name; + + // extra storage used for pointers + uint32_t value_regs[2]; + uint32_t invalidate_regs[eRegKindCount]; +}; + +struct PartialGPRRegSet { + const char *reg64; + PartialGPRReg r[eRegKindCount]; +}; + +static std::array partial_gpr_regs = {{ + {"rax", {{"eax"}, {"ax"}, {"ah"}, {"al"}}}, + {"rbx", {{"ebx"}, {"bx"}, {"bh"}, {"bl"}}}, + {"rcx", {{"ecx"}, {"cx"}, {"ch"}, {"cl"}}}, + {"rdx", {{"edx"}, {"dx"}, {"dh"}, {"dl"}}}, + {"rdi", {{"edi"}, {"di"}, {nullptr}, {"dil"}}}, + {"rsi", {{"esi"}, {"si"}, {nullptr}, {"sil"}}}, + {"rbp", {{"ebp"}, {"bp"}, {nullptr}, {"bpl"}}}, + {"rsp", {{"esp"}, {"sp"}, {nullptr}, {"spl"}}}, + {"r8", {{"r8d"}, {"r8w"}, {nullptr}, {"r8l"}}}, + {"r9", {{"r9d"}, {"r9w"}, {nullptr}, {"r9l"}}}, + {"r10", {{"r10d"}, {"r10w"}, {nullptr}, {"r10l"}}}, + {"r11", {{"r11d"}, {"r11w"}, {nullptr}, {"r11l"}}}, + {"r12", {{"r12d"}, {"r12w"}, {nullptr}, {"r12l"}}}, + {"r13", {{"r13d"}, {"r13w"}, {nullptr}, {"r13l"}}}, + {"r14", {{"r14d"}, {"r14w"}, {nullptr}, {"r14l"}}}, + {"r15", {{"r15d"}, {"r15w"}, {nullptr}, {"r15l"}}}, +}}; + +static uint32_t partial_gpr_sizes[eRegKindCount] = {4, 2, 1, 1}; + +struct MMReg { + const char *streg; + const char *name; + + // extra storage used for pointers + uint32_t value_regs[2]; +}; + +static std::array mm_regs = {{ + {"st0", "mm0"}, + {"st1", "mm1"}, + {"st2", "mm2"}, + {"st3", "mm3"}, + {"st4", "mm4"}, + {"st5", "mm5"}, + {"st6", "mm6"}, + {"st7", "mm7"}, +}}; + +void GDBRemoteDynamicRegisterInfo::PreFinalize_x86_64() { + uint32_t max_regnum = 0; + uint32_t next_regindex = m_regs.size(); + for (const RegisterInfo ® : m_regs) + max_regnum = std::max(max_regnum, reg.kinds[eRegisterKindProcessPlugin]); + + ConstString group; + group.SetCString("partial registers"); + + for (PartialGPRRegSet &partial_regset : partial_gpr_regs) { + const RegisterInfo *reg64 = GetRegisterInfo(partial_regset.reg64); + if (!reg64) + continue; + assert(reg64->byte_size == 8); + + const RegisterInfo *reginfo[eRegKindCount]; + uint32_t regno[eRegKindCount]; + + for (int i = 0; i < eRegKindCount; i++) { + if (!partial_regset.r[i].name) + continue; + + reginfo[i] = GetRegisterInfo(partial_regset.r[i].name); + if (reginfo[i]) + regno[i] = reginfo[i]->kinds[eRegisterKindProcessPlugin]; + else + regno[i] = ++max_regnum; + } + + for (int i = 0; i < eRegKindCount; i++) { + if (!partial_regset.r[i].name) + continue; + + partial_regset.r[i].value_regs[0] = + reg64->kinds[eRegisterKindProcessPlugin]; + partial_regset.r[i].value_regs[1] = LLDB_INVALID_REGNUM; + + int k = 0; + for (int j = 0; j < eRegKindCount; j++) { + if (i != j && partial_regset.r[j].name) + partial_regset.r[i].invalidate_regs[k++] = regno[j]; + } + partial_regset.r[i].invalidate_regs[k] = LLDB_INVALID_REGNUM; + + struct RegisterInfo new_reg { + partial_regset.r[i].name, nullptr, partial_gpr_sizes[i], + LLDB_INVALID_INDEX32, reg64->encoding, reg64->format, + { + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, regno[i], + next_regindex++, + }, + partial_regset.r[i].value_regs, partial_regset.r[i].invalidate_regs, + nullptr, 0 + }; + + ConstString name; + ConstString alt_name; + name.SetCString(new_reg.name); + AddRegister(new_reg, name, alt_name, group); + } + } + + for (MMReg &mmreg : mm_regs) { + const RegisterInfo *streg = GetRegisterInfo(mmreg.streg); + if (!streg) + continue; + assert(streg->byte_size == 10); + + if (GetRegisterInfo(mmreg.name)) + continue; + + mmreg.value_regs[0] = streg->kinds[eRegisterKindProcessPlugin]; + mmreg.value_regs[1] = LLDB_INVALID_REGNUM; + + struct RegisterInfo new_reg { + mmreg.name, nullptr, 8, LLDB_INVALID_INDEX32, eEncodingUint, eFormatHex, + { + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + ++max_regnum, next_regindex++, + }, + mmreg.value_regs, nullptr, nullptr, 0 + }; + + ConstString name; + ConstString alt_name; + name.SetCString(new_reg.name); + AddRegister(new_reg, name, alt_name, group); + } +} + +void GDBRemoteDynamicRegisterInfo::PostFinalize_x86_64() { + for (PartialGPRRegSet &partial_regset : partial_gpr_regs) { + if (!partial_regset.r[eRegKind8h].name) + continue; + + llvm::StringRef reg8h_name = partial_regset.r[eRegKind8h].name; + const RegisterInfo *reg8h = GetRegisterInfo(reg8h_name); + const RegisterInfo *reg8l = + GetRegisterInfo(partial_regset.r[eRegKind8l].name); + if (!reg8h || !reg8l) + continue; + assert(reg8h->byte_size == 1); + assert(reg8l->byte_size == 1); + + // we currently have no way of indicating offset via value_regs, so we + // instead increment it after Finalize() assigns byte offsets + if (reg8h->byte_offset == reg8l->byte_offset) { + for (auto ®_info : m_regs) { + if (reg_info.name == reg8h_name) { + reg_info.byte_offset++; + break; + } + } + } + } +} Index: lldb/test/API/functionalities/gdb_remote_client/TestGDBServerTargetXML.py =================================================================== --- lldb/test/API/functionalities/gdb_remote_client/TestGDBServerTargetXML.py +++ lldb/test/API/functionalities/gdb_remote_client/TestGDBServerTargetXML.py @@ -9,8 +9,32 @@ @skipIfXmlSupportMissing @skipIfRemote - def test_x86_64_vec_regs(self): - """Test rendering of x86_64 vector registers from gdbserver.""" + def test_x86_64_regs(self): + """Test grabbing various x86_64 registers from gdbserver.""" + reg_data = [ + "0102030405060708", # rcx + "1112131415161718", # rdx + "2122232425262728", # rsi + "3132333435363738", # rdi + "4142434445464748", # rbp + "5152535455565758", # rsp + "6162636465666768", # r8 + "7172737475767778", # r9 + "8182838485868788", # rip + "91929394", # eflags + "0102030405060708090a", # st0 + "1112131415161718191a", # st1 + ] + 6 * [ + "2122232425262728292a" # st2..st7 + ] + [ + "8182838485868788898a8b8c8d8e8f90", # xmm0 + "9192939495969798999a9b9c9d9e9fa0", # xmm1 + ] + 14 * [ + "a1a2a3a4a5a6a7a8a9aaabacadaeafb0", # xmm2..xmm15 + ] + [ + "00000000", # mxcsr + ] + class MyResponder(MockGDBServerResponder): def qXferRead(self, obj, annex, offset, length): if annex == "target.xml": @@ -66,25 +90,10 @@ return "" def readRegisters(self): - return ( - "0102030405060708" # rcx - "1112131415161718" # rdx - "2122232425262728" # rsi - "3132333435363738" # rdi - "4142434445464748" # rbp - "5152535455565758" # rsp - "6162636465666768" # r8 - "7172737475767778" # r9 - "8182838485868788" # rip - "91929394" # eflags - "0102030405060708090a" # st0 - "1112131415161718191a" + # st1 - "2122232425262728292a" * 6 + # st2..st7 - "8182838485868788898a8b8c8d8e8f90" # xmm0 - "9192939495969798999a9b9c9d9e9fa0" # xmm1 - "a1a2a3a4a5a6a7a8a9aaabacadaeafb0" * 14 + # xmm2..xmm15 - "00000000" # mxcsr - ) + return "".join(reg_data) + + def writeRegisters(self, reg_hex): + return "OK" def haltReason(self): return "T02thread:1ff0d;threads:1ff0d;thread-pcs:000000010001bc00;07:0102030405060708;10:1112131415161718;" @@ -113,8 +122,6 @@ ["rdi = 0x3837363534333231"]) self.match("register read fp", ["rbp = 0x4847464544434241"]) - self.match("register read sp", - ["rsp = 0x5857565554535251"]) self.match("register read arg5", ["r8 = 0x6867666564636261"]) self.match("register read arg6", @@ -124,6 +131,26 @@ self.match("register read flags", ["eflags = 0x94939291"]) + # test pseudo-registers + self.match("register read ecx", + ["ecx = 0x04030201"]) + self.match("register read cx", + ["cx = 0x0201"]) + self.match("register read ch", + ["ch = 0x02"]) + self.match("register read cl", + ["cl = 0x01"]) + self.match("register read r8d", + ["r8d = 0x64636261"]) + self.match("register read r8w", + ["r8w = 0x6261"]) + self.match("register read r8l", + ["r8l = 0x61"]) + self.match("register read mm0", + ["mm0 = 0x0807060504030201"]) + self.match("register read mm1", + ["mm1 = 0x1817161514131211"]) + # both stX and xmmX should be displayed as vectors self.match("register read st0", ["st0 = {0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a}"]) @@ -135,3 +162,44 @@ self.match("register read xmm1", ["xmm1 = {0x91 0x92 0x93 0x94 0x95 0x96 0x97 0x98 " "0x99 0x9a 0x9b 0x9c 0x9d 0x9e 0x9f 0xa0}"]) + + # test writing into pseudo-registers + self.runCmd("register write ecx 0xfffefdfc") + reg_data[0] = "fcfdfeff05060708" + self.assertPacketLogContains(["G" + "".join(reg_data)]) + self.match("register read rcx", + ["rcx = 0x08070605fffefdfc"]) + + self.runCmd("register write cx 0xfbfa") + reg_data[0] = "fafbfeff05060708" + self.assertPacketLogContains(["G" + "".join(reg_data)]) + self.match("register read ecx", + ["ecx = 0xfffefbfa"]) + self.match("register read rcx", + ["rcx = 0x08070605fffefbfa"]) + + self.runCmd("register write ch 0xf9") + reg_data[0] = "faf9feff05060708" + self.assertPacketLogContains(["G" + "".join(reg_data)]) + self.match("register read cx", + ["cx = 0xf9fa"]) + self.match("register read ecx", + ["ecx = 0xfffef9fa"]) + self.match("register read rcx", + ["rcx = 0x08070605fffef9fa"]) + + self.runCmd("register write cl 0xf8") + reg_data[0] = "f8f9feff05060708" + self.match("register read cx", + ["cx = 0xf9f8"]) + self.match("register read ecx", + ["ecx = 0xfffef9f8"]) + self.match("register read rcx", + ["rcx = 0x08070605fffef9f8"]) + + self.assertPacketLogContains(["G" + "".join(reg_data)]) + self.runCmd("register write mm0 0xfffefdfcfbfaf9f8") + reg_data[10] = "f8f9fafbfcfdfeff090a" + self.assertPacketLogContains(["G" + "".join(reg_data)]) + self.match("register read st0", + ["st0 = {0xf8 0xf9 0xfa 0xfb 0xfc 0xfd 0xfe 0xff 0x09 0x0a}"])