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