diff options
Diffstat (limited to 'lldb')
14 files changed, 698 insertions, 0 deletions
diff --git a/lldb/docs/lldb-gdb-remote.txt b/lldb/docs/lldb-gdb-remote.txt index d8f22fe604a..78c5b32a49e 100644 --- a/lldb/docs/lldb-gdb-remote.txt +++ b/lldb/docs/lldb-gdb-remote.txt @@ -1447,3 +1447,86 @@ for this region. // libcompression implements "LZMA level 6", the default compression for the // open source LZMA implementation. //---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// "jGetLoadedDynamicLibrariesInfos" +// +// BRIEF +// This packet asks the remote debug stub to send the details about libraries +// being added/removed from the process as a performance optimization. +// +// LLDB SENDS: jGetLoadedDynamicLibrariesInfos:{"image_count":1,"image_list_address":140734800075128} +// STUB REPLIES: ${"images":[{"load_address":4294967296,"mod_date":0,"pathname":"/tmp/a.out","uuid":"02CF262C-ED6F-3965-9E14-63538B465CFF","mach_header":{"magic":4277009103,"cputype":16777223,"cpusubtype":18446744071562067971,"filetype":2},"segments":{"name":"__PAGEZERO","vmaddr":0,"vmsize":4294967296,"fileoff":0,"filesize":0,"maxprot":0},{"name":"__TEXT","vmaddr":4294967296,"vmsize":4096,"fileoff":0,"filesize":4096,"maxprot":7},{"name":"__LINKEDIT","vmaddr":4294971392,"vmsize":4096,"fileoff":4096,"filesize":152,"maxprot":7}}]}#00 +// +// Or pretty-printed, +// +// STUB REPLIES: ${"images": +// [ +// {"load_address":4294967296, +// "mod_date":0, +// "pathname":"/tmp/a.out", +// "uuid":"02CF262C-ED6F-3965-9E14-63538B465CFF", +// "mach_header": +// {"magic":4277009103, +// "cputype":16777223, +// "cpusubtype":18446744071562067971, +// "filetype":2 +// }, +// "segments": +// [ +// {"name":"__PAGEZERO", +// "vmaddr":0, +// "vmsize":4294967296, +// "fileoff":0, +// "filesize":0, +// "maxprot":0 +// }, +// {"name":"__TEXT", +// "vmaddr":4294967296, +// "vmsize":4096, +// "fileoff":0, +// "filesize":4096, +// "maxprot":7 +// }, +// {"name":"__LINKEDIT", +// "vmaddr":4294971392, +// "vmsize":4096, +// "fileoff":4096, +// "filesize":152, +// "maxprot":7 +// } +// ] +// } +// ] +// } +// +// +// This is similar to the qXfer:libraries:read packet, and it could +// be argued that it should be merged into that packet. A separate +// packet was created primarily because lldb needs to specify the +// number of images to be read and the address from which the initial +// information is read. Also the XML DTD would need to be extended +// quite a bit to provide all the information that the DynamicLoaderMacOSX +// would need to work correctly on this platform. +// +// On Mac OS X / iOS, when libraries are added or removed, a stub +// function is called which lldb puts a breakpoint on. The arguments +// to the stub function include the number of libraries being added +// or removed and the address where the list of libraries can be +// found. The information at this address is the load address of the +// library, the filename, and the mod date of the library if available. +// DynamicLoaderMacOSX then parses the load commands in the Mach-O header +// at the load address before it can decide what action to take. +// +// The purpose of this packet is to eliminate all of the memory reads needed +// to read the Mach-O header and load commands for these libraries. +// On a typical GUI app, there can be a couple hundred shared libraries +// which results in megabytes of read packets. That same information can +// be returned in a couple hundred kilobytes in JSON format from the remote +// debugserver. +// +// +// PRIORITY TO IMPLEMENT +// Low. If this packet is absent, lldb will read the Mach-O headers/load +// commands out of memory. +//---------------------------------------------------------------------- diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index db0f0cfa028..39c5fcd0421 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -30,6 +30,7 @@ #include "lldb/Core/Event.h" #include "lldb/Core/ThreadSafeValue.h" #include "lldb/Core/PluginInterface.h" +#include "lldb/Core/StructuredData.h" #include "lldb/Core/UserSettingsController.h" #include "lldb/Breakpoint/BreakpointSiteList.h" #include "lldb/Host/HostThread.h" @@ -1887,6 +1888,37 @@ public: virtual void ModulesDidLoad (ModuleList &module_list); + + //------------------------------------------------------------------ + /// Retrieve the list of shared libraries that are loaded for this process + /// + /// For certain platforms, the time it takes for the DynamicLoader plugin to + /// read all of the shared libraries out of memory over a slow communication + /// channel may be too long. In that instance, the gdb-remote stub may be + /// able to retrieve the necessary information about the solibs out of memory + /// and return a concise summary sufficient for the DynamicLoader plugin. + /// + /// @param [in] image_list_address + /// The address where the table of shared libraries is stored in memory, + /// if that is appropriate for this platform. Else this may be + /// passed as LLDB_INVALID_ADDRESS. + /// + /// @param [in] image_count + /// The number of shared libraries that are present in this process, if + /// that is appropriate for this platofrm Else this may be passed as + /// LLDB_INVALID_ADDRESS. + /// + /// @return + /// A StructureDataSP object which, if non-empty, will contain the + /// information the DynamicLoader needs to get the initial scan of + /// solibs resolved. + //------------------------------------------------------------------ + virtual lldb_private::StructuredData::ObjectSP + GetLoadedDynamicLibrariesInfos (lldb::addr_t image_list_address, lldb::addr_t image_count) + { + return StructuredData::ObjectSP(); + } + protected: void diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp index a5e89327ac0..5d7b478e014 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include <uuid/uuid.h> #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/DataBuffer.h" @@ -793,6 +794,172 @@ DynamicLoaderMacOSXDYLD::ReadAllImageInfosStructure () } +// This method is an amalgamation of code from +// ReadMachHeader() +// ParseLoadCommands() +// UpdateImageInfosHeaderAndLoadCommands() +// but written to extract everything from the JSON packet from debugserver, instead of using memory reads. + +bool +DynamicLoaderMacOSXDYLD::AddModulesUsingInfosFromDebugserver (StructuredData::ObjectSP image_details, DYLDImageInfo::collection &image_infos) +{ + StructuredData::ObjectSP images_sp = image_details->GetAsDictionary()->GetValueForKey("images"); + if (images_sp.get() == nullptr) + return false; + + image_infos.resize (images_sp->GetAsArray()->GetSize()); + + uint32_t exe_idx = UINT32_MAX; + + for (size_t i = 0; i < image_infos.size(); i++) + { + StructuredData::ObjectSP image_sp = images_sp->GetAsArray()->GetItemAtIndex(i); + if (image_sp.get() == nullptr || image_sp->GetAsDictionary() == nullptr) + return false; + StructuredData::Dictionary *image = image_sp->GetAsDictionary(); + if (image->HasKey("load_address") == false + || image->HasKey("pathname") == false + || image->HasKey("mod_date") == false + || image->HasKey("mach_header") == false + || image->GetValueForKey("mach_header")->GetAsDictionary() == nullptr + || image->HasKey("segments") == false + || image->GetValueForKey("segments")->GetAsArray() == nullptr + || image->HasKey("uuid") == false ) + { + return false; + } + image_infos[i].address = image->GetValueForKey("load_address")->GetAsInteger()->GetValue(); + image_infos[i].mod_date = image->GetValueForKey("mod_date")->GetAsInteger()->GetValue(); + image_infos[i].file_spec.SetFile(image->GetValueForKey("pathname")->GetAsString()->GetValue().c_str(), false); + + StructuredData::Dictionary *mh = image->GetValueForKey("mach_header")->GetAsDictionary(); + image_infos[i].header.magic = mh->GetValueForKey("magic")->GetAsInteger()->GetValue(); + image_infos[i].header.cputype = mh->GetValueForKey("cputype")->GetAsInteger()->GetValue(); + image_infos[i].header.cpusubtype = mh->GetValueForKey("cpusubtype")->GetAsInteger()->GetValue(); + image_infos[i].header.filetype = mh->GetValueForKey("filetype")->GetAsInteger()->GetValue(); + + // Fields that aren't used by DynamicLoaderMacOSXDYLD so debugserver doesn't currently send them + // in the reply. + + if (mh->HasKey("flags")) + image_infos[i].header.flags = mh->GetValueForKey("flags")->GetAsInteger()->GetValue(); + else + image_infos[i].header.flags = 0; + + if (mh->HasKey("ncmds")) + image_infos[i].header.ncmds = mh->GetValueForKey("ncmds")->GetAsInteger()->GetValue(); + else + image_infos[i].header.ncmds = 0; + + if (mh->HasKey("sizeofcmds")) + image_infos[i].header.sizeofcmds = mh->GetValueForKey("sizeofcmds")->GetAsInteger()->GetValue(); + else + image_infos[i].header.sizeofcmds = 0; + + if (image_infos[i].header.filetype == llvm::MachO::MH_EXECUTE) + exe_idx = i; + + StructuredData::Array *segments = image->GetValueForKey("segments")->GetAsArray(); + uint32_t segcount = segments->GetSize(); + for (size_t j = 0; j < segcount; j++) + { + Segment segment; + StructuredData::Dictionary *seg = segments->GetItemAtIndex(j)->GetAsDictionary(); + segment.name = ConstString(seg->GetValueForKey("name")->GetAsString()->GetValue().c_str()); + segment.vmaddr = seg->GetValueForKey("vmaddr")->GetAsInteger()->GetValue(); + segment.vmsize = seg->GetValueForKey("vmsize")->GetAsInteger()->GetValue(); + segment.fileoff = seg->GetValueForKey("fileoff")->GetAsInteger()->GetValue(); + segment.filesize = seg->GetValueForKey("filesize")->GetAsInteger()->GetValue(); + segment.maxprot = seg->GetValueForKey("maxprot")->GetAsInteger()->GetValue(); + + // Fields that aren't used by DynamicLoaderMacOSXDYLD so debugserver doesn't currently send them + // in the reply. + + if (seg->HasKey("initprot")) + segment.initprot = seg->GetValueForKey("initprot")->GetAsInteger()->GetValue(); + else + segment.initprot = 0; + + if (seg->HasKey("flags")) + segment.flags = seg->GetValueForKey("flags")->GetAsInteger()->GetValue(); + else + segment.flags = 0; + + if (seg->HasKey("nsects")) + segment.nsects = seg->GetValueForKey("nsects")->GetAsInteger()->GetValue(); + else + segment.nsects = 0; + + image_infos[i].segments.push_back (segment); + } + + image_infos[i].uuid.SetFromCString (image->GetValueForKey("uuid")->GetAsString()->GetValue().c_str()); + + // All sections listed in the dyld image info structure will all + // either be fixed up already, or they will all be off by a single + // slide amount that is determined by finding the first segment + // that is at file offset zero which also has bytes (a file size + // that is greater than zero) in the object file. + + // Determine the slide amount (if any) + const size_t num_sections = image_infos[i].segments.size(); + for (size_t k = 0; k < num_sections; ++k) + { + // Iterate through the object file sections to find the + // first section that starts of file offset zero and that + // has bytes in the file... + if ((image_infos[i].segments[k].fileoff == 0 && image_infos[i].segments[k].filesize > 0) + || (image_infos[i].segments[k].name == ConstString("__TEXT"))) + { + image_infos[i].slide = image_infos[i].address - image_infos[i].segments[k].vmaddr; + // We have found the slide amount, so we can exit + // this for loop. + break; + } + } + } + + Target &target = m_process->GetTarget(); + + if (exe_idx < image_infos.size()) + { + const bool can_create = true; + ModuleSP exe_module_sp (FindTargetModuleForDYLDImageInfo (image_infos[exe_idx], can_create, NULL)); + + if (exe_module_sp) + { + UpdateImageLoadAddress (exe_module_sp.get(), image_infos[exe_idx]); + + if (exe_module_sp.get() != target.GetExecutableModulePointer()) + { + // Don't load dependent images since we are in dyld where we will know + // and find out about all images that are loaded. Also when setting the + // executable module, it will clear the targets module list, and if we + // have an in memory dyld module, it will get removed from the list + // so we will need to add it back after setting the executable module, + // so we first try and see if we already have a weak pointer to the + // dyld module, make it into a shared pointer, then add the executable, + // then re-add it back to make sure it is always in the list. + ModuleSP dyld_module_sp(m_dyld_module_wp.lock()); + + const bool get_dependent_images = false; + m_process->GetTarget().SetExecutableModule (exe_module_sp, + get_dependent_images); + + if (dyld_module_sp) + { + if(target.GetImages().AppendIfNeeded (dyld_module_sp)) + { + // Also add it to the section list. + UpdateImageLoadAddress(dyld_module_sp.get(), m_dyld); + } + } + } + } + } + return true; +} + bool DynamicLoaderMacOSXDYLD::AddModulesUsingImageInfosAddress (lldb::addr_t image_infos_addr, uint32_t image_infos_count) { @@ -805,6 +972,22 @@ DynamicLoaderMacOSXDYLD::AddModulesUsingImageInfosAddress (lldb::addr_t image_in if (m_process->GetStopID() == m_dyld_image_infos_stop_id) return true; + StructuredData::ObjectSP image_infos_json_sp = m_process->GetLoadedDynamicLibrariesInfos (image_infos_addr, image_infos_count); + if (image_infos_json_sp.get() + && image_infos_json_sp->GetAsDictionary() + && image_infos_json_sp->GetAsDictionary()->HasKey("images") + && image_infos_json_sp->GetAsDictionary()->GetValueForKey("images")->GetAsArray() + && image_infos_json_sp->GetAsDictionary()->GetValueForKey("images")->GetAsArray()->GetSize() == image_infos_count) + { + bool return_value = false; + if (AddModulesUsingInfosFromDebugserver (image_infos_json_sp, image_infos)) + { + return_value = AddModulesUsingImageInfos (image_infos); + } + m_dyld_image_infos_stop_id = m_process->GetStopID(); + return return_value; + } + if (!ReadImageInfos (image_infos_addr, image_infos_count, image_infos)) return false; diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h index c9fa43fcac0..4484e31515c 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h @@ -18,6 +18,7 @@ #include "lldb/Target/DynamicLoader.h" #include "lldb/Host/FileSpec.h" +#include "lldb/Core/StructuredData.h" #include "lldb/Core/UUID.h" #include "lldb/Host/Mutex.h" #include "lldb/Target/Process.h" @@ -344,6 +345,9 @@ protected: ReadAllImageInfosStructure (); bool + AddModulesUsingInfosFromDebugserver (lldb_private::StructuredData::ObjectSP image_details, DYLDImageInfo::collection &image_infos); + + bool AddModulesUsingImageInfosAddress (lldb::addr_t image_infos_addr, uint32_t image_infos_count); bool diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index ae0a2f5e66c..afc772d7345 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -87,6 +87,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() : m_supports_qXfer_features_read (eLazyBoolCalculate), m_supports_augmented_libraries_svr4_read (eLazyBoolCalculate), m_supports_jThreadExtendedInfo (eLazyBoolCalculate), + m_supports_jLoadedDynamicLibrariesInfos (eLazyBoolCalculate), m_supports_qProcessInfoPID (true), m_supports_qfProcessInfo (true), m_supports_qUserName (true), @@ -654,6 +655,24 @@ GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported () } bool +GDBRemoteCommunicationClient::GetLoadedDynamicLibrariesInfosSupported () +{ + if (m_supports_jLoadedDynamicLibrariesInfos == eLazyBoolCalculate) + { + StringExtractorGDBRemote response; + m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolNo; + if (SendPacketAndWaitForResponse("jGetLoadedDynamicLibrariesInfos:", response, false) == PacketResult::Success) + { + if (response.IsOKResponse()) + { + m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolYes; + } + } + } + return m_supports_jLoadedDynamicLibrariesInfos; +} + +bool GDBRemoteCommunicationClient::GetxPacketSupported () { if (m_supports_x == eLazyBoolCalculate) diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 65a2981018e..deb41b066b4 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -550,6 +550,9 @@ public: GetThreadExtendedInfoSupported(); bool + GetLoadedDynamicLibrariesInfosSupported(); + + bool GetModuleInfo (const FileSpec& module_file_spec, const ArchSpec& arch_spec, ModuleSpec &module_spec); @@ -614,6 +617,7 @@ protected: LazyBool m_supports_qXfer_features_read; LazyBool m_supports_augmented_libraries_svr4_read; LazyBool m_supports_jThreadExtendedInfo; + LazyBool m_supports_jLoadedDynamicLibrariesInfos; bool m_supports_qProcessInfoPID:1, diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 37dbff0913e..ef79e084f3b 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -3985,6 +3985,44 @@ ProcessGDBRemote::GetExtendedInfoForThread (lldb::tid_t tid) { if (!response.Empty()) { + object_sp = StructuredData::ParseJSON (response.GetStringRef()); + } + } + } + } + return object_sp; +} + +StructuredData::ObjectSP +ProcessGDBRemote::GetLoadedDynamicLibrariesInfos (lldb::addr_t image_list_address, lldb::addr_t image_count) +{ + StructuredData::ObjectSP object_sp; + + if (m_gdb_comm.GetLoadedDynamicLibrariesInfosSupported()) + { + StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); + args_dict->GetAsDictionary()->AddIntegerItem ("image_list_address", image_list_address); + args_dict->GetAsDictionary()->AddIntegerItem ("image_count", image_count); + + StreamString packet; + packet << "jGetLoadedDynamicLibrariesInfos:"; + args_dict->Dump (packet); + + // FIXME the final character of a JSON dictionary, '}', is the escape + // character in gdb-remote binary mode. lldb currently doesn't escape + // these characters in its packet output -- so we add the quoted version + // of the } character here manually in case we talk to a debugserver which + // un-escapes the characters at packet read time. + packet << (char) (0x7d ^ 0x20); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, false) == GDBRemoteCommunication::PacketResult::Success) + { + StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); + if (response_type == StringExtractorGDBRemote::eResponse) + { + if (!response.Empty()) + { // The packet has already had the 0x7d xor quoting stripped out at the // GDBRemoteCommunication packet receive level. object_sp = StructuredData::ParseJSON (response.GetStringRef()); @@ -3995,6 +4033,7 @@ ProcessGDBRemote::GetExtendedInfoForThread (lldb::tid_t tid) return object_sp; } + // Establish the largest memory read/write payloads we should use. // If the remote stub has a max packet size, stay under that size. // diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 2f825d3e1c3..45c74ea64ee 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -247,6 +247,9 @@ public: void ModulesDidLoad (ModuleList &module_list) override; + StructuredData::ObjectSP + GetLoadedDynamicLibrariesInfos (lldb::addr_t image_list_address, lldb::addr_t image_count) override; + protected: friend class ThreadGDBRemote; friend class GDBRemoteCommunicationClient; diff --git a/lldb/tools/debugserver/source/DNB.cpp b/lldb/tools/debugserver/source/DNB.cpp index 4cde3f87b08..f9cab2ff665 100644 --- a/lldb/tools/debugserver/source/DNB.cpp +++ b/lldb/tools/debugserver/source/DNB.cpp @@ -1066,6 +1066,18 @@ DNBGetTSDAddressForThread (nub_process_t pid, nub_thread_t tid, uint64_t plo_pth return INVALID_NUB_ADDRESS; } +JSONGenerator::ObjectSP +DNBGetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->GetLoadedDynamicLibrariesInfos (pid, image_list_address, image_count); + } + return JSONGenerator::ObjectSP(); +} + + const char * DNBProcessGetExecutablePath (nub_process_t pid) diff --git a/lldb/tools/debugserver/source/DNB.h b/lldb/tools/debugserver/source/DNB.h index 3f032798f23..45a0556670e 100644 --- a/lldb/tools/debugserver/source/DNB.h +++ b/lldb/tools/debugserver/source/DNB.h @@ -16,6 +16,7 @@ #include "MacOSX/Genealogy.h" #include "MacOSX/ThreadInfo.h" +#include "JSONGenerator.h" #include "DNBDefs.h" #include <mach/thread_info.h> #include <string> @@ -141,6 +142,7 @@ ThreadInfo::QoS DNBGetRequestedQoSForThread (nub_process_t pid, nub_thread_t nub_addr_t DNBGetPThreadT (nub_process_t pid, nub_thread_t tid); nub_addr_t DNBGetDispatchQueueT (nub_process_t pid, nub_thread_t tid); nub_addr_t DNBGetTSDAddressForThread (nub_process_t pid, nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size); +JSONGenerator::ObjectSP DNBGetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count); // //---------------------------------------------------------------------- // Breakpoint functions diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.h b/lldb/tools/debugserver/source/MacOSX/MachProcess.h index 3d007a36e31..66977079016 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.h +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.h @@ -27,6 +27,7 @@ #include "PThreadMutex.h" #include "Genealogy.h" #include "ThreadInfo.h" +#include "JSONGenerator.h" #include <mach/mach.h> #include <sys/signal.h> @@ -185,6 +186,7 @@ public: nub_addr_t GetPThreadT (nub_thread_t tid); nub_addr_t GetDispatchQueueT (nub_thread_t tid); nub_addr_t GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size); + JSONGenerator::ObjectSP GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count); nub_size_t GetNumThreads () const; nub_thread_t GetThreadAtIndex (nub_size_t thread_idx) const; diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm index 3be4d65458c..9a537939d8b 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm @@ -23,6 +23,8 @@ #include <sys/sysctl.h> #include <unistd.h> #include <pthread.h> +#include <mach-o/loader.h> +#include <uuid/uuid.h> #include "MacOSX/CFUtils.h" #include "SysSignal.h" @@ -281,6 +283,271 @@ MachProcess::GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_ return m_thread_list.GetTSDAddressForThread (tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); } + + +JSONGenerator::ObjectSP +MachProcess::GetLoadedDynamicLibrariesInfos (nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) +{ + JSONGenerator::ObjectSP reply_sp; + + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; + struct kinfo_proc processInfo; + size_t bufsize = sizeof(processInfo); + if (sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo, &bufsize, NULL, 0) == 0 && bufsize > 0) + { + uint32_t pointer_size = 4; + if (processInfo.kp_proc.p_flag & P_LP64) + pointer_size = 8; + + struct segment + { + std::string name; + uint64_t vmaddr; + uint64_t vmsize; + uint64_t fileoff; + uint64_t filesize; + uint64_t maxprot; + uint64_t initprot; + uint64_t nsects; + uint64_t flags; + }; + + struct image_info + { + uint64_t load_address; + std::string pathname; + uint64_t mod_date; + struct mach_header_64 mach_header; + std::vector<struct segment> segments; + uuid_t uuid; + }; + std::vector<image_info> image_infos; + size_t image_infos_size = image_count * 3 * pointer_size; + + uint8_t *image_info_buf = (uint8_t *) malloc (image_infos_size); + if (image_info_buf == NULL) + { + return reply_sp; + } + if (ReadMemory (image_list_address, image_infos_size, image_info_buf) != image_infos_size) + { + return reply_sp; + } + + + //// First the image_infos array with (load addr, pathname, mod date) tuples + + + for (size_t i = 0; i < image_count; i++) + { + struct image_info info; + nub_addr_t pathname_address; + if (pointer_size == 4) + { + uint32_t load_address_32; + uint32_t pathname_address_32; + uint32_t mod_date_32; + ::memcpy (&load_address_32, image_info_buf + (i * 3 * pointer_size), 4); + ::memcpy (&pathname_address_32, image_info_buf + (i * 3 * pointer_size) + pointer_size, 4); + ::memcpy (&mod_date_32, image_info_buf + (i * 3 * pointer_size) + pointer_size + pointer_size, 4); + info.load_address = load_address_32; + info.mod_date = mod_date_32; + pathname_address = pathname_address_32; + } + else + { + uint64_t load_address_64; + uint64_t pathname_address_64; + uint64_t mod_date_64; + ::memcpy (&load_address_64, image_info_buf + (i * 3 * pointer_size), 8); + ::memcpy (&pathname_address_64, image_info_buf + (i * 3 * pointer_size) + pointer_size, 8); + ::memcpy (&mod_date_64, image_info_buf + (i * 3 * pointer_size) + pointer_size + pointer_size, 8); + info.load_address = load_address_64; + info.mod_date = mod_date_64; + pathname_address = pathname_address_64; + } + char strbuf[17]; + info.pathname = ""; + uint64_t pathname_ptr = pathname_address; + bool still_reading = true; + while (still_reading && ReadMemory (pathname_ptr, sizeof (strbuf) - 1, strbuf) == sizeof (strbuf) - 1) + { + strbuf[sizeof(strbuf) - 1] = '\0'; + info.pathname += strbuf; + pathname_ptr += sizeof (strbuf) - 1; + // Stop if we found nul byte indicating the end of the string + for (int i = 0; i < sizeof(strbuf) - 1; i++) + { + if (strbuf[i] == '\0') + { + still_reading = false; + break; + } + } + } + uuid_clear (info.uuid); + image_infos.push_back (info); + } + if (image_infos.size() == 0) + { + return reply_sp; + } + + + //// Second, read the mach header / load commands for all the dylibs + + + for (size_t i = 0; i < image_count; i++) + { + uint64_t load_cmds_p; + if (pointer_size == 4) + { + struct mach_header header; + if (ReadMemory (image_infos[i].load_address, sizeof (struct mach_header), &header) != sizeof (struct mach_header)) + { + return reply_sp; + } + load_cmds_p = image_infos[i].load_address + sizeof (struct mach_header); + image_infos[i].mach_header.magic = header.magic; + image_infos[i].mach_header.cputype = header.cputype; + image_infos[i].mach_header.cpusubtype = header.cpusubtype; + image_infos[i].mach_header.filetype = header.filetype; + image_infos[i].mach_header.ncmds = header.ncmds; + image_infos[i].mach_header.sizeofcmds = header.sizeofcmds; + image_infos[i].mach_header.flags = header.flags; + } + else + { + struct mach_header_64 header; + if (ReadMemory (image_infos[i].load_address, sizeof (struct mach_header_64), &header) != sizeof (struct mach_header_64)) + { + return reply_sp; + } + load_cmds_p = image_infos[i].load_address + sizeof (struct mach_header_64); + image_infos[i].mach_header.magic = header.magic; + image_infos[i].mach_header.cputype = header.cputype; + image_infos[i].mach_header.cpusubtype = header.cpusubtype; + image_infos[i].mach_header.filetype = header.filetype; + image_infos[i].mach_header.ncmds = header.ncmds; + image_infos[i].mach_header.sizeofcmds = header.sizeofcmds; + image_infos[i].mach_header.flags = header.flags; + } + for (uint32_t j = 0; j < image_infos[i].mach_header.ncmds; j++) + { + struct load_command lc; + if (ReadMemory (load_cmds_p, sizeof (struct load_command), &lc) != sizeof (struct load_command)) + { + return reply_sp; + } + if (lc.cmd == LC_SEGMENT) + { + struct segment_command seg; + if (ReadMemory (load_cmds_p, sizeof (struct segment_command), &seg) != sizeof (struct segment_command)) + { + return reply_sp; + } + struct segment this_seg; + char name[17]; + ::memset (name, 0, sizeof (name)); + memcpy (name, seg.segname, sizeof (seg.segname)); + this_seg.name = name; + this_seg.vmaddr = seg.vmaddr; + this_seg.vmsize = seg.vmsize; + this_seg.fileoff = seg.fileoff; + this_seg.filesize = seg.filesize; + this_seg.maxprot = seg.maxprot; + this_seg.initprot = seg.initprot; + this_seg.nsects = seg.nsects; + this_seg.flags = seg.flags; + image_infos[i].segments.push_back(this_seg); + } + if (lc.cmd == LC_SEGMENT_64) + { + struct segment_command_64 seg; + if (ReadMemory (load_cmds_p, sizeof (struct segment_command_64), &seg) != sizeof (struct segment_command_64)) + { + return reply_sp; + } + struct segment this_seg; + char name[17]; + ::memset (name, 0, sizeof (name)); + memcpy (name, seg.segname, sizeof (seg.segname)); + this_seg.name = name; + this_seg.vmaddr = seg.vmaddr; + this_seg.vmsize = seg.vmsize; + this_seg.fileoff = seg.fileoff; + this_seg.filesize = seg.filesize; + this_seg.maxprot = seg.maxprot; + this_seg.initprot = seg.initprot; + this_seg.nsects = seg.nsects; + this_seg.flags = seg.flags; + image_infos[i].segments.push_back(this_seg); + } + if (lc.cmd == LC_UUID) + { + struct uuid_command uuidcmd; + if (ReadMemory (load_cmds_p, sizeof (struct uuid_command), &uuidcmd) == sizeof (struct uuid_command)) + uuid_copy (image_infos[i].uuid, uuidcmd.uuid); + } + load_cmds_p += lc.cmdsize; + } + } + + + //// Thrid, format all of the above in the JSONGenerator object. + + + JSONGenerator::ObjectSP image_infos_array_sp (new JSONGenerator::Array()); + for (size_t i = 0; i < image_count; i++) + { + JSONGenerator::ObjectSP image_info_dict_sp (new JSONGenerator::Dictionary()); + image_info_dict_sp->GetAsDictionary()->AddIntegerItem ("load_address", image_infos[i].load_address); + image_info_dict_sp->GetAsDictionary()->AddIntegerItem ("mod_date", image_infos[i].mod_date); + image_info_dict_sp->GetAsDictionary()->AddStringItem ("pathname", image_infos[i].pathname); + + uuid_string_t uuidstr; + uuid_unparse_upper (image_infos[i].uuid, uuidstr); + image_info_dict_sp->GetAsDictionary()->AddStringItem ("uuid", uuidstr); + + JSONGenerator::ObjectSP mach_header_dict_sp (new JSONGenerator::Dictionary()); + mach_header_dict_sp->GetAsDictionary()->AddIntegerItem ("magic", image_infos[i].mach_header.magic); + mach_header_dict_sp->GetAsDictionary()->AddIntegerItem ("cputype", image_infos[i].mach_header.cputype); + mach_header_dict_sp->GetAsDictionary()->AddIntegerItem ("cpusubtype", image_infos[i].mach_header.cpusubtype); + mach_header_dict_sp->GetAsDictionary()->AddIntegerItem ("filetype", image_infos[i].mach_header.filetype); + +// DynamicLoaderMacOSX doesn't currently need these fields, so don't send them. +// mach_header_dict_sp->GetAsDictionary()->AddIntegerItem ("ncmds", image_infos[i].mach_header.ncmds); +// mach_header_dict_sp->GetAsDictionary()->AddIntegerItem ("sizeofcmds", image_infos[i].mach_header.sizeofcmds); +// mach_header_dict_sp->GetAsDictionary()->AddIntegerItem ("flags", image_infos[i].mach_header.flags); + image_info_dict_sp->GetAsDictionary()->AddItem ("mach_header", mach_header_dict_sp); + + JSONGenerator::ObjectSP segments_sp (new JSONGenerator::Array()); + for (size_t j = 0; j < image_infos[i].segments.size(); j++) + { + JSONGenerator::ObjectSP segment_sp (new JSONGenerator::Dictionary()); + segment_sp->GetAsDictionary()->AddStringItem ("name", image_infos[i].segments[j].name); + segment_sp->GetAsDictionary()->AddIntegerItem ("vmaddr", image_infos[i].segments[j].vmaddr); + segment_sp->GetAsDictionary()->AddIntegerItem ("vmsize", image_infos[i].segments[j].vmsize); + segment_sp->GetAsDictionary()->AddIntegerItem ("fileoff", image_infos[i].segments[j].fileoff); + segment_sp->GetAsDictionary()->AddIntegerItem ("filesize", image_infos[i].segments[j].filesize); + segment_sp->GetAsDictionary()->AddIntegerItem ("maxprot", image_infos[i].segments[j].maxprot); + +// DynamicLoaderMacOSX doesn't currently need these fields, so don't send them. +// segment_sp->GetAsDictionary()->AddIntegerItem ("initprot", image_infos[i].segments[j].initprot); +// segment_sp->GetAsDictionary()->AddIntegerItem ("nsects", image_infos[i].segments[j].nsects); +// segment_sp->GetAsDictionary()->AddIntegerItem ("flags", image_infos[i].segments[j].flags); + segments_sp->GetAsArray()->AddItem (segment_sp); + } + image_info_dict_sp->GetAsDictionary()->AddItem ("segments", segments_sp); + + image_infos_array_sp->GetAsArray()->AddItem (image_info_dict_sp); + } + reply_sp.reset (new JSONGenerator::Dictionary()); + reply_sp->GetAsDictionary()->AddItem ("images", image_infos_array_sp); + } + return reply_sp; +} + nub_thread_t MachProcess::GetCurrentThread () { diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp index 30abfa5743b..f1c5d75ff4d 100644 --- a/lldb/tools/debugserver/source/RNBRemote.cpp +++ b/lldb/tools/debugserver/source/RNBRemote.cpp @@ -35,6 +35,7 @@ #include "RNBSocket.h" #include "lldb/Utility/StringExtractor.h" #include "MacOSX/Genealogy.h" +#include "JSONGenerator.h" #if defined (HAVE_LIBCOMPRESSION) #include <compression.h> @@ -263,6 +264,7 @@ RNBRemote::CreatePacketTable () t.push_back (Packet (query_process_info, &RNBRemote::HandlePacket_qProcessInfo , NULL, "qProcessInfo", "Replies with multiple 'key:value;' tuples appended to each other.")); t.push_back (Packet (query_symbol_lookup, &RNBRemote::HandlePacket_qSymbol , NULL, "qSymbol:", "Notify that host debugger is ready to do symbol lookups")); t.push_back (Packet (json_query_thread_extended_info,&RNBRemote::HandlePacket_jThreadExtendedInfo , NULL, "jThreadExtendedInfo", "Replies with JSON data of thread extended information.")); + t.push_back (Packet (json_query_get_loaded_dynamic_libraries_infos, &RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos, NULL, "jGetLoadedDynamicLibrariesInfos", "Replies with JSON data of all the shared libraries loaded in this process.")); //t.push_back (Packet (json_query_threads_info, &RNBRemote::HandlePacket_jThreadsInfo , NULL, "jThreadsInfo", "Replies with JSON data with information about all threads.")); t.push_back (Packet (start_noack_mode, &RNBRemote::HandlePacket_QStartNoAckMode , NULL, "QStartNoAckMode", "Request that " DEBUGSERVER_PROGRAM_NAME " stop acking remote protocol packets")); t.push_back (Packet (prefix_reg_packets_with_tid, &RNBRemote::HandlePacket_QThreadSuffixSupported , NULL, "QThreadSuffixSupported", "Check if thread specific packets (register packets 'g', 'G', 'p', and 'P') support having the thread ID appended to the end of the command")); @@ -5345,6 +5347,50 @@ RNBRemote::HandlePacket_jThreadExtendedInfo (const char *p) return SendPacket ("OK"); } +rnb_err_t +RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos (const char *p) +{ + nub_process_t pid; + // If we haven't run the process yet, return an error. + if (!m_ctx.HasValidProcessID()) + { + return SendPacket ("E83"); + } + + pid = m_ctx.ProcessID(); + + const char get_loaded_dynamic_libraries_infos_str[] = { "jGetLoadedDynamicLibrariesInfos:{" }; + if (strncmp (p, get_loaded_dynamic_libraries_infos_str, sizeof (get_loaded_dynamic_libraries_infos_str) - 1) == 0) + { + p += strlen (get_loaded_dynamic_libraries_infos_str); + + nub_addr_t image_list_address = get_integer_value_for_key_name_from_json ("image_list_address", p); + nub_addr_t image_count = get_integer_value_for_key_name_from_json ("image_count", p); + + if (image_list_address != INVALID_NUB_ADDRESS && image_count != INVALID_NUB_ADDRESS) + { + JSONGenerator::ObjectSP json_sp; + + json_sp = DNBGetLoadedDynamicLibrariesInfos (pid, image_list_address, image_count); + + if (json_sp.get()) + { + std::ostringstream json_str; + json_sp->Dump (json_str); + if (json_str.str().size() > 0) + { + std::string json_str_quoted = binary_encode_string (json_str.str()); + return SendPacket (json_str_quoted.c_str()); + } + else + { + SendPacket ("E84"); + } + } + } + } + return SendPacket ("OK"); +} rnb_err_t RNBRemote::HandlePacket_qSymbol (const char *command) diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h index 00cfe7991f9..570d0e02d99 100644 --- a/lldb/tools/debugserver/source/RNBRemote.h +++ b/lldb/tools/debugserver/source/RNBRemote.h @@ -103,6 +103,7 @@ public: query_gdb_server_version, // 'qGDBServerVersion' query_process_info, // 'qProcessInfo' json_query_thread_extended_info,// 'jThreadExtendedInfo' + json_query_get_loaded_dynamic_libraries_infos, // 'jGetLoadedDynamicLibrariesInfos' json_query_threads_info, // 'jThreadsInfo' pass_signals_to_inferior, // 'QPassSignals' start_noack_mode, // 'QStartNoAckMode' @@ -191,6 +192,7 @@ public: rnb_err_t HandlePacket_qSyncThreadStateSupported (const char *p); rnb_err_t HandlePacket_qThreadInfo (const char *p); rnb_err_t HandlePacket_jThreadExtendedInfo (const char *p); + rnb_err_t HandlePacket_jGetLoadedDynamicLibrariesInfos (const char *p); rnb_err_t HandlePacket_jThreadsInfo (const char *p); rnb_err_t HandlePacket_qThreadExtraInfo (const char *p); rnb_err_t HandlePacket_qThreadStopInfo (const char *p); |