Add a another packet to the gdb-remote protocol,

jGetLoadedDynamicLibrariesInfos.  This packet is similar to
qXfer:libraries:read except that lldb supplies the number of solibs
that should be reported about, and the start address for the list
of them.  At the initial process launch we'll read the full list
of solibs linked by the process -- at this point we could be using
qXfer:libraries:read -- but on subsequence solib-loaded notifications,
we'll be fetching a smaller number of solibs, often only one or two.

A typical Mac/iOS GUI app may have a couple hundred different 
solibs loaded  - doing all of the loads via memory reads takes 
a couple of megabytes of traffic between lldb and debugserver.
Having debugserver summarize the load addresses of all the solibs
and sending it in JSON requires a couple of hundred kilobytes
of traffic.  It's a significant performance improvement when 
communicating over a slower channel.

This patch leaves all of the logic for loading the libraries
in DynamicLoaderMacOSXDYLD -- it only call over ot ProcesGDBRemote
to get the JSON result.

If the jGetLoadedDynamicLibrariesInfos packet is not implemented,
the normal technique of using memory read packets to get all of
the details from the target will be used.

<rdar://problem/21007465>

llvm-svn: 241964
This commit is contained in:
Jason Molenda 2015-07-10 23:15:22 +00:00
parent c6ecb7c693
commit 20ee21bde6
14 changed files with 698 additions and 0 deletions

View File

@ -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.
//----------------------------------------------------------------------

View File

@ -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

View File

@ -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;

View File

@ -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"
@ -343,6 +344,9 @@ protected:
bool
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);

View File

@ -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),
@ -653,6 +654,24 @@ GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported ()
return m_supports_jThreadExtendedInfo;
}
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 ()
{

View File

@ -549,6 +549,9 @@ public:
bool
GetThreadExtendedInfoSupported();
bool
GetLoadedDynamicLibrariesInfosSupported();
bool
GetModuleInfo (const FileSpec& module_file_spec,
const ArchSpec& arch_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,

View File

@ -3977,6 +3977,44 @@ ProcessGDBRemote::GetExtendedInfoForThread (lldb::tid_t tid)
// 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())
{
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)
{
@ -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.
//

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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 ()
{

View File

@ -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)

View File

@ -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);