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