Index: cmake/modules/LLDBConfig.cmake =================================================================== --- cmake/modules/LLDBConfig.cmake +++ cmake/modules/LLDBConfig.cmake @@ -171,6 +171,9 @@ PATTERN ".svn" EXCLUDE ) +if (NOT LIBXML2_FOUND) + find_package(LibXml2) +endif() # Find libraries or frameworks that may be needed if (CMAKE_SYSTEM_NAME MATCHES "Darwin") @@ -181,13 +184,20 @@ find_library(SECURITY_LIBRARY Security) find_library(DEBUG_SYMBOLS_LIBRARY DebugSymbols PATHS "/System/Library/PrivateFrameworks") - if (NOT LIBXML2_FOUND) - find_package(LibXml2) - endif () + add_definitions( -DLIBXML2_DEFINED ) list(APPEND system_libs xml2 ncurses panel) list(APPEND system_libs ${CARBON_LIBRARY} ${FOUNDATION_LIBRARY} ${CORE_FOUNDATION_LIBRARY} ${CORE_SERVICES_LIBRARY} ${SECURITY_LIBRARY} ${DEBUG_SYMBOLS_LIBRARY}) + +else() + + if (LIBXML2_FOUND) + add_definitions( -DLIBXML2_DEFINED ) + list(APPEND system_libs ${LIBXML2_LIBRARIES}) + include_directories(${LIBXML2_INCLUDE_DIR}) + endif() + endif() if(LLDB_REQUIRES_EH) Index: source/Commands/CommandObjectRegister.cpp =================================================================== --- source/Commands/CommandObjectRegister.cpp +++ source/Commands/CommandObjectRegister.cpp @@ -144,7 +144,7 @@ const RegisterSet * const reg_set = reg_ctx->GetRegisterSet(set_idx); if (reg_set) { - strm.Printf ("%s:\n", reg_set->name); + strm.Printf ("%s:\n", (reg_set->name ? reg_set->name : "unknown") ); strm.IndentMore (); const size_t num_registers = reg_set->num_registers; for (size_t reg_idx = 0; reg_idx < num_registers; ++reg_idx) Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -426,6 +426,9 @@ bool GetAugmentedLibrariesSVR4ReadSupported (); + bool + GetQXferFeaturesReadSupported (); + LazyBool SupportsAllocDeallocMemory () // const { @@ -533,6 +536,12 @@ const ArchSpec& arch_spec, ModuleSpec &module_spec); + bool + ReadExtFeature (const lldb_private::ConstString object, + const lldb_private::ConstString annex, + std::string & out, + lldb_private::Error & err); + protected: PacketResult @@ -576,6 +585,7 @@ LazyBool m_supports_qXfer_auxv_read; LazyBool m_supports_qXfer_libraries_read; LazyBool m_supports_qXfer_libraries_svr4_read; + LazyBool m_supports_qXfer_features_read; LazyBool m_supports_augmented_libraries_svr4_read; LazyBool m_supports_jThreadExtendedInfo; Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -79,6 +79,7 @@ m_supports_QSaveRegisterState (eLazyBoolCalculate), m_supports_qXfer_auxv_read (eLazyBoolCalculate), m_supports_qXfer_libraries_read (eLazyBoolCalculate), + m_supports_qXfer_features_read (eLazyBoolCalculate), m_supports_qXfer_libraries_svr4_read (eLazyBoolCalculate), m_supports_augmented_libraries_svr4_read (eLazyBoolCalculate), m_supports_jThreadExtendedInfo (eLazyBoolCalculate), @@ -214,6 +215,16 @@ return (m_supports_qXfer_auxv_read == eLazyBoolYes); } +bool +GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported () +{ + if (m_supports_qXfer_features_read == eLazyBoolCalculate) + { + GetRemoteQSupported(); + } + return (m_supports_qXfer_features_read == eLazyBoolYes); +} + uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() { @@ -335,6 +346,7 @@ m_supports_qXfer_auxv_read = eLazyBoolCalculate; m_supports_qXfer_libraries_read = eLazyBoolCalculate; m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; + m_supports_qXfer_features_read = eLazyBoolCalculate; m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; m_supports_qProcessInfoPID = true; @@ -365,6 +377,22 @@ m_max_packet_size = 0; } +namespace { +void +buildQSupportedPacket( const std::vector & features, + std::string & out ) { + + std::stringstream packet; + packet << "qSupported"; + + for ( uint32_t i = 0; i < features.size( ); i++ ) + packet << (i==0 ? ":" : ";") << features[i]; + + out = packet.str( ); +} + +} // namespace {} + void GDBRemoteCommunicationClient::GetRemoteQSupported () { @@ -373,10 +401,14 @@ m_supports_qXfer_libraries_read = eLazyBoolNo; m_supports_qXfer_libraries_svr4_read = eLazyBoolNo; m_supports_augmented_libraries_svr4_read = eLazyBoolNo; + m_supports_qXfer_features_read = eLazyBoolNo; m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if not, we assume no limit + std::string packet; + buildQSupportedPacket( {"xmlRegisters=i386,arm,mips"}, packet ); + StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("qSupported", + if (SendPacketAndWaitForResponse(packet.c_str(), response, /*send_async=*/false) == PacketResult::Success) { @@ -392,6 +424,8 @@ } if (::strstr (response_cstr, "qXfer:libraries:read+")) m_supports_qXfer_libraries_read = eLazyBoolYes; + if (::strstr (response_cstr, "qXfer:features:read+")) + m_supports_qXfer_features_read = eLazyBoolYes; const char *packet_size_str = ::strstr (response_cstr, "PacketSize="); if (packet_size_str) @@ -3776,3 +3810,74 @@ return true; } + +// query the target remote for extended information using the qXfer packet +// +// example: object='features', annex='target.xml', out= +// return: 'true' on success +// 'false' on failure (err set) +bool +GDBRemoteCommunicationClient::ReadExtFeature (const lldb_private::ConstString object, + const lldb_private::ConstString annex, + std::string & out, + lldb_private::Error & err) { + + std::stringstream output; + StringExtractorGDBRemote chunk; + + const int size = 0xfff; + int offset = 0; + bool active = true; + + // loop until all data has been read + while ( active ) { + + // send query extended feature packet + std::stringstream packet; + packet << "qXfer:" + << object.AsCString( ) << ":read:" + << annex.AsCString( ) << ":" + << std::hex << offset << "," + << std::hex << size; + + GDBRemoteCommunication::PacketResult res = + SendPacketAndWaitForResponse( packet.str().c_str(), + chunk, + false ); + + if ( res != GDBRemoteCommunication::PacketResult::Success ) { + err.SetErrorString( "Error sending $qXfer packet" ); + return false; + } + + const std::string & str = chunk.GetStringRef( ); + if ( str.length() == 0 ) { + // should have some data in chunk + err.SetErrorString( "Empty response from $qXfer packet" ); + return false; + } + + // check packet code + switch ( str[0] ) { + // last chunk + case ( 'l' ): + active = false; + // fall through intensional + + // more chunks + case ( 'm' ) : + if ( str.length() > 1 ) + output << &str[1]; + break; + + // unknown chunk + default: + err.SetErrorString( "Invalid continuation code from $qXfer packet" ); + return false; + } + } + + out = output.str( ); + err.Success( ); + return true; +} Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.h =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -241,6 +241,10 @@ const ArchSpec& arch, ModuleSpec &module_spec) override; + // query remote gdbserver for information + bool + GetGDBServerInfo ( ); + protected: friend class ThreadGDBRemote; friend class GDBRemoteCommunicationClient; Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -27,6 +27,9 @@ #include // Other libraries and framework includes +#if defined( LIBXML2_DEFINED ) +#include +#endif #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Interpreter/Args.h" @@ -104,13 +107,15 @@ { { "packet-timeout" , OptionValue::eTypeUInt64 , true , 1, NULL, NULL, "Specify the default packet timeout in seconds." }, { "target-definition-file" , OptionValue::eTypeFileSpec , true, 0 , NULL, NULL, "The file that provides the description for remote target registers." }, + { "gdbserver-compatibility", OptionValue::eTypeBoolean , true , 1, NULL, NULL, "Enable gdbserver compatibility." }, { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } }; enum { ePropertyPacketTimeout, - ePropertyTargetDefinitionFile + ePropertyTargetDefinitionFile, + ePropertyGdbServerCompatibility, }; class PluginProperties : public Properties @@ -155,6 +160,13 @@ const uint32_t idx = ePropertyTargetDefinitionFile; return m_collection_sp->GetPropertyAtIndexAsFileSpec (NULL, idx); } + + bool + GetGDBServerCompatibility() const + { + const uint32_t idx = ePropertyGdbServerCompatibility; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, false); + } }; typedef std::shared_ptr ProcessKDPPropertiesSP; @@ -385,6 +397,12 @@ if (!force && m_register_info.GetNumRegisters() > 0) return; + // if we are connecting with gdbserver compatibility mode enabled then try to grab as much info as possible + if ( GetGlobalPluginProperties().get()->GetGDBServerCompatibility() ) { + if ( GetGDBServerInfo( ) ) + return; + } + char packet[128]; m_register_info.Clear(); uint32_t reg_offset = 0; @@ -1041,6 +1059,7 @@ error.SetErrorString("not connected to remote gdb server"); return error; } + m_gdb_comm.GetThreadSuffixSupported (); m_gdb_comm.GetListThreadsInStopReplySupported (); m_gdb_comm.GetHostInfo (); @@ -1658,7 +1677,7 @@ if (pid != LLDB_INVALID_PROCESS_ID) SetID (pid); } - BuildDynamicRegisterInfo (true); + BuildDynamicRegisterInfo (false); } // Stop with signal and thread info const uint8_t signo = stop_packet.GetHexU8(); @@ -3425,6 +3444,447 @@ return true; } +#if defined( LIBXML2_DEFINED ) +namespace { + +typedef std::vector stringVec; +typedef std::vector xmlNodePtrVec; + +struct GdbServerRegisterInfo { + + struct { + bool m_has_name : 1; + bool m_has_bitSize : 1; + bool m_has_type : 1; + bool m_has_group : 1; + bool m_has_regNum : 1; + } + m_flags; + + std::string m_name; + std::string m_group; + uint32_t m_bitSize; + uint32_t m_regNum; + + enum RegType { + e_unknown , + e_codePtr , + e_dataPtr , + e_int32 , + e_i387_ext , + } + m_type; + + void clear( ) { + memset( &m_flags, 0, sizeof( m_flags ) ); + } +}; + +typedef std::vector GDBServerRegisterVec; + +struct GdbServerTargetInfo { + + std::string m_arch; + std::string m_osabi; +}; + +// conversion table between gdb register type and enum +struct { + const char * m_name; + GdbServerRegisterInfo::RegType m_type; +} +RegTypeTable[] = { + { "int32" , GdbServerRegisterInfo::e_int32 }, + { "int" , GdbServerRegisterInfo::e_int32 }, + { "data_ptr", GdbServerRegisterInfo::e_dataPtr }, + { "code_ptr", GdbServerRegisterInfo::e_codePtr }, + { "i387_ext", GdbServerRegisterInfo::e_i387_ext }, // 80bit fpu + { nullptr } // sentinel +}; + +// find the first sibling with a matching name +xmlNodePtr +xmlExFindSibling (xmlNodePtr node, + const std::string & name) { + + if ( !node ) return nullptr; + // iterate through all siblings + for ( xmlNodePtr temp = node; temp; temp=temp->next ) { + // we are looking for elements + if ( temp->type != XML_ELEMENT_NODE ) + continue; + // check element name matches + if ( !temp->name ) continue; + if ( std::strcmp((const char*)temp->name, name.c_str() ) == 0 ) + return temp; + } + // no sibling found + return nullptr; +} + +// find an element from a given element path +xmlNodePtr +xmlExFindElement (xmlNodePtr node, + const stringVec & path) { + + if ( !node ) return nullptr; + xmlNodePtr temp = node; + // iterate all elements in path + for ( uint32_t i = 0; i < path.size( ); i++ ) { + + // search for a sibling with this name + temp = xmlExFindSibling( temp, path[i] ); + if ( !temp ) + return nullptr; + // enter this node if we still need to search + if ( (i+1) < path.size() ) + // enter the node we have found + temp = temp->children; + } + // note: node may still be nullptr at this step + return temp; +} + +// locate a specific attribute in an element +xmlAttr * +xmlExFindAttribute (xmlNodePtr node, + const std::string & name) { + + if ( !node ) + return nullptr; + if ( node->type != XML_ELEMENT_NODE ) + return nullptr; + // iterate over all attributes + for ( xmlAttrPtr attr = node->properties; attr != nullptr; attr=attr->next ) { + // check if name matches + if ( !attr->name ) continue; + if ( std::strcmp( (const char*)attr->name, name.c_str( ) ) == 0 ) + return attr; + } + return nullptr; +} + +// find all child elements with given name and add them to a vector +// +// input: node = xml element to search +// name = name used when matching child elements +// output: out = list of matches +// return: number of children added to 'out' +int +xmlExFindChildren (xmlNodePtr node, + const std::string & name, + xmlNodePtrVec & out) { + + if ( !node ) return 0; + int count = 0; + // iterate over all children + for ( xmlNodePtr child = node->children; child; child = child->next ) { + // if name matches + if ( !child->name ) continue; + if ( std::strcmp( (const char*)child->name, name.c_str( ) ) == 0 ) { + // add to output list + out.push_back( child ); + ++count; + } + } + return count; +} + +// get the text content from an attribute +std::string +xmlExGetTextContent (xmlAttrPtr attr) { + + if ( !attr ) + return std::string( ); + if ( attr->type != XML_ATTRIBUTE_NODE ) + return std::string( ); + // check child is a text node + xmlNodePtr child = attr->children; + if ( child->type != XML_TEXT_NODE ) + return std::string( ); + // access the content + assert( child->content != nullptr ); + return std::string( (const char*) child->content ); +} + +// get the text content from an node +std::string +xmlExGetTextContent (xmlNodePtr node) { + + if ( !node ) + return std::string( ); + if ( node->type != XML_ELEMENT_NODE ) + return std::string( ); + // check child is a text node + xmlNodePtr child = node->children; + if ( child->type != XML_TEXT_NODE ) + return std::string( ); + // access the content + assert( child->content != nullptr ); + return std::string( (const char*) child->content ); +} + +// compile a list of xml includes from the target file +// input: doc = target.xml +// output: includes = list of .xml names specified in target.xml +// return: number of .xml files specified in target.xml and added to includes +int +parseTargetIncludes (xmlDocPtr doc, stringVec & includes) { + + if ( !doc ) return 0; + int count = 0; + xmlNodePtr elm = xmlExFindElement( doc->children, { "target" } ); + if (! elm ) return 0; + xmlNodePtrVec nodes; + xmlExFindChildren( elm, "xi:include", nodes ); + // iterate over all includes + for ( uint32_t i = 0; i < nodes.size(); i++ ) { + xmlAttrPtr attr = xmlExFindAttribute( nodes[i], "href" ); + if ( attr != nullptr ) { + std::string text = xmlExGetTextContent( attr ); + includes.push_back( text ); + ++count; + } + } + return count; +} + +// extract target arch information from the target.xml file +// input: doc = target.xml document +// output: out = remote target information +// return: 'true' on success +// 'false' on failure +bool +parseTargetInfo (xmlDocPtr doc, GdbServerTargetInfo & out) { + + if ( !doc ) return false; + xmlNodePtr e1 = xmlExFindElement( doc->children, { "target", "architecture" } ); + if ( !e1 ) return false; + out.m_arch = xmlExGetTextContent( e1 ); + + xmlNodePtr e2 = xmlExFindElement( doc->children, { "target", "osabi" } ); + if ( !e2 ) return false; + out.m_osabi = xmlExGetTextContent( e2 ); + + return true; +} + +// extract register information from one of the xml files specified in target.xml +// input: doc = xml document +// output: regList = list of extracted register info +// return: 'true' on success +// 'false' on failure +bool +parseRegisters (xmlDocPtr doc, GDBServerRegisterVec & regList ) { + + if ( !doc ) return false; + xmlNodePtr elm = xmlExFindElement( doc->children, { "feature" } ); + if ( !elm ) return false; + + xmlAttrPtr attr = nullptr; + + xmlNodePtrVec regs; + xmlExFindChildren( elm, "reg", regs ); + for ( int i = 0; i < regs.size( ); i++ ) { + + GdbServerRegisterInfo reg; + reg.clear( ); + + if ( attr = xmlExFindAttribute( regs[i], "name" ) ) { + reg.m_name = xmlExGetTextContent( attr ).c_str(); + reg.m_flags.m_has_name = true; + } + + if ( attr = xmlExFindAttribute( regs[i], "bitsize" ) ) { + const std::string v = xmlExGetTextContent( attr ); + reg.m_bitSize = atoi( v.c_str( ) ); + reg.m_flags.m_has_bitSize = true; + } + + if ( attr = xmlExFindAttribute( regs[i], "type" ) ) { + const std::string v = xmlExGetTextContent( attr ); + reg.m_type = GdbServerRegisterInfo::e_unknown; + + // search the type table for a match + for ( int j = 0; RegTypeTable[j].m_name !=nullptr ;++j ) { + if (RegTypeTable[j].m_name == v) { + reg.m_type = RegTypeTable[j].m_type; + break; + } + } + + reg.m_flags.m_has_type = (reg.m_type != GdbServerRegisterInfo::e_unknown); + } + + if ( attr = xmlExFindAttribute( regs[i], "group" ) ) { + reg.m_group = xmlExGetTextContent( attr ); + reg.m_flags.m_has_group = true; + } + + if ( attr = xmlExFindAttribute( regs[i], "regnum" ) ) { + const std::string v = xmlExGetTextContent( attr ); + reg.m_regNum = atoi( v.c_str( ) ); + reg.m_flags.m_has_regNum = true; + } + + regList.push_back( reg ); + } + + //TODO: there is also a "vector" element to parse + //TODO: there is also eflags to parse + + return true; +} + +// build lldb gdb-remote's dynamic register info from a vector of gdb provided registers +// input: regList = register information provided by gdbserver +// output: regInfo = dynamic register information required by gdb-remote +void +BuildRegisters (const GDBServerRegisterVec & regList, + GDBRemoteDynamicRegisterInfo & regInfo) { + + using namespace lldb_private; + + uint32_t offset = 0; + + for ( uint32_t i = 0; i < regList.size( ); ++i ) { + + const GdbServerRegisterInfo & gdbReg = regList[i]; + + std::string name = gdbReg.m_flags.m_has_name ? gdbReg.m_name : "unknown"; + std::string group = gdbReg.m_flags.m_has_group ? gdbReg.m_group : "general"; + uint32_t byteSize = gdbReg.m_flags.m_has_bitSize ? (gdbReg.m_bitSize/8) : 8; + + uint32_t regNumGcc = LLDB_INVALID_REGNUM; + uint32_t regNumDwarf = LLDB_INVALID_REGNUM; + uint32_t regNumGeneric = LLDB_INVALID_REGNUM; + uint32_t regNumGdb = i; + uint32_t regNumNative = i; + + if ( name == "eip" || name == "pc" ) { + regNumGeneric = LLDB_REGNUM_GENERIC_PC; + } + if ( name == "esp" || name == "sp" ) { + regNumGeneric = LLDB_REGNUM_GENERIC_SP; + } + if ( name == "ebp") { + regNumGeneric = LLDB_REGNUM_GENERIC_FP; + } + if ( name == "lr" ) { + regNumGeneric = LLDB_REGNUM_GENERIC_RA; + } + + RegisterInfo info = { + name.c_str(), + nullptr , + byteSize , + offset , + lldb::Encoding::eEncodingUint, + lldb::Format::eFormatDefault, + { regNumGcc , + regNumDwarf , + regNumGeneric, + regNumGdb , + regNumNative }, + nullptr, + nullptr + }; + + ConstString regName = ConstString( gdbReg.m_name ); + ConstString regAltName = ConstString( ); + ConstString regGroup = ConstString( group ); + regInfo.AddRegister( info, regName, regAltName, regGroup ); + + offset += byteSize; + } + + regInfo.Finalize (); +} + +} // namespace {} + +// query the target of gdb-remote for extended target information +// return: 'true' on success +// 'false' on failure +bool +ProcessGDBRemote::GetGDBServerInfo () { + + GDBRemoteCommunicationClient & comm = m_gdb_comm; + GDBRemoteDynamicRegisterInfo & regInfo = m_register_info; + + // check that we have extended feature read support + if ( !comm.GetQXferFeaturesReadSupported( ) ) + return false; + + // request the target xml file + std::string raw; + lldb_private::Error lldberr; + if (! comm.ReadExtFeature( ConstString( "features" ), + ConstString( "target.xml" ), + raw, + lldberr ) ) { + return false; + } + + // parse the xml file in memory + xmlDocPtr doc = xmlReadMemory( raw.c_str( ), raw.size( ), "noname.xml", nullptr, 0 ); + if ( doc == nullptr ) + return false; + + // extract target info from target.xml + GdbServerTargetInfo gdbInfo; + if ( parseTargetInfo( doc, gdbInfo ) ) { + // use target info + // NOTE: We could deduce triple from gdbInfo if lldb doesn't already have one set. + } + + // collect registers from all of the includes + GDBServerRegisterVec regList; + stringVec includes; + if ( parseTargetIncludes( doc, includes ) > 0 ) { + + for ( uint32_t i = 0; i < includes.size( ); ++i ) { + + // request register file + if ( !comm.ReadExtFeature( ConstString( "features" ), + ConstString( includes[i] ), + raw, + lldberr ) ) + continue; + + // parse register file + xmlDocPtr regXml = xmlReadMemory( raw.c_str(), + raw.size( ), + includes[i].c_str(), + nullptr, + 0 ); + if ( !regXml ) + continue; + + // pass registers to lldb + parseRegisters( regXml, regList ); + } + } + + // pass all of these registers to lldb + BuildRegisters( regList, regInfo ); + + return true; +} + +#else // if defined( LIBXML2_DEFINED ) + +using namespace lldb_private::process_gdb_remote; + +bool +ProcessGDBRemote::GetGDBServerInfo () { + // stub (libxml2 not present) + return false; +} + +#endif // if defined( LIBXML2_DEFINED ) + + class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed { private: