Support nested target.xml register definition files, lack of reg group markers.

The qemu x86_64 target returns a target.xml register definition file which
includes other xml files and they include others, etc.  Also, the registers
are not put in register groups like lldb wants to see.

This patch (1) puts registers that aren't in a register group in a "general"
register group, (2) change ProcessGDBRemote::GetGDBServerRegisterInfo to
be a method that starts the parsing, asking a recurisve function to fetch
and parse target.xml, (3) adds 
ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess which can recusively
call itself to read and parse included xml files, (4) in addition to expecting
the top-level <target> element (which only happens in the top level xml file),
also an xml file that consists of a <feature> node - read the register 
defintions and includes from that <feature> element.  

<rdar://problem/49537922> 
Differential revision: https://reviews.llvm.org/D63802

llvm-svn: 364484
This commit is contained in:
Jason Molenda 2019-06-26 21:59:39 +00:00
parent e311a4d5c4
commit 1659276bee
3 changed files with 326 additions and 60 deletions

View File

@ -0,0 +1,238 @@
from __future__ import print_function
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
from gdbclientutils import *
class TestNestedRegDefinitions(GDBRemoteTestBase):
@skipIfXmlSupportMissing
@skipIfRemote
def test(self):
"""
Test lldb's parsing of the <architecture> tag in the target.xml register
description packet.
"""
class MyResponder(MockGDBServerResponder):
def qXferRead(self, obj, annex, offset, length):
if annex == "target.xml":
return """<?xml version="1.0"?><!DOCTYPE target SYSTEM "gdb-target.dtd"><target><architecture>i386:x86-64</architecture><xi:include href="i386-64bit.xml"/></target>""", False
if annex == "i386-64bit.xml":
return """<?xml version="1.0"?>
<!-- Copyright (C) 2010-2017 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!-- I386 64bit -->
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.64bit">
<xi:include href="i386-64bit-core.xml"/>
<xi:include href="i386-64bit-sse.xml"/>
</feature>""", False
if annex == "i386-64bit-core.xml":
return """<?xml version="1.0"?>
<!-- Copyright (C) 2010-2015 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.core">
<flags id="i386_eflags" size="4">
<field name="CF" start="0" end="0"/>
<field name="" start="1" end="1"/>
<field name="PF" start="2" end="2"/>
<field name="AF" start="4" end="4"/>
<field name="ZF" start="6" end="6"/>
<field name="SF" start="7" end="7"/>
<field name="TF" start="8" end="8"/>
<field name="IF" start="9" end="9"/>
<field name="DF" start="10" end="10"/>
<field name="OF" start="11" end="11"/>
<field name="NT" start="14" end="14"/>
<field name="RF" start="16" end="16"/>
<field name="VM" start="17" end="17"/>
<field name="AC" start="18" end="18"/>
<field name="VIF" start="19" end="19"/>
<field name="VIP" start="20" end="20"/>
<field name="ID" start="21" end="21"/>
</flags>
<reg name="rax" bitsize="64" type="int64"/>
<reg name="rbx" bitsize="64" type="int64"/>
<reg name="rcx" bitsize="64" type="int64"/>
<reg name="rdx" bitsize="64" type="int64"/>
<reg name="rsi" bitsize="64" type="int64"/>
<reg name="rdi" bitsize="64" type="int64"/>
<reg name="rbp" bitsize="64" type="data_ptr"/>
<reg name="rsp" bitsize="64" type="data_ptr"/>
<reg name="r8" bitsize="64" type="int64"/>
<reg name="r9" bitsize="64" type="int64"/>
<reg name="r10" bitsize="64" type="int64"/>
<reg name="r11" bitsize="64" type="int64"/>
<reg name="r12" bitsize="64" type="int64"/>
<reg name="r13" bitsize="64" type="int64"/>
<reg name="r14" bitsize="64" type="int64"/>
<reg name="r15" bitsize="64" type="int64"/>
<reg name="rip" bitsize="64" type="code_ptr"/>
<reg name="eflags" bitsize="32" type="i386_eflags"/>
<reg name="cs" bitsize="32" type="int32"/>
<reg name="ss" bitsize="32" type="int32"/>
<reg name="ds" bitsize="32" type="int32"/>
<reg name="es" bitsize="32" type="int32"/>
<reg name="fs" bitsize="32" type="int32"/>
<reg name="gs" bitsize="32" type="int32"/>
<reg name="st0" bitsize="80" type="i387_ext"/>
<reg name="st1" bitsize="80" type="i387_ext"/>
<reg name="st2" bitsize="80" type="i387_ext"/>
<reg name="st3" bitsize="80" type="i387_ext"/>
<reg name="st4" bitsize="80" type="i387_ext"/>
<reg name="st5" bitsize="80" type="i387_ext"/>
<reg name="st6" bitsize="80" type="i387_ext"/>
<reg name="st7" bitsize="80" type="i387_ext"/>
<reg name="fctrl" bitsize="32" type="int" group="float"/>
<reg name="fstat" bitsize="32" type="int" group="float"/>
<reg name="ftag" bitsize="32" type="int" group="float"/>
<reg name="fiseg" bitsize="32" type="int" group="float"/>
<reg name="fioff" bitsize="32" type="int" group="float"/>
<reg name="foseg" bitsize="32" type="int" group="float"/>
<reg name="fooff" bitsize="32" type="int" group="float"/>
<reg name="fop" bitsize="32" type="int" group="float"/>
</feature>""", False
if annex == "i386-64bit-sse.xml":
return """<?xml version="1.0"?>
<!-- Copyright (C) 2010-2017 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.64bit.sse">
<vector id="v4f" type="ieee_single" count="4"/>
<vector id="v2d" type="ieee_double" count="2"/>
<vector id="v16i8" type="int8" count="16"/>
<vector id="v8i16" type="int16" count="8"/>
<vector id="v4i32" type="int32" count="4"/>
<vector id="v2i64" type="int64" count="2"/>
<union id="vec128">
<field name="v4_float" type="v4f"/>
<field name="v2_double" type="v2d"/>
<field name="v16_int8" type="v16i8"/>
<field name="v8_int16" type="v8i16"/>
<field name="v4_int32" type="v4i32"/>
<field name="v2_int64" type="v2i64"/>
<field name="uint128" type="uint128"/>
</union>
<flags id="i386_mxcsr" size="4">
<field name="IE" start="0" end="0"/>
<field name="DE" start="1" end="1"/>
<field name="ZE" start="2" end="2"/>
<field name="OE" start="3" end="3"/>
<field name="UE" start="4" end="4"/>
<field name="PE" start="5" end="5"/>
<field name="DAZ" start="6" end="6"/>
<field name="IM" start="7" end="7"/>
<field name="DM" start="8" end="8"/>
<field name="ZM" start="9" end="9"/>
<field name="OM" start="10" end="10"/>
<field name="UM" start="11" end="11"/>
<field name="PM" start="12" end="12"/>
<field name="FZ" start="15" end="15"/>
</flags>
<reg name="xmm0" bitsize="128" type="vec128" regnum="40"/>
<reg name="xmm1" bitsize="128" type="vec128"/>
<reg name="xmm2" bitsize="128" type="vec128"/>
<reg name="xmm3" bitsize="128" type="vec128"/>
<reg name="xmm4" bitsize="128" type="vec128"/>
<reg name="xmm5" bitsize="128" type="vec128"/>
<reg name="xmm6" bitsize="128" type="vec128"/>
<reg name="xmm7" bitsize="128" type="vec128"/>
<reg name="xmm8" bitsize="128" type="vec128"/>
<reg name="xmm9" bitsize="128" type="vec128"/>
<reg name="xmm10" bitsize="128" type="vec128"/>
<reg name="xmm11" bitsize="128" type="vec128"/>
<reg name="xmm12" bitsize="128" type="vec128"/>
<reg name="xmm13" bitsize="128" type="vec128"/>
<reg name="xmm14" bitsize="128" type="vec128"/>
<reg name="xmm15" bitsize="128" type="vec128"/>
<reg name="mxcsr" bitsize="32" type="i386_mxcsr" group="vector"/>
</feature>""", False
return None, False
def readRegister(self, regnum):
return ""
def readRegisters(self):
return "0600000000000000c0b7c00080fffffff021c60080ffffff1a00000000000000020000000000000078b7c00080ffffff203f8ca090ffffff103f8ca090ffffff3025990a80ffffff809698000000000070009f0a80ffffff020000000000000000eae10080ffffff00000000000000001822d74f1a00000078b7c00080ffffff0e12410080ffff004602000008000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000801f0000"
def haltReason(self):
return "T02thread:dead;threads:dead;"
def qfThreadInfo(self):
return "mdead"
def qC(self):
return ""
def qSupported(self, client_supported):
return "PacketSize=4000;qXfer:features:read+"
def QThreadSuffixSupported(self):
return "OK"
def QListThreadsInStopReply(self):
return "OK"
self.server.responder = MyResponder()
if self.TraceOn():
self.runCmd("log enable gdb-remote packets")
self.addTearDownHook(
lambda: self.runCmd("log disable gdb-remote packets"))
target = self.dbg.CreateTargetWithFileAndArch(None, None)
process = self.connect(target)
if self.TraceOn():
interp = self.dbg.GetCommandInterpreter()
result = lldb.SBCommandReturnObject()
interp.HandleCommand("target list", result)
print(result.GetOutput())
rip_valobj = process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("rip")
self.assertEqual(rip_valobj.GetValueAsUnsigned(), 0x00ffff800041120e)
r15_valobj = process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("r15")
self.assertEqual(r15_valobj.GetValueAsUnsigned(), 0xffffff8000c0b778)
mxcsr_valobj = process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("mxcsr")
self.assertEqual(mxcsr_valobj.GetValueAsUnsigned(), 0x00001f80)
gpr_reg_set_name = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters().GetValueAtIndex(0).GetName()
self.assertEqual(gpr_reg_set_name, "general")
float_reg_set_name = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters().GetValueAtIndex(1).GetName()
self.assertEqual(float_reg_set_name, "float")
vector_reg_set_name = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters().GetValueAtIndex(2).GetName()
self.assertEqual(vector_reg_set_name, "vector")
if self.TraceOn():
print("rip is 0x%x" % rip_valobj.GetValueAsUnsigned())
print("r15 is 0x%x" % r15_valobj.GetValueAsUnsigned())
print("mxcsr is 0x%x" % mxcsr_valobj.GetValueAsUnsigned())

View File

@ -4490,8 +4490,15 @@ bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info,
// Only update the register set name if we didn't get a "reg_set" // Only update the register set name if we didn't get a "reg_set"
// attribute. "set_name" will be empty if we didn't have a "reg_set" // attribute. "set_name" will be empty if we didn't have a "reg_set"
// attribute. // attribute.
if (!set_name && !gdb_group.empty()) if (!set_name) {
if (!gdb_group.empty()) {
set_name.SetCString(gdb_group.c_str()); set_name.SetCString(gdb_group.c_str());
} else {
// If no register group name provided anywhere,
// we'll create a 'general' register set
set_name.SetCString("general");
}
}
reg_info.byte_offset = reg_offset; reg_info.byte_offset = reg_offset;
assert(reg_info.byte_size != 0); assert(reg_info.byte_size != 0);
@ -4516,38 +4523,33 @@ bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info,
} // namespace } // namespace
// query the target of gdb-remote for extended target information return: // This method fetches a register description feature xml file from
// 'true' on success // the remote stub and adds registers/register groupsets/architecture
// 'false' on failure // information to the current process. It will call itself recursively
bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { // for nested register definition files. It returns true if it was able
// Make sure LLDB has an XML parser it can use first // to fetch and parse an xml file.
if (!XMLDocument::XMLEnabled()) bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess(ArchSpec &arch_to_use,
return false; std::string xml_filename,
uint32_t &cur_reg_num,
// redirect libxml2's error handler since the default prints to stdout uint32_t &reg_offset) {
GDBRemoteCommunicationClient &comm = m_gdb_comm;
// check that we have extended feature read support
if (!comm.GetQXferFeaturesReadSupported())
return false;
// request the target xml file // request the target xml file
std::string raw; std::string raw;
lldb_private::Status lldberr; lldb_private::Status lldberr;
if (!comm.ReadExtFeature(ConstString("features"), ConstString("target.xml"), if (!m_gdb_comm.ReadExtFeature(ConstString("features"),
ConstString(xml_filename.c_str()),
raw, lldberr)) { raw, lldberr)) {
return false; return false;
} }
XMLDocument xml_document; XMLDocument xml_document;
if (xml_document.ParseMemory(raw.c_str(), raw.size(), "target.xml")) { if (xml_document.ParseMemory(raw.c_str(), raw.size(), xml_filename.c_str())) {
GdbServerTargetInfo target_info; GdbServerTargetInfo target_info;
std::vector<XMLNode> feature_nodes;
// The top level feature XML file will start with a <target> tag.
XMLNode target_node = xml_document.GetRootElement("target"); XMLNode target_node = xml_document.GetRootElement("target");
if (target_node) { if (target_node) {
std::vector<XMLNode> feature_nodes;
target_node.ForEachChildElement([&target_info, &feature_nodes]( target_node.ForEachChildElement([&target_info, &feature_nodes](
const XMLNode &node) -> bool { const XMLNode &node) -> bool {
llvm::StringRef name = node.GetName(); llvm::StringRef name = node.GetName();
@ -4585,6 +4587,26 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) {
} }
return true; // Keep iterating through all children of the target_node return true; // Keep iterating through all children of the target_node
}); });
} else {
// In an included XML feature file, we're already "inside" the <target>
// tag of the initial XML file; this included file will likely only have
// a <feature> tag. Need to check for any more included files in this
// <feature> element.
XMLNode feature_node = xml_document.GetRootElement("feature");
if (feature_node) {
feature_nodes.push_back(feature_node);
feature_node.ForEachChildElement([&target_info](
const XMLNode &node) -> bool {
llvm::StringRef name = node.GetName();
if (name == "xi:include" || name == "include") {
llvm::StringRef href = node.GetAttributeValue("href");
if (!href.empty())
target_info.includes.push_back(href.str());
}
return true;
});
}
}
// If the target.xml includes an architecture entry like // If the target.xml includes an architecture entry like
// <architecture>i386:x86-64</architecture> (seen from VMWare ESXi) // <architecture>i386:x86-64</architecture> (seen from VMWare ESXi)
@ -4606,11 +4628,7 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) {
} }
} }
// Initialize these outside of ParseRegisters, since they should not be if (arch_to_use.IsValid()) {
// reset inside each include feature
uint32_t cur_reg_num = 0;
uint32_t reg_offset = 0;
// Don't use Process::GetABI, this code gets called from DidAttach, and // Don't use Process::GetABI, this code gets called from DidAttach, and
// in that context we haven't set the Target's architecture yet, so the // in that context we haven't set the Target's architecture yet, so the
// ABI is also potentially incorrect. // ABI is also potentially incorrect.
@ -4621,26 +4639,31 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) {
} }
for (const auto &include : target_info.includes) { for (const auto &include : target_info.includes) {
// request register file GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, include,
std::string xml_data; cur_reg_num, reg_offset);
if (!comm.ReadExtFeature(ConstString("features"), ConstString(include), }
xml_data, lldberr)) }
continue; } else {
return false;
}
return true;
}
XMLDocument include_xml_document; // query the target of gdb-remote for extended target information returns
include_xml_document.ParseMemory(xml_data.data(), xml_data.size(), // true on success (got register definitions), false on failure (did not).
include.c_str()); bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) {
XMLNode include_feature_node = // Make sure LLDB has an XML parser it can use first
include_xml_document.GetRootElement("feature"); if (!XMLDocument::XMLEnabled())
if (include_feature_node) { return false;
ParseRegisters(include_feature_node, target_info,
this->m_register_info, abi_to_use_sp, cur_reg_num, // check that we have extended feature read support
reg_offset); if (!m_gdb_comm.GetQXferFeaturesReadSupported())
} return false;
}
uint32_t cur_reg_num = 0;
uint32_t reg_offset = 0;
if (GetGDBServerRegisterInfoXMLAndProcess (arch_to_use, "target.xml", cur_reg_num, reg_offset))
this->m_register_info.Finalize(arch_to_use); this->m_register_info.Finalize(arch_to_use);
}
}
return m_register_info.GetNumRegisters() > 0; return m_register_info.GetNumRegisters() > 0;
} }

View File

@ -383,6 +383,11 @@ protected:
DynamicLoader *GetDynamicLoader() override; DynamicLoader *GetDynamicLoader() override;
bool GetGDBServerRegisterInfoXMLAndProcess(ArchSpec &arch_to_use,
std::string xml_filename,
uint32_t &cur_reg_num,
uint32_t &reg_offset);
// Query remote GDBServer for register information // Query remote GDBServer for register information
bool GetGDBServerRegisterInfo(ArchSpec &arch); bool GetGDBServerRegisterInfo(ArchSpec &arch);