Index: lldb/trunk/cmake/modules/LLDBConfig.cmake =================================================================== --- lldb/trunk/cmake/modules/LLDBConfig.cmake +++ lldb/trunk/cmake/modules/LLDBConfig.cmake @@ -187,29 +187,39 @@ DESTINATION include FILES_MATCHING PATTERN "*.h" - PATTERN ".svn" EXCLUDE - ) - - -# Find libraries or frameworks that may be needed -if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + 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") find_library(CARBON_LIBRARY Carbon) find_library(FOUNDATION_LIBRARY Foundation) find_library(CORE_FOUNDATION_LIBRARY CoreFoundation) find_library(CORE_SERVICES_LIBRARY CoreServices) - find_library(SECURITY_LIBRARY Security) - find_library(DEBUG_SYMBOLS_LIBRARY DebugSymbols PATHS "/System/Library/PrivateFrameworks") - - if (NOT LIBXML2_FOUND) - find_package(LibXml2) - endif () - 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}) -endif() - -if(LLDB_REQUIRES_EH) + find_library(SECURITY_LIBRARY Security) + find_library(DEBUG_SYMBOLS_LIBRARY DebugSymbols PATHS "/System/Library/PrivateFrameworks") + + 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) set(LLDB_REQUIRES_RTTI ON) else() if(LLVM_COMPILER_IS_GCC_COMPATIBLE) Index: lldb/trunk/source/Commands/CommandObjectRegister.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectRegister.cpp +++ lldb/trunk/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: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ lldb/trunk/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: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ lldb/trunk/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; @@ -373,10 +385,21 @@ 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 + // build the qSupported packet + std::vector features = {"xmlRegisters=i386,arm,mips"}; + StreamString packet; + packet.PutCString( "qSupported" ); + for ( uint32_t i = 0; i < features.size( ); ++i ) + { + packet.PutCString( i==0 ? ":" : ";"); + packet.PutCString( features[i].c_str( ) ); + } + StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("qSupported", + if (SendPacketAndWaitForResponse(packet.GetData(), response, /*send_async=*/false) == PacketResult::Success) { @@ -392,6 +415,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 +3801,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: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ lldb/trunk/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: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ lldb/trunk/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" @@ -571,6 +574,10 @@ if (reg_num == 0) { + // try to extract information from servers target.xml + if ( GetGDBServerInfo( ) ) + return; + FileSpec target_definition_fspec = GetGlobalPluginProperties()->GetTargetDefinitionFile (); if (target_definition_fspec) @@ -3429,6 +3436,465 @@ 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 { + eUnknown , + eCodePtr , + eDataPtr , + eInt32 , + eI387Ext , + } + 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::eInt32 }, + { "int" , GdbServerRegisterInfo::eInt32 }, + { "data_ptr", GdbServerRegisterInfo::eDataPtr }, + { "code_ptr", GdbServerRegisterInfo::eCodePtr }, + { "i387_ext", GdbServerRegisterInfo::eI387Ext }, // 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::eUnknown; + + // 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::eUnknown); + } + + 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; + + const uint32_t defSize = 32; + uint32_t regNum = 0; + uint32_t byteOffset = 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) : defSize; + + if ( gdbReg.m_flags.m_has_regNum ) + regNum = gdbReg.m_regNum; + + uint32_t regNumGcc = LLDB_INVALID_REGNUM; + uint32_t regNumDwarf = LLDB_INVALID_REGNUM; + uint32_t regNumGeneric = LLDB_INVALID_REGNUM; + uint32_t regNumGdb = regNum; + uint32_t regNumNative = regNum; + + 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 , + byteOffset , + 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 ); + + // advance register info + byteOffset += byteSize; + regNum += 1; + } + + regInfo.Finalize (); +} + +} // namespace {} + +void XMLCDECL +libxml2NullErrorFunc (void *ctx, const char *msg, ...) +{ + // do nothing currently +} + +// query the target of gdb-remote for extended target information +// return: 'true' on success +// 'false' on failure +bool +ProcessGDBRemote::GetGDBServerInfo () +{ + + // redirect libxml2's error handler since the default prints to stdout + xmlGenericErrorFunc func = libxml2NullErrorFunc; + initGenericErrorDefaultFunc( &func ); + + 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 ) ) { + // 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: