diff options
Diffstat (limited to 'lldb/tools/debugserver')
-rw-r--r-- | lldb/tools/debugserver/source/DNB.cpp | 10 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/DNB.h | 3 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/DNBDefs.h | 3 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/MachTask.h | 2 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/MachTask.mm | 17 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp | 59 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/MachVMMemory.h | 2 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp | 46 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/MachVMRegion.h | 3 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/RNBRemote.cpp | 105 | ||||
-rw-r--r-- | lldb/tools/debugserver/source/RNBRemote.h | 2 |
11 files changed, 249 insertions, 3 deletions
diff --git a/lldb/tools/debugserver/source/DNB.cpp b/lldb/tools/debugserver/source/DNB.cpp index f541134..0cd48d9 100644 --- a/lldb/tools/debugserver/source/DNB.cpp +++ b/lldb/tools/debugserver/source/DNB.cpp @@ -1386,6 +1386,16 @@ int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr, return -1; } +nub_bool_t DNBProcessGetMemoryTags(nub_process_t pid, nub_addr_t addr, + nub_size_t size, + std::vector<uint8_t> &tags) { + MachProcessSP procSP; + if (GetProcessSP(pid, procSP)) + return procSP->Task().GetMemoryTags(addr, size, tags); + + return false; +} + std::string DNBProcessGetProfileData(nub_process_t pid, DNBProfileDataScanType scanType) { MachProcessSP procSP; diff --git a/lldb/tools/debugserver/source/DNB.h b/lldb/tools/debugserver/source/DNB.h index 10d1f68..1f3d539 100644 --- a/lldb/tools/debugserver/source/DNB.h +++ b/lldb/tools/debugserver/source/DNB.h @@ -105,6 +105,9 @@ nub_bool_t DNBProcessMemoryDeallocate(nub_process_t pid, nub_addr_t addr) DNB_EXPORT; int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr, DNBRegionInfo *region_info) DNB_EXPORT; +nub_bool_t DNBProcessGetMemoryTags(nub_process_t pid, nub_addr_t addr, + nub_size_t size, + std::vector<uint8_t> &tags) DNB_EXPORT; std::string DNBProcessGetProfileData(nub_process_t pid, DNBProfileDataScanType scanType) DNB_EXPORT; diff --git a/lldb/tools/debugserver/source/DNBDefs.h b/lldb/tools/debugserver/source/DNBDefs.h index df8ca80..d98399a 100644 --- a/lldb/tools/debugserver/source/DNBDefs.h +++ b/lldb/tools/debugserver/source/DNBDefs.h @@ -358,10 +358,11 @@ struct DNBExecutableImageInfo { struct DNBRegionInfo { public: DNBRegionInfo() - : addr(0), size(0), permissions(0), dirty_pages(), vm_types() {} + : addr(0), size(0), permissions(0), flags(), dirty_pages(), vm_types() {} nub_addr_t addr; nub_addr_t size; uint32_t permissions; + std::vector<std::string> flags; std::vector<nub_addr_t> dirty_pages; std::vector<std::string> vm_types; }; diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.h b/lldb/tools/debugserver/source/MacOSX/MachTask.h index 2284f6b..c4a20b8 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachTask.h +++ b/lldb/tools/debugserver/source/MacOSX/MachTask.h @@ -56,6 +56,8 @@ public: nub_size_t ReadMemory(nub_addr_t addr, nub_size_t size, void *buf); nub_size_t WriteMemory(nub_addr_t addr, nub_size_t size, const void *buf); int GetMemoryRegionInfo(nub_addr_t addr, DNBRegionInfo *region_info); + nub_bool_t GetMemoryTags(nub_addr_t addr, nub_size_t size, + std::vector<uint8_t> &tags); std::string GetProfileData(DNBProfileDataScanType scanType); nub_addr_t AllocateMemory(nub_size_t size, uint32_t permissions); diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.mm b/lldb/tools/debugserver/source/MacOSX/MachTask.mm index e2395cf..21156fe 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachTask.mm +++ b/lldb/tools/debugserver/source/MacOSX/MachTask.mm @@ -229,6 +229,23 @@ int MachTask::GetMemoryRegionInfo(nub_addr_t addr, DNBRegionInfo *region_info) { return ret; } +//---------------------------------------------------------------------- +// MachTask::GetMemoryTags +//---------------------------------------------------------------------- +nub_bool_t MachTask::GetMemoryTags(nub_addr_t addr, nub_size_t size, + std::vector<uint8_t> &tags) { + task_t task = TaskPort(); + if (task == TASK_NULL) + return false; + + bool ok = m_vm_memory.GetMemoryTags(task, addr, size, tags); + DNBLogThreadedIf(LOG_MEMORY, "MachTask::GetMemoryTags ( addr = 0x%8.8llx, " + "size = 0x%8.8llx ) => %s ( tag count = %llu)", + (uint64_t)addr, (uint64_t)size, (ok ? "ok" : "err"), + (uint64_t)tags.size()); + return ok; +} + #define TIME_VALUE_TO_TIMEVAL(a, r) \ do { \ (r)->tv_sec = (a)->seconds; \ diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp index f3aa4d7..bb57245 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp +++ b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp @@ -13,6 +13,7 @@ #include "MachVMMemory.h" #include "DNBLog.h" #include "MachVMRegion.h" +#include <cassert> #include <dlfcn.h> #include <mach/mach_vm.h> #include <mach/shared_region.h> @@ -123,6 +124,7 @@ nub_bool_t MachVMMemory::GetMemoryRegionInfo(task_t task, nub_addr_t address, region_info->addr = vmRegion.StartAddress(); region_info->size = vmRegion.GetByteSize(); region_info->permissions = vmRegion.GetDNBPermissions(); + region_info->flags = vmRegion.GetFlags(); region_info->dirty_pages = get_dirty_pages(task, vmRegion.StartAddress(), vmRegion.GetByteSize()); region_info->vm_types = vmRegion.GetMemoryTypes(); @@ -150,6 +152,63 @@ nub_bool_t MachVMMemory::GetMemoryRegionInfo(task_t task, nub_addr_t address, return true; } +// API availability: +// mach_vm_update_pointers_with_remote_tags() - 26.0 +// VM_OFFSET_LIST_MAX macro - 26.1 +#ifndef VM_OFFSET_LIST_MAX +#define VM_OFFSET_LIST_MAX 512 +#endif +using mach_vm_offset_list_t = mach_vm_offset_t *; +using mach_vm_update_pointers_with_remote_tags_t = kern_return_t( + mach_port_name_t target, mach_vm_offset_list_t in_pointer_list, + mach_msg_type_number_t in_pointer_listCnt, + mach_vm_offset_list_t out_pointer_list, + mach_msg_type_number_t *out_pointer_listCnt); + +nub_bool_t MachVMMemory::GetMemoryTags(task_t task, nub_addr_t address, + nub_size_t size, + std::vector<uint8_t> &tags) { + static auto mach_vm_update_pointers_with_remote_tags = + (mach_vm_update_pointers_with_remote_tags_t *)dlsym( + RTLD_DEFAULT, "mach_vm_update_pointers_with_remote_tags"); + assert(mach_vm_update_pointers_with_remote_tags); + + // Max batch size supported by mach_vm_update_pointers_with_remote_tags. + constexpr uint32_t max_ptr_count = VM_OFFSET_LIST_MAX; + constexpr uint32_t tag_shift = 56; + constexpr nub_addr_t tag_mask = + ((nub_addr_t)0x0f << tag_shift); // Lower half of top byte. + constexpr uint32_t tag_granule = 16; + + mach_msg_type_number_t ptr_count = + (size / tag_granule) + ((size % tag_granule > 0) ? 1 : 0); + ptr_count = std::min(ptr_count, max_ptr_count); + + auto ptr_arr = std::make_unique<mach_vm_offset_t[]>(ptr_count); + for (size_t i = 0; i < ptr_count; i++) + ptr_arr[i] = (address + i * tag_granule); + + mach_msg_type_number_t ptr_count_out = ptr_count; + m_err = mach_vm_update_pointers_with_remote_tags( + task, ptr_arr.get(), ptr_count, ptr_arr.get(), &ptr_count_out); + + const bool failed = (m_err.Fail() || (ptr_count != ptr_count_out)); + if (failed || DNBLogCheckLogBit(LOG_MEMORY)) + m_err.LogThreaded("::mach_vm_update_pointers_with_remote_tags ( task = " + "0x%4.4x, ptr_count = %d ) => %i ( ptr_count_out = %d)", + task, ptr_count, m_err.Status(), ptr_count_out); + if (failed) + return false; + + tags.reserve(ptr_count); + for (size_t i = 0; i < ptr_count; i++) { + nub_addr_t tag = (ptr_arr[i] & tag_mask) >> tag_shift; + tags.push_back(tag); + } + + return true; +} + static uint64_t GetPhysicalMemory() { // This doesn't change often at all. No need to poll each time. static uint64_t physical_memory = 0; diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h index 05d2c02..8a76160 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h +++ b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h @@ -28,6 +28,8 @@ public: nub_size_t PageSize(task_t task); nub_bool_t GetMemoryRegionInfo(task_t task, nub_addr_t address, DNBRegionInfo *region_info); + nub_bool_t GetMemoryTags(task_t task, nub_addr_t address, nub_size_t size, + std::vector<uint8_t> &tags); nub_bool_t GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics64_data_t &vminfo, diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp index 97908b4..9d0d60f 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp +++ b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp @@ -114,6 +114,11 @@ bool MachVMRegion::RestoreProtections() { return false; } +#ifdef VM_REGION_FLAG_JIT_ENABLED +#define VM_REGION_HAS_FLAGS 1 +#else +#define VM_REGION_HAS_FLAGS 0 +#endif bool MachVMRegion::GetRegionForAddress(nub_addr_t addr) { // Restore any original protections and clear our vars Clear(); @@ -140,6 +145,30 @@ bool MachVMRegion::GetRegionForAddress(nub_addr_t addr) { if (failed) return false; if (log_protections) { +#if VM_REGION_HAS_FLAGS + DNBLogThreaded("info = { prot = %u, " + "max_prot = %u, " + "inheritance = 0x%8.8x, " + "offset = 0x%8.8llx, " + "user_tag = 0x%8.8x, " + "ref_count = %u, " + "shadow_depth = %u, " + "ext_pager = %u, " + "share_mode = %u, " + "is_submap = %d, " + "behavior = %d, " + "object_id = 0x%8.8x, " + "user_wired_count = 0x%4.4x, " + "flags = %d }", + m_data.protection, m_data.max_protection, m_data.inheritance, + (uint64_t)m_data.offset, m_data.user_tag, m_data.ref_count, + m_data.shadow_depth, m_data.external_pager, + m_data.share_mode, m_data.is_submap, m_data.behavior, + m_data.object_id, m_data.user_wired_count, m_data.flags); +#else + // Duplicate log call instead of #if-defing printing of flags to avoid + // compiler warning: 'embedding a directive within macro arguments has + // undefined behavior' DNBLogThreaded("info = { prot = %u, " "max_prot = %u, " "inheritance = 0x%8.8x, " @@ -158,6 +187,7 @@ bool MachVMRegion::GetRegionForAddress(nub_addr_t addr) { m_data.shadow_depth, m_data.external_pager, m_data.share_mode, m_data.is_submap, m_data.behavior, m_data.object_id, m_data.user_wired_count); +#endif } m_curr_protection = m_data.protection; @@ -183,6 +213,22 @@ uint32_t MachVMRegion::GetDNBPermissions() const { return dnb_permissions; } +#ifndef VM_REGION_FLAG_MTE_ENABLED +#define VM_REGION_FLAG_MTE_ENABLED 0x4 +#endif +std::vector<std::string> MachVMRegion::GetFlags() const { + std::vector<std::string> flags; +#if VM_REGION_HAS_FLAGS + if (m_data.flags & VM_REGION_FLAG_JIT_ENABLED) + flags.push_back("jit"); + if (m_data.flags & VM_REGION_FLAG_TPRO_ENABLED) + flags.push_back("tpro"); + if (m_data.flags & VM_REGION_FLAG_MTE_ENABLED) + flags.push_back("mt"); +#endif + return flags; +} + std::vector<std::string> MachVMRegion::GetMemoryTypes() const { std::vector<std::string> types; if (m_data.user_tag == VM_MEMORY_STACK) { diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h index cb77058..ba6e1f3 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h +++ b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h @@ -40,9 +40,10 @@ public: vm_prot_t prot); bool RestoreProtections(); bool GetRegionForAddress(nub_addr_t addr); - std::vector<std::string> GetMemoryTypes() const; uint32_t GetDNBPermissions() const; + std::vector<std::string> GetFlags() const; + std::vector<std::string> GetMemoryTypes() const; const DNBError &GetError() { return m_err; } diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp index d9fb22c..434e9cf 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> @@ -502,6 +505,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.")); @@ -3475,6 +3480,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 +3522,9 @@ rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) { reply << "SupportedWatchpointTypes=x86_64;"; #endif + if (supports_memory_tagging()) + reply << "memory-tagging+;"; + return SendPacket(reply.str().c_str()); } @@ -4251,7 +4271,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 +4322,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 +4356,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(); @@ -6162,6 +6247,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 +6438,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__) diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h index ad254ae..cf1c978 100644 --- a/lldb/tools/debugserver/source/RNBRemote.h +++ b/lldb/tools/debugserver/source/RNBRemote.h @@ -121,6 +121,7 @@ public: set_list_threads_in_stop_reply, // 'QListThreadsInStopReply:' sync_thread_state, // 'QSyncThreadState:' memory_region_info, // 'qMemoryRegionInfo:' + get_memory_tags, // 'qMemTags:' get_profile_data, // 'qGetProfileData' set_enable_profiling, // 'QSetEnableAsyncProfiling' enable_compression, // 'QEnableCompression:' @@ -237,6 +238,7 @@ public: rnb_err_t HandlePacket_SaveRegisterState(const char *p); rnb_err_t HandlePacket_RestoreRegisterState(const char *p); rnb_err_t HandlePacket_MemoryRegionInfo(const char *p); + rnb_err_t HandlePacket_qMemTags(const char *p); rnb_err_t HandlePacket_GetProfileData(const char *p); rnb_err_t HandlePacket_SetEnableAsyncProfiling(const char *p); rnb_err_t HandlePacket_QEnableCompression(const char *p); |