DavidSpickett updated this revision to Diff 358976.
DavidSpickett added a comment.

Rebase


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D105180/new/

https://reviews.llvm.org/D105180

Files:
  lldb/include/lldb/Host/common/NativeProcessProtocol.h
  lldb/include/lldb/Utility/StringExtractorGDBRemote.h
  lldb/source/Host/common/NativeProcessProtocol.cpp
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
  lldb/source/Utility/StringExtractorGDBRemote.cpp
  lldb/test/API/tools/lldb-server/memory-tagging/TestGdbRemoteMemoryTagging.py

Index: lldb/test/API/tools/lldb-server/memory-tagging/TestGdbRemoteMemoryTagging.py
===================================================================
--- lldb/test/API/tools/lldb-server/memory-tagging/TestGdbRemoteMemoryTagging.py
+++ lldb/test/API/tools/lldb-server/memory-tagging/TestGdbRemoteMemoryTagging.py
@@ -3,27 +3,40 @@
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 
+"""
+Check that lldb-server correctly processes qMemTags and QMemTags packets.
+
+In the tests below E03 means the packet wasn't formed correctly
+and E01 means it was but we had some other error acting upon it.
+
+We do not test reading or writing over a page boundary
+within the same mapping. That logic is handled in the kernel
+so it's just a single ptrace call for lldb-server.
+"""
+
 class TestGdbRemoteMemoryTagging(gdbremote_testcase.GdbRemoteTestCaseBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
-    def check_qmemtags_response(self, body, expected):
-        self.test_sequence.add_log_lines(["read packet: $qMemTags:{}#00".format(body),
+    def check_memtags_response(self, packet_name, body, expected):
+        self.test_sequence.add_log_lines(["read packet: ${}:{}#00".format(packet_name, body),
                                           "send packet: ${}#00".format(expected),
                                           ],
                                          True)
         self.expect_gdbremote_sequence()
 
-    @skipUnlessArch("aarch64")
-    @skipUnlessPlatform(["linux"])
-    @skipUnlessAArch64MTELinuxCompiler
-    def test_qmemtags_packets(self):
-        """ Test that qMemTags packets are parsed correctly and/or rejected. """
+    def check_tag_read(self, body, expected):
+        self.check_memtags_response("qMemTags", body, expected)
 
+    def prep_memtags_test(self):
         self.build()
         self.set_inferior_startup_launch()
         procs = self.prep_debug_monitor_and_inferior()
 
+        # We don't use isAArch64MTE here because we cannot do runCmd in an
+        # lldb-server test. Instead we run the example and skip if it fails
+        # to allocate an MTE buffer.
+
         # Run the process
         self.test_sequence.add_log_lines(
             [
@@ -56,61 +69,153 @@
         buf_address = int(buf_address, 16)
         page_size = int(page_size, 16)
 
-        # In the tests below E03 means the packet wasn't formed correctly
-        # and E01 means it was but we had some other error acting upon it.
+        return buf_address, page_size
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    @skipUnlessAArch64MTELinuxCompiler
+    def test_qMemTags_packets(self):
+        """ Test that qMemTags packets are parsed correctly and/or rejected. """
+        buf_address, page_size = self.prep_memtags_test()
 
         # Sanity check that address is correct
-        self.check_qmemtags_response("{:x},20:1".format(buf_address), "m0001")
+        self.check_tag_read("{:x},20:1".format(buf_address), "m0001")
 
         # Invalid packets
 
         # No content
-        self.check_qmemtags_response("", "E03")
+        self.check_tag_read("", "E03")
         # Only address
-        self.check_qmemtags_response("{:x}".format(buf_address), "E03")
+        self.check_tag_read("{:x}".format(buf_address), "E03")
         # Only address and length
-        self.check_qmemtags_response("{:x},20".format(buf_address), "E03")
+        self.check_tag_read("{:x},20".format(buf_address), "E03")
         # Empty address
-        self.check_qmemtags_response(",20:1", "E03")
+        self.check_tag_read(",20:1", "E03")
         # Invalid addresses
-        self.check_qmemtags_response("aardvark,20:1", "E03")
-        self.check_qmemtags_response("-100,20:1", "E03")
-        self.check_qmemtags_response("cafe?,20:1", "E03")
+        self.check_tag_read("aardvark,20:1", "E03")
+        self.check_tag_read("-100,20:1", "E03")
+        self.check_tag_read("cafe?,20:1", "E03")
         # Empty length
-        self.check_qmemtags_response("{:x},:1".format(buf_address), "E03")
+        self.check_tag_read("{:x},:1".format(buf_address), "E03")
         # Invalid lengths
-        self.check_qmemtags_response("{:x},food:1".format(buf_address), "E03")
-        self.check_qmemtags_response("{:x},-1:1".format(buf_address), "E03")
-        self.check_qmemtags_response("{:x},12??:1".format(buf_address), "E03")
+        self.check_tag_read("{:x},food:1".format(buf_address), "E03")
+        self.check_tag_read("{:x},-1:1".format(buf_address), "E03")
+        self.check_tag_read("{:x},12??:1".format(buf_address), "E03")
         # Empty type
-        self.check_qmemtags_response("{:x},10:".format(buf_address), "E03")
+        self.check_tag_read("{:x},10:".format(buf_address), "E03")
         # Types we don't support
-        self.check_qmemtags_response("{:x},10:FF".format(buf_address), "E01")
+        self.check_tag_read("{:x},10:FF".format(buf_address), "E01")
         # (even if the length of the read is zero)
-        self.check_qmemtags_response("{:x},0:FF".format(buf_address), "E01")
-        self.check_qmemtags_response("{:x},10:-1".format(buf_address), "E01")
-        self.check_qmemtags_response("{:x},10:+20".format(buf_address), "E01")
+        self.check_tag_read("{:x},0:FF".format(buf_address), "E01")
+        self.check_tag_read("{:x},10:-1".format(buf_address), "E01")
+        self.check_tag_read("{:x},10:+20".format(buf_address), "E01")
         # Invalid type format
-        self.check_qmemtags_response("{:x},10:cat".format(buf_address), "E03")
-        self.check_qmemtags_response("{:x},10:?11".format(buf_address), "E03")
+        self.check_tag_read("{:x},10:cat".format(buf_address), "E03")
+        self.check_tag_read("{:x},10:?11".format(buf_address), "E03")
 
         # Valid packets
 
         # Reading nothing is allowed
-        self.check_qmemtags_response("{:x},0:1".format(buf_address), "m")
+        self.check_tag_read("{:x},0:1".format(buf_address), "m")
         # A range that's already aligned
-        self.check_qmemtags_response("{:x},20:1".format(buf_address), "m0001")
+        self.check_tag_read("{:x},20:1".format(buf_address), "m0001")
         # lldb-server should re-align the range
         # Here we read from 1/2 way through a granule, into the next. Expands to 2 granules
-        self.check_qmemtags_response("{:x},10:1".format(buf_address+64-8), "m0304")
+        self.check_tag_read("{:x},10:1".format(buf_address+64-8), "m0304")
         # Read up to the end of an MTE page.
         # We know that the last tag should be 0xF since page size will always be a
         # multiple of 256 bytes, which is 16 granules and we have 16 tags to use.
-        self.check_qmemtags_response("{:x},10:1".format(buf_address+page_size-16), "m0f")
+        self.check_tag_read("{:x},10:1".format(buf_address+page_size-16), "m0f")
         # Here we read off of the end of the MTE range so ptrace gives us one tag,
         # then fails on the second call. To lldb-server this means the response
         # should just be an error, not a partial read.
-        self.check_qmemtags_response("{:x},20:1".format(buf_address+page_size-16), "E01")
-        # Note that we do not test reading over a page boundary within the same
-        # mapping. That logic is handled in the kernel itself so it's just a single
-        # ptrace call for lldb-server.
+        self.check_tag_read("{:x},20:1".format(buf_address+page_size-16), "E01")
+
+    def check_tag_write(self, body, expected):
+        self.check_memtags_response("QMemTags", body, expected)
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    @skipUnlessAArch64MTELinuxCompiler
+    def test_QMemTags_packets(self):
+        """ Test that QMemTags packets are parsed correctly and/or rejected. """
+        buf_address, page_size = self.prep_memtags_test()
+
+        # Sanity check that address is correct
+        self.check_tag_write("{:x},10:1:0e".format(buf_address), "OK")
+        self.check_tag_read("{:x},10:1".format(buf_address), "m0e")
+
+        # No content
+        self.check_tag_write("", "E03")
+        # Only address
+        self.check_tag_write("{:x}".format(buf_address), "E03")
+        # Only address and length
+        self.check_tag_write("{:x},20".format(buf_address), "E03")
+        # Missing data
+        self.check_tag_write("{:x},20:1".format(buf_address), "E03")
+        # Zero length write must still include seperator after type
+        self.check_tag_write("{:x},0:1".format(buf_address), "E03")
+        # Empty address
+        self.check_tag_write(",10:1:01", "E03")
+        # Invalid addresses
+        self.check_tag_write("aardvark,10:1:01", "E03")
+        self.check_tag_write("-100,10:1:01", "E03")
+        self.check_tag_write("cafe?,10:1:01", "E03")
+        # Empty length
+        self.check_tag_write("{:x},:1:01".format(buf_address), "E03")
+        # Invalid lengths
+        self.check_tag_write("{:x},food:1:01".format(buf_address), "E03")
+        self.check_tag_write("{:x},-1:1:01".format(buf_address), "E03")
+        self.check_tag_write("{:x},12??:1:01".format(buf_address), "E03")
+        # Empty type
+        self.check_tag_write("{:x},10::01".format(buf_address), "E03")
+        # Types we don't support
+        self.check_tag_write("{:x},10:FF:01".format(buf_address), "E01")
+        # (even if the length of the write is zero)
+        self.check_tag_write("{:x},0:FF:".format(buf_address), "E01")
+        # Invalid type format
+        self.check_tag_write("{:x},0:cat:".format(buf_address), "E03")
+        self.check_tag_write("{:x},0:?11:".format(buf_address), "E03")
+        # Leading +/- not allowed
+        self.check_tag_write("{:x},10:-1:".format(buf_address), "E03")
+        self.check_tag_write("{:x},10:+20:".format(buf_address), "E03")
+        # We use a uint64_t when parsing, but dont expect anything > 32 bits
+        self.check_tag_write("{:x},10:123412341:".format(buf_address), "E03")
+        # Invalid tag data
+        self.check_tag_write("{:x},10:1:??".format(buf_address), "E03")
+        self.check_tag_write("{:x},10:1:45?".format(buf_address), "E03")
+        # (must be 2 chars per byte)
+        self.check_tag_write("{:x},10:1:123".format(buf_address), "E03")
+        # Tag out of range
+        self.check_tag_write("{:x},10:1:23".format(buf_address), "E01")
+        # Non-zero length write must include some tag data
+        self.check_tag_write("{:x},10:1:".format(buf_address), "E01")
+
+        # Valid packets
+
+        # Zero length write doesn't need any tag data (but should include the :)
+        self.check_tag_write("{:x},0:1:".format(buf_address), "OK")
+        # Zero length unaligned is the same
+        self.check_tag_write("{:x},0:1:".format(buf_address+8), "OK")
+        # Ranges can be aligned already
+        self.check_tag_write("{:x},20:1:0405".format(buf_address), "OK")
+        self.check_tag_read("{:x},20:1".format(buf_address), "m0405")
+        # Unaliged ranges will be aligned by the server
+        self.check_tag_write("{:x},10:1:0607".format(buf_address+8), "OK")
+        self.check_tag_read("{:x},20:1".format(buf_address), "m0607")
+        # Tags will be repeated as needed to cover the range
+        self.check_tag_write("{:x},30:1:09".format(buf_address), "OK")
+        self.check_tag_read("{:x},30:1".format(buf_address), "m090909")
+        # One more repeating tags for good measure, part repetition this time
+        # (for full tests see the MemoryTagManagerAArch64MTE unittests)
+        self.check_tag_write("{:x},30:1:0a0b".format(buf_address), "OK")
+        self.check_tag_read("{:x},30:1".format(buf_address), "m0a0b0a")
+        # We can write up to the end of the MTE page
+        last_granule = buf_address + page_size - 16;
+        self.check_tag_write("{:x},10:1:0c".format(last_granule), "OK")
+        self.check_tag_read("{:x},10:1".format(last_granule), "m0c")
+        # Writing over the end of the page is an error
+        self.check_tag_write("{:x},20:1:0d".format(last_granule), "E01")
+        # The last tag in the page was written thought, and we do not
+        # attempt to restore its original value.
+        self.check_tag_read("{:x},10:1".format(last_granule), "m0d")
Index: lldb/source/Utility/StringExtractorGDBRemote.cpp
===================================================================
--- lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -143,6 +143,11 @@
         return eServerPacketType_QListThreadsInStopReply;
       break;
 
+    case 'M':
+      if (PACKET_STARTS_WITH("QMemTags"))
+        return eServerPacketType_QMemTags;
+      break;
+
     case 'R':
       if (PACKET_STARTS_WITH("QRestoreRegisterState:"))
         return eServerPacketType_QRestoreRegisterState;
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -218,6 +218,8 @@
 
   PacketResult Handle_qMemTags(StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_QMemTags(StringExtractorGDBRemote &packet);
+
   void SetCurrentThreadID(lldb::tid_t tid);
 
   lldb::tid_t GetCurrentThreadID() const;
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -216,6 +216,10 @@
       StringExtractorGDBRemote::eServerPacketType_qMemTags,
       &GDBRemoteCommunicationServerLLGS::Handle_qMemTags);
 
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_QMemTags,
+      &GDBRemoteCommunicationServerLLGS::Handle_QMemTags);
+
   RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k,
                         [this](StringExtractorGDBRemote packet, Status &error,
                                bool &interrupt, bool &quit) {
@@ -3492,6 +3496,94 @@
   return SendPacketNoLock(response.GetString());
 }
 
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_QMemTags(
+    StringExtractorGDBRemote &packet) {
+  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+  // Ensure we have a process.
+  if (!m_current_process ||
+      (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) {
+    LLDB_LOGF(
+        log,
+        "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+        __FUNCTION__);
+    return SendErrorResponse(1);
+  }
+
+  // We are expecting
+  // QMemTags:<hex address>,<hex length>:<hex type>:<tags as hex bytes>
+
+  // Address
+  packet.SetFilePos(strlen("QMemTags:"));
+  const char *current_char = packet.Peek();
+  if (!current_char || *current_char == ',')
+    return SendIllFormedResponse(packet, "Missing address in QMemTags packet");
+  const lldb::addr_t addr = packet.GetHexMaxU64(/*little_endian=*/false, 0);
+
+  // Length
+  char previous_char = packet.GetChar();
+  current_char = packet.Peek();
+  // If we don't have a separator or the length field is empty
+  if (previous_char != ',' || (current_char && *current_char == ':'))
+    return SendIllFormedResponse(packet,
+                                 "Invalid addr,length pair in QMemTags packet");
+
+  if (packet.GetBytesLeft() < 1)
+    return SendIllFormedResponse(
+        packet, "Too short QMemtags: packet (looking for length)");
+  const size_t length = packet.GetHexMaxU64(/*little_endian=*/false, 0);
+
+  // Type
+  const char *invalid_type_err = "Invalid type field in QMemTags: packet";
+  if (packet.GetBytesLeft() < 1 || packet.GetChar() != ':')
+    return SendIllFormedResponse(packet, invalid_type_err);
+
+  // Our GetU64 uses strtoull which allows leading +/-, we don't want that.
+  const char *first_type_char = packet.Peek();
+  if (first_type_char && (*first_type_char == '+' || *first_type_char == '-'))
+    return SendIllFormedResponse(packet, invalid_type_err);
+
+  // The type is a signed integer but is in the packet as its raw bytes.
+  // So parse first as unsigned then cast to signed later.
+  // We extract to 64 bit, even though we only expect 32, so that we've
+  // got some invalid value we can check for.
+  uint64_t raw_type =
+      packet.GetU64(std::numeric_limits<uint64_t>::max(), /*base=*/16);
+  if (raw_type > std::numeric_limits<uint32_t>::max())
+    return SendIllFormedResponse(packet, invalid_type_err);
+  int32_t type = static_cast<int32_t>(raw_type);
+
+  // Tag data
+  if (packet.GetBytesLeft() < 1 || packet.GetChar() != ':')
+    return SendIllFormedResponse(packet,
+                                 "Missing tag data in QMemTags: packet");
+
+  // Must be 2 chars per byte
+  const char *invalid_data_err = "Invalid tag data in QMemTags: packet";
+  if (packet.GetBytesLeft() % 2)
+    return SendIllFormedResponse(packet, invalid_data_err);
+
+  // This is bytes here and is unpacked into target specific tags later
+  // We cannot assume that number of bytes == length here because the server
+  // can repeat tags to fill a given range.
+  std::vector<uint8_t> tag_data;
+  // Zero length writes will not have any tag data
+  // (but we pass them on because it will still check that tagging is enabled)
+  if (packet.GetBytesLeft()) {
+    size_t byte_count = packet.GetBytesLeft() / 2;
+    tag_data.resize(byte_count);
+    size_t converted_bytes = packet.GetHexBytes(tag_data, 0);
+    if (converted_bytes != byte_count) {
+      return SendIllFormedResponse(packet, invalid_data_err);
+    }
+  }
+
+  Status status =
+      m_current_process->WriteMemoryTags(type, addr, length, tag_data);
+  return status.Success() ? SendOKResponse() : SendErrorResponse(1);
+}
+
 void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() {
   Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
 
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -83,6 +83,9 @@
   Status ReadMemoryTags(int32_t type, lldb::addr_t addr, size_t len,
                         std::vector<uint8_t> &tags) override;
 
+  Status WriteMemoryTags(int32_t type, lldb::addr_t addr, size_t len,
+                         const std::vector<uint8_t> &tags) override;
+
   size_t UpdateThreads() override;
 
   const ArchSpec &GetArchitecture() const override { return m_arch; }
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -1486,6 +1486,77 @@
   return Status();
 }
 
+Status NativeProcessLinux::WriteMemoryTags(int32_t type, lldb::addr_t addr,
+                                           size_t len,
+                                           const std::vector<uint8_t> &tags) {
+  llvm::Expected<NativeRegisterContextLinux::MemoryTaggingDetails> details =
+      GetCurrentThread()->GetRegisterContext().GetMemoryTaggingDetails(type);
+  if (!details)
+    return Status(details.takeError());
+
+  // Ignore 0 length write
+  if (!len)
+    return Status();
+
+  // lldb will align the range it requests but it is not required to by
+  // the protocol so we'll do it again just in case.
+  // Remove non address bits too. Ptrace calls may work regardless but that
+  // is not a guarantee.
+  MemoryTagManager::TagRange range(details->manager->RemoveNonAddressBits(addr),
+                                   len);
+  range = details->manager->ExpandToGranule(range);
+
+  // Not checking number of tags here, we may repeat them below
+  llvm::Expected<std::vector<lldb::addr_t>> unpacked_tags_or_err =
+      details->manager->UnpackTagsData(tags);
+  if (!unpacked_tags_or_err)
+    return Status(unpacked_tags_or_err.takeError());
+
+  llvm::Expected<std::vector<lldb::addr_t>> repeated_tags_or_err =
+      details->manager->RepeatTagsForRange(*unpacked_tags_or_err, range);
+  if (!repeated_tags_or_err)
+    return Status(repeated_tags_or_err.takeError());
+
+  // Repack them for ptrace to use
+  llvm::Expected<std::vector<uint8_t>> final_tag_data =
+      details->manager->PackTags(*repeated_tags_or_err);
+  if (!final_tag_data)
+    return Status(final_tag_data.takeError());
+
+  struct iovec tags_vec;
+  uint8_t *src = final_tag_data->data();
+  lldb::addr_t write_addr = range.GetRangeBase();
+  // unpacked tags size because the number of bytes per tag might not be 1
+  size_t num_tags = repeated_tags_or_err->size();
+
+  // This call can partially write tags, so we loop until we
+  // error or all tags have been written.
+  while (num_tags > 0) {
+    tags_vec.iov_base = src;
+    tags_vec.iov_len = num_tags;
+
+    Status error = NativeProcessLinux::PtraceWrapper(
+        details->ptrace_write_req, GetID(),
+        reinterpret_cast<void *>(write_addr), static_cast<void *>(&tags_vec), 0,
+        nullptr);
+
+    if (error.Fail()) {
+      // Don't attempt to restore the original values in the case of a partial
+      // write
+      return error;
+    }
+
+    size_t tags_written = tags_vec.iov_len;
+    assert(tags_written && (tags_written <= num_tags));
+
+    src += tags_written * details->manager->GetTagSizeInBytes();
+    write_addr += details->manager->GetGranuleSize() * tags_written;
+    num_tags -= tags_written;
+  }
+
+  return Status();
+}
+
 size_t NativeProcessLinux::UpdateThreads() {
   // The NativeProcessLinux monitoring threads are always up to date with
   // respect to thread state and they keep the thread list populated properly.
Index: lldb/source/Host/common/NativeProcessProtocol.cpp
===================================================================
--- lldb/source/Host/common/NativeProcessProtocol.cpp
+++ lldb/source/Host/common/NativeProcessProtocol.cpp
@@ -58,6 +58,13 @@
   return Status("not implemented");
 }
 
+lldb_private::Status
+NativeProcessProtocol::WriteMemoryTags(int32_t type, lldb::addr_t addr,
+                                       size_t len,
+                                       const std::vector<uint8_t> &tags) {
+  return Status("not implemented");
+}
+
 llvm::Optional<WaitStatus> NativeProcessProtocol::GetExitStatus() {
   if (m_state == lldb::eStateExited)
     return m_exit_status;
Index: lldb/include/lldb/Utility/StringExtractorGDBRemote.h
===================================================================
--- lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -168,7 +168,8 @@
     eServerPacketType_jLLDBTraceGetState,
     eServerPacketType_jLLDBTraceGetBinaryData,
 
-    eServerPacketType_qMemTags,
+    eServerPacketType_qMemTags, // read memory tags
+    eServerPacketType_QMemTags, // write memory tags
   };
 
   ServerPacketType GetServerPacketType() const;
Index: lldb/include/lldb/Host/common/NativeProcessProtocol.h
===================================================================
--- lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -90,6 +90,9 @@
   virtual Status ReadMemoryTags(int32_t type, lldb::addr_t addr, size_t len,
                                 std::vector<uint8_t> &tags);
 
+  virtual Status WriteMemoryTags(int32_t type, lldb::addr_t addr, size_t len,
+                                 const std::vector<uint8_t> &tags);
+
   /// Reads a null terminated string from memory.
   ///
   /// Reads up to \p max_size bytes of memory until it finds a '\0'.
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to