@@ -270,6 +270,11 @@ void RNBRemote::CreatePacketTable() {
270270 " Read memory" ));
271271 t.push_back (Packet (read_register, &RNBRemote::HandlePacket_p, NULL , " p" ,
272272 " Read one register" ));
273+ // Careful: this *must* come before the `M` packet, as debugserver matches
274+ // packet prefixes against known packet names. Inverting the order would match
275+ // `MultiMemRead` as an `M` packet.
276+ t.push_back (Packet (multi_mem_read, &RNBRemote::HandlePacket_MultiMemRead,
277+ NULL , " MultiMemRead" , " Read multiple memory addresses" ));
273278 t.push_back (Packet (write_memory, &RNBRemote::HandlePacket_M, NULL , " M" ,
274279 " Write memory" ));
275280 t.push_back (Packet (write_register, &RNBRemote::HandlePacket_P, NULL , " P" ,
@@ -3150,6 +3155,140 @@ rnb_err_t RNBRemote::HandlePacket_m(const char *p) {
31503155 return SendPacket (ostrm.str ());
31513156}
31523157
3158+ // / Returns true if `str` starts with `prefix`.
3159+ static bool starts_with (std::string_view str, std::string_view prefix) {
3160+ return str.size () >= prefix.size () &&
3161+ str.compare (0 , prefix.size (), prefix) == 0 ;
3162+ }
3163+
3164+ // / Attempts to parse a prefix of `number_str` as a number of type `T`. If
3165+ // / successful, the number is returned and the prefix is dropped from
3166+ // / `number_str`.
3167+ template <typename T>
3168+ static std::optional<T> extract_number (std::string_view &number_str) {
3169+ static_assert (std::is_integral<T>());
3170+ char *str_end = nullptr ;
3171+ errno = 0 ;
3172+ nub_addr_t number = strtoull (number_str.data (), &str_end, 16 );
3173+ if (errno != 0 )
3174+ return std::nullopt ;
3175+ assert (str_end);
3176+ number_str.remove_prefix (str_end - number_str.data ());
3177+ return number;
3178+ }
3179+
3180+ // / Splits `list_str` into multiple string_views separated by `,`.
3181+ static std::vector<std::string_view>
3182+ parse_comma_separated_list (std::string_view list_str) {
3183+ std::vector<std::string_view> list;
3184+ while (!list_str.empty ()) {
3185+ auto pos = list_str.find (' ,' );
3186+ list.push_back (list_str.substr (0 , pos));
3187+ if (pos == list_str.npos )
3188+ break ;
3189+ list_str.remove_prefix (pos + 1 );
3190+ }
3191+ return list;
3192+ }
3193+
3194+ rnb_err_t RNBRemote::HandlePacket_MultiMemRead (const char *p) {
3195+ const std::string_view packet_name (" MultiMemRead:" );
3196+ std::string_view packet (p);
3197+
3198+ if (!starts_with (packet, packet_name))
3199+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p,
3200+ " Invalid MultiMemRead packet prefix" );
3201+
3202+ packet.remove_prefix (packet_name.size ());
3203+
3204+ const std::string_view ranges_prefix (" ranges:" );
3205+ if (!starts_with (packet, ranges_prefix))
3206+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, packet.data (),
3207+ " Missing 'ranges' in MultiMemRead packet" );
3208+ packet.remove_prefix (ranges_prefix.size ());
3209+
3210+ std::vector<std::pair<nub_addr_t , std::size_t >> ranges;
3211+ std::size_t total_length = 0 ;
3212+
3213+ // Ranges should have the form: <addr>,<size>[,<addr>,<size>]*;
3214+ auto end_of_ranges_pos = packet.find (' ;' );
3215+ if (end_of_ranges_pos == packet.npos )
3216+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, packet.data (),
3217+ " MultiMemRead missing end of ranges marker" );
3218+
3219+ std::vector<std::string_view> numbers_list =
3220+ parse_comma_separated_list (packet.substr (0 , end_of_ranges_pos));
3221+ packet.remove_prefix (end_of_ranges_pos + 1 );
3222+
3223+ // Ranges are pairs, so the number of elements must be even.
3224+ if (numbers_list.size () % 2 == 1 )
3225+ return HandlePacket_ILLFORMED (
3226+ __FILE__, __LINE__, p,
3227+ " MultiMemRead has an odd number of numbers for the ranges" );
3228+
3229+ for (unsigned idx = 0 ; idx < numbers_list.size (); idx += 2 ) {
3230+ std::optional<nub_addr_t > maybe_addr =
3231+ extract_number<nub_addr_t >(numbers_list[idx]);
3232+ std::optional<size_t > maybe_length =
3233+ extract_number<size_t >(numbers_list[idx + 1 ]);
3234+ if (!maybe_addr || !maybe_length)
3235+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, packet.data (),
3236+ " Invalid MultiMemRead range" );
3237+ // A sanity check that the packet requested is not too large or a negative
3238+ // number.
3239+ if (*maybe_length > 4 * 1024 * 1024 )
3240+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, packet.data (),
3241+ " MultiMemRead length is too large" );
3242+
3243+ ranges.emplace_back (*maybe_addr, *maybe_length);
3244+ total_length += *maybe_length;
3245+ }
3246+
3247+ if (ranges.empty ())
3248+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p,
3249+ " MultiMemRead has an empty range list" );
3250+
3251+ // If 'options:' is present, ensure it's empty.
3252+ const std::string_view options_prefix (" options:" );
3253+ if (starts_with (packet, options_prefix)) {
3254+ packet.remove_prefix (options_prefix.size ());
3255+ if (packet.empty ())
3256+ return HandlePacket_ILLFORMED (
3257+ __FILE__, __LINE__, packet.data (),
3258+ " Too short 'options' in MultiMemRead packet" );
3259+ if (packet[0 ] != ' ;' )
3260+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, packet.data (),
3261+ " Unimplemented 'options' in MultiMemRead" );
3262+ packet.remove_prefix (1 );
3263+ }
3264+
3265+ if (!packet.empty ())
3266+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p,
3267+ " Invalid MultiMemRead address_range" );
3268+
3269+ std::vector<std::vector<uint8_t >> buffers;
3270+ buffers.reserve (ranges.size ());
3271+ for (auto [base_addr, length] : ranges) {
3272+ buffers.emplace_back (length, ' \0 ' );
3273+ nub_size_t bytes_read = DNBProcessMemoryRead (m_ctx.ProcessID (), base_addr,
3274+ length, buffers.back ().data ());
3275+ buffers.back ().resize (bytes_read);
3276+ }
3277+
3278+ std::ostringstream reply_stream;
3279+ bool first = true ;
3280+ for (const std::vector<uint8_t > &buffer : buffers) {
3281+ reply_stream << (first ? " " : " ," ) << std::hex << buffer.size ();
3282+ first = false ;
3283+ }
3284+ reply_stream << ' ;' ;
3285+
3286+ for (const std::vector<uint8_t > &buffer : buffers)
3287+ binary_encode_data_vector (reply_stream, buffer);
3288+
3289+ return SendPacket (reply_stream.str ());
3290+ }
3291+
31533292// Read memory, sent it up as binary data.
31543293// Usage: xADDR,LEN
31553294// ADDR and LEN are both base 16.
@@ -3503,6 +3642,7 @@ rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
35033642 if (supports_memory_tagging ())
35043643 reply << " memory-tagging+;" ;
35053644
3645+ reply << " MultiMemRead+;" ;
35063646 return SendPacket (reply.str ().c_str ());
35073647}
35083648
0 commit comments