diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -1737,6 +1737,29 @@ llvm::Expected> ReadMemoryTags(lldb::addr_t addr, size_t len); + /// Write memory tags for a range of memory. + /// (calls DoWriteMemoryTags to do the target specific work) + /// + /// \param[in] addr + /// The address to start writing tags from. It is assumed that this + /// address is granule aligned. + /// + /// \param[in] len + /// The size of the range to write tags for. It is assumed that this + /// is some multiple of the granule size. This len can be different + /// from (number of tags * granule size) in the case where you want + /// lldb-server to repeat tags across the range. + /// + /// \param[in] tags + /// Allocation tags to be written. Since lldb-server can repeat tags for a + /// range, the number of tags doesn't have to match the number of granules + /// in the range. (though most of the time it will) + /// + /// \return + /// A Status telling you if the write succeeded or not. + Status WriteMemoryTags(lldb::addr_t addr, size_t len, + const std::vector &tags); + /// Resolve dynamically loaded indirect functions. /// /// \param[in] address @@ -2779,6 +2802,30 @@ GetPluginName().GetCString()); } + /// Does the final operation to write memory tags. E.g. sending a GDB packet. + /// It assumes that WriteMemoryTags has checked that memory tagging is enabled + /// and has packed the tag data. + /// + /// \param[in] addr + /// Start of address range to write memory tags for. + /// + /// \param[in] len + /// Length of the memory range to write tags for (in bytes). + /// + /// \param[in] type + /// Type of tags to read (get this from a MemoryTagManager) + /// + /// \param[in] tags + /// Packed tags to be written. + /// + /// \return + /// Status telling you whether the write succeeded. + virtual Status DoWriteMemoryTags(lldb::addr_t addr, size_t len, int32_t type, + const std::vector &tags) { + return Status("%s does not support writing memory tags", + GetPluginName().GetCString()); + } + // Type definitions typedef std::map LanguageRuntimeCollection; diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -468,6 +468,9 @@ lldb::DataBufferSP ReadMemoryTags(lldb::addr_t addr, size_t len, int32_t type); + Status WriteMemoryTags(lldb::addr_t addr, size_t len, int32_t type, + const std::vector &tags); + /// Use qOffsets to query the offset used when relocating the target /// executable. If successful, the returned structure will contain at least /// one value in the offsets field. diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -628,6 +628,24 @@ return buffer_sp; } +Status GDBRemoteCommunicationClient::WriteMemoryTags( + lldb::addr_t addr, size_t len, int32_t type, + const std::vector &tags) { + // Format QMemTags:address,length:type:tags + StreamString packet; + packet.Printf("QMemTags:%" PRIx64 ",%zx:%" PRIx32 ":", addr, len, type); + packet.PutBytesAsRawHex8(tags.data(), tags.size()); + + Status status; + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) != + PacketResult::Success || + !response.IsOKResponse()) { + status.SetErrorString("QMemTags packet failed"); + } + return status; +} + bool GDBRemoteCommunicationClient::GetxPacketSupported() { if (m_supports_x == eLazyBoolCalculate) { StringExtractorGDBRemote response; diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -411,6 +411,9 @@ llvm::Expected> DoReadMemoryTags(lldb::addr_t addr, size_t len, int32_t type) override; + Status DoWriteMemoryTags(lldb::addr_t addr, size_t len, int32_t type, + const std::vector &tags) override; + private: // For ProcessGDBRemote only std::string m_partial_profile_data; diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -2791,6 +2791,14 @@ return got; } +Status ProcessGDBRemote::DoWriteMemoryTags(lldb::addr_t addr, size_t len, + int32_t type, + const std::vector &tags) { + // By now WriteMemoryTags should have validated that tagging is enabled + // for this target/process. + return m_gdb_comm.WriteMemoryTags(addr, len, type, tags); +} + Status ProcessGDBRemote::WriteObjectFile( std::vector entries) { Status error; diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -6102,3 +6102,21 @@ return tag_manager->UnpackTagsData(*tag_data, len / tag_manager->GetGranuleSize()); } + +Status Process::WriteMemoryTags(lldb::addr_t addr, size_t len, + const std::vector &tags) { + llvm::Expected tag_manager_or_err = + GetMemoryTagManager(); + if (!tag_manager_or_err) + return Status(tag_manager_or_err.takeError()); + + const MemoryTagManager *tag_manager = *tag_manager_or_err; + llvm::Expected> packed_tags = + tag_manager->PackTags(tags); + if (!packed_tags) { + return Status(packed_tags.takeError()); + } + + return DoWriteMemoryTags(addr, len, tag_manager->GetAllocationTagType(), + *packed_tags); +} diff --git a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp --- a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp +++ b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp @@ -17,6 +17,7 @@ #include "llvm/Testing/Support/Error.h" #include "gmock/gmock.h" #include +#include using namespace lldb_private::process_gdb_remote; using namespace lldb_private; @@ -531,3 +532,51 @@ check_qmemtags(client, server, 32, "qMemTags:def0,20:1", "m01020", llvm::None); } + +static void check_Qmemtags(TestClient &client, MockServer &server, + lldb::addr_t addr, size_t len, int32_t type, + const std::vector &tags, const char *packet, + llvm::StringRef response, bool should_succeed) { + const auto &WriteMemoryTags = [&]() { + std::future result = std::async(std::launch::async, [&] { + return client.WriteMemoryTags(addr, len, type, tags); + }); + + HandlePacket(server, packet, response); + return result.get(); + }; + + auto result = WriteMemoryTags(); + if (should_succeed) + ASSERT_TRUE(result.Success()); + else + ASSERT_TRUE(result.Fail()); +} + +TEST_F(GDBRemoteCommunicationClientTest, WriteMemoryTags) { + check_Qmemtags(client, server, 0xABCD, 0x20, 1, + std::vector{0x12, 0x34}, "QMemTags:abcd,20:1:1234", + "OK", true); + + // The GDB layer doesn't care that the number of tags != + // the length of the write. + check_Qmemtags(client, server, 0x4321, 0x20, 9, std::vector{}, + "QMemTags:4321,20:9:", "OK", true); + + check_Qmemtags(client, server, 0x8877, 0x123, 0x34, + std::vector{0x55, 0x66, 0x77}, + "QMemTags:8877,123:34:556677", "E01", false); + + // Type is a signed integer but is packed as its raw bytes, + // instead of having a +/-. + check_Qmemtags(client, server, 0x456789, 0, -1, std::vector{0x99}, + "QMemTags:456789,0:ffffffff:99", "E03", false); + check_Qmemtags(client, server, 0x456789, 0, + std::numeric_limits::max(), + std::vector{0x99}, "QMemTags:456789,0:7fffffff:99", + "E03", false); + check_Qmemtags(client, server, 0x456789, 0, + std::numeric_limits::min(), + std::vector{0x99}, "QMemTags:456789,0:80000000:99", + "E03", false); +}