This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG7d27230de333: [lldb][AArch64] Add memory tag writing to
lldb-server (authored by DavidSpickett).
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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits