diff options
Diffstat (limited to 'lldb/source/Commands/CommandObjectMemoryTag.cpp')
-rw-r--r-- | lldb/source/Commands/CommandObjectMemoryTag.cpp | 182 |
1 files changed, 177 insertions, 5 deletions
diff --git a/lldb/source/Commands/CommandObjectMemoryTag.cpp b/lldb/source/Commands/CommandObjectMemoryTag.cpp index 1dfb32a92f3b..840f81719d7d 100644 --- a/lldb/source/Commands/CommandObjectMemoryTag.cpp +++ b/lldb/source/Commands/CommandObjectMemoryTag.cpp @@ -7,8 +7,11 @@ //===----------------------------------------------------------------------===// #include "CommandObjectMemoryTag.h" +#include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionValueString.h" #include "lldb/Target/Process.h" using namespace lldb; @@ -21,7 +24,8 @@ class CommandObjectMemoryTagRead : public CommandObjectParsed { public: CommandObjectMemoryTagRead(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "tag", - "Read memory tags for the given range of memory.", + "Read memory tags for the given range of memory." + " Mismatched tags will be marked.", nullptr, eCommandRequiresTarget | eCommandRequiresProcess | eCommandProcessMustBePaused) { @@ -97,16 +101,17 @@ protected: return false; } - result.AppendMessageWithFormatv("Logical tag: {0:x}", - tag_manager->GetLogicalTag(start_addr)); + lldb::addr_t logical_tag = tag_manager->GetLogicalTag(start_addr); + result.AppendMessageWithFormatv("Logical tag: {0:x}", logical_tag); result.AppendMessage("Allocation tags:"); addr_t addr = tagged_range->GetRangeBase(); for (auto tag : *tags) { addr_t next_addr = addr + tag_manager->GetGranuleSize(); // Showing tagged adresses here until we have non address bit handling - result.AppendMessageWithFormatv("[{0:x}, {1:x}): {2:x}", addr, next_addr, - tag); + result.AppendMessageWithFormatv("[{0:x}, {1:x}): {2:x}{3}", addr, + next_addr, tag, + logical_tag == tag ? "" : " (mismatch)"); addr = next_addr; } @@ -115,6 +120,168 @@ protected: } }; +#define LLDB_OPTIONS_memory_tag_write +#include "CommandOptions.inc" + +class CommandObjectMemoryTagWrite : public CommandObjectParsed { +public: + class OptionGroupTagWrite : public OptionGroup { + public: + OptionGroupTagWrite() : OptionGroup(), m_end_addr(LLDB_INVALID_ADDRESS) {} + + ~OptionGroupTagWrite() override = default; + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::makeArrayRef(g_memory_tag_write_options); + } + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, + ExecutionContext *execution_context) override { + Status status; + const int short_option = + g_memory_tag_write_options[option_idx].short_option; + + switch (short_option) { + case 'e': + m_end_addr = OptionArgParser::ToAddress(execution_context, option_value, + LLDB_INVALID_ADDRESS, &status); + break; + default: + llvm_unreachable("Unimplemented option"); + } + + return status; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_end_addr = LLDB_INVALID_ADDRESS; + } + + lldb::addr_t m_end_addr; + }; + + CommandObjectMemoryTagWrite(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "tag", + "Write memory tags starting from the granule that " + "contains the given address.", + nullptr, + eCommandRequiresTarget | eCommandRequiresProcess | + eCommandProcessMustBePaused), + m_option_group(), m_tag_write_options() { + // Address + m_arguments.push_back( + CommandArgumentEntry{CommandArgumentData(eArgTypeAddressOrExpression)}); + // One or more tag values + m_arguments.push_back(CommandArgumentEntry{ + CommandArgumentData(eArgTypeValue, eArgRepeatPlus)}); + + m_option_group.Append(&m_tag_write_options); + m_option_group.Finalize(); + } + + ~CommandObjectMemoryTagWrite() override = default; + + Options *GetOptions() override { return &m_option_group; } + +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(); + + lldb::addr_t end_addr = 0; + // When you have an end address you want to align the range like tag read + // does. Meaning, align the start down (which we've done) and align the end + // up. + if (m_tag_write_options.m_end_addr != LLDB_INVALID_ADDRESS) + end_addr = m_tag_write_options.m_end_addr; + else + // Without an end address assume number of tags matches number of granules + // to write to + end_addr = + aligned_start_addr + (tags.size() * tag_manager->GetGranuleSize()); + + // 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, end_addr, + 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; + } + + OptionGroupOptions m_option_group; + OptionGroupTagWrite m_tag_write_options; +}; + CommandObjectMemoryTag::CommandObjectMemoryTag(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "tag", "Commands for manipulating memory tags", @@ -123,6 +290,11 @@ CommandObjectMemoryTag::CommandObjectMemoryTag(CommandInterpreter &interpreter) 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; |