summaryrefslogtreecommitdiff
path: root/lldb/tools/debugserver/source/RNBRemote.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/tools/debugserver/source/RNBRemote.cpp')
-rw-r--r--lldb/tools/debugserver/source/RNBRemote.cpp403
1 files changed, 293 insertions, 110 deletions
diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp
index d9fb22c6a1c0..b2d79377f1ee 100644
--- a/lldb/tools/debugserver/source/RNBRemote.cpp
+++ b/lldb/tools/debugserver/source/RNBRemote.cpp
@@ -22,6 +22,9 @@
#include <mach/mach_vm.h>
#include <mach/task_info.h>
#include <memory>
+#if __has_include(<os/security_config.h>)
+#include <os/security_config.h>
+#endif
#include <pwd.h>
#include <string>
#include <sys/stat.h>
@@ -90,11 +93,34 @@ static const std::string JSON_ASYNC_TYPE_KEY_NAME("type");
std::setfill(' ') << std::setw((iword_idx)) << ""
#define INDENT_WITH_TABS(iword_idx) \
std::setfill('\t') << std::setw((iword_idx)) << ""
-// Class to handle communications via gdb remote protocol.
-// Prototypes
+// If `ch` is a meta character as per the binary packet convention in the
+// gdb-remote protocol, quote it and write it into `stream`, otherwise write it
+// as is.
+static void binary_encode_char(std::ostringstream &stream, char ch) {
+ if (ch == '#' || ch == '$' || ch == '}' || ch == '*') {
+ stream.put('}');
+ stream.put(ch ^ 0x20);
+ } else {
+ stream.put(ch);
+ }
+}
+
+// Equivalent to calling binary_encode_char for every element of `data`.
+static void binary_encode_data_vector(std::ostringstream &stream,
+ std::vector<uint8_t> data) {
+ for (auto ch : data)
+ binary_encode_char(stream, ch);
+}
-static std::string binary_encode_string(const std::string &s);
+// Quote any meta characters in a std::string as per the binary
+// packet convention in the gdb-remote protocol.
+static std::string binary_encode_string(const std::string &s) {
+ std::ostringstream stream;
+ for (char ch : s)
+ binary_encode_char(stream, ch);
+ return stream.str();
+}
// Decode a single hex character and return the hex value as a number or
// -1 if "ch" is not a hex character.
@@ -136,16 +162,30 @@ static std::string decode_hex_ascii_string(const char *p,
return arg;
}
-uint64_t decode_uint64(const char *p, int base, char **end = nullptr,
- uint64_t fail_value = 0) {
+static uint64_t decode_uint64(const char *p, int base, char **end = nullptr,
+ uint64_t fail_value = 0) {
nub_addr_t addr = strtoull(p, end, 16);
if (addr == 0 && errno != 0)
return fail_value;
return addr;
}
-void append_hex_value(std::ostream &ostrm, const void *buf, size_t buf_size,
- bool swap) {
+/// Attempts to parse a prefix of `number_str` as a uint64_t. If
+/// successful, the number is returned and the prefix is dropped from
+/// `number_str`.
+static std::optional<uint64_t> extract_u64(std::string_view &number_str) {
+ char *str_end = nullptr;
+ errno = 0;
+ uint64_t number = strtoull(number_str.data(), &str_end, 16);
+ if (errno != 0)
+ return std::nullopt;
+ assert(str_end);
+ number_str.remove_prefix(str_end - number_str.data());
+ return number;
+}
+
+static void append_hex_value(std::ostream &ostrm, const void *buf,
+ size_t buf_size, bool swap) {
int i;
const uint8_t *p = (const uint8_t *)buf;
if (swap) {
@@ -157,7 +197,7 @@ void append_hex_value(std::ostream &ostrm, const void *buf, size_t buf_size,
}
}
-std::string cstring_to_asciihex_string(const char *str) {
+static std::string cstring_to_asciihex_string(const char *str) {
std::string hex_str;
hex_str.reserve(strlen(str) * 2);
while (str && *str) {
@@ -169,7 +209,8 @@ std::string cstring_to_asciihex_string(const char *str) {
return hex_str;
}
-void append_hexified_string(std::ostream &ostrm, const std::string &string) {
+static void append_hexified_string(std::ostream &ostrm,
+ const std::string &string) {
size_t string_size = string.size();
const char *string_buf = string.c_str();
for (size_t i = 0; i < string_size; i++) {
@@ -177,6 +218,25 @@ void append_hexified_string(std::ostream &ostrm, const std::string &string) {
}
}
+/// Returns true if `str` starts with `prefix`.
+static bool starts_with(std::string_view str, std::string_view prefix) {
+ return str.substr(0, prefix.size()) == prefix;
+}
+
+/// Splits `list_str` into multiple string_views separated by `,`.
+static std::vector<std::string_view>
+parse_comma_separated_list(std::string_view list_str) {
+ std::vector<std::string_view> list;
+ while (!list_str.empty()) {
+ auto pos = list_str.find(',');
+ list.push_back(list_str.substr(0, pos));
+ if (pos == list_str.npos)
+ break;
+ list_str.remove_prefix(pos + 1);
+ }
+ return list;
+}
+
// from System.framework/Versions/B/PrivateHeaders/sys/codesign.h
extern "C" {
#define CS_OPS_STATUS 0 /* return status */
@@ -243,6 +303,11 @@ void RNBRemote::CreatePacketTable() {
"Read memory"));
t.push_back(Packet(read_register, &RNBRemote::HandlePacket_p, NULL, "p",
"Read one register"));
+ // Careful: this *must* come before the `M` packet, as debugserver matches
+ // packet prefixes against known packet names. Inverting the order would match
+ // `MultiMemRead` as an `M` packet.
+ t.push_back(Packet(multi_mem_read, &RNBRemote::HandlePacket_MultiMemRead,
+ NULL, "MultiMemRead", "Read multiple memory addresses"));
t.push_back(Packet(write_memory, &RNBRemote::HandlePacket_M, NULL, "M",
"Write memory"));
t.push_back(Packet(write_register, &RNBRemote::HandlePacket_P, NULL, "P",
@@ -502,6 +567,8 @@ void RNBRemote::CreatePacketTable() {
memory_region_info, &RNBRemote::HandlePacket_MemoryRegionInfo, NULL,
"qMemoryRegionInfo", "Return size and attributes of a memory region that "
"contains the given address"));
+ t.push_back(Packet(get_memory_tags, &RNBRemote::HandlePacket_qMemTags, NULL,
+ "qMemTags", "Return tags for a region of memory"));
t.push_back(Packet(get_profile_data, &RNBRemote::HandlePacket_GetProfileData,
NULL, "qGetProfileData",
"Return profiling data of the current target."));
@@ -1022,8 +1089,6 @@ rnb_err_t RNBRemote::HandleAsyncPacket(PacketEnum *type) {
rnb_err_t RNBRemote::HandleReceivedPacket(PacketEnum *type) {
static DNBTimer g_packetTimer(true);
- // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s",
- // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
rnb_err_t err = rnb_err;
std::string packet_data;
RNBRemote::Packet packet_info;
@@ -1279,8 +1344,7 @@ static cpu_type_t best_guess_cpu_type() {
LEN is the number of bytes to be processed. If a character is escaped,
it is 2 characters for LEN. A LEN of -1 means decode-until-nul-byte
(end of string). */
-
-std::vector<uint8_t> decode_binary_data(const char *str, size_t len) {
+static std::vector<uint8_t> decode_binary_data(const char *str, size_t len) {
std::vector<uint8_t> bytes;
if (len == 0) {
return bytes;
@@ -1299,31 +1363,10 @@ std::vector<uint8_t> decode_binary_data(const char *str, size_t len) {
return bytes;
}
-// Quote any meta characters in a std::string as per the binary
-// packet convention in the gdb-remote protocol.
-
-static std::string binary_encode_string(const std::string &s) {
- std::string output;
- const size_t s_size = s.size();
- const char *s_chars = s.c_str();
-
- for (size_t i = 0; i < s_size; i++) {
- unsigned char ch = *(s_chars + i);
- if (ch == '#' || ch == '$' || ch == '}' || ch == '*') {
- output.push_back('}'); // 0x7d
- output.push_back(ch ^ 0x20);
- } else {
- output.push_back(ch);
- }
- }
- return output;
-}
-
// If the value side of a key-value pair in JSON is a string,
// and that string has a " character in it, the " character must
// be escaped.
-
-std::string json_string_quote_metachars(const std::string &s) {
+static std::string json_string_quote_metachars(const std::string &s) {
if (s.find('"') == std::string::npos)
return s;
@@ -1457,15 +1500,6 @@ bool RNBRemote::InitializeRegisters(bool force) {
}
}
- // for (auto &reg_entry: g_dynamic_register_map)
- // {
- // DNBLogThreaded("%4i: size = %3u, pseudo = %i, name = %s",
- // reg_entry.offset,
- // reg_entry.nub_info.size,
- // reg_entry.nub_info.value_regs != NULL,
- // reg_entry.nub_info.name);
- // }
-
g_reg_entries = g_dynamic_register_map.data();
g_num_reg_entries = g_dynamic_register_map.size();
}
@@ -1714,7 +1748,7 @@ rnb_err_t RNBRemote::HandlePacket_qThreadExtraInfo(const char *p) {
return SendPacket("");
}
-const char *k_space_delimiters = " \t";
+static const char *k_space_delimiters = " \t";
static void skip_spaces(std::string &line) {
if (!line.empty()) {
size_t space_pos = line.find_first_not_of(k_space_delimiters);
@@ -2019,7 +2053,7 @@ rnb_err_t RNBRemote::HandlePacket_qRegisterInfo(const char *p) {
QSetLogging:bitmask=LOG_ALL;mode=asl;
*/
-rnb_err_t set_logging(const char *p) {
+static rnb_err_t set_logging(const char *p) {
int bitmask = 0;
while (p && *p != '\0') {
if (strncmp(p, "bitmask=", sizeof("bitmask=") - 1) == 0) {
@@ -2563,11 +2597,10 @@ rnb_err_t RNBRemote::HandlePacket_QSetProcessEvent(const char *p) {
// If a fail_value is provided, a correct-length reply is always provided,
// even if the register cannot be read right now on this thread.
-bool register_value_in_hex_fixed_width(std::ostream &ostrm, nub_process_t pid,
- nub_thread_t tid,
- const register_map_entry_t *reg,
- const DNBRegisterValue *reg_value_ptr,
- std::optional<uint8_t> fail_value) {
+static bool register_value_in_hex_fixed_width(
+ std::ostream &ostrm, nub_process_t pid, nub_thread_t tid,
+ const register_map_entry_t *reg, const DNBRegisterValue *reg_value_ptr,
+ std::optional<uint8_t> fail_value) {
if (reg != NULL) {
std::unique_ptr<DNBRegisterValue> reg_value =
std::make_unique<DNBRegisterValue>();
@@ -2594,7 +2627,7 @@ bool register_value_in_hex_fixed_width(std::ostream &ostrm, nub_process_t pid,
return false;
}
-void debugserver_regnum_with_fixed_width_hex_register_value(
+static void debugserver_regnum_with_fixed_width_hex_register_value(
std::ostream &ostrm, nub_process_t pid, nub_thread_t tid,
const register_map_entry_t *reg, const DNBRegisterValue *reg_value_ptr,
std::optional<uint8_t> fail_value) {
@@ -3155,6 +3188,88 @@ rnb_err_t RNBRemote::HandlePacket_m(const char *p) {
return SendPacket(ostrm.str());
}
+rnb_err_t RNBRemote::HandlePacket_MultiMemRead(const char *p) {
+ const std::string_view packet_name("MultiMemRead:");
+ std::string_view packet(p);
+
+ if (!starts_with(packet, packet_name))
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid MultiMemRead packet prefix");
+
+ packet.remove_prefix(packet_name.size());
+
+ const std::string_view ranges_prefix("ranges:");
+ if (!starts_with(packet, ranges_prefix))
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(),
+ "Missing 'ranges' in MultiMemRead packet");
+ packet.remove_prefix(ranges_prefix.size());
+
+ std::vector<std::pair<nub_addr_t, std::size_t>> ranges;
+ std::size_t total_length = 0;
+
+ // Ranges should have the form: <addr>,<size>[,<addr>,<size>]*;
+ auto end_of_ranges_pos = packet.find(';');
+ if (end_of_ranges_pos == packet.npos)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(),
+ "MultiMemRead missing end of ranges marker");
+
+ std::vector<std::string_view> numbers_list =
+ parse_comma_separated_list(packet.substr(0, end_of_ranges_pos));
+ packet.remove_prefix(end_of_ranges_pos + 1);
+
+ // Ranges are pairs, so the number of elements must be even.
+ if (numbers_list.size() % 2 == 1)
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p,
+ "MultiMemRead has an odd number of numbers for the ranges");
+
+ for (unsigned idx = 0; idx < numbers_list.size(); idx += 2) {
+ std::optional<uint64_t> maybe_addr = extract_u64(numbers_list[idx]);
+ std::optional<uint64_t> maybe_length = extract_u64(numbers_list[idx + 1]);
+ if (!maybe_addr || !maybe_length)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(),
+ "Invalid MultiMemRead range");
+ // A sanity check that the packet requested is not too large or a negative
+ // number.
+ if (*maybe_length > 4 * 1024 * 1024)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(),
+ "MultiMemRead length is too large");
+
+ ranges.emplace_back(*maybe_addr, *maybe_length);
+ total_length += *maybe_length;
+ }
+
+ if (ranges.empty())
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "MultiMemRead has an empty range list");
+
+ if (!packet.empty())
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p, "MultiMemRead packet has unrecognized fields");
+
+ std::vector<std::vector<uint8_t>> buffers;
+ buffers.reserve(ranges.size());
+ for (auto [base_addr, length] : ranges) {
+ buffers.emplace_back(length, 0);
+ nub_size_t bytes_read = DNBProcessMemoryRead(m_ctx.ProcessID(), base_addr,
+ length, buffers.back().data());
+ buffers.back().resize(bytes_read);
+ }
+
+ std::ostringstream reply_stream;
+ bool first = true;
+ for (const std::vector<uint8_t> &buffer : buffers) {
+ reply_stream << (first ? "" : ",") << std::hex << buffer.size();
+ first = false;
+ }
+ reply_stream << ';';
+
+ for (const std::vector<uint8_t> &buffer : buffers)
+ binary_encode_data_vector(reply_stream, buffer);
+
+ return SendPacket(reply_stream.str());
+}
+
// Read memory, sent it up as binary data.
// Usage: xADDR,LEN
// ADDR and LEN are both base 16.
@@ -3211,21 +3326,9 @@ rnb_err_t RNBRemote::HandlePacket_x(const char *p) {
return SendErrorPacket("E80");
}
- std::vector<uint8_t> buf_quoted;
- buf_quoted.reserve(bytes_read + 30);
- for (nub_size_t i = 0; i < bytes_read; i++) {
- if (buf[i] == '#' || buf[i] == '$' || buf[i] == '}' || buf[i] == '*') {
- buf_quoted.push_back(0x7d);
- buf_quoted.push_back(buf[i] ^ 0x20);
- } else {
- buf_quoted.push_back(buf[i]);
- }
- }
- length = buf_quoted.size();
-
+ buf.resize(bytes_read);
std::ostringstream ostrm;
- for (unsigned long i = 0; i < length; i++)
- ostrm << buf_quoted[i];
+ binary_encode_data_vector(ostrm, buf);
return SendPacket(ostrm.str());
}
@@ -3475,6 +3578,18 @@ static bool GetProcessNameFrom_vAttach(const char *&p,
return return_val;
}
+static bool supports_memory_tagging() {
+ const char *name = "hw.optional.arm.FEAT_MTE4";
+ uint32_t val;
+ size_t len = sizeof(val);
+ int ret = ::sysctlbyname(name, &val, &len, nullptr, 0);
+ if (ret != 0)
+ return false;
+
+ assert(len == sizeof(val));
+ return val;
+}
+
rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
uint32_t max_packet_size = 128 * 1024; // 128 KiB is a reasonable max packet
// size--debugger can always use less
@@ -3505,6 +3620,10 @@ rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
reply << "SupportedWatchpointTypes=x86_64;";
#endif
+ if (supports_memory_tagging())
+ reply << "memory-tagging+;";
+
+ reply << "MultiMemRead+;";
return SendPacket(reply.str().c_str());
}
@@ -4251,7 +4370,6 @@ rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) {
is in unmapped memory
Region lookup cannot be performed on this platform or process is not
yet launched
- This packet isn't implemented
Examples of use:
qMemoryRegionInfo:3a55140
@@ -4303,6 +4421,16 @@ rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) {
ostrm << 'x';
ostrm << ';';
+ if (!region_info.flags.empty()) {
+ ostrm << "flags:";
+ for (size_t i = 0; i < region_info.flags.size(); i++) {
+ if (i != 0)
+ ostrm << " "; // Separator is whitespace
+ ostrm << region_info.flags[i];
+ }
+ ostrm << ";";
+ }
+
ostrm << "dirty-pages:";
if (region_info.dirty_pages.size() > 0) {
bool first = true;
@@ -4327,6 +4455,62 @@ rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) {
return SendPacket(ostrm.str());
}
+// qMemTags:<hex address>,<hex length>:<hex type>
+rnb_err_t RNBRemote::HandlePacket_qMemTags(const char *p) {
+ nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("OK");
+
+ StdStringExtractor packet(p);
+ packet.SetFilePos(strlen("qMemTags:"));
+
+ // Address
+ nub_addr_t addr =
+ packet.GetHexMaxU64(StdStringExtractor::BigEndian, INVALID_NUB_ADDRESS);
+ if (addr == INVALID_NUB_ADDRESS)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid/missing address in qMemTags packet");
+ // ,
+ if (packet.GetChar() != ',')
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid qMemTags packet format");
+ // Length
+ uint64_t length = packet.GetHexMaxU64(StdStringExtractor::BigEndian, 0);
+ if (length == 0)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid/missing length in qMemTags packet");
+ // :
+ if (packet.GetChar() != ':')
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid qMemTags packet format");
+ // Type
+ // On the LLDB side this is a `int32_t` serialized as (unsigned) hex, which
+ // means negative values will show up as large positive values here. Right
+ // now, we only support MTE (type 1), so we can ignore this complication.
+ uint32_t type = packet.GetHexMaxU32(StdStringExtractor::BigEndian, 0);
+ if (type != 1 /* MTE */)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid/missing type in qMemTags packet, "
+ "only MTE (type 1) is supported");
+ // <EOF>
+ if (packet.GetBytesLeft() != 0)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid qMemTags packet format");
+
+ std::vector<uint8_t> tags;
+ bool ok = DNBProcessGetMemoryTags(pid, addr, length, tags);
+ if (!ok)
+ return SendErrorPacket("E91");
+
+ std::ostringstream ostrm;
+ ostrm << "m"; // Multi part replies
+ for (uint8_t tag : tags) {
+ ostrm << RAWHEX8(tag); // 2 hex chars per tag
+ }
+
+ return SendPacket(ostrm.str());
+}
+
// qGetProfileData;scan_type:0xYYYYYYY
rnb_err_t RNBRemote::HandlePacket_GetProfileData(const char *p) {
nub_process_t pid = m_ctx.ProcessID();
@@ -4817,8 +5001,8 @@ rnb_err_t RNBRemote::HandlePacket_qHostInfo(const char *p) {
return SendPacket(strm.str());
}
-void XMLElementStart(std::ostringstream &s, uint32_t indent, const char *name,
- bool has_attributes) {
+static void XMLElementStart(std::ostringstream &s, uint32_t indent,
+ const char *name, bool has_attributes) {
if (indent)
s << INDENT_WITH_SPACES(indent);
s << '<' << name;
@@ -4826,43 +5010,22 @@ void XMLElementStart(std::ostringstream &s, uint32_t indent, const char *name,
s << '>' << std::endl;
}
-void XMLElementStartEndAttributes(std::ostringstream &s, bool empty) {
+static void XMLElementStartEndAttributes(std::ostringstream &s, bool empty) {
if (empty)
s << '/';
s << '>' << std::endl;
}
-void XMLElementEnd(std::ostringstream &s, uint32_t indent, const char *name) {
+static void XMLElementEnd(std::ostringstream &s, uint32_t indent,
+ const char *name) {
if (indent)
s << INDENT_WITH_SPACES(indent);
s << '<' << '/' << name << '>' << std::endl;
}
-void XMLElementWithStringValue(std::ostringstream &s, uint32_t indent,
- const char *name, const char *value,
- bool close = true) {
- if (value) {
- if (indent)
- s << INDENT_WITH_SPACES(indent);
- s << '<' << name << '>' << value;
- if (close)
- XMLElementEnd(s, 0, name);
- }
-}
-
-void XMLElementWithUnsignedValue(std::ostringstream &s, uint32_t indent,
- const char *name, uint64_t value,
- bool close = true) {
- if (indent)
- s << INDENT_WITH_SPACES(indent);
-
- s << '<' << name << '>' << DECIMAL << value;
- if (close)
- XMLElementEnd(s, 0, name);
-}
-
-void XMLAttributeString(std::ostringstream &s, const char *name,
- const char *value, const char *default_value = NULL) {
+static void XMLAttributeString(std::ostringstream &s, const char *name,
+ const char *value,
+ const char *default_value = NULL) {
if (value) {
if (default_value && strcmp(value, default_value) == 0)
return; // No need to emit the attribute because it matches the default
@@ -4871,15 +5034,16 @@ void XMLAttributeString(std::ostringstream &s, const char *name,
}
}
-void XMLAttributeUnsignedDecimal(std::ostringstream &s, const char *name,
- uint64_t value) {
+static void XMLAttributeUnsignedDecimal(std::ostringstream &s, const char *name,
+ uint64_t value) {
s << ' ' << name << "=\"" << DECIMAL << value << "\"";
}
-void GenerateTargetXMLRegister(std::ostringstream &s, const uint32_t reg_num,
- nub_size_t num_reg_sets,
- const DNBRegisterSetInfo *reg_set_info,
- const register_map_entry_t &reg) {
+static void GenerateTargetXMLRegister(std::ostringstream &s,
+ const uint32_t reg_num,
+ nub_size_t num_reg_sets,
+ const DNBRegisterSetInfo *reg_set_info,
+ const register_map_entry_t &reg) {
const char *default_lldb_encoding = "uint";
const char *lldb_encoding = default_lldb_encoding;
const char *gdb_group = "general";
@@ -5050,7 +5214,7 @@ void GenerateTargetXMLRegister(std::ostringstream &s, const uint32_t reg_num,
XMLElementStartEndAttributes(s, true);
}
-void GenerateTargetXMLRegisters(std::ostringstream &s) {
+static void GenerateTargetXMLRegisters(std::ostringstream &s) {
nub_size_t num_reg_sets = 0;
const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo(&num_reg_sets);
@@ -5089,7 +5253,7 @@ static const char *g_target_xml_footer = "</target>";
static std::string g_target_xml;
-void UpdateTargetXML() {
+static void UpdateTargetXML() {
std::ostringstream s;
s << g_target_xml_header << std::endl;
@@ -5224,8 +5388,9 @@ rnb_err_t RNBRemote::HandlePacket_jGetDyldProcessState(const char *p) {
// a one-level-deep JSON dictionary of key-value pairs. e.g.
// jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":144305}]
//
-uint64_t get_integer_value_for_key_name_from_json(const char *key,
- const char *json_string) {
+static uint64_t
+get_integer_value_for_key_name_from_json(const char *key,
+ const char *json_string) {
uint64_t retval = INVALID_NUB_ADDRESS;
std::string key_with_quotes = "\"";
key_with_quotes += key;
@@ -5261,9 +5426,9 @@ uint64_t get_integer_value_for_key_name_from_json(const char *key,
// Returns true if it was able to find the key name, and sets the 'value'
// argument to the value found.
-bool get_boolean_value_for_key_name_from_json(const char *key,
- const char *json_string,
- bool &value) {
+static bool get_boolean_value_for_key_name_from_json(const char *key,
+ const char *json_string,
+ bool &value) {
std::string key_with_quotes = "\"";
key_with_quotes += key;
key_with_quotes += "\"";
@@ -5300,7 +5465,7 @@ bool get_boolean_value_for_key_name_from_json(const char *key,
// Returns true if it was able to find the key name, false if it did not.
// "ints" will have all integers found in the array appended to it.
-bool get_array_of_ints_value_for_key_name_from_json(
+static bool get_array_of_ints_value_for_key_name_from_json(
const char *key, const char *json_string, std::vector<uint64_t> &ints) {
std::string key_with_quotes = "\"";
key_with_quotes += key;
@@ -6162,6 +6327,21 @@ GetCPUTypesFromHost(nub_process_t pid) {
return {cputype, cpusubtype};
}
+static bool ProcessRunningWithMemoryTagging(pid_t pid) {
+#if __has_include(<os/security_config.h>)
+ if (__builtin_available(macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0,
+ visionOS 26.0, driverkit 25.0, *)) {
+ os_security_config_t config;
+ int ret = ::os_security_config_get_for_proc(pid, &config);
+ if (ret != 0)
+ return false;
+
+ return (config & OS_SECURITY_CONFIG_MTE);
+ }
+#endif
+ return false;
+}
+
// Note that all numeric values returned by qProcessInfo are hex encoded,
// including the pid and the cpu type.
@@ -6338,6 +6518,9 @@ rnb_err_t RNBRemote::HandlePacket_qProcessInfo(const char *p) {
rep << "vendor:apple;";
+ if (ProcessRunningWithMemoryTagging(pid))
+ rep << "mte:enabled;";
+
#if defined(__LITTLE_ENDIAN__)
rep << "endian:little;";
#elif defined(__BIG_ENDIAN__)