This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG6a7a2ee8161d: [lldb] Add "memory tag write" 
command (authored by DavidSpickett).

Changed prior to commit:
  https://reviews.llvm.org/D105182?vs=362055&id=362306#toc

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D105182

Files:
  lldb/source/Commands/CommandObjectMemoryTag.cpp
  lldb/test/API/functionalities/memory/tag/TestMemoryTag.py
  lldb/test/API/linux/aarch64/mte_tag_access/Makefile
  
lldb/test/API/linux/aarch64/mte_tag_access/TestAArch64LinuxMTEMemoryTagAccess.py
  lldb/test/API/linux/aarch64/mte_tag_access/main.c
  lldb/test/API/linux/aarch64/mte_tag_read/Makefile
  lldb/test/API/linux/aarch64/mte_tag_read/TestAArch64LinuxMTEMemoryTagRead.py
  lldb/test/API/linux/aarch64/mte_tag_read/main.c

Index: lldb/test/API/linux/aarch64/mte_tag_read/TestAArch64LinuxMTEMemoryTagRead.py
===================================================================
--- lldb/test/API/linux/aarch64/mte_tag_read/TestAArch64LinuxMTEMemoryTagRead.py
+++ /dev/null
@@ -1,126 +0,0 @@
-"""
-Test "memory tag read" command on AArch64 Linux with MTE.
-"""
-
-
-import lldb
-from lldbsuite.test.decorators import *
-from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
-
-
-class AArch64LinuxMTEMemoryTagReadTestCase(TestBase):
-
-    mydir = TestBase.compute_mydir(__file__)
-
-    NO_DEBUG_INFO_TESTCASE = True
-
-    @skipUnlessArch("aarch64")
-    @skipUnlessPlatform(["linux"])
-    @skipUnlessAArch64MTELinuxCompiler
-    def test_mte_tag_read(self):
-        if not self.isAArch64MTE():
-            self.skipTest('Target must support MTE.')
-
-        self.build()
-        self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
-
-        lldbutil.run_break_set_by_file_and_line(self, "main.c",
-            line_number('main.c', '// Breakpoint here'),
-            num_expected_locations=1)
-
-        self.runCmd("run", RUN_SUCCEEDED)
-
-        if self.process().GetState() == lldb.eStateExited:
-            self.fail("Test program failed to run.")
-
-        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
-            substrs=['stopped',
-                     'stop reason = breakpoint'])
-
-        # Argument validation
-        self.expect("memory tag read",
-                substrs=["error: wrong number of arguments; expected at least <address-expression>, "
-                         "at most <address-expression> <end-address-expression>"],
-                error=True)
-        self.expect("memory tag read buf buf+16 32",
-                substrs=["error: wrong number of arguments; expected at least <address-expression>, "
-                         "at most <address-expression> <end-address-expression>"],
-                error=True)
-        self.expect("memory tag read not_a_symbol",
-                substrs=["error: Invalid address expression, address expression \"not_a_symbol\" "
-                         "evaluation failed"],
-                error=True)
-        self.expect("memory tag read buf not_a_symbol",
-                substrs=["error: Invalid end address expression, address expression \"not_a_symbol\" "
-                         "evaluation failed"],
-                error=True)
-        # Inverted range
-        self.expect("memory tag read buf buf-16",
-                patterns=["error: End address \(0x[A-Fa-f0-9]+\) must be "
-                          "greater than the start address \(0x[A-Fa-f0-9]+\)"],
-                error=True)
-        # Range of length 0
-        self.expect("memory tag read buf buf",
-                patterns=["error: End address \(0x[A-Fa-f0-9]+\) must be "
-                          "greater than the start address \(0x[A-Fa-f0-9]+\)"],
-                error=True)
-
-
-        # Can't read from a region without tagging
-        self.expect("memory tag read non_mte_buf",
-                patterns=["error: Address range 0x[0-9A-Fa-f]+00:0x[0-9A-Fa-f]+10 is not "
-                         "in a memory tagged region"],
-                error=True)
-
-        # If there's no end address we assume 1 granule
-        self.expect("memory tag read buf",
-                patterns=["Logical tag: 0x9\n"
-                          "Allocation tags:\n"
-                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0$"])
-
-        # Range of <1 granule is rounded up to 1 granule
-        self.expect("memory tag read buf buf+8",
-                patterns=["Logical tag: 0x9\n"
-                          "Allocation tags:\n"
-                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0$"])
-
-        # Start address is aligned down, end aligned up
-        self.expect("memory tag read buf+8 buf+24",
-                patterns=["Logical tag: 0x9\n"
-                          "Allocation tags:\n"
-                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0\n"
-                          "\[0x[0-9A-Fa-f]+10, 0x[0-9A-Fa-f]+20\): 0x1$"])
-
-        # You may read up to the end of the tagged region
-        # Layout is buf (MTE), buf2 (MTE), <unmapped/non MTE>
-        # so we read from the end of buf2 here.
-        self.expect("memory tag read buf2+page_size-16 buf2+page_size",
-                patterns=["Logical tag: 0x0\n"
-                          "Allocation tags:\n"
-                          "\[0x[0-9A-Fa-f]+, 0x[0-9A-Fa-f]+\): 0x0$"])
-
-        # Ranges with any part outside the region will error
-        self.expect("memory tag read buf2+page_size-16 buf2+page_size+32",
-                patterns=["error: Address range 0x[0-9A-fa-f]+f0:0x[0-9A-Fa-f]+20 "
-                          "is not in a memory tagged region"],
-                error=True)
-        self.expect("memory tag read buf2+page_size",
-                patterns=["error: Address range 0x[0-9A-fa-f]+00:0x[0-9A-Fa-f]+10 "
-                          "is not in a memory tagged region"],
-                error=True)
-
-        # You can read a range that spans more than one mapping
-        # This spills into buf2 which is also MTE
-        self.expect("memory tag read buf+page_size-16 buf+page_size+16",
-                patterns=["Logical tag: 0x9\n"
-                          "Allocation tags:\n"
-                          "\[0x[0-9A-Fa-f]+f0, 0x[0-9A-Fa-f]+00\): 0xf\n"
-                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0$"])
-
-        # Tags in start/end are ignored when creating the range.
-        # So this is not an error despite start/end having different tags
-        self.expect("memory tag read buf buf_alt_tag+16 ",
-                patterns=["Logical tag: 0x9\n"
-                          "Allocation tags:\n"
-                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0$"])
Index: lldb/test/API/linux/aarch64/mte_tag_access/main.c
===================================================================
--- lldb/test/API/linux/aarch64/mte_tag_access/main.c
+++ lldb/test/API/linux/aarch64/mte_tag_access/main.c
@@ -1,11 +1,22 @@
 #include <arm_acle.h>
 #include <asm/hwcap.h>
 #include <asm/mman.h>
+#include <stdlib.h>
 #include <sys/auxv.h>
 #include <sys/mman.h>
 #include <sys/prctl.h>
 #include <unistd.h>
 
+// This file uses ACLE intrinsics as detailed in:
+// https://developer.arm.com/documentation/101028/0012/10--Memory-tagging-intrinsics?lang=en
+
+char *checked_mmap(size_t page_size, int prot) {
+  char *ptr = mmap(0, page_size, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (ptr == MAP_FAILED)
+    exit(1);
+  return ptr;
+}
+
 int main(int argc, char const *argv[]) {
   // We assume that the test runner has checked we're on an MTE system
 
@@ -20,38 +31,32 @@
 
   size_t page_size = sysconf(_SC_PAGESIZE);
 
-  // Allocate memory with MTE
-  // We ask for two pages. One is read only so that we get
-  // 2 mappings in /proc/.../smaps so we can check reading
-  // a range across mappings.
-  // The first allocation will start at the highest address,
-  // so we allocate buf2 first to get:
-  // <low address> | buf | buf2 | <high address>
-  int prot = PROT_READ | PROT_MTE;
-  int flags = MAP_PRIVATE | MAP_ANONYMOUS;
-
-  char *buf2 = mmap(0, page_size, prot, flags, -1, 0);
-  if (buf2 == MAP_FAILED)
-    return 1;
-
-  // Writeable so we can set tags on it later
-  char *buf = mmap(0, page_size, prot | PROT_WRITE, flags, -1, 0);
-  if (buf == MAP_FAILED)
-    return 1;
+  // We're going to mmap pages in this order:
+  // <high addres>
+  // MTE read/write
+  // MTE read/write executable
+  // non MTE
+  // MTE read only
+  // <low address>
+  //
+  // This means that the first two MTE pages end up next
+  // to each other. Since the second one is also executable
+  // it will create a new entry in /proc/smaps.
+  int mte_prot = PROT_READ | PROT_MTE;
 
+  char *mte_buf_2 = checked_mmap(page_size, mte_prot | PROT_WRITE);
+  char *mte_buf = checked_mmap(page_size, mte_prot | PROT_WRITE | PROT_EXEC);
   // We expect the mappings to be next to each other
-  if (buf2 - buf != page_size)
+  if (mte_buf_2 - mte_buf != page_size)
     return 1;
 
-  // And without MTE
-  char *non_mte_buf = mmap(0, page_size, PROT_READ | PROT_WRITE, flags, -1, 0);
-  if (non_mte_buf == MAP_FAILED)
-    return 1;
+  char *non_mte_buf = checked_mmap(page_size, PROT_READ);
+  char *mte_read_only = checked_mmap(page_size, mte_prot);
 
   // Set incrementing tags until end of the first page
-  char *tagged_ptr = buf;
+  char *tagged_ptr = mte_buf;
   // This ignores tag bits when subtracting the addresses
-  while (__arm_mte_ptrdiff(tagged_ptr, buf) < page_size) {
+  while (__arm_mte_ptrdiff(tagged_ptr, mte_buf) < page_size) {
     // Set the allocation tag for this location
     __arm_mte_set_tag(tagged_ptr);
     // + 16 for 16 byte granules
@@ -61,16 +66,17 @@
   }
 
   // Tag the original pointer with 9
-  buf = __arm_mte_create_random_tag(buf, ~(1 << 9));
+  mte_buf = __arm_mte_create_random_tag(mte_buf, ~(1 << 9));
   // A different tag so that buf_alt_tag > buf if you don't handle the tag
-  char *buf_alt_tag = __arm_mte_create_random_tag(buf, ~(1 << 10));
+  char *mte_buf_alt_tag = __arm_mte_create_random_tag(mte_buf, ~(1 << 10));
 
   // lldb should be removing the whole top byte, not just the tags.
   // So fill 63-60 with something non zero so we'll fail if we only remove tags.
 #define SET_TOP_NIBBLE(ptr) (char *)((size_t)(ptr) | (0xA << 60))
-  buf = SET_TOP_NIBBLE(buf);
-  buf_alt_tag = SET_TOP_NIBBLE(buf_alt_tag);
-  buf2 = SET_TOP_NIBBLE(buf2);
+  mte_buf = SET_TOP_NIBBLE(mte_buf);
+  mte_buf_alt_tag = SET_TOP_NIBBLE(mte_buf_alt_tag);
+  mte_buf_2 = SET_TOP_NIBBLE(mte_buf_2);
+  mte_read_only = SET_TOP_NIBBLE(mte_read_only);
 
   // Breakpoint here
   return 0;
Index: lldb/test/API/linux/aarch64/mte_tag_access/TestAArch64LinuxMTEMemoryTagAccess.py
===================================================================
--- /dev/null
+++ lldb/test/API/linux/aarch64/mte_tag_access/TestAArch64LinuxMTEMemoryTagAccess.py
@@ -0,0 +1,218 @@
+"""
+Test "memory tag read" and "memory tag write" commands
+on AArch64 Linux with MTE.
+"""
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class AArch64LinuxMTEMemoryTagAccessTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def setup_mte_test(self):
+        if not self.isAArch64MTE():
+            self.skipTest('Target must support MTE.')
+
+        self.build()
+        self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line(self, "main.c",
+            line_number('main.c', '// Breakpoint here'),
+            num_expected_locations=1)
+
+        self.runCmd("run", RUN_SUCCEEDED)
+
+        if self.process().GetState() == lldb.eStateExited:
+            self.fail("Test program failed to run.")
+
+        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+            substrs=['stopped',
+                     'stop reason = breakpoint'])
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    @skipUnlessAArch64MTELinuxCompiler
+    def test_mte_tag_read(self):
+        self.setup_mte_test()
+
+        # Argument validation
+        self.expect("memory tag read",
+                substrs=["error: wrong number of arguments; expected at least <address-expression>, "
+                         "at most <address-expression> <end-address-expression>"],
+                error=True)
+        self.expect("memory tag read mte_buf buf+16 32",
+                substrs=["error: wrong number of arguments; expected at least <address-expression>, "
+                         "at most <address-expression> <end-address-expression>"],
+                error=True)
+        self.expect("memory tag read not_a_symbol",
+                substrs=["error: Invalid address expression, address expression \"not_a_symbol\" "
+                         "evaluation failed"],
+                error=True)
+        self.expect("memory tag read mte_buf not_a_symbol",
+                substrs=["error: Invalid end address expression, address expression \"not_a_symbol\" "
+                         "evaluation failed"],
+                error=True)
+        # Inverted range
+        self.expect("memory tag read mte_buf mte_buf-16",
+                patterns=["error: End address \(0x[A-Fa-f0-9]+\) must be "
+                          "greater than the start address \(0x[A-Fa-f0-9]+\)"],
+                error=True)
+        # Range of length 0
+        self.expect("memory tag read mte_buf mte_buf",
+                patterns=["error: End address \(0x[A-Fa-f0-9]+\) must be "
+                          "greater than the start address \(0x[A-Fa-f0-9]+\)"],
+                error=True)
+
+
+        # Can't read from a region without tagging
+        self.expect("memory tag read non_mte_buf",
+                patterns=["error: Address range 0x[0-9A-Fa-f]+00:0x[0-9A-Fa-f]+10 is not "
+                         "in a memory tagged region"],
+                error=True)
+
+        # If there's no end address we assume 1 granule
+        self.expect("memory tag read mte_buf",
+                patterns=["Logical tag: 0x9\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0$"])
+
+        # Range of <1 granule is rounded up to 1 granule
+        self.expect("memory tag read mte_buf mte_buf+8",
+                patterns=["Logical tag: 0x9\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0$"])
+
+        # Start address is aligned down, end aligned up
+        self.expect("memory tag read mte_buf+8 mte_buf+24",
+                patterns=["Logical tag: 0x9\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0\n"
+                          "\[0x[0-9A-Fa-f]+10, 0x[0-9A-Fa-f]+20\): 0x1$"])
+
+        # You may read up to the end of the tagged region
+        # Layout is mte_buf, mte_buf_2, non_mte_buf.
+        # So we read from the end of mte_buf_2 here.
+        self.expect("memory tag read mte_buf_2+page_size-16 mte_buf_2+page_size",
+                patterns=["Logical tag: 0x0\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+, 0x[0-9A-Fa-f]+\): 0x0$"])
+
+        # Ranges with any part outside the region will error
+        self.expect("memory tag read mte_buf_2+page_size-16 mte_buf_2+page_size+32",
+                patterns=["error: Address range 0x[0-9A-fa-f]+f0:0x[0-9A-Fa-f]+20 "
+                          "is not in a memory tagged region"],
+                error=True)
+        self.expect("memory tag read mte_buf_2+page_size",
+                patterns=["error: Address range 0x[0-9A-fa-f]+00:0x[0-9A-Fa-f]+10 "
+                          "is not in a memory tagged region"],
+                error=True)
+
+        # You can read a range that spans more than one mapping
+        # This spills into mte_buf2 which is also MTE
+        self.expect("memory tag read mte_buf+page_size-16 mte_buf+page_size+16",
+                patterns=["Logical tag: 0x9\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+f0, 0x[0-9A-Fa-f]+00\): 0xf\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0$"])
+
+        # Tags in start/end are ignored when creating the range.
+        # So this is not an error despite start/end having different tags
+        self.expect("memory tag read mte_buf mte_buf_alt_tag+16 ",
+                patterns=["Logical tag: 0x9\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0$"])
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    @skipUnlessAArch64MTELinuxCompiler
+    def test_mte_tag_write(self):
+        self.setup_mte_test()
+
+        # Argument validation
+        self.expect("memory tag write",
+                substrs=[" wrong number of arguments; expected "
+                         "<address-expression> <tag> [<tag> [...]]"],
+                error=True)
+        self.expect("memory tag write mte_buf",
+                substrs=[" wrong number of arguments; expected "
+                         "<address-expression> <tag> [<tag> [...]]"],
+                error=True)
+        self.expect("memory tag write not_a_symbol 9",
+                substrs=["error: Invalid address expression, address expression \"not_a_symbol\" "
+                         "evaluation failed"],
+                error=True)
+
+        # Can't write to a region without tagging
+        self.expect("memory tag write non_mte_buf 9",
+                patterns=["error: Address range 0x[0-9A-Fa-f]+00:0x[0-9A-Fa-f]+10 is not "
+                         "in a memory tagged region"],
+                error=True)
+
+        # Start address is aligned down so we write to the granule that contains it
+        self.expect("memory tag write mte_buf+8 9")
+        # Make sure we only modified the first granule
+        self.expect("memory tag read mte_buf mte_buf+32",
+                patterns=["Logical tag: 0x9\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x9\n"
+                          "\[0x[0-9A-Fa-f]+10, 0x[0-9A-Fa-f]+20\): 0x1$"])
+
+        # You can write multiple tags, range calculated for you
+        self.expect("memory tag write mte_buf 10 11 12")
+        self.expect("memory tag read mte_buf mte_buf+48",
+                patterns=["Logical tag: 0x9\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0xa\n"
+                          "\[0x[0-9A-Fa-f]+10, 0x[0-9A-Fa-f]+20\): 0xb\n"
+                          "\[0x[0-9A-Fa-f]+20, 0x[0-9A-Fa-f]+30\): 0xc$"])
+
+        # You may write up to the end of a tagged region
+        # (mte_buf_2's intial tags will all be 0)
+        self.expect("memory tag write mte_buf_2+page_size-16 0xe")
+        self.expect("memory tag read mte_buf_2+page_size-16 mte_buf_2+page_size",
+                patterns=["Logical tag: 0x0\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+, 0x[0-9A-Fa-f]+\): 0xe$"])
+
+        # Ranges with any part outside the region will error
+        self.expect("memory tag write mte_buf_2+page_size-16 6 7",
+                patterns=["error: Address range 0x[0-9A-fa-f]+f0:0x[0-9A-Fa-f]+10 "
+                          "is not in a memory tagged region"],
+                error=True)
+        self.expect("memory tag write mte_buf_2+page_size 6",
+                patterns=["error: Address range 0x[0-9A-fa-f]+00:0x[0-9A-Fa-f]+10 "
+                          "is not in a memory tagged region"],
+                error=True)
+        self.expect("memory tag write mte_buf_2+page_size 6 7 8",
+                patterns=["error: Address range 0x[0-9A-fa-f]+00:0x[0-9A-Fa-f]+30 "
+                          "is not in a memory tagged region"],
+                error=True)
+
+        # You can write to a range that spans two mappings, as long
+        # as they are both tagged.
+        # buf and buf2 are next to each other so this wirtes into buf2.
+        self.expect("memory tag write mte_buf+page_size-16 1 2")
+        self.expect("memory tag read mte_buf+page_size-16 mte_buf+page_size+16",
+                patterns=["Logical tag: 0x9\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+f0, 0x[0-9A-Fa-f]+00\): 0x1\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x2$"])
+
+        # Even if a page is read only the debugger can still write to it
+        self.expect("memory tag write mte_read_only 1")
+        self.expect("memory tag read mte_read_only",
+                patterns=["Logical tag: 0x0\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x1$"])
+
+        # Trying to write a value > maximum tag value is an error
+        self.expect("memory tag write mte_buf 99",
+                patterns=["error: Found tag 0x63 which is > max MTE tag value of 0xf."],
+                error=True)
Index: lldb/test/API/functionalities/memory/tag/TestMemoryTag.py
===================================================================
--- lldb/test/API/functionalities/memory/tag/TestMemoryTag.py
+++ lldb/test/API/functionalities/memory/tag/TestMemoryTag.py
@@ -39,3 +39,4 @@
             expected = "error: This architecture does not support memory tagging"
 
         self.expect("memory tag read 0 1", substrs=[expected], error=True)
+        self.expect("memory tag write 0 1 2", substrs=[expected], error=True)
Index: lldb/source/Commands/CommandObjectMemoryTag.cpp
===================================================================
--- lldb/source/Commands/CommandObjectMemoryTag.cpp
+++ lldb/source/Commands/CommandObjectMemoryTag.cpp
@@ -115,6 +115,114 @@
   }
 };
 
+#define LLDB_OPTIONS_memory_tag_write
+#include "CommandOptions.inc"
+
+class CommandObjectMemoryTagWrite : public CommandObjectParsed {
+public:
+  CommandObjectMemoryTagWrite(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "tag",
+                            "Write memory tags starting from the granule that "
+                            "contains the given address.",
+                            nullptr,
+                            eCommandRequiresTarget | eCommandRequiresProcess |
+                                eCommandProcessMustBePaused) {
+    // Address
+    m_arguments.push_back(
+        CommandArgumentEntry{CommandArgumentData(eArgTypeAddressOrExpression)});
+    // One or more tag values
+    m_arguments.push_back(CommandArgumentEntry{
+        CommandArgumentData(eArgTypeValue, eArgRepeatPlus)});
+  }
+
+  ~CommandObjectMemoryTagWrite() override = default;
+
+protected:
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    if (command.GetArgumentCount() < 2) {
+      result.AppendError("wrong number of arguments; expected "
+                         "<address-expression> <tag> [<tag> [...]]");
+      return false;
+    }
+
+    Status error;
+    addr_t start_addr = OptionArgParser::ToAddress(
+        &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error);
+    if (start_addr == LLDB_INVALID_ADDRESS) {
+      result.AppendErrorWithFormatv("Invalid address expression, {0}",
+                                    error.AsCString());
+      return false;
+    }
+
+    command.Shift(); // shift off start address
+
+    std::vector<lldb::addr_t> tags;
+    for (auto &entry : command) {
+      lldb::addr_t tag_value;
+      // getAsInteger returns true on failure
+      if (entry.ref().getAsInteger(0, tag_value)) {
+        result.AppendErrorWithFormat(
+            "'%s' is not a valid unsigned decimal string value.\n",
+            entry.c_str());
+        return false;
+      }
+      tags.push_back(tag_value);
+    }
+
+    Process *process = m_exe_ctx.GetProcessPtr();
+    llvm::Expected<const MemoryTagManager *> tag_manager_or_err =
+        process->GetMemoryTagManager();
+
+    if (!tag_manager_or_err) {
+      result.SetError(Status(tag_manager_or_err.takeError()));
+      return false;
+    }
+
+    const MemoryTagManager *tag_manager = *tag_manager_or_err;
+
+    MemoryRegionInfos memory_regions;
+    // If this fails the list of regions is cleared, so we don't need to read
+    // the return status here.
+    process->GetMemoryRegions(memory_regions);
+
+    // We have to assume start_addr is not granule aligned.
+    // So if we simply made a range:
+    // (start_addr, start_addr + (N * granule_size))
+    // We would end up with a range that isn't N granules but N+1
+    // granules. To avoid this we'll align the start first using the method that
+    // doesn't check memory attributes. (if the final range is untagged we'll
+    // handle that error later)
+    lldb::addr_t aligned_start_addr =
+        tag_manager->ExpandToGranule(MemoryTagManager::TagRange(start_addr, 1))
+            .GetRangeBase();
+
+    // Now we've aligned the start address so if we ask for another range
+    // using the number of tags N, we'll get back a range that is also N
+    // granules in size.
+    llvm::Expected<MemoryTagManager::TagRange> tagged_range =
+        tag_manager->MakeTaggedRange(
+            aligned_start_addr,
+            aligned_start_addr + (tags.size() * tag_manager->GetGranuleSize()),
+            memory_regions);
+
+    if (!tagged_range) {
+      result.SetError(Status(tagged_range.takeError()));
+      return false;
+    }
+
+    Status status = process->WriteMemoryTags(tagged_range->GetRangeBase(),
+                                             tagged_range->GetByteSize(), tags);
+
+    if (status.Fail()) {
+      result.SetError(status);
+      return false;
+    }
+
+    result.SetStatus(eReturnStatusSuccessFinishResult);
+    return true;
+  }
+};
+
 CommandObjectMemoryTag::CommandObjectMemoryTag(CommandInterpreter &interpreter)
     : CommandObjectMultiword(
           interpreter, "tag", "Commands for manipulating memory tags",
@@ -123,6 +231,11 @@
       new CommandObjectMemoryTagRead(interpreter));
   read_command_object->SetCommandName("memory tag read");
   LoadSubCommand("read", read_command_object);
+
+  CommandObjectSP write_command_object(
+      new CommandObjectMemoryTagWrite(interpreter));
+  write_command_object->SetCommandName("memory tag write");
+  LoadSubCommand("write", write_command_object);
 }
 
 CommandObjectMemoryTag::~CommandObjectMemoryTag() = default;
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to