Skip to content

Commit 2d38a6b

Browse files
committed
Add a new report_load_commands option to jGetLoadedDynamicLibrariesInfos
jGetLoadedDynamicLibrariesInfos has a mode where it will list every binary in the process - the load address and filepath from dyld SPI, and the mach-o header and load commands from a scan by debugserver for perf reasons. With a large enough number of libraries, creating that StructuredData representation of all of this, and formatting it into an ascii string to send up to lldb, can grow debugserver's heap size too large for some environments. This patch adds a new report_load_commands:false boolean to the jGetLoadedDynamicLibrariesInfos packet, where debugserver will now only report the dyld SPI load address and filepath for all of the binaries. lldb can then ask for the detailed information on the process binaries in smaller chunks, and avoid debugserver having ever growing heap use as the number of binaries inevitably increases. This patch also removes a version of jGetLoadedDynamicLibrariesInfos for pre-iOS 10 and pre-macOS 10.12 systems where we did not use dyld SPI. We can't back compile to those OS builds any longer with modern Xcode. Finally, it removes a requirement in DynamicLoaderMacOS that the JSON reply from jGetLoadedDynamicLibrariesInfos include the mod_date field for each binary. This has always been reported as 0 in modern dyld, and is another reason for packet growth in the reply. debugserver still puts the mod_date field in its replies for interop with existing lldb's, but we will be able to remove it the field from debugserver's output after the next release cycle when this patch has had time to circulate. I'll add lldb support for requesting the load addresses only and splitting the request up into chunks in a separate patch. Differential Revision: https://reviews.llvm.org/D150158 rdar://107848326 (cherry picked from commit 4e93f91)
1 parent 78d6507 commit 2d38a6b

File tree

9 files changed

+65
-183
lines changed

9 files changed

+65
-183
lines changed

lldb/docs/lldb-gdb-remote.txt

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,19 +1961,16 @@ for this region.
19611961
// This packet asks the remote debug stub to send the details about libraries
19621962
// being added/removed from the process as a performance optimization.
19631963
//
1964-
// There are three ways this packet can be used. All three return a dictionary of
1964+
// There are two ways this packet can be used. Both return a dictionary of
19651965
// binary images formatted the same way.
19661966
//
1967-
// On OS X 10.11, iOS 9, tvOS 9, watchOS 2 and earlier, the packet is used like
1968-
// jGetLoadedDynamicLibrariesInfos:{"image_count":1,"image_list_address":140734800075128}
1969-
// where the image_list_address is an array of {void* load_addr, void* mod_date, void* pathname}
1970-
// in the inferior process memory (and image_count is the number of elements in this array).
1971-
// lldb is using information from the dyld_all_image_infos structure to make these requests to
1972-
// debugserver. This use is not supported on macOS 10.12, iOS 10, tvOS 10, watchOS 3 or newer.
1973-
//
1974-
// On macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer, there are two calls. One requests information
1975-
// on all shared libraries:
1967+
// One requests information on all shared libraries:
19761968
// jGetLoadedDynamicLibrariesInfos:{"fetch_all_solibs":true}
1969+
// with an optional `"report_load_commands":false` which can be added, asking
1970+
// that only the dyld SPI information (load addresses, filenames) be returned.
1971+
// The default behavior is that debugserver scans the mach-o header and load
1972+
// commands of each binary, and returns it in the JSON reply.
1973+
//
19771974
// And the second requests information about a list of shared libraries, given their load addresses:
19781975
// jGetLoadedDynamicLibrariesInfos:{"solib_addresses":[8382824135,3258302053,830202858503]}
19791976
//

lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,6 @@ bool DynamicLoaderDarwin::JSONImageInformationIntoImageInfo(
372372
// clang-format off
373373
if (!image->HasKey("load_address") ||
374374
!image->HasKey("pathname") ||
375-
!image->HasKey("mod_date") ||
376375
!image->HasKey("mach_header") ||
377376
image->GetValueForKey("mach_header")->GetAsDictionary() == nullptr ||
378377
!image->HasKey("segments") ||
@@ -383,8 +382,6 @@ bool DynamicLoaderDarwin::JSONImageInformationIntoImageInfo(
383382
// clang-format on
384383
image_infos[i].address =
385384
image->GetValueForKey("load_address")->GetAsInteger()->GetValue();
386-
image_infos[i].mod_date =
387-
image->GetValueForKey("mod_date")->GetAsInteger()->GetValue();
388385
image_infos[i].file_spec.SetFile(
389386
image->GetValueForKey("pathname")->GetAsString()->GetValue(),
390387
FileSpec::Style::native);
@@ -811,11 +808,11 @@ void DynamicLoaderDarwin::ImageInfo::PutToLog(Log *log) const {
811808
if (!log)
812809
return;
813810
if (address == LLDB_INVALID_ADDRESS) {
814-
LLDB_LOG(log, "modtime={0:x+8} uuid={1} path='{2}' (UNLOADED)", mod_date,
815-
uuid.GetAsString(), file_spec.GetPath());
811+
LLDB_LOG(log, "uuid={1} path='{2}' (UNLOADED)", uuid.GetAsString(),
812+
file_spec.GetPath());
816813
} else {
817-
LLDB_LOG(log, "address={0:x+16} modtime={1:x+8} uuid={2} path='{3}'",
818-
address, mod_date, uuid.GetAsString(), file_spec.GetPath());
814+
LLDB_LOG(log, "address={0:x+16} uuid={2} path='{3}'", address,
815+
uuid.GetAsString(), file_spec.GetPath());
819816
for (uint32_t i = 0; i < segments.size(); ++i)
820817
segments[i].PutToLog(log, slide);
821818
}

lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,6 @@ class DynamicLoaderDarwin : public lldb_private::DynamicLoader {
100100
/// The amount to slide all segments by if there is a global
101101
/// slide.
102102
lldb::addr_t slide = 0;
103-
/// Modification date for this dylib.
104-
lldb::addr_t mod_date = 0;
105103
/// Resolved path for this dylib.
106104
lldb_private::FileSpec file_spec;
107105
/// UUID for this dylib if it has one, else all zeros.
@@ -128,7 +126,6 @@ class DynamicLoaderDarwin : public lldb_private::DynamicLoader {
128126
if (!load_cmd_data_only) {
129127
address = LLDB_INVALID_ADDRESS;
130128
slide = 0;
131-
mod_date = 0;
132129
file_spec.Clear();
133130
::memset(&header, 0, sizeof(header));
134131
}
@@ -142,8 +139,7 @@ class DynamicLoaderDarwin : public lldb_private::DynamicLoader {
142139

143140
bool operator==(const ImageInfo &rhs) const {
144141
return address == rhs.address && slide == rhs.slide &&
145-
mod_date == rhs.mod_date && file_spec == rhs.file_spec &&
146-
uuid == rhs.uuid &&
142+
file_spec == rhs.file_spec && uuid == rhs.uuid &&
147143
memcmp(&header, &rhs.header, sizeof(header)) == 0 &&
148144
segments == rhs.segments && os_type == rhs.os_type &&
149145
os_env == rhs.os_env;

lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ bool DynamicLoaderMacOSXDYLD::ReadImageInfos(
693693
i++) {
694694
image_infos[i].address = info_data_ref.GetAddress(&info_data_offset);
695695
lldb::addr_t path_addr = info_data_ref.GetAddress(&info_data_offset);
696-
image_infos[i].mod_date = info_data_ref.GetAddress(&info_data_offset);
696+
info_data_ref.GetAddress(&info_data_offset); // mod_date, unused */
697697

698698
char raw_path[PATH_MAX];
699699
m_process->ReadCStringFromMemory(path_addr, raw_path, sizeof(raw_path),

lldb/tools/debugserver/source/DNB.cpp

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,20 +1023,11 @@ DNBGetTSDAddressForThread(nub_process_t pid, nub_thread_t tid,
10231023
return INVALID_NUB_ADDRESS;
10241024
}
10251025

1026-
JSONGenerator::ObjectSP DNBGetLoadedDynamicLibrariesInfos(
1027-
nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) {
1028-
MachProcessSP procSP;
1029-
if (GetProcessSP(pid, procSP)) {
1030-
return procSP->GetLoadedDynamicLibrariesInfos(pid, image_list_address,
1031-
image_count);
1032-
}
1033-
return JSONGenerator::ObjectSP();
1034-
}
1035-
1036-
JSONGenerator::ObjectSP DNBGetAllLoadedLibrariesInfos(nub_process_t pid) {
1026+
JSONGenerator::ObjectSP
1027+
DNBGetAllLoadedLibrariesInfos(nub_process_t pid, bool report_load_commands) {
10371028
MachProcessSP procSP;
10381029
if (GetProcessSP(pid, procSP)) {
1039-
return procSP->GetAllLoadedLibrariesInfos(pid);
1030+
return procSP->GetAllLoadedLibrariesInfos(pid, report_load_commands);
10401031
}
10411032
return JSONGenerator::ObjectSP();
10421033
}

lldb/tools/debugserver/source/DNB.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,8 @@ DNBGetTSDAddressForThread(nub_process_t pid, nub_thread_t tid,
210210
uint64_t plo_pthread_tsd_base_address_offset,
211211
uint64_t plo_pthread_tsd_base_offset,
212212
uint64_t plo_pthread_tsd_entry_size);
213-
JSONGenerator::ObjectSP DNBGetLoadedDynamicLibrariesInfos(
214-
nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count);
215-
JSONGenerator::ObjectSP DNBGetAllLoadedLibrariesInfos(nub_process_t pid);
213+
JSONGenerator::ObjectSP
214+
DNBGetAllLoadedLibrariesInfos(nub_process_t pid, bool report_load_commands);
216215
JSONGenerator::ObjectSP
217216
DNBGetLibrariesInfoForAddresses(nub_process_t pid,
218217
std::vector<uint64_t> &macho_addresses);

lldb/tools/debugserver/source/MacOSX/MachProcess.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,11 @@ class MachProcess {
7272
struct binary_image_information {
7373
std::string filename;
7474
uint64_t load_address;
75-
uint64_t mod_date; // may not be available - 0 if so
7675
struct mach_o_information macho_info;
7776
bool is_valid_mach_header;
7877

7978
binary_image_information()
80-
: filename(), load_address(INVALID_NUB_ADDRESS), mod_date(0),
79+
: filename(), load_address(INVALID_NUB_ADDRESS),
8180
is_valid_mach_header(false) {}
8281
};
8382

@@ -259,7 +258,8 @@ class MachProcess {
259258
int wordsize,
260259
struct mach_o_information &inf);
261260
JSONGenerator::ObjectSP FormatDynamicLibrariesIntoJSON(
262-
const std::vector<struct binary_image_information> &image_infos);
261+
const std::vector<struct binary_image_information> &image_infos,
262+
bool report_load_commands);
263263
uint32_t GetPlatform();
264264
/// Get the runtime platform from DYLD via SPI.
265265
uint32_t GetProcessPlatformViaDYLDSPI();
@@ -271,12 +271,12 @@ class MachProcess {
271271
/// command details.
272272
void GetAllLoadedBinariesViaDYLDSPI(
273273
std::vector<struct binary_image_information> &image_infos);
274-
JSONGenerator::ObjectSP GetLoadedDynamicLibrariesInfos(
275-
nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count);
276274
JSONGenerator::ObjectSP
277275
GetLibrariesInfoForAddresses(nub_process_t pid,
278276
std::vector<uint64_t> &macho_addresses);
279-
JSONGenerator::ObjectSP GetAllLoadedLibrariesInfos(nub_process_t pid);
277+
JSONGenerator::ObjectSP
278+
GetAllLoadedLibrariesInfos(nub_process_t pid,
279+
bool fetch_report_load_commands);
280280
JSONGenerator::ObjectSP GetSharedCacheInfo(nub_process_t pid);
281281

282282
nub_size_t GetNumThreads() const;

lldb/tools/debugserver/source/MacOSX/MachProcess.mm

Lines changed: 29 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -912,22 +912,34 @@ static bool mach_header_validity_test(uint32_t magic, uint32_t cputype) {
912912
// create a JSONGenerator object
913913
// with all the details we want to send to lldb.
914914
JSONGenerator::ObjectSP MachProcess::FormatDynamicLibrariesIntoJSON(
915-
const std::vector<struct binary_image_information> &image_infos) {
915+
const std::vector<struct binary_image_information> &image_infos,
916+
bool report_load_commands) {
916917

917918
JSONGenerator::ArraySP image_infos_array_sp(new JSONGenerator::Array());
918919

919920
const size_t image_count = image_infos.size();
920921

921922
for (size_t i = 0; i < image_count; i++) {
922-
if (!image_infos[i].is_valid_mach_header)
923+
// If we should report the Mach-O header and load commands,
924+
// and those were unreadable, don't report anything about this
925+
// binary.
926+
if (report_load_commands && !image_infos[i].is_valid_mach_header)
923927
continue;
924928
JSONGenerator::DictionarySP image_info_dict_sp(
925929
new JSONGenerator::Dictionary());
926930
image_info_dict_sp->AddIntegerItem("load_address",
927931
image_infos[i].load_address);
928-
image_info_dict_sp->AddIntegerItem("mod_date", image_infos[i].mod_date);
932+
// TODO: lldb currently rejects a response without this, but it
933+
// is always zero from dyld. It can be removed once we've had time
934+
// for lldb's that require it to be present are obsolete.
935+
image_info_dict_sp->AddIntegerItem("mod_date", 0);
929936
image_info_dict_sp->AddStringItem("pathname", image_infos[i].filename);
930937

938+
if (!report_load_commands) {
939+
image_infos_array_sp->AddItem(image_info_dict_sp);
940+
continue;
941+
}
942+
931943
uuid_string_t uuidstr;
932944
uuid_unparse_upper(image_infos[i].macho_info.uuid, uuidstr);
933945
image_info_dict_sp->AddStringItem("uuid", uuidstr);
@@ -1000,109 +1012,6 @@ static bool mach_header_validity_test(uint32_t magic, uint32_t cputype) {
10001012
return reply_sp;
10011013
}
10021014

1003-
// Get the shared library information using the old (pre-macOS 10.12, pre-iOS
1004-
// 10, pre-tvOS 10, pre-watchOS 3)
1005-
// code path. We'll be given the address of an array of structures in the form
1006-
// {void* load_addr, void* mod_date, void* pathname}
1007-
//
1008-
// In macOS 10.12 etc and newer, we'll use SPI calls into dyld to gather this
1009-
// information.
1010-
JSONGenerator::ObjectSP MachProcess::GetLoadedDynamicLibrariesInfos(
1011-
nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) {
1012-
1013-
JSONGenerator::ObjectSP empty_reply_sp(new JSONGenerator::Dictionary());
1014-
int pointer_size = GetInferiorAddrSize(pid);
1015-
1016-
std::vector<struct binary_image_information> image_infos;
1017-
size_t image_infos_size = image_count * 3 * pointer_size;
1018-
1019-
uint8_t *image_info_buf = (uint8_t *)malloc(image_infos_size);
1020-
if (image_info_buf == NULL) {
1021-
return empty_reply_sp;
1022-
}
1023-
if (ReadMemory(image_list_address, image_infos_size, image_info_buf) !=
1024-
image_infos_size) {
1025-
return empty_reply_sp;
1026-
}
1027-
1028-
/// First the image_infos array with (load addr, pathname, mod date)
1029-
/// tuples
1030-
1031-
for (size_t i = 0; i < image_count; i++) {
1032-
struct binary_image_information info;
1033-
nub_addr_t pathname_address;
1034-
if (pointer_size == 4) {
1035-
uint32_t load_address_32;
1036-
uint32_t pathname_address_32;
1037-
uint32_t mod_date_32;
1038-
::memcpy(&load_address_32, image_info_buf + (i * 3 * pointer_size), 4);
1039-
::memcpy(&pathname_address_32,
1040-
image_info_buf + (i * 3 * pointer_size) + pointer_size, 4);
1041-
::memcpy(&mod_date_32,
1042-
image_info_buf + (i * 3 * pointer_size) + pointer_size +
1043-
pointer_size,
1044-
4);
1045-
info.load_address = load_address_32;
1046-
info.mod_date = mod_date_32;
1047-
pathname_address = pathname_address_32;
1048-
} else {
1049-
uint64_t load_address_64;
1050-
uint64_t pathname_address_64;
1051-
uint64_t mod_date_64;
1052-
::memcpy(&load_address_64, image_info_buf + (i * 3 * pointer_size), 8);
1053-
::memcpy(&pathname_address_64,
1054-
image_info_buf + (i * 3 * pointer_size) + pointer_size, 8);
1055-
::memcpy(&mod_date_64,
1056-
image_info_buf + (i * 3 * pointer_size) + pointer_size +
1057-
pointer_size,
1058-
8);
1059-
info.load_address = load_address_64;
1060-
info.mod_date = mod_date_64;
1061-
pathname_address = pathname_address_64;
1062-
}
1063-
char strbuf[17];
1064-
info.filename = "";
1065-
uint64_t pathname_ptr = pathname_address;
1066-
bool still_reading = true;
1067-
while (still_reading && ReadMemory(pathname_ptr, sizeof(strbuf) - 1,
1068-
strbuf) == sizeof(strbuf) - 1) {
1069-
strbuf[sizeof(strbuf) - 1] = '\0';
1070-
info.filename += strbuf;
1071-
pathname_ptr += sizeof(strbuf) - 1;
1072-
// Stop if we found nul byte indicating the end of the string
1073-
for (size_t i = 0; i < sizeof(strbuf) - 1; i++) {
1074-
if (strbuf[i] == '\0') {
1075-
still_reading = false;
1076-
break;
1077-
}
1078-
}
1079-
}
1080-
uuid_clear(info.macho_info.uuid);
1081-
image_infos.push_back(info);
1082-
}
1083-
if (image_infos.size() == 0) {
1084-
return empty_reply_sp;
1085-
}
1086-
1087-
free(image_info_buf);
1088-
1089-
/// Second, read the mach header / load commands for all the dylibs
1090-
1091-
for (size_t i = 0; i < image_count; i++) {
1092-
// The SPI to provide platform is not available on older systems.
1093-
uint32_t platform = 0;
1094-
if (GetMachOInformationFromMemory(platform, image_infos[i].load_address,
1095-
pointer_size,
1096-
image_infos[i].macho_info)) {
1097-
image_infos[i].is_valid_mach_header = true;
1098-
}
1099-
}
1100-
1101-
/// Third, format all of the above in the JSONGenerator object.
1102-
1103-
return FormatDynamicLibrariesIntoJSON(image_infos);
1104-
}
1105-
11061015
/// From dyld SPI header dyld_process_info.h
11071016
typedef void *dyld_process_info;
11081017
struct dyld_process_cache_info {
@@ -1162,21 +1071,24 @@ static bool mach_header_validity_test(uint32_t magic, uint32_t cputype) {
11621071
// in
11631072
// macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer.
11641073
JSONGenerator::ObjectSP
1165-
MachProcess::GetAllLoadedLibrariesInfos(nub_process_t pid) {
1074+
MachProcess::GetAllLoadedLibrariesInfos(nub_process_t pid,
1075+
bool report_load_commands) {
11661076

11671077
int pointer_size = GetInferiorAddrSize(pid);
11681078
std::vector<struct binary_image_information> image_infos;
11691079
GetAllLoadedBinariesViaDYLDSPI(image_infos);
1170-
uint32_t platform = GetPlatform();
1171-
const size_t image_count = image_infos.size();
1172-
for (size_t i = 0; i < image_count; i++) {
1173-
if (GetMachOInformationFromMemory(platform, image_infos[i].load_address,
1174-
pointer_size,
1175-
image_infos[i].macho_info)) {
1176-
image_infos[i].is_valid_mach_header = true;
1080+
if (report_load_commands) {
1081+
uint32_t platform = GetPlatform();
1082+
const size_t image_count = image_infos.size();
1083+
for (size_t i = 0; i < image_count; i++) {
1084+
if (GetMachOInformationFromMemory(platform, image_infos[i].load_address,
1085+
pointer_size,
1086+
image_infos[i].macho_info)) {
1087+
image_infos[i].is_valid_mach_header = true;
1088+
}
11771089
}
11781090
}
1179-
return FormatDynamicLibrariesIntoJSON(image_infos);
1091+
return FormatDynamicLibrariesIntoJSON(image_infos, report_load_commands);
11801092
}
11811093

11821094
// Fetch information about the shared libraries at the given load addresses
@@ -1226,7 +1138,8 @@ static bool mach_header_validity_test(uint32_t magic, uint32_t cputype) {
12261138
image_infos[i].is_valid_mach_header = true;
12271139
}
12281140
}
1229-
return FormatDynamicLibrariesIntoJSON(image_infos);
1141+
return FormatDynamicLibrariesIntoJSON(image_infos,
1142+
/* report_load_commands = */ true);
12301143
}
12311144

12321145
// From dyld's internal podyld_process_info.h:

0 commit comments

Comments
 (0)