@@ -170,6 +170,20 @@ static uint64_t decode_uint64(const char *p, int base, char **end = nullptr,
170170 return addr;
171171}
172172
173+ // / Attempts to parse a prefix of `number_str` as a uint64_t. If
174+ // / successful, the number is returned and the prefix is dropped from
175+ // / `number_str`.
176+ static std::optional<uint64_t > extract_u64 (std::string_view &number_str) {
177+ char *str_end = nullptr ;
178+ errno = 0 ;
179+ uint64_t number = strtoull (number_str.data (), &str_end, 16 );
180+ if (errno != 0 )
181+ return std::nullopt ;
182+ assert (str_end);
183+ number_str.remove_prefix (str_end - number_str.data ());
184+ return number;
185+ }
186+
173187static void append_hex_value (std::ostream &ostrm, const void *buf,
174188 size_t buf_size, bool swap) {
175189 int i;
@@ -204,6 +218,25 @@ static void append_hexified_string(std::ostream &ostrm,
204218 }
205219}
206220
221+ // / Returns true if `str` starts with `prefix`.
222+ static bool starts_with (std::string_view str, std::string_view prefix) {
223+ return str.substr (0 , prefix.size ()) == prefix;
224+ }
225+
226+ // / Splits `list_str` into multiple string_views separated by `,`.
227+ static std::vector<std::string_view>
228+ parse_comma_separated_list (std::string_view list_str) {
229+ std::vector<std::string_view> list;
230+ while (!list_str.empty ()) {
231+ auto pos = list_str.find (' ,' );
232+ list.push_back (list_str.substr (0 , pos));
233+ if (pos == list_str.npos )
234+ break ;
235+ list_str.remove_prefix (pos + 1 );
236+ }
237+ return list;
238+ }
239+
207240// from System.framework/Versions/B/PrivateHeaders/sys/codesign.h
208241extern " C" {
209242#define CS_OPS_STATUS 0 /* return status */
@@ -270,6 +303,11 @@ void RNBRemote::CreatePacketTable() {
270303 " Read memory" ));
271304 t.push_back (Packet (read_register, &RNBRemote::HandlePacket_p, NULL , " p" ,
272305 " Read one register" ));
306+ // Careful: this *must* come before the `M` packet, as debugserver matches
307+ // packet prefixes against known packet names. Inverting the order would match
308+ // `MultiMemRead` as an `M` packet.
309+ t.push_back (Packet (multi_mem_read, &RNBRemote::HandlePacket_MultiMemRead,
310+ NULL , " MultiMemRead" , " Read multiple memory addresses" ));
273311 t.push_back (Packet (write_memory, &RNBRemote::HandlePacket_M, NULL , " M" ,
274312 " Write memory" ));
275313 t.push_back (Packet (write_register, &RNBRemote::HandlePacket_P, NULL , " P" ,
@@ -3150,6 +3188,88 @@ rnb_err_t RNBRemote::HandlePacket_m(const char *p) {
31503188 return SendPacket (ostrm.str ());
31513189}
31523190
3191+ rnb_err_t RNBRemote::HandlePacket_MultiMemRead (const char *p) {
3192+ const std::string_view packet_name (" MultiMemRead:" );
3193+ std::string_view packet (p);
3194+
3195+ if (!starts_with (packet, packet_name))
3196+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p,
3197+ " Invalid MultiMemRead packet prefix" );
3198+
3199+ packet.remove_prefix (packet_name.size ());
3200+
3201+ const std::string_view ranges_prefix (" ranges:" );
3202+ if (!starts_with (packet, ranges_prefix))
3203+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, packet.data (),
3204+ " Missing 'ranges' in MultiMemRead packet" );
3205+ packet.remove_prefix (ranges_prefix.size ());
3206+
3207+ std::vector<std::pair<nub_addr_t , std::size_t >> ranges;
3208+ std::size_t total_length = 0 ;
3209+
3210+ // Ranges should have the form: <addr>,<size>[,<addr>,<size>]*;
3211+ auto end_of_ranges_pos = packet.find (' ;' );
3212+ if (end_of_ranges_pos == packet.npos )
3213+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, packet.data (),
3214+ " MultiMemRead missing end of ranges marker" );
3215+
3216+ std::vector<std::string_view> numbers_list =
3217+ parse_comma_separated_list (packet.substr (0 , end_of_ranges_pos));
3218+ packet.remove_prefix (end_of_ranges_pos + 1 );
3219+
3220+ // Ranges are pairs, so the number of elements must be even.
3221+ if (numbers_list.size () % 2 == 1 )
3222+ return HandlePacket_ILLFORMED (
3223+ __FILE__, __LINE__, p,
3224+ " MultiMemRead has an odd number of numbers for the ranges" );
3225+
3226+ for (unsigned idx = 0 ; idx < numbers_list.size (); idx += 2 ) {
3227+ std::optional<uint64_t > maybe_addr = extract_u64 (numbers_list[idx]);
3228+ std::optional<uint64_t > maybe_length = extract_u64 (numbers_list[idx + 1 ]);
3229+ if (!maybe_addr || !maybe_length)
3230+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, packet.data (),
3231+ " Invalid MultiMemRead range" );
3232+ // A sanity check that the packet requested is not too large or a negative
3233+ // number.
3234+ if (*maybe_length > 4 * 1024 * 1024 )
3235+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, packet.data (),
3236+ " MultiMemRead length is too large" );
3237+
3238+ ranges.emplace_back (*maybe_addr, *maybe_length);
3239+ total_length += *maybe_length;
3240+ }
3241+
3242+ if (ranges.empty ())
3243+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p,
3244+ " MultiMemRead has an empty range list" );
3245+
3246+ if (!packet.empty ())
3247+ return HandlePacket_ILLFORMED (
3248+ __FILE__, __LINE__, p, " MultiMemRead packet has unrecognized fields" );
3249+
3250+ std::vector<std::vector<uint8_t >> buffers;
3251+ buffers.reserve (ranges.size ());
3252+ for (auto [base_addr, length] : ranges) {
3253+ buffers.emplace_back (length, 0 );
3254+ nub_size_t bytes_read = DNBProcessMemoryRead (m_ctx.ProcessID (), base_addr,
3255+ length, buffers.back ().data ());
3256+ buffers.back ().resize (bytes_read);
3257+ }
3258+
3259+ std::ostringstream reply_stream;
3260+ bool first = true ;
3261+ for (const std::vector<uint8_t > &buffer : buffers) {
3262+ reply_stream << (first ? " " : " ," ) << std::hex << buffer.size ();
3263+ first = false ;
3264+ }
3265+ reply_stream << ' ;' ;
3266+
3267+ for (const std::vector<uint8_t > &buffer : buffers)
3268+ binary_encode_data_vector (reply_stream, buffer);
3269+
3270+ return SendPacket (reply_stream.str ());
3271+ }
3272+
31533273// Read memory, sent it up as binary data.
31543274// Usage: xADDR,LEN
31553275// ADDR and LEN are both base 16.
@@ -3503,6 +3623,7 @@ rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
35033623 if (supports_memory_tagging ())
35043624 reply << " memory-tagging+;" ;
35053625
3626+ reply << " MultiMemRead+;" ;
35063627 return SendPacket (reply.str ().c_str ());
35073628}
35083629
0 commit comments