#define LLDB_CORE_ARCHITECTURE_H
#include "lldb/Core/PluginInterface.h"
+#include "lldb/Target/MemoryTagManager.h"
namespace lldb_private {
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
#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"
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
/// 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;
--- /dev/null
+//===-- 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; }
--- /dev/null
+//===-- 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
--- /dev/null
+add_lldb_library(lldbPluginArchitectureAArch64 PLUGIN
+ ArchitectureAArch64.cpp
+
+ LINK_LIBS
+ lldbPluginProcessUtility
+ lldbCore
+ lldbTarget
+ lldbUtility
+ LINK_COMPONENTS
+ Support
+ )
add_subdirectory(Arm)
add_subdirectory(Mips)
add_subdirectory(PPC64)
+add_subdirectory(AArch64)
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;
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.
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;
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;
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());
+}
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);
+}