1 | //===-- CommandObjectMemory.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 "CommandObjectMemory.h" |
10 | #include "CommandObjectMemoryTag.h" |
11 | #include "lldb/Core/DumpDataExtractor.h" |
12 | #include "lldb/Core/Section.h" |
13 | #include "lldb/Core/ValueObjectMemory.h" |
14 | #include "lldb/Expression/ExpressionVariable.h" |
15 | #include "lldb/Host/OptionParser.h" |
16 | #include "lldb/Interpreter/CommandOptionArgumentTable.h" |
17 | #include "lldb/Interpreter/CommandReturnObject.h" |
18 | #include "lldb/Interpreter/OptionArgParser.h" |
19 | #include "lldb/Interpreter/OptionGroupFormat.h" |
20 | #include "lldb/Interpreter/OptionGroupMemoryTag.h" |
21 | #include "lldb/Interpreter/OptionGroupOutputFile.h" |
22 | #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" |
23 | #include "lldb/Interpreter/OptionValueLanguage.h" |
24 | #include "lldb/Interpreter/OptionValueString.h" |
25 | #include "lldb/Interpreter/Options.h" |
26 | #include "lldb/Symbol/SymbolFile.h" |
27 | #include "lldb/Symbol/TypeList.h" |
28 | #include "lldb/Target/ABI.h" |
29 | #include "lldb/Target/Language.h" |
30 | #include "lldb/Target/MemoryHistory.h" |
31 | #include "lldb/Target/MemoryRegionInfo.h" |
32 | #include "lldb/Target/Process.h" |
33 | #include "lldb/Target/StackFrame.h" |
34 | #include "lldb/Target/Target.h" |
35 | #include "lldb/Target/Thread.h" |
36 | #include "lldb/Utility/Args.h" |
37 | #include "lldb/Utility/DataBufferHeap.h" |
38 | #include "lldb/Utility/StreamString.h" |
39 | #include "llvm/Support/MathExtras.h" |
40 | #include <cinttypes> |
41 | #include <memory> |
42 | #include <optional> |
43 | |
44 | using namespace lldb; |
45 | using namespace lldb_private; |
46 | |
47 | #define LLDB_OPTIONS_memory_read |
48 | #include "CommandOptions.inc" |
49 | |
50 | class OptionGroupReadMemory : public OptionGroup { |
51 | public: |
52 | OptionGroupReadMemory() |
53 | : m_num_per_line(1, 1), m_offset(0, 0), |
54 | m_language_for_type(eLanguageTypeUnknown) {} |
55 | |
56 | ~OptionGroupReadMemory() override = default; |
57 | |
58 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
59 | return llvm::ArrayRef(g_memory_read_options); |
60 | } |
61 | |
62 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, |
63 | ExecutionContext *execution_context) override { |
64 | Status error; |
65 | const int short_option = g_memory_read_options[option_idx].short_option; |
66 | |
67 | switch (short_option) { |
68 | case 'l': |
69 | error = m_num_per_line.SetValueFromString(value: option_value); |
70 | if (m_num_per_line.GetCurrentValue() == 0) |
71 | error.SetErrorStringWithFormat( |
72 | "invalid value for --num-per-line option '%s'" , |
73 | option_value.str().c_str()); |
74 | break; |
75 | |
76 | case 'b': |
77 | m_output_as_binary = true; |
78 | break; |
79 | |
80 | case 't': |
81 | error = m_view_as_type.SetValueFromString(value: option_value); |
82 | break; |
83 | |
84 | case 'r': |
85 | m_force = true; |
86 | break; |
87 | |
88 | case 'x': |
89 | error = m_language_for_type.SetValueFromString(value: option_value); |
90 | break; |
91 | |
92 | case 'E': |
93 | error = m_offset.SetValueFromString(value: option_value); |
94 | break; |
95 | |
96 | default: |
97 | llvm_unreachable("Unimplemented option" ); |
98 | } |
99 | return error; |
100 | } |
101 | |
102 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
103 | m_num_per_line.Clear(); |
104 | m_output_as_binary = false; |
105 | m_view_as_type.Clear(); |
106 | m_force = false; |
107 | m_offset.Clear(); |
108 | m_language_for_type.Clear(); |
109 | } |
110 | |
111 | Status FinalizeSettings(Target *target, OptionGroupFormat &format_options) { |
112 | Status error; |
113 | OptionValueUInt64 &byte_size_value = format_options.GetByteSizeValue(); |
114 | OptionValueUInt64 &count_value = format_options.GetCountValue(); |
115 | const bool byte_size_option_set = byte_size_value.OptionWasSet(); |
116 | const bool num_per_line_option_set = m_num_per_line.OptionWasSet(); |
117 | const bool count_option_set = format_options.GetCountValue().OptionWasSet(); |
118 | |
119 | switch (format_options.GetFormat()) { |
120 | default: |
121 | break; |
122 | |
123 | case eFormatBoolean: |
124 | if (!byte_size_option_set) |
125 | byte_size_value = 1; |
126 | if (!num_per_line_option_set) |
127 | m_num_per_line = 1; |
128 | if (!count_option_set) |
129 | format_options.GetCountValue() = 8; |
130 | break; |
131 | |
132 | case eFormatCString: |
133 | break; |
134 | |
135 | case eFormatInstruction: |
136 | if (count_option_set) |
137 | byte_size_value = target->GetArchitecture().GetMaximumOpcodeByteSize(); |
138 | m_num_per_line = 1; |
139 | break; |
140 | |
141 | case eFormatAddressInfo: |
142 | if (!byte_size_option_set) |
143 | byte_size_value = target->GetArchitecture().GetAddressByteSize(); |
144 | m_num_per_line = 1; |
145 | if (!count_option_set) |
146 | format_options.GetCountValue() = 8; |
147 | break; |
148 | |
149 | case eFormatPointer: |
150 | byte_size_value = target->GetArchitecture().GetAddressByteSize(); |
151 | if (!num_per_line_option_set) |
152 | m_num_per_line = 4; |
153 | if (!count_option_set) |
154 | format_options.GetCountValue() = 8; |
155 | break; |
156 | |
157 | case eFormatBinary: |
158 | case eFormatFloat: |
159 | case eFormatOctal: |
160 | case eFormatDecimal: |
161 | case eFormatEnum: |
162 | case eFormatUnicode8: |
163 | case eFormatUnicode16: |
164 | case eFormatUnicode32: |
165 | case eFormatUnsigned: |
166 | case eFormatHexFloat: |
167 | if (!byte_size_option_set) |
168 | byte_size_value = 4; |
169 | if (!num_per_line_option_set) |
170 | m_num_per_line = 1; |
171 | if (!count_option_set) |
172 | format_options.GetCountValue() = 8; |
173 | break; |
174 | |
175 | case eFormatBytes: |
176 | case eFormatBytesWithASCII: |
177 | if (byte_size_option_set) { |
178 | if (byte_size_value > 1) |
179 | error.SetErrorStringWithFormat( |
180 | "display format (bytes/bytes with ASCII) conflicts with the " |
181 | "specified byte size %" PRIu64 "\n" |
182 | "\tconsider using a different display format or don't specify " |
183 | "the byte size." , |
184 | byte_size_value.GetCurrentValue()); |
185 | } else |
186 | byte_size_value = 1; |
187 | if (!num_per_line_option_set) |
188 | m_num_per_line = 16; |
189 | if (!count_option_set) |
190 | format_options.GetCountValue() = 32; |
191 | break; |
192 | |
193 | case eFormatCharArray: |
194 | case eFormatChar: |
195 | case eFormatCharPrintable: |
196 | if (!byte_size_option_set) |
197 | byte_size_value = 1; |
198 | if (!num_per_line_option_set) |
199 | m_num_per_line = 32; |
200 | if (!count_option_set) |
201 | format_options.GetCountValue() = 64; |
202 | break; |
203 | |
204 | case eFormatComplex: |
205 | if (!byte_size_option_set) |
206 | byte_size_value = 8; |
207 | if (!num_per_line_option_set) |
208 | m_num_per_line = 1; |
209 | if (!count_option_set) |
210 | format_options.GetCountValue() = 8; |
211 | break; |
212 | |
213 | case eFormatComplexInteger: |
214 | if (!byte_size_option_set) |
215 | byte_size_value = 8; |
216 | if (!num_per_line_option_set) |
217 | m_num_per_line = 1; |
218 | if (!count_option_set) |
219 | format_options.GetCountValue() = 8; |
220 | break; |
221 | |
222 | case eFormatHex: |
223 | if (!byte_size_option_set) |
224 | byte_size_value = 4; |
225 | if (!num_per_line_option_set) { |
226 | switch (byte_size_value) { |
227 | case 1: |
228 | case 2: |
229 | m_num_per_line = 8; |
230 | break; |
231 | case 4: |
232 | m_num_per_line = 4; |
233 | break; |
234 | case 8: |
235 | m_num_per_line = 2; |
236 | break; |
237 | default: |
238 | m_num_per_line = 1; |
239 | break; |
240 | } |
241 | } |
242 | if (!count_option_set) |
243 | count_value = 8; |
244 | break; |
245 | |
246 | case eFormatVectorOfChar: |
247 | case eFormatVectorOfSInt8: |
248 | case eFormatVectorOfUInt8: |
249 | case eFormatVectorOfSInt16: |
250 | case eFormatVectorOfUInt16: |
251 | case eFormatVectorOfSInt32: |
252 | case eFormatVectorOfUInt32: |
253 | case eFormatVectorOfSInt64: |
254 | case eFormatVectorOfUInt64: |
255 | case eFormatVectorOfFloat16: |
256 | case eFormatVectorOfFloat32: |
257 | case eFormatVectorOfFloat64: |
258 | case eFormatVectorOfUInt128: |
259 | if (!byte_size_option_set) |
260 | byte_size_value = 128; |
261 | if (!num_per_line_option_set) |
262 | m_num_per_line = 1; |
263 | if (!count_option_set) |
264 | count_value = 4; |
265 | break; |
266 | } |
267 | return error; |
268 | } |
269 | |
270 | bool AnyOptionWasSet() const { |
271 | return m_num_per_line.OptionWasSet() || m_output_as_binary || |
272 | m_view_as_type.OptionWasSet() || m_offset.OptionWasSet() || |
273 | m_language_for_type.OptionWasSet(); |
274 | } |
275 | |
276 | OptionValueUInt64 m_num_per_line; |
277 | bool m_output_as_binary = false; |
278 | OptionValueString m_view_as_type; |
279 | bool m_force = false; |
280 | OptionValueUInt64 m_offset; |
281 | OptionValueLanguage m_language_for_type; |
282 | }; |
283 | |
284 | // Read memory from the inferior process |
285 | class CommandObjectMemoryRead : public CommandObjectParsed { |
286 | public: |
287 | CommandObjectMemoryRead(CommandInterpreter &interpreter) |
288 | : CommandObjectParsed( |
289 | interpreter, "memory read" , |
290 | "Read from the memory of the current target process." , nullptr, |
291 | eCommandRequiresTarget | eCommandProcessMustBePaused), |
292 | m_format_options(eFormatBytesWithASCII, 1, 8), |
293 | m_memory_tag_options(/*note_binary=*/true), |
294 | m_prev_format_options(eFormatBytesWithASCII, 1, 8) { |
295 | CommandArgumentEntry arg1; |
296 | CommandArgumentEntry arg2; |
297 | CommandArgumentData start_addr_arg; |
298 | CommandArgumentData end_addr_arg; |
299 | |
300 | // Define the first (and only) variant of this arg. |
301 | start_addr_arg.arg_type = eArgTypeAddressOrExpression; |
302 | start_addr_arg.arg_repetition = eArgRepeatPlain; |
303 | |
304 | // There is only one variant this argument could be; put it into the |
305 | // argument entry. |
306 | arg1.push_back(x: start_addr_arg); |
307 | |
308 | // Define the first (and only) variant of this arg. |
309 | end_addr_arg.arg_type = eArgTypeAddressOrExpression; |
310 | end_addr_arg.arg_repetition = eArgRepeatOptional; |
311 | |
312 | // There is only one variant this argument could be; put it into the |
313 | // argument entry. |
314 | arg2.push_back(x: end_addr_arg); |
315 | |
316 | // Push the data for the first argument into the m_arguments vector. |
317 | m_arguments.push_back(x: arg1); |
318 | m_arguments.push_back(x: arg2); |
319 | |
320 | // Add the "--format" and "--count" options to group 1 and 3 |
321 | m_option_group.Append(group: &m_format_options, |
322 | src_mask: OptionGroupFormat::OPTION_GROUP_FORMAT | |
323 | OptionGroupFormat::OPTION_GROUP_COUNT, |
324 | LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3); |
325 | m_option_group.Append(group: &m_format_options, |
326 | src_mask: OptionGroupFormat::OPTION_GROUP_GDB_FMT, |
327 | LLDB_OPT_SET_1 | LLDB_OPT_SET_3); |
328 | // Add the "--size" option to group 1 and 2 |
329 | m_option_group.Append(group: &m_format_options, |
330 | src_mask: OptionGroupFormat::OPTION_GROUP_SIZE, |
331 | LLDB_OPT_SET_1 | LLDB_OPT_SET_2); |
332 | m_option_group.Append(group: &m_memory_options); |
333 | m_option_group.Append(group: &m_outfile_options, LLDB_OPT_SET_ALL, |
334 | LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3); |
335 | m_option_group.Append(group: &m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_3); |
336 | m_option_group.Append(group: &m_memory_tag_options, LLDB_OPT_SET_ALL, |
337 | LLDB_OPT_SET_ALL); |
338 | m_option_group.Finalize(); |
339 | } |
340 | |
341 | ~CommandObjectMemoryRead() override = default; |
342 | |
343 | Options *GetOptions() override { return &m_option_group; } |
344 | |
345 | std::optional<std::string> GetRepeatCommand(Args ¤t_command_args, |
346 | uint32_t index) override { |
347 | return m_cmd_name; |
348 | } |
349 | |
350 | protected: |
351 | void DoExecute(Args &command, CommandReturnObject &result) override { |
352 | // No need to check "target" for validity as eCommandRequiresTarget ensures |
353 | // it is valid |
354 | Target *target = m_exe_ctx.GetTargetPtr(); |
355 | |
356 | const size_t argc = command.GetArgumentCount(); |
357 | |
358 | if ((argc == 0 && m_next_addr == LLDB_INVALID_ADDRESS) || argc > 2) { |
359 | result.AppendErrorWithFormat(format: "%s takes a start address expression with " |
360 | "an optional end address expression.\n" , |
361 | m_cmd_name.c_str()); |
362 | result.AppendWarning(in_string: "Expressions should be quoted if they contain " |
363 | "spaces or other special characters." ); |
364 | return; |
365 | } |
366 | |
367 | CompilerType compiler_type; |
368 | Status error; |
369 | |
370 | const char *view_as_type_cstr = |
371 | m_memory_options.m_view_as_type.GetCurrentValue(); |
372 | if (view_as_type_cstr && view_as_type_cstr[0]) { |
373 | // We are viewing memory as a type |
374 | |
375 | uint32_t reference_count = 0; |
376 | uint32_t pointer_count = 0; |
377 | size_t idx; |
378 | |
379 | #define ALL_KEYWORDS \ |
380 | KEYWORD("const") \ |
381 | KEYWORD("volatile") \ |
382 | KEYWORD("restrict") \ |
383 | KEYWORD("struct") \ |
384 | KEYWORD("class") \ |
385 | KEYWORD("union") |
386 | |
387 | #define KEYWORD(s) s, |
388 | static const char *g_keywords[] = {ALL_KEYWORDS}; |
389 | #undef KEYWORD |
390 | |
391 | #define KEYWORD(s) (sizeof(s) - 1), |
392 | static const int g_keyword_lengths[] = {ALL_KEYWORDS}; |
393 | #undef KEYWORD |
394 | |
395 | #undef ALL_KEYWORDS |
396 | |
397 | static size_t g_num_keywords = sizeof(g_keywords) / sizeof(const char *); |
398 | std::string type_str(view_as_type_cstr); |
399 | |
400 | // Remove all instances of g_keywords that are followed by spaces |
401 | for (size_t i = 0; i < g_num_keywords; ++i) { |
402 | const char *keyword = g_keywords[i]; |
403 | int keyword_len = g_keyword_lengths[i]; |
404 | |
405 | idx = 0; |
406 | while ((idx = type_str.find(s: keyword, pos: idx)) != std::string::npos) { |
407 | if (type_str[idx + keyword_len] == ' ' || |
408 | type_str[idx + keyword_len] == '\t') { |
409 | type_str.erase(pos: idx, n: keyword_len + 1); |
410 | idx = 0; |
411 | } else { |
412 | idx += keyword_len; |
413 | } |
414 | } |
415 | } |
416 | bool done = type_str.empty(); |
417 | // |
418 | idx = type_str.find_first_not_of(s: " \t" ); |
419 | if (idx > 0 && idx != std::string::npos) |
420 | type_str.erase(pos: 0, n: idx); |
421 | while (!done) { |
422 | // Strip trailing spaces |
423 | if (type_str.empty()) |
424 | done = true; |
425 | else { |
426 | switch (type_str[type_str.size() - 1]) { |
427 | case '*': |
428 | ++pointer_count; |
429 | [[fallthrough]]; |
430 | case ' ': |
431 | case '\t': |
432 | type_str.erase(pos: type_str.size() - 1); |
433 | break; |
434 | |
435 | case '&': |
436 | if (reference_count == 0) { |
437 | reference_count = 1; |
438 | type_str.erase(pos: type_str.size() - 1); |
439 | } else { |
440 | result.AppendErrorWithFormat(format: "invalid type string: '%s'\n" , |
441 | view_as_type_cstr); |
442 | return; |
443 | } |
444 | break; |
445 | |
446 | default: |
447 | done = true; |
448 | break; |
449 | } |
450 | } |
451 | } |
452 | |
453 | ConstString lookup_type_name(type_str.c_str()); |
454 | StackFrame *frame = m_exe_ctx.GetFramePtr(); |
455 | ModuleSP search_first; |
456 | if (frame) |
457 | search_first = frame->GetSymbolContext(resolve_scope: eSymbolContextModule).module_sp; |
458 | TypeQuery query(lookup_type_name.GetStringRef(), |
459 | TypeQueryOptions::e_find_one); |
460 | TypeResults results; |
461 | target->GetImages().FindTypes(search_first: search_first.get(), query, results); |
462 | TypeSP type_sp = results.GetFirstType(); |
463 | |
464 | if (!type_sp && lookup_type_name.GetCString()) { |
465 | LanguageType language_for_type = |
466 | m_memory_options.m_language_for_type.GetCurrentValue(); |
467 | std::set<LanguageType> languages_to_check; |
468 | if (language_for_type != eLanguageTypeUnknown) { |
469 | languages_to_check.insert(x: language_for_type); |
470 | } else { |
471 | languages_to_check = Language::GetSupportedLanguages(); |
472 | } |
473 | |
474 | std::set<CompilerType> user_defined_types; |
475 | for (auto lang : languages_to_check) { |
476 | if (auto *persistent_vars = |
477 | target->GetPersistentExpressionStateForLanguage(language: lang)) { |
478 | if (std::optional<CompilerType> type = |
479 | persistent_vars->GetCompilerTypeFromPersistentDecl( |
480 | type_name: lookup_type_name)) { |
481 | user_defined_types.emplace(args&: *type); |
482 | } |
483 | } |
484 | } |
485 | |
486 | if (user_defined_types.size() > 1) { |
487 | result.AppendErrorWithFormat( |
488 | format: "Mutiple types found matching raw type '%s', please disambiguate " |
489 | "by specifying the language with -x" , |
490 | lookup_type_name.GetCString()); |
491 | return; |
492 | } |
493 | |
494 | if (user_defined_types.size() == 1) { |
495 | compiler_type = *user_defined_types.begin(); |
496 | } |
497 | } |
498 | |
499 | if (!compiler_type.IsValid()) { |
500 | if (type_sp) { |
501 | compiler_type = type_sp->GetFullCompilerType(); |
502 | } else { |
503 | result.AppendErrorWithFormat(format: "unable to find any types that match " |
504 | "the raw type '%s' for full type '%s'\n" , |
505 | lookup_type_name.GetCString(), |
506 | view_as_type_cstr); |
507 | return; |
508 | } |
509 | } |
510 | |
511 | while (pointer_count > 0) { |
512 | CompilerType pointer_type = compiler_type.GetPointerType(); |
513 | if (pointer_type.IsValid()) |
514 | compiler_type = pointer_type; |
515 | else { |
516 | result.AppendError(in_string: "unable make a pointer type\n" ); |
517 | return; |
518 | } |
519 | --pointer_count; |
520 | } |
521 | |
522 | std::optional<uint64_t> size = compiler_type.GetByteSize(exe_scope: nullptr); |
523 | if (!size) { |
524 | result.AppendErrorWithFormat( |
525 | format: "unable to get the byte size of the type '%s'\n" , |
526 | view_as_type_cstr); |
527 | return; |
528 | } |
529 | m_format_options.GetByteSizeValue() = *size; |
530 | |
531 | if (!m_format_options.GetCountValue().OptionWasSet()) |
532 | m_format_options.GetCountValue() = 1; |
533 | } else { |
534 | error = m_memory_options.FinalizeSettings(target, format_options&: m_format_options); |
535 | } |
536 | |
537 | // Look for invalid combinations of settings |
538 | if (error.Fail()) { |
539 | result.AppendError(in_string: error.AsCString()); |
540 | return; |
541 | } |
542 | |
543 | lldb::addr_t addr; |
544 | size_t total_byte_size = 0; |
545 | if (argc == 0) { |
546 | // Use the last address and byte size and all options as they were if no |
547 | // options have been set |
548 | addr = m_next_addr; |
549 | total_byte_size = m_prev_byte_size; |
550 | compiler_type = m_prev_compiler_type; |
551 | if (!m_format_options.AnyOptionWasSet() && |
552 | !m_memory_options.AnyOptionWasSet() && |
553 | !m_outfile_options.AnyOptionWasSet() && |
554 | !m_varobj_options.AnyOptionWasSet() && |
555 | !m_memory_tag_options.AnyOptionWasSet()) { |
556 | m_format_options = m_prev_format_options; |
557 | m_memory_options = m_prev_memory_options; |
558 | m_outfile_options = m_prev_outfile_options; |
559 | m_varobj_options = m_prev_varobj_options; |
560 | m_memory_tag_options = m_prev_memory_tag_options; |
561 | } |
562 | } |
563 | |
564 | size_t item_count = m_format_options.GetCountValue().GetCurrentValue(); |
565 | |
566 | // TODO For non-8-bit byte addressable architectures this needs to be |
567 | // revisited to fully support all lldb's range of formatting options. |
568 | // Furthermore code memory reads (for those architectures) will not be |
569 | // correctly formatted even w/o formatting options. |
570 | size_t item_byte_size = |
571 | target->GetArchitecture().GetDataByteSize() > 1 |
572 | ? target->GetArchitecture().GetDataByteSize() |
573 | : m_format_options.GetByteSizeValue().GetCurrentValue(); |
574 | |
575 | const size_t num_per_line = |
576 | m_memory_options.m_num_per_line.GetCurrentValue(); |
577 | |
578 | if (total_byte_size == 0) { |
579 | total_byte_size = item_count * item_byte_size; |
580 | if (total_byte_size == 0) |
581 | total_byte_size = 32; |
582 | } |
583 | |
584 | if (argc > 0) |
585 | addr = OptionArgParser::ToAddress(exe_ctx: &m_exe_ctx, s: command[0].ref(), |
586 | LLDB_INVALID_ADDRESS, error_ptr: &error); |
587 | |
588 | if (addr == LLDB_INVALID_ADDRESS) { |
589 | result.AppendError(in_string: "invalid start address expression." ); |
590 | result.AppendError(in_string: error.AsCString()); |
591 | return; |
592 | } |
593 | |
594 | if (argc == 2) { |
595 | lldb::addr_t end_addr = OptionArgParser::ToAddress( |
596 | exe_ctx: &m_exe_ctx, s: command[1].ref(), LLDB_INVALID_ADDRESS, error_ptr: nullptr); |
597 | |
598 | if (end_addr == LLDB_INVALID_ADDRESS) { |
599 | result.AppendError(in_string: "invalid end address expression." ); |
600 | result.AppendError(in_string: error.AsCString()); |
601 | return; |
602 | } else if (end_addr <= addr) { |
603 | result.AppendErrorWithFormat( |
604 | format: "end address (0x%" PRIx64 |
605 | ") must be greater than the start address (0x%" PRIx64 ").\n" , |
606 | end_addr, addr); |
607 | return; |
608 | } else if (m_format_options.GetCountValue().OptionWasSet()) { |
609 | result.AppendErrorWithFormat( |
610 | format: "specify either the end address (0x%" PRIx64 |
611 | ") or the count (--count %" PRIu64 "), not both.\n" , |
612 | end_addr, (uint64_t)item_count); |
613 | return; |
614 | } |
615 | |
616 | total_byte_size = end_addr - addr; |
617 | item_count = total_byte_size / item_byte_size; |
618 | } |
619 | |
620 | uint32_t max_unforced_size = target->GetMaximumMemReadSize(); |
621 | |
622 | if (total_byte_size > max_unforced_size && !m_memory_options.m_force) { |
623 | result.AppendErrorWithFormat( |
624 | format: "Normally, \'memory read\' will not read over %" PRIu32 |
625 | " bytes of data.\n" , |
626 | max_unforced_size); |
627 | result.AppendErrorWithFormat( |
628 | format: "Please use --force to override this restriction just once.\n" ); |
629 | result.AppendErrorWithFormat(format: "or set target.max-memory-read-size if you " |
630 | "will often need a larger limit.\n" ); |
631 | return; |
632 | } |
633 | |
634 | WritableDataBufferSP data_sp; |
635 | size_t bytes_read = 0; |
636 | if (compiler_type.GetOpaqueQualType()) { |
637 | // Make sure we don't display our type as ASCII bytes like the default |
638 | // memory read |
639 | if (!m_format_options.GetFormatValue().OptionWasSet()) |
640 | m_format_options.GetFormatValue().SetCurrentValue(eFormatDefault); |
641 | |
642 | std::optional<uint64_t> size = compiler_type.GetByteSize(exe_scope: nullptr); |
643 | if (!size) { |
644 | result.AppendError(in_string: "can't get size of type" ); |
645 | return; |
646 | } |
647 | bytes_read = *size * m_format_options.GetCountValue().GetCurrentValue(); |
648 | |
649 | if (argc > 0) |
650 | addr = addr + (*size * m_memory_options.m_offset.GetCurrentValue()); |
651 | } else if (m_format_options.GetFormatValue().GetCurrentValue() != |
652 | eFormatCString) { |
653 | data_sp = std::make_shared<DataBufferHeap>(args&: total_byte_size, args: '\0'); |
654 | if (data_sp->GetBytes() == nullptr) { |
655 | result.AppendErrorWithFormat( |
656 | format: "can't allocate 0x%" PRIx32 |
657 | " bytes for the memory read buffer, specify a smaller size to read" , |
658 | (uint32_t)total_byte_size); |
659 | return; |
660 | } |
661 | |
662 | Address address(addr, nullptr); |
663 | bytes_read = target->ReadMemory(addr: address, dst: data_sp->GetBytes(), |
664 | dst_len: data_sp->GetByteSize(), error, force_live_memory: true); |
665 | if (bytes_read == 0) { |
666 | const char *error_cstr = error.AsCString(); |
667 | if (error_cstr && error_cstr[0]) { |
668 | result.AppendError(in_string: error_cstr); |
669 | } else { |
670 | result.AppendErrorWithFormat( |
671 | format: "failed to read memory from 0x%" PRIx64 ".\n" , addr); |
672 | } |
673 | return; |
674 | } |
675 | |
676 | if (bytes_read < total_byte_size) |
677 | result.AppendWarningWithFormat( |
678 | format: "Not all bytes (%" PRIu64 "/%" PRIu64 |
679 | ") were able to be read from 0x%" PRIx64 ".\n" , |
680 | (uint64_t)bytes_read, (uint64_t)total_byte_size, addr); |
681 | } else { |
682 | // we treat c-strings as a special case because they do not have a fixed |
683 | // size |
684 | if (m_format_options.GetByteSizeValue().OptionWasSet() && |
685 | !m_format_options.HasGDBFormat()) |
686 | item_byte_size = m_format_options.GetByteSizeValue().GetCurrentValue(); |
687 | else |
688 | item_byte_size = target->GetMaximumSizeOfStringSummary(); |
689 | if (!m_format_options.GetCountValue().OptionWasSet()) |
690 | item_count = 1; |
691 | data_sp = std::make_shared<DataBufferHeap>( |
692 | args: (item_byte_size + 1) * item_count, |
693 | args: '\0'); // account for NULLs as necessary |
694 | if (data_sp->GetBytes() == nullptr) { |
695 | result.AppendErrorWithFormat( |
696 | format: "can't allocate 0x%" PRIx64 |
697 | " bytes for the memory read buffer, specify a smaller size to read" , |
698 | (uint64_t)((item_byte_size + 1) * item_count)); |
699 | return; |
700 | } |
701 | uint8_t *data_ptr = data_sp->GetBytes(); |
702 | auto data_addr = addr; |
703 | auto count = item_count; |
704 | item_count = 0; |
705 | bool break_on_no_NULL = false; |
706 | while (item_count < count) { |
707 | std::string buffer; |
708 | buffer.resize(n: item_byte_size + 1, c: 0); |
709 | Status error; |
710 | size_t read = target->ReadCStringFromMemory(addr: data_addr, dst: &buffer[0], |
711 | dst_max_len: item_byte_size + 1, result_error&: error); |
712 | if (error.Fail()) { |
713 | result.AppendErrorWithFormat( |
714 | format: "failed to read memory from 0x%" PRIx64 ".\n" , addr); |
715 | return; |
716 | } |
717 | |
718 | if (item_byte_size == read) { |
719 | result.AppendWarningWithFormat( |
720 | format: "unable to find a NULL terminated string at 0x%" PRIx64 |
721 | ". Consider increasing the maximum read length.\n" , |
722 | data_addr); |
723 | --read; |
724 | break_on_no_NULL = true; |
725 | } else |
726 | ++read; // account for final NULL byte |
727 | |
728 | memcpy(dest: data_ptr, src: &buffer[0], n: read); |
729 | data_ptr += read; |
730 | data_addr += read; |
731 | bytes_read += read; |
732 | item_count++; // if we break early we know we only read item_count |
733 | // strings |
734 | |
735 | if (break_on_no_NULL) |
736 | break; |
737 | } |
738 | data_sp = |
739 | std::make_shared<DataBufferHeap>(args: data_sp->GetBytes(), args: bytes_read + 1); |
740 | } |
741 | |
742 | m_next_addr = addr + bytes_read; |
743 | m_prev_byte_size = bytes_read; |
744 | m_prev_format_options = m_format_options; |
745 | m_prev_memory_options = m_memory_options; |
746 | m_prev_outfile_options = m_outfile_options; |
747 | m_prev_varobj_options = m_varobj_options; |
748 | m_prev_memory_tag_options = m_memory_tag_options; |
749 | m_prev_compiler_type = compiler_type; |
750 | |
751 | std::unique_ptr<Stream> output_stream_storage; |
752 | Stream *output_stream_p = nullptr; |
753 | const FileSpec &outfile_spec = |
754 | m_outfile_options.GetFile().GetCurrentValue(); |
755 | |
756 | std::string path = outfile_spec.GetPath(); |
757 | if (outfile_spec) { |
758 | |
759 | File::OpenOptions open_options = |
760 | File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate; |
761 | const bool append = m_outfile_options.GetAppend().GetCurrentValue(); |
762 | open_options |= |
763 | append ? File::eOpenOptionAppend : File::eOpenOptionTruncate; |
764 | |
765 | auto outfile = FileSystem::Instance().Open(file_spec: outfile_spec, options: open_options); |
766 | |
767 | if (outfile) { |
768 | auto outfile_stream_up = |
769 | std::make_unique<StreamFile>(args: std::move(outfile.get())); |
770 | if (m_memory_options.m_output_as_binary) { |
771 | const size_t bytes_written = |
772 | outfile_stream_up->Write(src: data_sp->GetBytes(), src_len: bytes_read); |
773 | if (bytes_written > 0) { |
774 | result.GetOutputStream().Printf( |
775 | format: "%zi bytes %s to '%s'\n" , bytes_written, |
776 | append ? "appended" : "written" , path.c_str()); |
777 | return; |
778 | } else { |
779 | result.AppendErrorWithFormat(format: "Failed to write %" PRIu64 |
780 | " bytes to '%s'.\n" , |
781 | (uint64_t)bytes_read, path.c_str()); |
782 | return; |
783 | } |
784 | } else { |
785 | // We are going to write ASCII to the file just point the |
786 | // output_stream to our outfile_stream... |
787 | output_stream_storage = std::move(outfile_stream_up); |
788 | output_stream_p = output_stream_storage.get(); |
789 | } |
790 | } else { |
791 | result.AppendErrorWithFormat(format: "Failed to open file '%s' for %s:\n" , |
792 | path.c_str(), append ? "append" : "write" ); |
793 | |
794 | result.AppendError(in_string: llvm::toString(E: outfile.takeError())); |
795 | return; |
796 | } |
797 | } else { |
798 | output_stream_p = &result.GetOutputStream(); |
799 | } |
800 | |
801 | ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope(); |
802 | if (compiler_type.GetOpaqueQualType()) { |
803 | for (uint32_t i = 0; i < item_count; ++i) { |
804 | addr_t item_addr = addr + (i * item_byte_size); |
805 | Address address(item_addr); |
806 | StreamString name_strm; |
807 | name_strm.Printf(format: "0x%" PRIx64, item_addr); |
808 | ValueObjectSP valobj_sp(ValueObjectMemory::Create( |
809 | exe_scope, name: name_strm.GetString(), address, ast_type: compiler_type)); |
810 | if (valobj_sp) { |
811 | Format format = m_format_options.GetFormat(); |
812 | if (format != eFormatDefault) |
813 | valobj_sp->SetFormat(format); |
814 | |
815 | DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions( |
816 | lang_descr_verbosity: eLanguageRuntimeDescriptionDisplayVerbosityFull, format)); |
817 | |
818 | valobj_sp->Dump(s&: *output_stream_p, options); |
819 | } else { |
820 | result.AppendErrorWithFormat( |
821 | format: "failed to create a value object for: (%s) %s\n" , |
822 | view_as_type_cstr, name_strm.GetData()); |
823 | return; |
824 | } |
825 | } |
826 | return; |
827 | } |
828 | |
829 | result.SetStatus(eReturnStatusSuccessFinishResult); |
830 | DataExtractor data(data_sp, target->GetArchitecture().GetByteOrder(), |
831 | target->GetArchitecture().GetAddressByteSize(), |
832 | target->GetArchitecture().GetDataByteSize()); |
833 | |
834 | Format format = m_format_options.GetFormat(); |
835 | if (((format == eFormatChar) || (format == eFormatCharPrintable)) && |
836 | (item_byte_size != 1)) { |
837 | // if a count was not passed, or it is 1 |
838 | if (!m_format_options.GetCountValue().OptionWasSet() || item_count == 1) { |
839 | // this turns requests such as |
840 | // memory read -fc -s10 -c1 *charPtrPtr |
841 | // which make no sense (what is a char of size 10?) into a request for |
842 | // fetching 10 chars of size 1 from the same memory location |
843 | format = eFormatCharArray; |
844 | item_count = item_byte_size; |
845 | item_byte_size = 1; |
846 | } else { |
847 | // here we passed a count, and it was not 1 so we have a byte_size and |
848 | // a count we could well multiply those, but instead let's just fail |
849 | result.AppendErrorWithFormat( |
850 | format: "reading memory as characters of size %" PRIu64 " is not supported" , |
851 | (uint64_t)item_byte_size); |
852 | return; |
853 | } |
854 | } |
855 | |
856 | assert(output_stream_p); |
857 | size_t bytes_dumped = DumpDataExtractor( |
858 | DE: data, s: output_stream_p, offset: 0, item_format: format, item_byte_size, item_count, |
859 | num_per_line: num_per_line / target->GetArchitecture().GetDataByteSize(), base_addr: addr, item_bit_size: 0, item_bit_offset: 0, |
860 | exe_scope, show_memory_tags: m_memory_tag_options.GetShowTags().GetCurrentValue()); |
861 | m_next_addr = addr + bytes_dumped; |
862 | output_stream_p->EOL(); |
863 | } |
864 | |
865 | OptionGroupOptions m_option_group; |
866 | OptionGroupFormat m_format_options; |
867 | OptionGroupReadMemory m_memory_options; |
868 | OptionGroupOutputFile m_outfile_options; |
869 | OptionGroupValueObjectDisplay m_varobj_options; |
870 | OptionGroupMemoryTag m_memory_tag_options; |
871 | lldb::addr_t m_next_addr = LLDB_INVALID_ADDRESS; |
872 | lldb::addr_t m_prev_byte_size = 0; |
873 | OptionGroupFormat m_prev_format_options; |
874 | OptionGroupReadMemory m_prev_memory_options; |
875 | OptionGroupOutputFile m_prev_outfile_options; |
876 | OptionGroupValueObjectDisplay m_prev_varobj_options; |
877 | OptionGroupMemoryTag m_prev_memory_tag_options; |
878 | CompilerType m_prev_compiler_type; |
879 | }; |
880 | |
881 | #define LLDB_OPTIONS_memory_find |
882 | #include "CommandOptions.inc" |
883 | |
884 | // Find the specified data in memory |
885 | class CommandObjectMemoryFind : public CommandObjectParsed { |
886 | public: |
887 | class OptionGroupFindMemory : public OptionGroup { |
888 | public: |
889 | OptionGroupFindMemory() : m_count(1), m_offset(0) {} |
890 | |
891 | ~OptionGroupFindMemory() override = default; |
892 | |
893 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
894 | return llvm::ArrayRef(g_memory_find_options); |
895 | } |
896 | |
897 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, |
898 | ExecutionContext *execution_context) override { |
899 | Status error; |
900 | const int short_option = g_memory_find_options[option_idx].short_option; |
901 | |
902 | switch (short_option) { |
903 | case 'e': |
904 | m_expr.SetValueFromString(value: option_value); |
905 | break; |
906 | |
907 | case 's': |
908 | m_string.SetValueFromString(value: option_value); |
909 | break; |
910 | |
911 | case 'c': |
912 | if (m_count.SetValueFromString(value: option_value).Fail()) |
913 | error.SetErrorString("unrecognized value for count" ); |
914 | break; |
915 | |
916 | case 'o': |
917 | if (m_offset.SetValueFromString(value: option_value).Fail()) |
918 | error.SetErrorString("unrecognized value for dump-offset" ); |
919 | break; |
920 | |
921 | default: |
922 | llvm_unreachable("Unimplemented option" ); |
923 | } |
924 | return error; |
925 | } |
926 | |
927 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
928 | m_expr.Clear(); |
929 | m_string.Clear(); |
930 | m_count.Clear(); |
931 | } |
932 | |
933 | OptionValueString m_expr; |
934 | OptionValueString m_string; |
935 | OptionValueUInt64 m_count; |
936 | OptionValueUInt64 m_offset; |
937 | }; |
938 | |
939 | CommandObjectMemoryFind(CommandInterpreter &interpreter) |
940 | : CommandObjectParsed( |
941 | interpreter, "memory find" , |
942 | "Find a value in the memory of the current target process." , |
943 | nullptr, eCommandRequiresProcess | eCommandProcessMustBeLaunched) { |
944 | CommandArgumentEntry arg1; |
945 | CommandArgumentEntry arg2; |
946 | CommandArgumentData addr_arg; |
947 | CommandArgumentData value_arg; |
948 | |
949 | // Define the first (and only) variant of this arg. |
950 | addr_arg.arg_type = eArgTypeAddressOrExpression; |
951 | addr_arg.arg_repetition = eArgRepeatPlain; |
952 | |
953 | // There is only one variant this argument could be; put it into the |
954 | // argument entry. |
955 | arg1.push_back(x: addr_arg); |
956 | |
957 | // Define the first (and only) variant of this arg. |
958 | value_arg.arg_type = eArgTypeAddressOrExpression; |
959 | value_arg.arg_repetition = eArgRepeatPlain; |
960 | |
961 | // There is only one variant this argument could be; put it into the |
962 | // argument entry. |
963 | arg2.push_back(x: value_arg); |
964 | |
965 | // Push the data for the first argument into the m_arguments vector. |
966 | m_arguments.push_back(x: arg1); |
967 | m_arguments.push_back(x: arg2); |
968 | |
969 | m_option_group.Append(group: &m_memory_options); |
970 | m_option_group.Append(group: &m_memory_tag_options, LLDB_OPT_SET_ALL, |
971 | LLDB_OPT_SET_ALL); |
972 | m_option_group.Finalize(); |
973 | } |
974 | |
975 | ~CommandObjectMemoryFind() override = default; |
976 | |
977 | Options *GetOptions() override { return &m_option_group; } |
978 | |
979 | protected: |
980 | class ProcessMemoryIterator { |
981 | public: |
982 | ProcessMemoryIterator(ProcessSP process_sp, lldb::addr_t base) |
983 | : m_process_sp(process_sp), m_base_addr(base) { |
984 | lldbassert(process_sp.get() != nullptr); |
985 | } |
986 | |
987 | bool IsValid() { return m_is_valid; } |
988 | |
989 | uint8_t operator[](lldb::addr_t offset) { |
990 | if (!IsValid()) |
991 | return 0; |
992 | |
993 | uint8_t retval = 0; |
994 | Status error; |
995 | if (0 == |
996 | m_process_sp->ReadMemory(vm_addr: m_base_addr + offset, buf: &retval, size: 1, error)) { |
997 | m_is_valid = false; |
998 | return 0; |
999 | } |
1000 | |
1001 | return retval; |
1002 | } |
1003 | |
1004 | private: |
1005 | ProcessSP m_process_sp; |
1006 | lldb::addr_t m_base_addr; |
1007 | bool m_is_valid = true; |
1008 | }; |
1009 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1010 | // No need to check "process" for validity as eCommandRequiresProcess |
1011 | // ensures it is valid |
1012 | Process *process = m_exe_ctx.GetProcessPtr(); |
1013 | |
1014 | const size_t argc = command.GetArgumentCount(); |
1015 | |
1016 | if (argc != 2) { |
1017 | result.AppendError(in_string: "two addresses needed for memory find" ); |
1018 | return; |
1019 | } |
1020 | |
1021 | Status error; |
1022 | lldb::addr_t low_addr = OptionArgParser::ToAddress( |
1023 | exe_ctx: &m_exe_ctx, s: command[0].ref(), LLDB_INVALID_ADDRESS, error_ptr: &error); |
1024 | if (low_addr == LLDB_INVALID_ADDRESS || error.Fail()) { |
1025 | result.AppendError(in_string: "invalid low address" ); |
1026 | return; |
1027 | } |
1028 | lldb::addr_t high_addr = OptionArgParser::ToAddress( |
1029 | exe_ctx: &m_exe_ctx, s: command[1].ref(), LLDB_INVALID_ADDRESS, error_ptr: &error); |
1030 | if (high_addr == LLDB_INVALID_ADDRESS || error.Fail()) { |
1031 | result.AppendError(in_string: "invalid high address" ); |
1032 | return; |
1033 | } |
1034 | |
1035 | if (high_addr <= low_addr) { |
1036 | result.AppendError( |
1037 | in_string: "starting address must be smaller than ending address" ); |
1038 | return; |
1039 | } |
1040 | |
1041 | lldb::addr_t found_location = LLDB_INVALID_ADDRESS; |
1042 | |
1043 | DataBufferHeap buffer; |
1044 | |
1045 | if (m_memory_options.m_string.OptionWasSet()) { |
1046 | llvm::StringRef str = |
1047 | m_memory_options.m_string.GetValueAs<llvm::StringRef>().value_or("" ); |
1048 | if (str.empty()) { |
1049 | result.AppendError(in_string: "search string must have non-zero length." ); |
1050 | return; |
1051 | } |
1052 | buffer.CopyData(src: str); |
1053 | } else if (m_memory_options.m_expr.OptionWasSet()) { |
1054 | StackFrame *frame = m_exe_ctx.GetFramePtr(); |
1055 | ValueObjectSP result_sp; |
1056 | if ((eExpressionCompleted == |
1057 | process->GetTarget().EvaluateExpression( |
1058 | expression: m_memory_options.m_expr.GetValueAs<llvm::StringRef>().value_or( |
1059 | "" ), |
1060 | exe_scope: frame, result_valobj_sp&: result_sp)) && |
1061 | result_sp) { |
1062 | uint64_t value = result_sp->GetValueAsUnsigned(fail_value: 0); |
1063 | std::optional<uint64_t> size = |
1064 | result_sp->GetCompilerType().GetByteSize(exe_scope: nullptr); |
1065 | if (!size) |
1066 | return; |
1067 | switch (*size) { |
1068 | case 1: { |
1069 | uint8_t byte = (uint8_t)value; |
1070 | buffer.CopyData(src: &byte, src_len: 1); |
1071 | } break; |
1072 | case 2: { |
1073 | uint16_t word = (uint16_t)value; |
1074 | buffer.CopyData(src: &word, src_len: 2); |
1075 | } break; |
1076 | case 4: { |
1077 | uint32_t lword = (uint32_t)value; |
1078 | buffer.CopyData(src: &lword, src_len: 4); |
1079 | } break; |
1080 | case 8: { |
1081 | buffer.CopyData(src: &value, src_len: 8); |
1082 | } break; |
1083 | case 3: |
1084 | case 5: |
1085 | case 6: |
1086 | case 7: |
1087 | result.AppendError(in_string: "unknown type. pass a string instead" ); |
1088 | return; |
1089 | default: |
1090 | result.AppendError( |
1091 | in_string: "result size larger than 8 bytes. pass a string instead" ); |
1092 | return; |
1093 | } |
1094 | } else { |
1095 | result.AppendError( |
1096 | in_string: "expression evaluation failed. pass a string instead" ); |
1097 | return; |
1098 | } |
1099 | } else { |
1100 | result.AppendError( |
1101 | in_string: "please pass either a block of text, or an expression to evaluate." ); |
1102 | return; |
1103 | } |
1104 | |
1105 | size_t count = m_memory_options.m_count.GetCurrentValue(); |
1106 | found_location = low_addr; |
1107 | bool ever_found = false; |
1108 | while (count) { |
1109 | found_location = FastSearch(low: found_location, high: high_addr, buffer: buffer.GetBytes(), |
1110 | buffer_size: buffer.GetByteSize()); |
1111 | if (found_location == LLDB_INVALID_ADDRESS) { |
1112 | if (!ever_found) { |
1113 | result.AppendMessage(in_string: "data not found within the range.\n" ); |
1114 | result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); |
1115 | } else |
1116 | result.AppendMessage(in_string: "no more matches within the range.\n" ); |
1117 | break; |
1118 | } |
1119 | result.AppendMessageWithFormat(format: "data found at location: 0x%" PRIx64 "\n" , |
1120 | found_location); |
1121 | |
1122 | DataBufferHeap dumpbuffer(32, 0); |
1123 | process->ReadMemory( |
1124 | vm_addr: found_location + m_memory_options.m_offset.GetCurrentValue(), |
1125 | buf: dumpbuffer.GetBytes(), size: dumpbuffer.GetByteSize(), error); |
1126 | if (!error.Fail()) { |
1127 | DataExtractor data(dumpbuffer.GetBytes(), dumpbuffer.GetByteSize(), |
1128 | process->GetByteOrder(), |
1129 | process->GetAddressByteSize()); |
1130 | DumpDataExtractor( |
1131 | DE: data, s: &result.GetOutputStream(), offset: 0, item_format: lldb::eFormatBytesWithASCII, item_byte_size: 1, |
1132 | item_count: dumpbuffer.GetByteSize(), num_per_line: 16, |
1133 | base_addr: found_location + m_memory_options.m_offset.GetCurrentValue(), item_bit_size: 0, item_bit_offset: 0, |
1134 | exe_scope: m_exe_ctx.GetBestExecutionContextScope(), |
1135 | show_memory_tags: m_memory_tag_options.GetShowTags().GetCurrentValue()); |
1136 | result.GetOutputStream().EOL(); |
1137 | } |
1138 | |
1139 | --count; |
1140 | found_location++; |
1141 | ever_found = true; |
1142 | } |
1143 | |
1144 | result.SetStatus(lldb::eReturnStatusSuccessFinishResult); |
1145 | } |
1146 | |
1147 | lldb::addr_t FastSearch(lldb::addr_t low, lldb::addr_t high, uint8_t *buffer, |
1148 | size_t buffer_size) { |
1149 | const size_t region_size = high - low; |
1150 | |
1151 | if (region_size < buffer_size) |
1152 | return LLDB_INVALID_ADDRESS; |
1153 | |
1154 | std::vector<size_t> bad_char_heuristic(256, buffer_size); |
1155 | ProcessSP process_sp = m_exe_ctx.GetProcessSP(); |
1156 | ProcessMemoryIterator iterator(process_sp, low); |
1157 | |
1158 | for (size_t idx = 0; idx < buffer_size - 1; idx++) { |
1159 | decltype(bad_char_heuristic)::size_type bcu_idx = buffer[idx]; |
1160 | bad_char_heuristic[bcu_idx] = buffer_size - idx - 1; |
1161 | } |
1162 | for (size_t s = 0; s <= (region_size - buffer_size);) { |
1163 | int64_t j = buffer_size - 1; |
1164 | while (j >= 0 && buffer[j] == iterator[s + j]) |
1165 | j--; |
1166 | if (j < 0) |
1167 | return low + s; |
1168 | else |
1169 | s += bad_char_heuristic[iterator[s + buffer_size - 1]]; |
1170 | } |
1171 | |
1172 | return LLDB_INVALID_ADDRESS; |
1173 | } |
1174 | |
1175 | OptionGroupOptions m_option_group; |
1176 | OptionGroupFindMemory m_memory_options; |
1177 | OptionGroupMemoryTag m_memory_tag_options; |
1178 | }; |
1179 | |
1180 | #define LLDB_OPTIONS_memory_write |
1181 | #include "CommandOptions.inc" |
1182 | |
1183 | // Write memory to the inferior process |
1184 | class CommandObjectMemoryWrite : public CommandObjectParsed { |
1185 | public: |
1186 | class OptionGroupWriteMemory : public OptionGroup { |
1187 | public: |
1188 | OptionGroupWriteMemory() = default; |
1189 | |
1190 | ~OptionGroupWriteMemory() override = default; |
1191 | |
1192 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
1193 | return llvm::ArrayRef(g_memory_write_options); |
1194 | } |
1195 | |
1196 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, |
1197 | ExecutionContext *execution_context) override { |
1198 | Status error; |
1199 | const int short_option = g_memory_write_options[option_idx].short_option; |
1200 | |
1201 | switch (short_option) { |
1202 | case 'i': |
1203 | m_infile.SetFile(path: option_value, style: FileSpec::Style::native); |
1204 | FileSystem::Instance().Resolve(file_spec&: m_infile); |
1205 | if (!FileSystem::Instance().Exists(file_spec: m_infile)) { |
1206 | m_infile.Clear(); |
1207 | error.SetErrorStringWithFormat("input file does not exist: '%s'" , |
1208 | option_value.str().c_str()); |
1209 | } |
1210 | break; |
1211 | |
1212 | case 'o': { |
1213 | if (option_value.getAsInteger(Radix: 0, Result&: m_infile_offset)) { |
1214 | m_infile_offset = 0; |
1215 | error.SetErrorStringWithFormat("invalid offset string '%s'" , |
1216 | option_value.str().c_str()); |
1217 | } |
1218 | } break; |
1219 | |
1220 | default: |
1221 | llvm_unreachable("Unimplemented option" ); |
1222 | } |
1223 | return error; |
1224 | } |
1225 | |
1226 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
1227 | m_infile.Clear(); |
1228 | m_infile_offset = 0; |
1229 | } |
1230 | |
1231 | FileSpec m_infile; |
1232 | off_t m_infile_offset; |
1233 | }; |
1234 | |
1235 | CommandObjectMemoryWrite(CommandInterpreter &interpreter) |
1236 | : CommandObjectParsed( |
1237 | interpreter, "memory write" , |
1238 | "Write to the memory of the current target process." , nullptr, |
1239 | eCommandRequiresProcess | eCommandProcessMustBeLaunched), |
1240 | m_format_options( |
1241 | eFormatBytes, 1, UINT64_MAX, |
1242 | {std::make_tuple( |
1243 | args: eArgTypeFormat, |
1244 | args: "The format to use for each of the value to be written." ), |
1245 | std::make_tuple(args: eArgTypeByteSize, |
1246 | args: "The size in bytes to write from input file or " |
1247 | "each value." )}) { |
1248 | CommandArgumentEntry arg1; |
1249 | CommandArgumentEntry arg2; |
1250 | CommandArgumentData addr_arg; |
1251 | CommandArgumentData value_arg; |
1252 | |
1253 | // Define the first (and only) variant of this arg. |
1254 | addr_arg.arg_type = eArgTypeAddress; |
1255 | addr_arg.arg_repetition = eArgRepeatPlain; |
1256 | |
1257 | // There is only one variant this argument could be; put it into the |
1258 | // argument entry. |
1259 | arg1.push_back(x: addr_arg); |
1260 | |
1261 | // Define the first (and only) variant of this arg. |
1262 | value_arg.arg_type = eArgTypeValue; |
1263 | value_arg.arg_repetition = eArgRepeatPlus; |
1264 | value_arg.arg_opt_set_association = LLDB_OPT_SET_1; |
1265 | |
1266 | // There is only one variant this argument could be; put it into the |
1267 | // argument entry. |
1268 | arg2.push_back(x: value_arg); |
1269 | |
1270 | // Push the data for the first argument into the m_arguments vector. |
1271 | m_arguments.push_back(x: arg1); |
1272 | m_arguments.push_back(x: arg2); |
1273 | |
1274 | m_option_group.Append(group: &m_format_options, |
1275 | src_mask: OptionGroupFormat::OPTION_GROUP_FORMAT, |
1276 | LLDB_OPT_SET_1); |
1277 | m_option_group.Append(group: &m_format_options, |
1278 | src_mask: OptionGroupFormat::OPTION_GROUP_SIZE, |
1279 | LLDB_OPT_SET_1 | LLDB_OPT_SET_2); |
1280 | m_option_group.Append(group: &m_memory_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_2); |
1281 | m_option_group.Finalize(); |
1282 | } |
1283 | |
1284 | ~CommandObjectMemoryWrite() override = default; |
1285 | |
1286 | Options *GetOptions() override { return &m_option_group; } |
1287 | |
1288 | protected: |
1289 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1290 | // No need to check "process" for validity as eCommandRequiresProcess |
1291 | // ensures it is valid |
1292 | Process *process = m_exe_ctx.GetProcessPtr(); |
1293 | |
1294 | const size_t argc = command.GetArgumentCount(); |
1295 | |
1296 | if (m_memory_options.m_infile) { |
1297 | if (argc < 1) { |
1298 | result.AppendErrorWithFormat( |
1299 | format: "%s takes a destination address when writing file contents.\n" , |
1300 | m_cmd_name.c_str()); |
1301 | return; |
1302 | } |
1303 | if (argc > 1) { |
1304 | result.AppendErrorWithFormat( |
1305 | format: "%s takes only a destination address when writing file contents.\n" , |
1306 | m_cmd_name.c_str()); |
1307 | return; |
1308 | } |
1309 | } else if (argc < 2) { |
1310 | result.AppendErrorWithFormat( |
1311 | format: "%s takes a destination address and at least one value.\n" , |
1312 | m_cmd_name.c_str()); |
1313 | return; |
1314 | } |
1315 | |
1316 | StreamString buffer( |
1317 | Stream::eBinary, |
1318 | process->GetTarget().GetArchitecture().GetAddressByteSize(), |
1319 | process->GetTarget().GetArchitecture().GetByteOrder()); |
1320 | |
1321 | OptionValueUInt64 &byte_size_value = m_format_options.GetByteSizeValue(); |
1322 | size_t item_byte_size = byte_size_value.GetCurrentValue(); |
1323 | |
1324 | Status error; |
1325 | lldb::addr_t addr = OptionArgParser::ToAddress( |
1326 | exe_ctx: &m_exe_ctx, s: command[0].ref(), LLDB_INVALID_ADDRESS, error_ptr: &error); |
1327 | |
1328 | if (addr == LLDB_INVALID_ADDRESS) { |
1329 | result.AppendError(in_string: "invalid address expression\n" ); |
1330 | result.AppendError(in_string: error.AsCString()); |
1331 | return; |
1332 | } |
1333 | |
1334 | if (m_memory_options.m_infile) { |
1335 | size_t length = SIZE_MAX; |
1336 | if (item_byte_size > 1) |
1337 | length = item_byte_size; |
1338 | auto data_sp = FileSystem::Instance().CreateDataBuffer( |
1339 | path: m_memory_options.m_infile.GetPath(), size: length, |
1340 | offset: m_memory_options.m_infile_offset); |
1341 | if (data_sp) { |
1342 | length = data_sp->GetByteSize(); |
1343 | if (length > 0) { |
1344 | Status error; |
1345 | size_t bytes_written = |
1346 | process->WriteMemory(vm_addr: addr, buf: data_sp->GetBytes(), size: length, error); |
1347 | |
1348 | if (bytes_written == length) { |
1349 | // All bytes written |
1350 | result.GetOutputStream().Printf( |
1351 | format: "%" PRIu64 " bytes were written to 0x%" PRIx64 "\n" , |
1352 | (uint64_t)bytes_written, addr); |
1353 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1354 | } else if (bytes_written > 0) { |
1355 | // Some byte written |
1356 | result.GetOutputStream().Printf( |
1357 | format: "%" PRIu64 " bytes of %" PRIu64 |
1358 | " requested were written to 0x%" PRIx64 "\n" , |
1359 | (uint64_t)bytes_written, (uint64_t)length, addr); |
1360 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1361 | } else { |
1362 | result.AppendErrorWithFormat(format: "Memory write to 0x%" PRIx64 |
1363 | " failed: %s.\n" , |
1364 | addr, error.AsCString()); |
1365 | } |
1366 | } |
1367 | } else { |
1368 | result.AppendErrorWithFormat(format: "Unable to read contents of file.\n" ); |
1369 | } |
1370 | return; |
1371 | } else if (item_byte_size == 0) { |
1372 | if (m_format_options.GetFormat() == eFormatPointer) |
1373 | item_byte_size = buffer.GetAddressByteSize(); |
1374 | else |
1375 | item_byte_size = 1; |
1376 | } |
1377 | |
1378 | command.Shift(); // shift off the address argument |
1379 | uint64_t uval64; |
1380 | int64_t sval64; |
1381 | bool success = false; |
1382 | for (auto &entry : command) { |
1383 | switch (m_format_options.GetFormat()) { |
1384 | case kNumFormats: |
1385 | case eFormatFloat: // TODO: add support for floats soon |
1386 | case eFormatCharPrintable: |
1387 | case eFormatBytesWithASCII: |
1388 | case eFormatComplex: |
1389 | case eFormatEnum: |
1390 | case eFormatUnicode8: |
1391 | case eFormatUnicode16: |
1392 | case eFormatUnicode32: |
1393 | case eFormatVectorOfChar: |
1394 | case eFormatVectorOfSInt8: |
1395 | case eFormatVectorOfUInt8: |
1396 | case eFormatVectorOfSInt16: |
1397 | case eFormatVectorOfUInt16: |
1398 | case eFormatVectorOfSInt32: |
1399 | case eFormatVectorOfUInt32: |
1400 | case eFormatVectorOfSInt64: |
1401 | case eFormatVectorOfUInt64: |
1402 | case eFormatVectorOfFloat16: |
1403 | case eFormatVectorOfFloat32: |
1404 | case eFormatVectorOfFloat64: |
1405 | case eFormatVectorOfUInt128: |
1406 | case eFormatOSType: |
1407 | case eFormatComplexInteger: |
1408 | case eFormatAddressInfo: |
1409 | case eFormatHexFloat: |
1410 | case eFormatInstruction: |
1411 | case eFormatVoid: |
1412 | result.AppendError(in_string: "unsupported format for writing memory" ); |
1413 | return; |
1414 | |
1415 | case eFormatDefault: |
1416 | case eFormatBytes: |
1417 | case eFormatHex: |
1418 | case eFormatHexUppercase: |
1419 | case eFormatPointer: { |
1420 | // Decode hex bytes |
1421 | // Be careful, getAsInteger with a radix of 16 rejects "0xab" so we |
1422 | // have to special case that: |
1423 | bool success = false; |
1424 | if (entry.ref().starts_with(Prefix: "0x" )) |
1425 | success = !entry.ref().getAsInteger(Radix: 0, Result&: uval64); |
1426 | if (!success) |
1427 | success = !entry.ref().getAsInteger(Radix: 16, Result&: uval64); |
1428 | if (!success) { |
1429 | result.AppendErrorWithFormat( |
1430 | format: "'%s' is not a valid hex string value.\n" , entry.c_str()); |
1431 | return; |
1432 | } else if (!llvm::isUIntN(N: item_byte_size * 8, x: uval64)) { |
1433 | result.AppendErrorWithFormat(format: "Value 0x%" PRIx64 |
1434 | " is too large to fit in a %" PRIu64 |
1435 | " byte unsigned integer value.\n" , |
1436 | uval64, (uint64_t)item_byte_size); |
1437 | return; |
1438 | } |
1439 | buffer.PutMaxHex64(uvalue: uval64, byte_size: item_byte_size); |
1440 | break; |
1441 | } |
1442 | case eFormatBoolean: |
1443 | uval64 = OptionArgParser::ToBoolean(s: entry.ref(), fail_value: false, success_ptr: &success); |
1444 | if (!success) { |
1445 | result.AppendErrorWithFormat( |
1446 | format: "'%s' is not a valid boolean string value.\n" , entry.c_str()); |
1447 | return; |
1448 | } |
1449 | buffer.PutMaxHex64(uvalue: uval64, byte_size: item_byte_size); |
1450 | break; |
1451 | |
1452 | case eFormatBinary: |
1453 | if (entry.ref().getAsInteger(Radix: 2, Result&: uval64)) { |
1454 | result.AppendErrorWithFormat( |
1455 | format: "'%s' is not a valid binary string value.\n" , entry.c_str()); |
1456 | return; |
1457 | } else if (!llvm::isUIntN(N: item_byte_size * 8, x: uval64)) { |
1458 | result.AppendErrorWithFormat(format: "Value 0x%" PRIx64 |
1459 | " is too large to fit in a %" PRIu64 |
1460 | " byte unsigned integer value.\n" , |
1461 | uval64, (uint64_t)item_byte_size); |
1462 | return; |
1463 | } |
1464 | buffer.PutMaxHex64(uvalue: uval64, byte_size: item_byte_size); |
1465 | break; |
1466 | |
1467 | case eFormatCharArray: |
1468 | case eFormatChar: |
1469 | case eFormatCString: { |
1470 | if (entry.ref().empty()) |
1471 | break; |
1472 | |
1473 | size_t len = entry.ref().size(); |
1474 | // Include the NULL for C strings... |
1475 | if (m_format_options.GetFormat() == eFormatCString) |
1476 | ++len; |
1477 | Status error; |
1478 | if (process->WriteMemory(vm_addr: addr, buf: entry.c_str(), size: len, error) == len) { |
1479 | addr += len; |
1480 | } else { |
1481 | result.AppendErrorWithFormat(format: "Memory write to 0x%" PRIx64 |
1482 | " failed: %s.\n" , |
1483 | addr, error.AsCString()); |
1484 | return; |
1485 | } |
1486 | break; |
1487 | } |
1488 | case eFormatDecimal: |
1489 | if (entry.ref().getAsInteger(Radix: 0, Result&: sval64)) { |
1490 | result.AppendErrorWithFormat( |
1491 | format: "'%s' is not a valid signed decimal value.\n" , entry.c_str()); |
1492 | return; |
1493 | } else if (!llvm::isIntN(N: item_byte_size * 8, x: sval64)) { |
1494 | result.AppendErrorWithFormat( |
1495 | format: "Value %" PRIi64 " is too large or small to fit in a %" PRIu64 |
1496 | " byte signed integer value.\n" , |
1497 | sval64, (uint64_t)item_byte_size); |
1498 | return; |
1499 | } |
1500 | buffer.PutMaxHex64(uvalue: sval64, byte_size: item_byte_size); |
1501 | break; |
1502 | |
1503 | case eFormatUnsigned: |
1504 | |
1505 | if (entry.ref().getAsInteger(Radix: 0, Result&: uval64)) { |
1506 | result.AppendErrorWithFormat( |
1507 | format: "'%s' is not a valid unsigned decimal string value.\n" , |
1508 | entry.c_str()); |
1509 | return; |
1510 | } else if (!llvm::isUIntN(N: item_byte_size * 8, x: uval64)) { |
1511 | result.AppendErrorWithFormat(format: "Value %" PRIu64 |
1512 | " is too large to fit in a %" PRIu64 |
1513 | " byte unsigned integer value.\n" , |
1514 | uval64, (uint64_t)item_byte_size); |
1515 | return; |
1516 | } |
1517 | buffer.PutMaxHex64(uvalue: uval64, byte_size: item_byte_size); |
1518 | break; |
1519 | |
1520 | case eFormatOctal: |
1521 | if (entry.ref().getAsInteger(Radix: 8, Result&: uval64)) { |
1522 | result.AppendErrorWithFormat( |
1523 | format: "'%s' is not a valid octal string value.\n" , entry.c_str()); |
1524 | return; |
1525 | } else if (!llvm::isUIntN(N: item_byte_size * 8, x: uval64)) { |
1526 | result.AppendErrorWithFormat(format: "Value %" PRIo64 |
1527 | " is too large to fit in a %" PRIu64 |
1528 | " byte unsigned integer value.\n" , |
1529 | uval64, (uint64_t)item_byte_size); |
1530 | return; |
1531 | } |
1532 | buffer.PutMaxHex64(uvalue: uval64, byte_size: item_byte_size); |
1533 | break; |
1534 | } |
1535 | } |
1536 | |
1537 | if (!buffer.GetString().empty()) { |
1538 | Status error; |
1539 | const char *buffer_data = buffer.GetString().data(); |
1540 | const size_t buffer_size = buffer.GetString().size(); |
1541 | const size_t write_size = |
1542 | process->WriteMemory(vm_addr: addr, buf: buffer_data, size: buffer_size, error); |
1543 | |
1544 | if (write_size != buffer_size) { |
1545 | result.AppendErrorWithFormat(format: "Memory write to 0x%" PRIx64 |
1546 | " failed: %s.\n" , |
1547 | addr, error.AsCString()); |
1548 | return; |
1549 | } |
1550 | } |
1551 | } |
1552 | |
1553 | OptionGroupOptions m_option_group; |
1554 | OptionGroupFormat m_format_options; |
1555 | OptionGroupWriteMemory m_memory_options; |
1556 | }; |
1557 | |
1558 | // Get malloc/free history of a memory address. |
1559 | class CommandObjectMemoryHistory : public CommandObjectParsed { |
1560 | public: |
1561 | CommandObjectMemoryHistory(CommandInterpreter &interpreter) |
1562 | : CommandObjectParsed(interpreter, "memory history" , |
1563 | "Print recorded stack traces for " |
1564 | "allocation/deallocation events " |
1565 | "associated with an address." , |
1566 | nullptr, |
1567 | eCommandRequiresTarget | eCommandRequiresProcess | |
1568 | eCommandProcessMustBePaused | |
1569 | eCommandProcessMustBeLaunched) { |
1570 | CommandArgumentEntry arg1; |
1571 | CommandArgumentData addr_arg; |
1572 | |
1573 | // Define the first (and only) variant of this arg. |
1574 | addr_arg.arg_type = eArgTypeAddress; |
1575 | addr_arg.arg_repetition = eArgRepeatPlain; |
1576 | |
1577 | // There is only one variant this argument could be; put it into the |
1578 | // argument entry. |
1579 | arg1.push_back(x: addr_arg); |
1580 | |
1581 | // Push the data for the first argument into the m_arguments vector. |
1582 | m_arguments.push_back(x: arg1); |
1583 | } |
1584 | |
1585 | ~CommandObjectMemoryHistory() override = default; |
1586 | |
1587 | std::optional<std::string> GetRepeatCommand(Args ¤t_command_args, |
1588 | uint32_t index) override { |
1589 | return m_cmd_name; |
1590 | } |
1591 | |
1592 | protected: |
1593 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1594 | const size_t argc = command.GetArgumentCount(); |
1595 | |
1596 | if (argc == 0 || argc > 1) { |
1597 | result.AppendErrorWithFormat(format: "%s takes an address expression" , |
1598 | m_cmd_name.c_str()); |
1599 | return; |
1600 | } |
1601 | |
1602 | Status error; |
1603 | lldb::addr_t addr = OptionArgParser::ToAddress( |
1604 | exe_ctx: &m_exe_ctx, s: command[0].ref(), LLDB_INVALID_ADDRESS, error_ptr: &error); |
1605 | |
1606 | if (addr == LLDB_INVALID_ADDRESS) { |
1607 | result.AppendError(in_string: "invalid address expression" ); |
1608 | result.AppendError(in_string: error.AsCString()); |
1609 | return; |
1610 | } |
1611 | |
1612 | Stream *output_stream = &result.GetOutputStream(); |
1613 | |
1614 | const ProcessSP &process_sp = m_exe_ctx.GetProcessSP(); |
1615 | const MemoryHistorySP &memory_history = |
1616 | MemoryHistory::FindPlugin(process: process_sp); |
1617 | |
1618 | if (!memory_history) { |
1619 | result.AppendError(in_string: "no available memory history provider" ); |
1620 | return; |
1621 | } |
1622 | |
1623 | HistoryThreads thread_list = memory_history->GetHistoryThreads(address: addr); |
1624 | |
1625 | const bool stop_format = false; |
1626 | for (auto thread : thread_list) { |
1627 | thread->GetStatus(strm&: *output_stream, start_frame: 0, UINT32_MAX, num_frames_with_source: 0, stop_format); |
1628 | } |
1629 | |
1630 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1631 | } |
1632 | }; |
1633 | |
1634 | // CommandObjectMemoryRegion |
1635 | #pragma mark CommandObjectMemoryRegion |
1636 | |
1637 | #define LLDB_OPTIONS_memory_region |
1638 | #include "CommandOptions.inc" |
1639 | |
1640 | class CommandObjectMemoryRegion : public CommandObjectParsed { |
1641 | public: |
1642 | class OptionGroupMemoryRegion : public OptionGroup { |
1643 | public: |
1644 | OptionGroupMemoryRegion() : m_all(false, false) {} |
1645 | |
1646 | ~OptionGroupMemoryRegion() override = default; |
1647 | |
1648 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
1649 | return llvm::ArrayRef(g_memory_region_options); |
1650 | } |
1651 | |
1652 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, |
1653 | ExecutionContext *execution_context) override { |
1654 | Status status; |
1655 | const int short_option = g_memory_region_options[option_idx].short_option; |
1656 | |
1657 | switch (short_option) { |
1658 | case 'a': |
1659 | m_all.SetCurrentValue(true); |
1660 | m_all.SetOptionWasSet(); |
1661 | break; |
1662 | default: |
1663 | llvm_unreachable("Unimplemented option" ); |
1664 | } |
1665 | |
1666 | return status; |
1667 | } |
1668 | |
1669 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
1670 | m_all.Clear(); |
1671 | } |
1672 | |
1673 | OptionValueBoolean m_all; |
1674 | }; |
1675 | |
1676 | CommandObjectMemoryRegion(CommandInterpreter &interpreter) |
1677 | : CommandObjectParsed(interpreter, "memory region" , |
1678 | "Get information on the memory region containing " |
1679 | "an address in the current target process." , |
1680 | "memory region <address-expression> (or --all)" , |
1681 | eCommandRequiresProcess | eCommandTryTargetAPILock | |
1682 | eCommandProcessMustBeLaunched) { |
1683 | // Address in option set 1. |
1684 | m_arguments.push_back(x: CommandArgumentEntry{CommandArgumentData( |
1685 | eArgTypeAddressOrExpression, eArgRepeatPlain, LLDB_OPT_SET_1)}); |
1686 | // "--all" will go in option set 2. |
1687 | m_option_group.Append(group: &m_memory_region_options); |
1688 | m_option_group.Finalize(); |
1689 | } |
1690 | |
1691 | ~CommandObjectMemoryRegion() override = default; |
1692 | |
1693 | Options *GetOptions() override { return &m_option_group; } |
1694 | |
1695 | protected: |
1696 | void DumpRegion(CommandReturnObject &result, Target &target, |
1697 | const MemoryRegionInfo &range_info, lldb::addr_t load_addr) { |
1698 | lldb_private::Address addr; |
1699 | ConstString section_name; |
1700 | if (target.ResolveLoadAddress(load_addr, so_addr&: addr)) { |
1701 | SectionSP section_sp(addr.GetSection()); |
1702 | if (section_sp) { |
1703 | // Got the top most section, not the deepest section |
1704 | while (section_sp->GetParent()) |
1705 | section_sp = section_sp->GetParent(); |
1706 | section_name = section_sp->GetName(); |
1707 | } |
1708 | } |
1709 | |
1710 | ConstString name = range_info.GetName(); |
1711 | result.AppendMessageWithFormatv( |
1712 | "[{0:x16}-{1:x16}) {2:r}{3:w}{4:x}{5}{6}{7}{8}" , |
1713 | range_info.GetRange().GetRangeBase(), |
1714 | range_info.GetRange().GetRangeEnd(), range_info.GetReadable(), |
1715 | range_info.GetWritable(), range_info.GetExecutable(), name ? " " : "" , |
1716 | name, section_name ? " " : "" , section_name); |
1717 | MemoryRegionInfo::OptionalBool memory_tagged = range_info.GetMemoryTagged(); |
1718 | if (memory_tagged == MemoryRegionInfo::OptionalBool::eYes) |
1719 | result.AppendMessage(in_string: "memory tagging: enabled" ); |
1720 | |
1721 | const std::optional<std::vector<addr_t>> &dirty_page_list = |
1722 | range_info.GetDirtyPageList(); |
1723 | if (dirty_page_list) { |
1724 | const size_t page_count = dirty_page_list->size(); |
1725 | result.AppendMessageWithFormat( |
1726 | format: "Modified memory (dirty) page list provided, %zu entries.\n" , |
1727 | page_count); |
1728 | if (page_count > 0) { |
1729 | bool print_comma = false; |
1730 | result.AppendMessageWithFormat(format: "Dirty pages: " ); |
1731 | for (size_t i = 0; i < page_count; i++) { |
1732 | if (print_comma) |
1733 | result.AppendMessageWithFormat(format: ", " ); |
1734 | else |
1735 | print_comma = true; |
1736 | result.AppendMessageWithFormat(format: "0x%" PRIx64, (*dirty_page_list)[i]); |
1737 | } |
1738 | result.AppendMessageWithFormat(format: ".\n" ); |
1739 | } |
1740 | } |
1741 | } |
1742 | |
1743 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1744 | ProcessSP process_sp = m_exe_ctx.GetProcessSP(); |
1745 | if (!process_sp) { |
1746 | m_prev_end_addr = LLDB_INVALID_ADDRESS; |
1747 | result.AppendError(in_string: "invalid process" ); |
1748 | return; |
1749 | } |
1750 | |
1751 | Status error; |
1752 | lldb::addr_t load_addr = m_prev_end_addr; |
1753 | m_prev_end_addr = LLDB_INVALID_ADDRESS; |
1754 | |
1755 | const size_t argc = command.GetArgumentCount(); |
1756 | const lldb::ABISP &abi = process_sp->GetABI(); |
1757 | |
1758 | if (argc == 1) { |
1759 | if (m_memory_region_options.m_all) { |
1760 | result.AppendError( |
1761 | in_string: "The \"--all\" option cannot be used when an address " |
1762 | "argument is given" ); |
1763 | return; |
1764 | } |
1765 | |
1766 | auto load_addr_str = command[0].ref(); |
1767 | load_addr = OptionArgParser::ToAddress(exe_ctx: &m_exe_ctx, s: load_addr_str, |
1768 | LLDB_INVALID_ADDRESS, error_ptr: &error); |
1769 | if (error.Fail() || load_addr == LLDB_INVALID_ADDRESS) { |
1770 | result.AppendErrorWithFormat(format: "invalid address argument \"%s\": %s\n" , |
1771 | command[0].c_str(), error.AsCString()); |
1772 | return; |
1773 | } |
1774 | } else if (argc > 1 || |
1775 | // When we're repeating the command, the previous end address is |
1776 | // used for load_addr. If that was 0xF...F then we must have |
1777 | // reached the end of memory. |
1778 | (argc == 0 && !m_memory_region_options.m_all && |
1779 | load_addr == LLDB_INVALID_ADDRESS) || |
1780 | // If the target has non-address bits (tags, limited virtual |
1781 | // address size, etc.), the end of mappable memory will be lower |
1782 | // than that. So if we find any non-address bit set, we must be |
1783 | // at the end of the mappable range. |
1784 | (abi && (abi->FixAnyAddress(pc: load_addr) != load_addr))) { |
1785 | result.AppendErrorWithFormat( |
1786 | format: "'%s' takes one argument or \"--all\" option:\nUsage: %s\n" , |
1787 | m_cmd_name.c_str(), m_cmd_syntax.c_str()); |
1788 | return; |
1789 | } |
1790 | |
1791 | // It is important that we track the address used to request the region as |
1792 | // this will give the correct section name in the case that regions overlap. |
1793 | // On Windows we get mutliple regions that start at the same place but are |
1794 | // different sizes and refer to different sections. |
1795 | std::vector<std::pair<lldb_private::MemoryRegionInfo, lldb::addr_t>> |
1796 | region_list; |
1797 | if (m_memory_region_options.m_all) { |
1798 | // We don't use GetMemoryRegions here because it doesn't include unmapped |
1799 | // areas like repeating the command would. So instead, emulate doing that. |
1800 | lldb::addr_t addr = 0; |
1801 | while (error.Success() && addr != LLDB_INVALID_ADDRESS && |
1802 | // When there are non-address bits the last range will not extend |
1803 | // to LLDB_INVALID_ADDRESS but to the max virtual address. |
1804 | // This prevents us looping forever if that is the case. |
1805 | (!abi || (abi->FixAnyAddress(pc: addr) == addr))) { |
1806 | lldb_private::MemoryRegionInfo region_info; |
1807 | error = process_sp->GetMemoryRegionInfo(load_addr: addr, range_info&: region_info); |
1808 | |
1809 | if (error.Success()) { |
1810 | region_list.push_back({region_info, addr}); |
1811 | addr = region_info.GetRange().GetRangeEnd(); |
1812 | } |
1813 | } |
1814 | } else { |
1815 | lldb_private::MemoryRegionInfo region_info; |
1816 | error = process_sp->GetMemoryRegionInfo(load_addr, range_info&: region_info); |
1817 | if (error.Success()) |
1818 | region_list.push_back({region_info, load_addr}); |
1819 | } |
1820 | |
1821 | if (error.Success()) { |
1822 | for (std::pair<MemoryRegionInfo, addr_t> &range : region_list) { |
1823 | DumpRegion(result, process_sp->GetTarget(), range.first, range.second); |
1824 | m_prev_end_addr = range.first.GetRange().GetRangeEnd(); |
1825 | } |
1826 | |
1827 | result.SetStatus(eReturnStatusSuccessFinishResult); |
1828 | return; |
1829 | } |
1830 | |
1831 | result.AppendErrorWithFormat(format: "%s\n" , error.AsCString()); |
1832 | } |
1833 | |
1834 | std::optional<std::string> GetRepeatCommand(Args ¤t_command_args, |
1835 | uint32_t index) override { |
1836 | // If we repeat this command, repeat it without any arguments so we can |
1837 | // show the next memory range |
1838 | return m_cmd_name; |
1839 | } |
1840 | |
1841 | lldb::addr_t m_prev_end_addr = LLDB_INVALID_ADDRESS; |
1842 | |
1843 | OptionGroupOptions m_option_group; |
1844 | OptionGroupMemoryRegion m_memory_region_options; |
1845 | }; |
1846 | |
1847 | // CommandObjectMemory |
1848 | |
1849 | CommandObjectMemory::CommandObjectMemory(CommandInterpreter &interpreter) |
1850 | : CommandObjectMultiword( |
1851 | interpreter, "memory" , |
1852 | "Commands for operating on memory in the current target process." , |
1853 | "memory <subcommand> [<subcommand-options>]" ) { |
1854 | LoadSubCommand(cmd_name: "find" , |
1855 | command_obj: CommandObjectSP(new CommandObjectMemoryFind(interpreter))); |
1856 | LoadSubCommand(cmd_name: "read" , |
1857 | command_obj: CommandObjectSP(new CommandObjectMemoryRead(interpreter))); |
1858 | LoadSubCommand(cmd_name: "write" , |
1859 | command_obj: CommandObjectSP(new CommandObjectMemoryWrite(interpreter))); |
1860 | LoadSubCommand(cmd_name: "history" , |
1861 | command_obj: CommandObjectSP(new CommandObjectMemoryHistory(interpreter))); |
1862 | LoadSubCommand(cmd_name: "region" , |
1863 | command_obj: CommandObjectSP(new CommandObjectMemoryRegion(interpreter))); |
1864 | LoadSubCommand(cmd_name: "tag" , |
1865 | command_obj: CommandObjectSP(new CommandObjectMemoryTag(interpreter))); |
1866 | } |
1867 | |
1868 | CommandObjectMemory::~CommandObjectMemory() = default; |
1869 | |