This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG5d3436200147: [lldb][AArch64] Add MTE memory tag reading to 
lldb (authored by DavidSpickett).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D95602

Files:
  lldb/include/lldb/Core/Architecture.h
  lldb/include/lldb/Target/Process.h
  lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.cpp
  lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.h
  lldb/source/Plugins/Architecture/AArch64/CMakeLists.txt
  lldb/source/Plugins/Architecture/CMakeLists.txt
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
  lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
  lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
  lldb/source/Target/Process.cpp
  lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp

Index: lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
===================================================================
--- lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
+++ lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
@@ -465,3 +465,68 @@
   EXPECT_EQ(llvm::None, GetQOffsets("TextSeg=0x1234"));
   EXPECT_EQ(llvm::None, GetQOffsets("TextSeg=12345678123456789"));
 }
+
+static void
+check_qmemtags(TestClient &client, MockServer &server, size_t read_len,
+               const char *packet, llvm::StringRef response,
+               llvm::Optional<std::vector<uint8_t>> expected_tag_data) {
+  const auto &ReadMemoryTags = [&](size_t len, const char *packet,
+                                   llvm::StringRef response) {
+    std::future<DataBufferSP> result = std::async(std::launch::async, [&] {
+      return client.ReadMemoryTags(0xDEF0, read_len, 1);
+    });
+
+    HandlePacket(server, packet, response);
+    return result.get();
+  };
+
+  auto result = ReadMemoryTags(0, packet, response);
+  if (expected_tag_data) {
+    ASSERT_TRUE(result);
+    llvm::ArrayRef<uint8_t> expected(*expected_tag_data);
+    llvm::ArrayRef<uint8_t> got = result->GetData();
+    ASSERT_THAT(expected, testing::ContainerEq(got));
+  } else {
+    ASSERT_FALSE(result);
+  }
+}
+
+TEST_F(GDBRemoteCommunicationClientTest, ReadMemoryTags) {
+  // Zero length reads are valid
+  check_qmemtags(client, server, 0, "qMemTags:def0,0:1", "m",
+                 std::vector<uint8_t>{});
+
+  // The client layer does not check the length of the received data.
+  // All we need is the "m" and for the decode to use all of the chars
+  check_qmemtags(client, server, 32, "qMemTags:def0,20:1", "m09",
+                 std::vector<uint8_t>{0x9});
+
+  // Zero length response is fine as long as the "m" is present
+  check_qmemtags(client, server, 0, "qMemTags:def0,0:1", "m",
+                 std::vector<uint8_t>{});
+
+  // Normal responses
+  check_qmemtags(client, server, 16, "qMemTags:def0,10:1", "m66",
+                 std::vector<uint8_t>{0x66});
+  check_qmemtags(client, server, 32, "qMemTags:def0,20:1", "m0102",
+                 std::vector<uint8_t>{0x1, 0x2});
+
+  // Empty response is an error
+  check_qmemtags(client, server, 17, "qMemTags:def0,11:1", "", llvm::None);
+  // Usual error response
+  check_qmemtags(client, server, 17, "qMemTags:def0,11:1", "E01", llvm::None);
+  // Leading m missing
+  check_qmemtags(client, server, 17, "qMemTags:def0,11:1", "01", llvm::None);
+  // Anything other than m is an error
+  check_qmemtags(client, server, 17, "qMemTags:def0,11:1", "z01", llvm::None);
+  // Decoding tag data doesn't use all the chars in the packet
+  check_qmemtags(client, server, 32, "qMemTags:def0,20:1", "m09zz", llvm::None);
+  // Data that is not hex bytes
+  check_qmemtags(client, server, 32, "qMemTags:def0,20:1", "mhello",
+                 llvm::None);
+  // Data is not a complete hex char
+  check_qmemtags(client, server, 32, "qMemTags:def0,20:1", "m9", llvm::None);
+  // Data has a trailing hex char
+  check_qmemtags(client, server, 32, "qMemTags:def0,20:1", "m01020",
+                 llvm::None);
+}
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -6065,3 +6065,84 @@
 
   return false;
 }
+
+llvm::Expected<const MemoryTagManager *>
+Process::GetMemoryTagManager(lldb::addr_t addr, lldb::addr_t end_addr) {
+  Architecture *arch = GetTarget().GetArchitecturePlugin();
+  const MemoryTagManager *tag_manager =
+      arch ? arch->GetMemoryTagManager() : nullptr;
+  if (!arch || !tag_manager) {
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        "This architecture does not support memory tagging",
+        GetPluginName().GetCString());
+  }
+
+  if (!SupportsMemoryTagging()) {
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "Process does not support memory tagging");
+  }
+
+  ptrdiff_t len = tag_manager->AddressDiff(end_addr, addr);
+  if (len <= 0) {
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        "End address (0x%" PRIx64
+        ") must be greater than the start address (0x%" PRIx64 ")",
+        end_addr, addr);
+  }
+
+  // Region lookup is not address size aware so mask the address
+  MemoryRegionInfo::RangeType tag_range(tag_manager->RemoveNonAddressBits(addr),
+                                        len);
+  tag_range = tag_manager->ExpandToGranule(tag_range);
+
+  // Make a copy so we can use the original range in errors
+  MemoryRegionInfo::RangeType remaining_range(tag_range);
+
+  // While we haven't found a matching memory region for some of the range
+  while (remaining_range.IsValid()) {
+    MemoryRegionInfo region;
+    Status status = GetMemoryRegionInfo(remaining_range.GetRangeBase(), region);
+
+    if (status.Fail() || region.GetMemoryTagged() != MemoryRegionInfo::eYes) {
+      return llvm::createStringError(
+          llvm::inconvertibleErrorCode(),
+          "Address range 0x%lx:0x%lx is not in a memory tagged region",
+          tag_range.GetRangeBase(), tag_range.GetRangeEnd());
+    }
+
+    if (region.GetRange().GetRangeEnd() >= remaining_range.GetRangeEnd()) {
+      // We've found a region for the whole range or the last piece of a range
+      remaining_range.SetByteSize(0);
+    } else {
+      // We've found some part of the range, look for the rest
+      remaining_range.SetRangeBase(region.GetRange().GetRangeEnd());
+    }
+  }
+
+  return tag_manager;
+}
+
+llvm::Expected<std::vector<lldb::addr_t>>
+Process::ReadMemoryTags(const MemoryTagManager *tag_manager, lldb::addr_t addr,
+                        size_t len) {
+  if (!tag_manager) {
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        "A memory tag manager is required for reading memory tags.");
+  }
+
+  MemoryTagManager::TagRange range(tag_manager->RemoveNonAddressBits(addr),
+                                   len);
+  range = tag_manager->ExpandToGranule(range);
+
+  llvm::Expected<std::vector<uint8_t>> tag_data =
+      DoReadMemoryTags(range.GetRangeBase(), range.GetByteSize(),
+                       tag_manager->GetAllocationTagType());
+  if (!tag_data)
+    return tag_data.takeError();
+
+  return tag_manager->UnpackTagsData(
+      *tag_data, range.GetByteSize() / tag_manager->GetGranuleSize());
+}
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -408,6 +408,9 @@
 
   bool HasErased(FlashRange range);
 
+  llvm::Expected<std::vector<uint8_t>>
+  DoReadMemoryTags(lldb::addr_t addr, size_t len, int32_t type) override;
+
 private:
   // For ProcessGDBRemote only
   std::string m_partial_profile_data;
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -2771,6 +2771,25 @@
   return m_gdb_comm.GetMemoryTaggingSupported();
 }
 
+llvm::Expected<std::vector<uint8_t>>
+ProcessGDBRemote::DoReadMemoryTags(lldb::addr_t addr, size_t len,
+                                   int32_t type) {
+  // By this point ReadMemoryTags has validated that tagging is enabled
+  // for this target/process/address.
+  DataBufferSP buffer_sp = m_gdb_comm.ReadMemoryTags(addr, len, type);
+  if (!buffer_sp) {
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "Error reading memory tags from remote");
+  }
+
+  // Return the raw tag data
+  llvm::ArrayRef<uint8_t> tag_data = buffer_sp->GetData();
+  std::vector<uint8_t> got;
+  got.reserve(tag_data.size());
+  std::copy(tag_data.begin(), tag_data.end(), std::back_inserter(got));
+  return got;
+}
+
 Status ProcessGDBRemote::WriteObjectFile(
     std::vector<ObjectFile::LoadableData> entries) {
   Status error;
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -453,6 +453,9 @@
 
   bool GetMemoryTaggingSupported();
 
+  lldb::DataBufferSP ReadMemoryTags(lldb::addr_t addr, size_t len,
+                                    int32_t type);
+
   /// 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.
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -586,6 +586,50 @@
   return m_supports_memory_tagging == eLazyBoolYes;
 }
 
+DataBufferSP GDBRemoteCommunicationClient::ReadMemoryTags(lldb::addr_t addr,
+                                                          size_t len,
+                                                          int32_t type) {
+  StreamString packet;
+  packet.Printf("qMemTags:%lx,%lx:%x", addr, len, type);
+  StringExtractorGDBRemote response;
+
+  Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_MEMORY);
+
+  if (SendPacketAndWaitForResponse(packet.GetString(), response, false) !=
+          PacketResult::Success ||
+      !response.IsNormalResponse()) {
+    LLDB_LOGF(log, "GDBRemoteCommunicationClient::%s: qMemTags packet failed",
+              __FUNCTION__);
+    return nullptr;
+  }
+
+  // We are expecting
+  // m<hex encoded bytes>
+
+  if (response.GetChar() != 'm') {
+    LLDB_LOGF(log,
+              "GDBRemoteCommunicationClient::%s: qMemTags response did not "
+              "begin with \"m\"",
+              __FUNCTION__);
+    return nullptr;
+  }
+
+  size_t expected_bytes = response.GetBytesLeft() / 2;
+  DataBufferSP buffer_sp(new DataBufferHeap(expected_bytes, 0));
+  size_t got_bytes = response.GetHexBytesAvail(buffer_sp->GetData());
+  // Check both because in some situations chars are consumed even
+  // if the decoding fails.
+  if (response.GetBytesLeft() || (expected_bytes != got_bytes)) {
+    LLDB_LOGF(
+        log,
+        "GDBRemoteCommunicationClient::%s: Invalid data in qMemTags response",
+        __FUNCTION__);
+    return nullptr;
+  }
+
+  return buffer_sp;
+}
+
 bool GDBRemoteCommunicationClient::GetxPacketSupported() {
   if (m_supports_x == eLazyBoolCalculate) {
     StringExtractorGDBRemote response;
Index: lldb/source/Plugins/Architecture/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/Architecture/CMakeLists.txt
+++ lldb/source/Plugins/Architecture/CMakeLists.txt
@@ -1,3 +1,4 @@
 add_subdirectory(Arm)
 add_subdirectory(Mips)
 add_subdirectory(PPC64)
+add_subdirectory(AArch64)
Index: lldb/source/Plugins/Architecture/AArch64/CMakeLists.txt
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Architecture/AArch64/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_lldb_library(lldbPluginArchitectureAArch64 PLUGIN
+  ArchitectureAArch64.cpp
+
+  LINK_LIBS
+    lldbPluginProcessUtility
+    lldbCore
+    lldbTarget
+    lldbUtility
+  LINK_COMPONENTS
+    Support
+  )
Index: lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.h
@@ -0,0 +1,40 @@
+//===-- ArchitectureAArch64.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_ARCHITECTURE_AARCH64_ARCHITECTUREAARCH64_H
+#define LLDB_SOURCE_PLUGINS_ARCHITECTURE_AARCH64_ARCHITECTUREAARCH64_H
+
+#include "Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h"
+#include "lldb/Core/Architecture.h"
+
+namespace lldb_private {
+
+class ArchitectureAArch64 : public Architecture {
+public:
+  static ConstString GetPluginNameStatic();
+  static void Initialize();
+  static void Terminate();
+
+  ConstString GetPluginName() override;
+  uint32_t GetPluginVersion() override;
+
+  void OverrideStopInfo(Thread &thread) const override{};
+
+  const MemoryTagManager *GetMemoryTagManager() const override {
+    return &m_memory_tag_manager;
+  }
+
+private:
+  static std::unique_ptr<Architecture> Create(const ArchSpec &arch);
+  ArchitectureAArch64() = default;
+  MemoryTagManagerAArch64MTE m_memory_tag_manager;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_ARCHITECTURE_AARCH64_ARCHITECTUREAARCH64_H
Index: lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.cpp
@@ -0,0 +1,45 @@
+//===-- ArchitectureAArch64.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/Architecture/AArch64/ArchitectureAArch64.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Utility/ArchSpec.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+LLDB_PLUGIN_DEFINE(ArchitectureAArch64)
+
+ConstString ArchitectureAArch64::GetPluginNameStatic() {
+  return ConstString("aarch64");
+}
+
+void ArchitectureAArch64::Initialize() {
+  PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                "AArch64-specific algorithms",
+                                &ArchitectureAArch64::Create);
+}
+
+void ArchitectureAArch64::Terminate() {
+  PluginManager::UnregisterPlugin(&ArchitectureAArch64::Create);
+}
+
+std::unique_ptr<Architecture>
+ArchitectureAArch64::Create(const ArchSpec &arch) {
+  auto machine = arch.GetMachine();
+  if (machine != llvm::Triple::aarch64 && machine != llvm::Triple::aarch64_be &&
+      machine != llvm::Triple::aarch64_32) {
+    return nullptr;
+  }
+  return std::unique_ptr<Architecture>(new ArchitectureAArch64());
+}
+
+ConstString ArchitectureAArch64::GetPluginName() {
+  return GetPluginNameStatic();
+}
+uint32_t ArchitectureAArch64::GetPluginVersion() { return 1; }
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -34,6 +34,7 @@
 #include "lldb/Target/ExecutionContextScope.h"
 #include "lldb/Target/InstrumentationRuntime.h"
 #include "lldb/Target/Memory.h"
+#include "lldb/Target/MemoryTagManager.h"
 #include "lldb/Target/QueueList.h"
 #include "lldb/Target/ThreadList.h"
 #include "lldb/Target/ThreadPlanStack.h"
@@ -1709,6 +1710,44 @@
   lldb::addr_t CallocateMemory(size_t size, uint32_t permissions,
                                Status &error);
 
+  /// If the address range given is in a memory tagged range and this
+  /// architecture and process supports memory tagging, return a tag
+  /// manager that can be used to maniupulate those memory tags.
+  /// Tags present in the addresses given are ignored.
+  ///
+  /// \param[in] addr
+  ///     Start of memory range.
+  ///
+  /// \param[in] end_addr
+  ///     End of the memory range. Where end is one beyond the last byte to be
+  ///     included.
+  ///
+  /// \return
+  ///     Either a valid pointer to a tag manager or an error describing why one
+  ///     could not be provided.
+  llvm::Expected<const MemoryTagManager *>
+  GetMemoryTagManager(lldb::addr_t addr, lldb::addr_t end_addr);
+
+  /// Expands the range addr to addr+len to align with granule boundaries and
+  /// then calls DoReadMemoryTags to do the target specific operations.
+  /// Tags are returned unpacked so can be used without conversion.
+  ///
+  /// \param[in] tag_manager
+  ///     The tag manager to get memory tagging information from.
+  ///
+  /// \param[in] addr
+  ///     Start of memory range to read tags for.
+  ///
+  /// \param[in] len
+  ///     Length of memory range to read tags for (in bytes).
+  ///
+  /// \return
+  ///     Either the unpacked tags or an error describing a failure to read
+  ///     or unpack them.
+  llvm::Expected<std::vector<lldb::addr_t>>
+  ReadMemoryTags(const MemoryTagManager *tag_manager, lldb::addr_t addr,
+                 size_t len);
+
   /// Resolve dynamically loaded indirect functions.
   ///
   /// \param[in] address
@@ -2728,6 +2767,29 @@
   ///     false otherwise.
   virtual bool SupportsMemoryTagging() { return false; }
 
+  /// Does the final operation to read memory tags. E.g. sending a GDB packet.
+  /// It assumes that ReadMemoryTags has checked that memory tagging is enabled
+  /// and has expanded the memory range as needed.
+  ///
+  /// \param[in] addr
+  ///    Start of address range to read memory tags for.
+  ///
+  /// \param[in] len
+  ///    Length of the memory range to read tags for (in bytes).
+  ///
+  /// \param[in] type
+  ///    Type of tags to read (get this from a MemoryTagManager)
+  ///
+  /// \return
+  ///     The packed tag data received from the remote or an error
+  ///     if the read failed.
+  virtual llvm::Expected<std::vector<uint8_t>>
+  DoReadMemoryTags(lldb::addr_t addr, size_t len, int32_t type) {
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "%s does not support reading memory tags",
+                                   GetPluginName().GetCString());
+  }
+
   // Type definitions
   typedef std::map<lldb::LanguageType, lldb::LanguageRuntimeSP>
       LanguageRuntimeCollection;
Index: lldb/include/lldb/Core/Architecture.h
===================================================================
--- lldb/include/lldb/Core/Architecture.h
+++ lldb/include/lldb/Core/Architecture.h
@@ -10,6 +10,7 @@
 #define LLDB_CORE_ARCHITECTURE_H
 
 #include "lldb/Core/PluginInterface.h"
+#include "lldb/Target/MemoryTagManager.h"
 
 namespace lldb_private {
 
@@ -97,6 +98,17 @@
                                                Target &target) const {
     return addr;
   }
+
+  // Returns a pointer to an object that can manage memory tags for this
+  // Architecture E.g. masking out tags, unpacking tag streams etc. Returns
+  // nullptr if the architecture does not have a memory tagging extension.
+  //
+  // The return pointer being valid does not mean that the current process has
+  // memory tagging enabled, just that a tagging technology exists for this
+  // architecture.
+  virtual const MemoryTagManager *GetMemoryTagManager() const {
+    return nullptr;
+  }
 };
 
 } // namespace lldb_private
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to