1 //===-- CommandObjectMemoryTag.cpp ----------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "CommandObjectMemoryTag.h" 10 #include "lldb/Host/OptionParser.h" 11 #include "lldb/Interpreter/CommandReturnObject.h" 12 #include "lldb/Interpreter/OptionArgParser.h" 13 #include "lldb/Interpreter/OptionGroupFormat.h" 14 #include "lldb/Interpreter/OptionValueString.h" 15 #include "lldb/Target/Process.h" 16 17 using namespace lldb; 18 using namespace lldb_private; 19 20 #define LLDB_OPTIONS_memory_tag_read 21 #include "CommandOptions.inc" 22 23 class CommandObjectMemoryTagRead : public CommandObjectParsed { 24 public: 25 CommandObjectMemoryTagRead(CommandInterpreter &interpreter) 26 : CommandObjectParsed(interpreter, "tag", 27 "Read memory tags for the given range of memory." 28 " Mismatched tags will be marked.", 29 nullptr, 30 eCommandRequiresTarget | eCommandRequiresProcess | 31 eCommandProcessMustBePaused) { 32 // Address 33 m_arguments.push_back( 34 CommandArgumentEntry{CommandArgumentData(eArgTypeAddressOrExpression)}); 35 // Optional end address 36 m_arguments.push_back(CommandArgumentEntry{ 37 CommandArgumentData(eArgTypeAddressOrExpression, eArgRepeatOptional)}); 38 } 39 40 ~CommandObjectMemoryTagRead() override = default; 41 42 protected: 43 bool DoExecute(Args &command, CommandReturnObject &result) override { 44 if ((command.GetArgumentCount() < 1) || (command.GetArgumentCount() > 2)) { 45 result.AppendError( 46 "wrong number of arguments; expected at least <address-expression>, " 47 "at most <address-expression> <end-address-expression>"); 48 return false; 49 } 50 51 Status error; 52 addr_t start_addr = OptionArgParser::ToAddress( 53 &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); 54 if (start_addr == LLDB_INVALID_ADDRESS) { 55 result.AppendErrorWithFormatv("Invalid address expression, {0}", 56 error.AsCString()); 57 return false; 58 } 59 60 // Default 1 byte beyond start, rounds up to at most 1 granule later 61 addr_t end_addr = start_addr + 1; 62 63 if (command.GetArgumentCount() > 1) { 64 end_addr = OptionArgParser::ToAddress(&m_exe_ctx, command[1].ref(), 65 LLDB_INVALID_ADDRESS, &error); 66 if (end_addr == LLDB_INVALID_ADDRESS) { 67 result.AppendErrorWithFormatv("Invalid end address expression, {0}", 68 error.AsCString()); 69 return false; 70 } 71 } 72 73 Process *process = m_exe_ctx.GetProcessPtr(); 74 llvm::Expected<const MemoryTagManager *> tag_manager_or_err = 75 process->GetMemoryTagManager(); 76 77 if (!tag_manager_or_err) { 78 result.SetError(Status(tag_manager_or_err.takeError())); 79 return false; 80 } 81 82 const MemoryTagManager *tag_manager = *tag_manager_or_err; 83 84 MemoryRegionInfos memory_regions; 85 // If this fails the list of regions is cleared, so we don't need to read 86 // the return status here. 87 process->GetMemoryRegions(memory_regions); 88 llvm::Expected<MemoryTagManager::TagRange> tagged_range = 89 tag_manager->MakeTaggedRange(start_addr, end_addr, memory_regions); 90 91 if (!tagged_range) { 92 result.SetError(Status(tagged_range.takeError())); 93 return false; 94 } 95 96 llvm::Expected<std::vector<lldb::addr_t>> tags = process->ReadMemoryTags( 97 tagged_range->GetRangeBase(), tagged_range->GetByteSize()); 98 99 if (!tags) { 100 result.SetError(Status(tags.takeError())); 101 return false; 102 } 103 104 lldb::addr_t logical_tag = tag_manager->GetLogicalTag(start_addr); 105 result.AppendMessageWithFormatv("Logical tag: {0:x}", logical_tag); 106 result.AppendMessage("Allocation tags:"); 107 108 addr_t addr = tagged_range->GetRangeBase(); 109 for (auto tag : *tags) { 110 addr_t next_addr = addr + tag_manager->GetGranuleSize(); 111 // Showing tagged adresses here until we have non address bit handling 112 result.AppendMessageWithFormatv("[{0:x}, {1:x}): {2:x}{3}", addr, 113 next_addr, tag, 114 logical_tag == tag ? "" : " (mismatch)"); 115 addr = next_addr; 116 } 117 118 result.SetStatus(eReturnStatusSuccessFinishResult); 119 return true; 120 } 121 }; 122 123 #define LLDB_OPTIONS_memory_tag_write 124 #include "CommandOptions.inc" 125 126 class CommandObjectMemoryTagWrite : public CommandObjectParsed { 127 public: 128 class OptionGroupTagWrite : public OptionGroup { 129 public: 130 OptionGroupTagWrite() : OptionGroup(), m_end_addr(LLDB_INVALID_ADDRESS) {} 131 132 ~OptionGroupTagWrite() override = default; 133 134 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 135 return llvm::makeArrayRef(g_memory_tag_write_options); 136 } 137 138 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, 139 ExecutionContext *execution_context) override { 140 Status status; 141 const int short_option = 142 g_memory_tag_write_options[option_idx].short_option; 143 144 switch (short_option) { 145 case 'e': 146 m_end_addr = OptionArgParser::ToAddress(execution_context, option_value, 147 LLDB_INVALID_ADDRESS, &status); 148 break; 149 default: 150 llvm_unreachable("Unimplemented option"); 151 } 152 153 return status; 154 } 155 156 void OptionParsingStarting(ExecutionContext *execution_context) override { 157 m_end_addr = LLDB_INVALID_ADDRESS; 158 } 159 160 lldb::addr_t m_end_addr; 161 }; 162 163 CommandObjectMemoryTagWrite(CommandInterpreter &interpreter) 164 : CommandObjectParsed(interpreter, "tag", 165 "Write memory tags starting from the granule that " 166 "contains the given address.", 167 nullptr, 168 eCommandRequiresTarget | eCommandRequiresProcess | 169 eCommandProcessMustBePaused), 170 m_option_group(), m_tag_write_options() { 171 // Address 172 m_arguments.push_back( 173 CommandArgumentEntry{CommandArgumentData(eArgTypeAddressOrExpression)}); 174 // One or more tag values 175 m_arguments.push_back(CommandArgumentEntry{ 176 CommandArgumentData(eArgTypeValue, eArgRepeatPlus)}); 177 178 m_option_group.Append(&m_tag_write_options); 179 m_option_group.Finalize(); 180 } 181 182 ~CommandObjectMemoryTagWrite() override = default; 183 184 Options *GetOptions() override { return &m_option_group; } 185 186 protected: 187 bool DoExecute(Args &command, CommandReturnObject &result) override { 188 if (command.GetArgumentCount() < 2) { 189 result.AppendError("wrong number of arguments; expected " 190 "<address-expression> <tag> [<tag> [...]]"); 191 return false; 192 } 193 194 Status error; 195 addr_t start_addr = OptionArgParser::ToAddress( 196 &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); 197 if (start_addr == LLDB_INVALID_ADDRESS) { 198 result.AppendErrorWithFormatv("Invalid address expression, {0}", 199 error.AsCString()); 200 return false; 201 } 202 203 command.Shift(); // shift off start address 204 205 std::vector<lldb::addr_t> tags; 206 for (auto &entry : command) { 207 lldb::addr_t tag_value; 208 // getAsInteger returns true on failure 209 if (entry.ref().getAsInteger(0, tag_value)) { 210 result.AppendErrorWithFormat( 211 "'%s' is not a valid unsigned decimal string value.\n", 212 entry.c_str()); 213 return false; 214 } 215 tags.push_back(tag_value); 216 } 217 218 Process *process = m_exe_ctx.GetProcessPtr(); 219 llvm::Expected<const MemoryTagManager *> tag_manager_or_err = 220 process->GetMemoryTagManager(); 221 222 if (!tag_manager_or_err) { 223 result.SetError(Status(tag_manager_or_err.takeError())); 224 return false; 225 } 226 227 const MemoryTagManager *tag_manager = *tag_manager_or_err; 228 229 MemoryRegionInfos memory_regions; 230 // If this fails the list of regions is cleared, so we don't need to read 231 // the return status here. 232 process->GetMemoryRegions(memory_regions); 233 234 // We have to assume start_addr is not granule aligned. 235 // So if we simply made a range: 236 // (start_addr, start_addr + (N * granule_size)) 237 // We would end up with a range that isn't N granules but N+1 238 // granules. To avoid this we'll align the start first using the method that 239 // doesn't check memory attributes. (if the final range is untagged we'll 240 // handle that error later) 241 lldb::addr_t aligned_start_addr = 242 tag_manager->ExpandToGranule(MemoryTagManager::TagRange(start_addr, 1)) 243 .GetRangeBase(); 244 245 lldb::addr_t end_addr = 0; 246 // When you have an end address you want to align the range like tag read 247 // does. Meaning, align the start down (which we've done) and align the end 248 // up. 249 if (m_tag_write_options.m_end_addr != LLDB_INVALID_ADDRESS) 250 end_addr = m_tag_write_options.m_end_addr; 251 else 252 // Without an end address assume number of tags matches number of granules 253 // to write to 254 end_addr = 255 aligned_start_addr + (tags.size() * tag_manager->GetGranuleSize()); 256 257 // Now we've aligned the start address so if we ask for another range 258 // using the number of tags N, we'll get back a range that is also N 259 // granules in size. 260 llvm::Expected<MemoryTagManager::TagRange> tagged_range = 261 tag_manager->MakeTaggedRange(aligned_start_addr, end_addr, 262 memory_regions); 263 264 if (!tagged_range) { 265 result.SetError(Status(tagged_range.takeError())); 266 return false; 267 } 268 269 Status status = process->WriteMemoryTags(tagged_range->GetRangeBase(), 270 tagged_range->GetByteSize(), tags); 271 272 if (status.Fail()) { 273 result.SetError(status); 274 return false; 275 } 276 277 result.SetStatus(eReturnStatusSuccessFinishResult); 278 return true; 279 } 280 281 OptionGroupOptions m_option_group; 282 OptionGroupTagWrite m_tag_write_options; 283 }; 284 285 CommandObjectMemoryTag::CommandObjectMemoryTag(CommandInterpreter &interpreter) 286 : CommandObjectMultiword( 287 interpreter, "tag", "Commands for manipulating memory tags", 288 "memory tag <sub-command> [<sub-command-options>]") { 289 CommandObjectSP read_command_object( 290 new CommandObjectMemoryTagRead(interpreter)); 291 read_command_object->SetCommandName("memory tag read"); 292 LoadSubCommand("read", read_command_object); 293 294 CommandObjectSP write_command_object( 295 new CommandObjectMemoryTagWrite(interpreter)); 296 write_command_object->SetCommandName("memory tag write"); 297 LoadSubCommand("write", write_command_object); 298 } 299 300 CommandObjectMemoryTag::~CommandObjectMemoryTag() = default; 301