@@ -166,6 +166,20 @@ static uint64_t decode_uint64(const char *p, int base, char **end = nullptr,
166166 return addr;
167167}
168168
169+ // / Attempts to parse a prefix of `number_str` as a uint64_t. If
170+ // / successful, the number is returned and the prefix is dropped from
171+ // / `number_str`.
172+ static std::optional<uint64_t > extract_u64 (std::string_view &number_str) {
173+ char *str_end = nullptr ;
174+ errno = 0 ;
175+ uint64_t number = strtoull (number_str.data (), &str_end, 16 );
176+ if (errno != 0 )
177+ return std::nullopt ;
178+ assert (str_end);
179+ number_str.remove_prefix (str_end - number_str.data ());
180+ return number;
181+ }
182+
169183static void append_hex_value (std::ostream &ostrm, const void *buf,
170184 size_t buf_size, bool swap) {
171185 int i;
@@ -200,6 +214,25 @@ static void append_hexified_string(std::ostream &ostrm,
200214 }
201215}
202216
217+ // / Returns true if `str` starts with `prefix`.
218+ static bool starts_with (std::string_view str, std::string_view prefix) {
219+ return str.substr (0 , prefix.size ()) == prefix;
220+ }
221+
222+ // / Splits `list_str` into multiple string_views separated by `,`.
223+ static std::vector<std::string_view>
224+ parse_comma_separated_list (std::string_view list_str) {
225+ std::vector<std::string_view> list;
226+ while (!list_str.empty ()) {
227+ auto pos = list_str.find (' ,' );
228+ list.push_back (list_str.substr (0 , pos));
229+ if (pos == list_str.npos )
230+ break ;
231+ list_str.remove_prefix (pos + 1 );
232+ }
233+ return list;
234+ }
235+
203236// from System.framework/Versions/B/PrivateHeaders/sys/codesign.h
204237extern " C" {
205238#define CS_OPS_STATUS 0 /* return status */
@@ -266,6 +299,11 @@ void RNBRemote::CreatePacketTable() {
266299 " Read memory" ));
267300 t.push_back (Packet (read_register, &RNBRemote::HandlePacket_p, NULL , " p" ,
268301 " Read one register" ));
302+ // Careful: this *must* come before the `M` packet, as debugserver matches
303+ // packet prefixes against known packet names. Inverting the order would match
304+ // `MultiMemRead` as an `M` packet.
305+ t.push_back (Packet (multi_mem_read, &RNBRemote::HandlePacket_MultiMemRead,
306+ NULL , " MultiMemRead" , " Read multiple memory addresses" ));
269307 t.push_back (Packet (write_memory, &RNBRemote::HandlePacket_M, NULL , " M" ,
270308 " Write memory" ));
271309 t.push_back (Packet (write_register, &RNBRemote::HandlePacket_P, NULL , " P" ,
@@ -3144,6 +3182,88 @@ rnb_err_t RNBRemote::HandlePacket_m(const char *p) {
31443182 return SendPacket (ostrm.str ());
31453183}
31463184
3185+ rnb_err_t RNBRemote::HandlePacket_MultiMemRead (const char *p) {
3186+ const std::string_view packet_name (" MultiMemRead:" );
3187+ std::string_view packet (p);
3188+
3189+ if (!starts_with (packet, packet_name))
3190+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p,
3191+ " Invalid MultiMemRead packet prefix" );
3192+
3193+ packet.remove_prefix (packet_name.size ());
3194+
3195+ const std::string_view ranges_prefix (" ranges:" );
3196+ if (!starts_with (packet, ranges_prefix))
3197+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, packet.data (),
3198+ " Missing 'ranges' in MultiMemRead packet" );
3199+ packet.remove_prefix (ranges_prefix.size ());
3200+
3201+ std::vector<std::pair<nub_addr_t , std::size_t >> ranges;
3202+ std::size_t total_length = 0 ;
3203+
3204+ // Ranges should have the form: <addr>,<size>[,<addr>,<size>]*;
3205+ auto end_of_ranges_pos = packet.find (' ;' );
3206+ if (end_of_ranges_pos == packet.npos )
3207+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, packet.data (),
3208+ " MultiMemRead missing end of ranges marker" );
3209+
3210+ std::vector<std::string_view> numbers_list =
3211+ parse_comma_separated_list (packet.substr (0 , end_of_ranges_pos));
3212+ packet.remove_prefix (end_of_ranges_pos + 1 );
3213+
3214+ // Ranges are pairs, so the number of elements must be even.
3215+ if (numbers_list.size () % 2 == 1 )
3216+ return HandlePacket_ILLFORMED (
3217+ __FILE__, __LINE__, p,
3218+ " MultiMemRead has an odd number of numbers for the ranges" );
3219+
3220+ for (unsigned idx = 0 ; idx < numbers_list.size (); idx += 2 ) {
3221+ std::optional<uint64_t > maybe_addr = extract_u64 (numbers_list[idx]);
3222+ std::optional<uint64_t > maybe_length = extract_u64 (numbers_list[idx + 1 ]);
3223+ if (!maybe_addr || !maybe_length)
3224+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, packet.data (),
3225+ " Invalid MultiMemRead range" );
3226+ // A sanity check that the packet requested is not too large or a negative
3227+ // number.
3228+ if (*maybe_length > 4 * 1024 * 1024 )
3229+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, packet.data (),
3230+ " MultiMemRead length is too large" );
3231+
3232+ ranges.emplace_back (*maybe_addr, *maybe_length);
3233+ total_length += *maybe_length;
3234+ }
3235+
3236+ if (ranges.empty ())
3237+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p,
3238+ " MultiMemRead has an empty range list" );
3239+
3240+ if (!packet.empty ())
3241+ return HandlePacket_ILLFORMED (
3242+ __FILE__, __LINE__, p, " MultiMemRead packet has unrecognized fields" );
3243+
3244+ std::vector<std::vector<uint8_t >> buffers;
3245+ buffers.reserve (ranges.size ());
3246+ for (auto [base_addr, length] : ranges) {
3247+ buffers.emplace_back (length, 0 );
3248+ nub_size_t bytes_read = DNBProcessMemoryRead (m_ctx.ProcessID (), base_addr,
3249+ length, buffers.back ().data ());
3250+ buffers.back ().resize (bytes_read);
3251+ }
3252+
3253+ std::ostringstream reply_stream;
3254+ bool first = true ;
3255+ for (const std::vector<uint8_t > &buffer : buffers) {
3256+ reply_stream << (first ? " " : " ," ) << std::hex << buffer.size ();
3257+ first = false ;
3258+ }
3259+ reply_stream << ' ;' ;
3260+
3261+ for (const std::vector<uint8_t > &buffer : buffers)
3262+ binary_encode_data_vector (reply_stream, buffer);
3263+
3264+ return SendPacket (reply_stream.str ());
3265+ }
3266+
31473267// Read memory, sent it up as binary data.
31483268// Usage: xADDR,LEN
31493269// ADDR and LEN are both base 16.
@@ -3482,6 +3602,7 @@ rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
34823602 reply << " SupportedWatchpointTypes=x86_64;" ;
34833603#endif
34843604
3605+ reply << " MultiMemRead+;" ;
34853606 return SendPacket (reply.str ().c_str ());
34863607}
34873608
0 commit comments