1 //===-- CommandObjectMemory.cpp ---------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/lldb-python.h"
11 
12 #include "CommandObjectMemory.h"
13 
14 // C Includes
15 // C++ Includes
16 // Other libraries and framework includes
17 // Project includes
18 #include "lldb/Core/DataBufferHeap.h"
19 #include "lldb/Core/DataExtractor.h"
20 #include "lldb/Core/Debugger.h"
21 #include "lldb/Core/Module.h"
22 #include "lldb/Core/StreamString.h"
23 #include "lldb/Core/ValueObjectMemory.h"
24 #include "lldb/Interpreter/Args.h"
25 #include "lldb/Interpreter/CommandReturnObject.h"
26 #include "lldb/Interpreter/CommandInterpreter.h"
27 #include "lldb/Interpreter/Options.h"
28 #include "lldb/Interpreter/OptionGroupFormat.h"
29 #include "lldb/Interpreter/OptionGroupOutputFile.h"
30 #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
31 #include "lldb/Interpreter/OptionValueString.h"
32 #include "lldb/Symbol/TypeList.h"
33 #include "lldb/Target/Process.h"
34 #include "lldb/Target/StackFrame.h"
35 
36 using namespace lldb;
37 using namespace lldb_private;
38 
39 static OptionDefinition
40 g_option_table[] =
41 {
42     { LLDB_OPT_SET_1, false, "num-per-line" ,'l', required_argument, NULL, 0, eArgTypeNumberPerLine ,"The number of items per line to display."},
43     { LLDB_OPT_SET_2, false, "binary"       ,'b', no_argument      , NULL, 0, eArgTypeNone          ,"If true, memory will be saved as binary. If false, the memory is saved save as an ASCII dump that uses the format, size, count and number per line settings."},
44     { LLDB_OPT_SET_3, true , "type"         ,'t', required_argument, NULL, 0, eArgTypeNone          ,"The name of a type to view memory as."},
45     { LLDB_OPT_SET_1|
46       LLDB_OPT_SET_2|
47       LLDB_OPT_SET_3, false, "force"        ,'r', no_argument,       NULL, 0, eArgTypeNone          ,"Necessary if reading over target.max-memory-read-size bytes."},
48 };
49 
50 
51 
52 class OptionGroupReadMemory : public OptionGroup
53 {
54 public:
55 
56     OptionGroupReadMemory () :
57         m_num_per_line (1,1),
58         m_output_as_binary (false),
59         m_view_as_type()
60     {
61     }
62 
63     virtual
64     ~OptionGroupReadMemory ()
65     {
66     }
67 
68 
69     virtual uint32_t
70     GetNumDefinitions ()
71     {
72         return sizeof (g_option_table) / sizeof (OptionDefinition);
73     }
74 
75     virtual const OptionDefinition*
76     GetDefinitions ()
77     {
78         return g_option_table;
79     }
80 
81     virtual Error
82     SetOptionValue (CommandInterpreter &interpreter,
83                     uint32_t option_idx,
84                     const char *option_arg)
85     {
86         Error error;
87         const int short_option = g_option_table[option_idx].short_option;
88 
89         switch (short_option)
90         {
91             case 'l':
92                 error = m_num_per_line.SetValueFromCString (option_arg);
93                 if (m_num_per_line.GetCurrentValue() == 0)
94                     error.SetErrorStringWithFormat("invalid value for --num-per-line option '%s'", option_arg);
95                 break;
96 
97             case 'b':
98                 m_output_as_binary = true;
99                 break;
100 
101             case 't':
102                 error = m_view_as_type.SetValueFromCString (option_arg);
103                 break;
104 
105             case 'r':
106                 m_force = true;
107                 break;
108 
109             default:
110                 error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option);
111                 break;
112         }
113         return error;
114     }
115 
116     virtual void
117     OptionParsingStarting (CommandInterpreter &interpreter)
118     {
119         m_num_per_line.Clear();
120         m_output_as_binary = false;
121         m_view_as_type.Clear();
122         m_force = false;
123     }
124 
125     Error
126     FinalizeSettings (Target *target, OptionGroupFormat& format_options)
127     {
128         Error error;
129         OptionValueUInt64 &byte_size_value = format_options.GetByteSizeValue();
130         OptionValueUInt64 &count_value = format_options.GetCountValue();
131         const bool byte_size_option_set = byte_size_value.OptionWasSet();
132         const bool num_per_line_option_set = m_num_per_line.OptionWasSet();
133         const bool count_option_set = format_options.GetCountValue().OptionWasSet();
134 
135         switch (format_options.GetFormat())
136         {
137             default:
138                 break;
139 
140             case eFormatBoolean:
141                 if (!byte_size_option_set)
142                     byte_size_value = 1;
143                 if (!num_per_line_option_set)
144                     m_num_per_line = 1;
145                 if (!count_option_set)
146                     format_options.GetCountValue() = 8;
147                 break;
148 
149             case eFormatCString:
150                 break;
151 
152             case eFormatInstruction:
153                 if (count_option_set)
154                     byte_size_value = target->GetArchitecture().GetMaximumOpcodeByteSize();
155                 m_num_per_line = 1;
156                 break;
157 
158             case eFormatAddressInfo:
159                 if (!byte_size_option_set)
160                     byte_size_value = target->GetArchitecture().GetAddressByteSize();
161                 m_num_per_line = 1;
162                 if (!count_option_set)
163                     format_options.GetCountValue() = 8;
164                 break;
165 
166             case eFormatPointer:
167                 byte_size_value = target->GetArchitecture().GetAddressByteSize();
168                 if (!num_per_line_option_set)
169                     m_num_per_line = 4;
170                 if (!count_option_set)
171                     format_options.GetCountValue() = 8;
172                 break;
173 
174             case eFormatBinary:
175             case eFormatFloat:
176             case eFormatOctal:
177             case eFormatDecimal:
178             case eFormatEnum:
179             case eFormatUnicode16:
180             case eFormatUnicode32:
181             case eFormatUnsigned:
182             case eFormatHexFloat:
183                 if (!byte_size_option_set)
184                     byte_size_value = 4;
185                 if (!num_per_line_option_set)
186                     m_num_per_line = 1;
187                 if (!count_option_set)
188                     format_options.GetCountValue() = 8;
189                 break;
190 
191             case eFormatBytes:
192             case eFormatBytesWithASCII:
193                 if (byte_size_option_set)
194                 {
195                     if (byte_size_value > 1)
196                         error.SetErrorStringWithFormat ("display format (bytes/bytes with ascii) conflicts with the specified byte size %" PRIu64 "\n"
197                                                         "\tconsider using a different display format or don't specify the byte size",
198                                                         byte_size_value.GetCurrentValue());
199                 }
200                 else
201                     byte_size_value = 1;
202                 if (!num_per_line_option_set)
203                     m_num_per_line = 16;
204                 if (!count_option_set)
205                     format_options.GetCountValue() = 32;
206                 break;
207             case eFormatCharArray:
208             case eFormatChar:
209             case eFormatCharPrintable:
210                 if (!byte_size_option_set)
211                     byte_size_value = 1;
212                 if (!num_per_line_option_set)
213                     m_num_per_line = 32;
214                 if (!count_option_set)
215                     format_options.GetCountValue() = 64;
216                 break;
217             case eFormatComplex:
218                 if (!byte_size_option_set)
219                     byte_size_value = 8;
220                 if (!num_per_line_option_set)
221                     m_num_per_line = 1;
222                 if (!count_option_set)
223                     format_options.GetCountValue() = 8;
224                 break;
225             case eFormatComplexInteger:
226                 if (!byte_size_option_set)
227                     byte_size_value = 8;
228                 if (!num_per_line_option_set)
229                     m_num_per_line = 1;
230                 if (!count_option_set)
231                     format_options.GetCountValue() = 8;
232                 break;
233             case eFormatHex:
234                 if (!byte_size_option_set)
235                     byte_size_value = 4;
236                 if (!num_per_line_option_set)
237                 {
238                     switch (byte_size_value)
239                     {
240                         case 1:
241                         case 2:
242                             m_num_per_line = 8;
243                             break;
244                         case 4:
245                             m_num_per_line = 4;
246                             break;
247                         case 8:
248                             m_num_per_line = 2;
249                             break;
250                         default:
251                             m_num_per_line = 1;
252                             break;
253                     }
254                 }
255                 if (!count_option_set)
256                     count_value = 8;
257                 break;
258 
259             case eFormatVectorOfChar:
260             case eFormatVectorOfSInt8:
261             case eFormatVectorOfUInt8:
262             case eFormatVectorOfSInt16:
263             case eFormatVectorOfUInt16:
264             case eFormatVectorOfSInt32:
265             case eFormatVectorOfUInt32:
266             case eFormatVectorOfSInt64:
267             case eFormatVectorOfUInt64:
268             case eFormatVectorOfFloat32:
269             case eFormatVectorOfFloat64:
270             case eFormatVectorOfUInt128:
271                 if (!byte_size_option_set)
272                     byte_size_value = 128;
273                 if (!num_per_line_option_set)
274                     m_num_per_line = 1;
275                 if (!count_option_set)
276                     count_value = 4;
277                 break;
278         }
279         return error;
280     }
281 
282     bool
283     AnyOptionWasSet () const
284     {
285         return m_num_per_line.OptionWasSet() ||
286                m_output_as_binary ||
287                m_view_as_type.OptionWasSet();
288     }
289 
290     OptionValueUInt64 m_num_per_line;
291     bool m_output_as_binary;
292     OptionValueString m_view_as_type;
293     bool m_force;
294 };
295 
296 
297 
298 //----------------------------------------------------------------------
299 // Read memory from the inferior process
300 //----------------------------------------------------------------------
301 class CommandObjectMemoryRead : public CommandObjectParsed
302 {
303 public:
304 
305     CommandObjectMemoryRead (CommandInterpreter &interpreter) :
306         CommandObjectParsed (interpreter,
307                              "memory read",
308                              "Read from the memory of the process being debugged.",
309                              NULL,
310                              eFlagRequiresTarget | eFlagProcessMustBePaused),
311         m_option_group (interpreter),
312         m_format_options (eFormatBytesWithASCII, 1, 8),
313         m_memory_options (),
314         m_outfile_options (),
315         m_varobj_options(),
316         m_next_addr(LLDB_INVALID_ADDRESS),
317         m_prev_byte_size(0),
318         m_prev_format_options (eFormatBytesWithASCII, 1, 8),
319         m_prev_memory_options (),
320         m_prev_outfile_options (),
321         m_prev_varobj_options()
322     {
323         CommandArgumentEntry arg1;
324         CommandArgumentEntry arg2;
325         CommandArgumentData start_addr_arg;
326         CommandArgumentData end_addr_arg;
327 
328         // Define the first (and only) variant of this arg.
329         start_addr_arg.arg_type = eArgTypeAddressOrExpression;
330         start_addr_arg.arg_repetition = eArgRepeatPlain;
331 
332         // There is only one variant this argument could be; put it into the argument entry.
333         arg1.push_back (start_addr_arg);
334 
335         // Define the first (and only) variant of this arg.
336         end_addr_arg.arg_type = eArgTypeAddressOrExpression;
337         end_addr_arg.arg_repetition = eArgRepeatOptional;
338 
339         // There is only one variant this argument could be; put it into the argument entry.
340         arg2.push_back (end_addr_arg);
341 
342         // Push the data for the first argument into the m_arguments vector.
343         m_arguments.push_back (arg1);
344         m_arguments.push_back (arg2);
345 
346         // Add the "--format" and "--count" options to group 1 and 3
347         m_option_group.Append (&m_format_options,
348                                OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_COUNT,
349                                LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3);
350         m_option_group.Append (&m_format_options,
351                                OptionGroupFormat::OPTION_GROUP_GDB_FMT,
352                                LLDB_OPT_SET_1 | LLDB_OPT_SET_3);
353         // Add the "--size" option to group 1 and 2
354         m_option_group.Append (&m_format_options,
355                                OptionGroupFormat::OPTION_GROUP_SIZE,
356                                LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
357         m_option_group.Append (&m_memory_options);
358         m_option_group.Append (&m_outfile_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3);
359         m_option_group.Append (&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_3);
360         m_option_group.Finalize();
361     }
362 
363     virtual
364     ~CommandObjectMemoryRead ()
365     {
366     }
367 
368     Options *
369     GetOptions ()
370     {
371         return &m_option_group;
372     }
373 
374     virtual const char *GetRepeatCommand (Args &current_command_args, uint32_t index)
375     {
376         return m_cmd_name.c_str();
377     }
378 
379 protected:
380     virtual bool
381     DoExecute (Args& command, CommandReturnObject &result)
382     {
383         // No need to check "target" for validity as eFlagRequiresTarget ensures it is valid
384         Target *target = m_exe_ctx.GetTargetPtr();
385 
386         const size_t argc = command.GetArgumentCount();
387 
388         if ((argc == 0 && m_next_addr == LLDB_INVALID_ADDRESS) || argc > 2)
389         {
390             result.AppendErrorWithFormat ("%s takes a start address expression with an optional end address expression.\n", m_cmd_name.c_str());
391             result.AppendRawWarning("Expressions should be quoted if they contain spaces or other special characters.\n");
392             result.SetStatus(eReturnStatusFailed);
393             return false;
394         }
395 
396         ClangASTType clang_ast_type;
397         Error error;
398 
399         const char *view_as_type_cstr = m_memory_options.m_view_as_type.GetCurrentValue();
400         if (view_as_type_cstr && view_as_type_cstr[0])
401         {
402             // We are viewing memory as a type
403 
404             SymbolContext sc;
405             const bool exact_match = false;
406             TypeList type_list;
407             uint32_t reference_count = 0;
408             uint32_t pointer_count = 0;
409             size_t idx;
410 
411 #define ALL_KEYWORDS        \
412     KEYWORD("const")        \
413     KEYWORD("volatile")     \
414     KEYWORD("restrict")     \
415     KEYWORD("struct")       \
416     KEYWORD("class")        \
417     KEYWORD("union")
418 
419 #define KEYWORD(s) s,
420             static const char *g_keywords[] =
421             {
422                 ALL_KEYWORDS
423             };
424 #undef KEYWORD
425 
426 #define KEYWORD(s) (sizeof(s) - 1),
427             static const int g_keyword_lengths[] =
428             {
429                 ALL_KEYWORDS
430             };
431 #undef KEYWORD
432 
433 #undef ALL_KEYWORDS
434 
435             static size_t g_num_keywords = sizeof(g_keywords) / sizeof(const char *);
436             std::string type_str(view_as_type_cstr);
437 
438             // Remove all instances of g_keywords that are followed by spaces
439             for (size_t i = 0; i < g_num_keywords; ++i)
440             {
441                 const char *keyword = g_keywords[i];
442                 int keyword_len = g_keyword_lengths[i];
443 
444                 idx = 0;
445                 while ((idx = type_str.find (keyword, idx)) != std::string::npos)
446                 {
447                     if (type_str[idx + keyword_len] == ' ' || type_str[idx + keyword_len] == '\t')
448                     {
449                         type_str.erase(idx, keyword_len+1);
450                         idx = 0;
451                     }
452                     else
453                     {
454                         idx += keyword_len;
455                     }
456                 }
457             }
458             bool done = type_str.empty();
459             //
460             idx = type_str.find_first_not_of (" \t");
461             if (idx > 0 && idx != std::string::npos)
462                 type_str.erase (0, idx);
463             while (!done)
464             {
465                 // Strip trailing spaces
466                 if (type_str.empty())
467                     done = true;
468                 else
469                 {
470                     switch (type_str[type_str.size()-1])
471                     {
472                     case '*':
473                         ++pointer_count;
474                         // fall through...
475                     case ' ':
476                     case '\t':
477                         type_str.erase(type_str.size()-1);
478                         break;
479 
480                     case '&':
481                         if (reference_count == 0)
482                         {
483                             reference_count = 1;
484                             type_str.erase(type_str.size()-1);
485                         }
486                         else
487                         {
488                             result.AppendErrorWithFormat ("invalid type string: '%s'\n", view_as_type_cstr);
489                             result.SetStatus(eReturnStatusFailed);
490                             return false;
491                         }
492                         break;
493 
494                     default:
495                         done = true;
496                         break;
497                     }
498                 }
499             }
500 
501             ConstString lookup_type_name(type_str.c_str());
502             StackFrame *frame = m_exe_ctx.GetFramePtr();
503             if (frame)
504             {
505                 sc = frame->GetSymbolContext (eSymbolContextModule);
506                 if (sc.module_sp)
507                 {
508                     sc.module_sp->FindTypes (sc,
509                                              lookup_type_name,
510                                              exact_match,
511                                              1,
512                                              type_list);
513                 }
514             }
515             if (type_list.GetSize() == 0)
516             {
517                 target->GetImages().FindTypes (sc,
518                                                lookup_type_name,
519                                                exact_match,
520                                                1,
521                                                type_list);
522             }
523 
524             if (type_list.GetSize() == 0 && lookup_type_name.GetCString() && *lookup_type_name.GetCString() == '$')
525             {
526                 clang::TypeDecl *tdecl = target->GetPersistentVariables().GetPersistentType(ConstString(lookup_type_name));
527                 if (tdecl)
528                 {
529                     clang_ast_type.SetClangType(&tdecl->getASTContext(),(lldb::clang_type_t)tdecl->getTypeForDecl());
530                 }
531             }
532 
533             if (clang_ast_type.IsValid() == false)
534             {
535                 if (type_list.GetSize() == 0)
536                 {
537                     result.AppendErrorWithFormat ("unable to find any types that match the raw type '%s' for full type '%s'\n",
538                                                   lookup_type_name.GetCString(),
539                                                   view_as_type_cstr);
540                     result.SetStatus(eReturnStatusFailed);
541                     return false;
542                 }
543                 else
544                 {
545                     TypeSP type_sp (type_list.GetTypeAtIndex(0));
546                     clang_ast_type.SetClangType (type_sp->GetClangAST(), type_sp->GetClangFullType());
547                 }
548             }
549 
550             while (pointer_count > 0)
551             {
552                 clang_type_t pointer_type = ClangASTContext::CreatePointerType (clang_ast_type.GetASTContext(), clang_ast_type.GetOpaqueQualType());
553                 if (pointer_type)
554                     clang_ast_type.SetClangType (clang_ast_type.GetASTContext(), pointer_type);
555                 else
556                 {
557                     result.AppendError ("unable make a pointer type\n");
558                     result.SetStatus(eReturnStatusFailed);
559                     return false;
560                 }
561                 --pointer_count;
562             }
563 
564             m_format_options.GetByteSizeValue() = clang_ast_type.GetClangTypeByteSize();
565 
566             if (m_format_options.GetByteSizeValue() == 0)
567             {
568                 result.AppendErrorWithFormat ("unable to get the byte size of the type '%s'\n",
569                                               view_as_type_cstr);
570                 result.SetStatus(eReturnStatusFailed);
571                 return false;
572             }
573 
574             if (!m_format_options.GetCountValue().OptionWasSet())
575                 m_format_options.GetCountValue() = 1;
576         }
577         else
578         {
579             error = m_memory_options.FinalizeSettings (target, m_format_options);
580         }
581 
582         // Look for invalid combinations of settings
583         if (error.Fail())
584         {
585             result.AppendError(error.AsCString());
586             result.SetStatus(eReturnStatusFailed);
587             return false;
588         }
589 
590         lldb::addr_t addr;
591         size_t total_byte_size = 0;
592         if (argc == 0)
593         {
594             // Use the last address and byte size and all options as they were
595             // if no options have been set
596             addr = m_next_addr;
597             total_byte_size = m_prev_byte_size;
598             clang_ast_type = m_prev_clang_ast_type;
599             if (!m_format_options.AnyOptionWasSet() &&
600                 !m_memory_options.AnyOptionWasSet() &&
601                 !m_outfile_options.AnyOptionWasSet() &&
602                 !m_varobj_options.AnyOptionWasSet())
603             {
604                 m_format_options = m_prev_format_options;
605                 m_memory_options = m_prev_memory_options;
606                 m_outfile_options = m_prev_outfile_options;
607                 m_varobj_options = m_prev_varobj_options;
608             }
609         }
610 
611         size_t item_count = m_format_options.GetCountValue().GetCurrentValue();
612         size_t item_byte_size = m_format_options.GetByteSizeValue().GetCurrentValue();
613         const size_t num_per_line = m_memory_options.m_num_per_line.GetCurrentValue();
614 
615         if (total_byte_size == 0)
616         {
617             total_byte_size = item_count * item_byte_size;
618             if (total_byte_size == 0)
619                 total_byte_size = 32;
620         }
621 
622         if (argc > 0)
623             addr = Args::StringToAddress(&m_exe_ctx, command.GetArgumentAtIndex(0), LLDB_INVALID_ADDRESS, &error);
624 
625         if (addr == LLDB_INVALID_ADDRESS)
626         {
627             result.AppendError("invalid start address expression.");
628             result.AppendError(error.AsCString());
629             result.SetStatus(eReturnStatusFailed);
630             return false;
631         }
632 
633         if (argc == 2)
634         {
635             lldb::addr_t end_addr = Args::StringToAddress(&m_exe_ctx, command.GetArgumentAtIndex(1), LLDB_INVALID_ADDRESS, 0);
636             if (end_addr == LLDB_INVALID_ADDRESS)
637             {
638                 result.AppendError("invalid end address expression.");
639                 result.AppendError(error.AsCString());
640                 result.SetStatus(eReturnStatusFailed);
641                 return false;
642             }
643             else if (end_addr <= addr)
644             {
645                 result.AppendErrorWithFormat("end address (0x%" PRIx64 ") must be greater that the start address (0x%" PRIx64 ").\n", end_addr, addr);
646                 result.SetStatus(eReturnStatusFailed);
647                 return false;
648             }
649             else if (m_format_options.GetCountValue().OptionWasSet())
650             {
651                 result.AppendErrorWithFormat("specify either the end address (0x%" PRIx64 ") or the count (--count %lu), not both.\n", end_addr, item_count);
652                 result.SetStatus(eReturnStatusFailed);
653                 return false;
654             }
655 
656             total_byte_size = end_addr - addr;
657             item_count = total_byte_size / item_byte_size;
658         }
659 
660         uint32_t max_unforced_size = target->GetMaximumMemReadSize();
661 
662         if (total_byte_size > max_unforced_size && !m_memory_options.m_force)
663         {
664             result.AppendErrorWithFormat("Normally, \'memory read\' will not read over %" PRIu32 " bytes of data.\n",max_unforced_size);
665             result.AppendErrorWithFormat("Please use --force to override this restriction just once.\n");
666             result.AppendErrorWithFormat("or set target.max-memory-read-size if you will often need a larger limit.\n");
667             return false;
668         }
669 
670         DataBufferSP data_sp;
671         size_t bytes_read = 0;
672         if (clang_ast_type.GetOpaqueQualType())
673         {
674             // Make sure we don't display our type as ASCII bytes like the default memory read
675             if (m_format_options.GetFormatValue().OptionWasSet() == false)
676                 m_format_options.GetFormatValue().SetCurrentValue(eFormatDefault);
677 
678             bytes_read = clang_ast_type.GetTypeByteSize() * m_format_options.GetCountValue().GetCurrentValue();
679         }
680         else if (m_format_options.GetFormatValue().GetCurrentValue() != eFormatCString)
681         {
682             data_sp.reset (new DataBufferHeap (total_byte_size, '\0'));
683             Address address(addr, NULL);
684             bytes_read = target->ReadMemory(address, false, data_sp->GetBytes (), data_sp->GetByteSize(), error);
685             if (bytes_read == 0)
686             {
687                 const char *error_cstr = error.AsCString();
688                 if (error_cstr && error_cstr[0])
689                 {
690                     result.AppendError(error_cstr);
691                 }
692                 else
693                 {
694                     result.AppendErrorWithFormat("failed to read memory from 0x%" PRIx64 ".\n", addr);
695                 }
696                 result.SetStatus(eReturnStatusFailed);
697                 return false;
698             }
699 
700             if (bytes_read < total_byte_size)
701                 result.AppendWarningWithFormat("Not all bytes (%lu/%lu) were able to be read from 0x%" PRIx64 ".\n", bytes_read, total_byte_size, addr);
702         }
703         else
704         {
705             // we treat c-strings as a special case because they do not have a fixed size
706             if (m_format_options.GetByteSizeValue().OptionWasSet() && !m_format_options.HasGDBFormat())
707                 item_byte_size = m_format_options.GetByteSizeValue().GetCurrentValue();
708             else
709                 item_byte_size = target->GetMaximumSizeOfStringSummary();
710             if (!m_format_options.GetCountValue().OptionWasSet())
711                 item_count = 1;
712             data_sp.reset (new DataBufferHeap ((item_byte_size+1) * item_count, '\0')); // account for NULLs as necessary
713             uint8_t *data_ptr = data_sp->GetBytes();
714             auto data_addr = addr;
715             auto count = item_count;
716             item_count = 0;
717             while (item_count < count)
718             {
719                 std::string buffer;
720                 buffer.resize(item_byte_size+1,0);
721                 Error error;
722                 size_t read = target->ReadCStringFromMemory(data_addr, &buffer[0], item_byte_size+1, error);
723                 if (error.Fail())
724                 {
725                     result.AppendErrorWithFormat("failed to read memory from 0x%" PRIx64 ".\n", addr);
726                     result.SetStatus(eReturnStatusFailed);
727                     return false;
728                 }
729                 if (item_byte_size == read)
730                 {
731                     result.AppendWarningWithFormat("unable to find a NULL terminated string at 0x%" PRIx64 ".Consider increasing the maximum read length.\n", data_addr);
732                     break;
733                 }
734                 read+=1; // account for final NULL byte
735                 memcpy(data_ptr, &buffer[0], read);
736                 data_ptr += read;
737                 data_addr += read;
738                 bytes_read += read;
739                 item_count++; // if we break early we know we only read item_count strings
740             }
741             data_sp.reset(new DataBufferHeap(data_sp->GetBytes(),bytes_read+1));
742         }
743 
744         m_next_addr = addr + bytes_read;
745         m_prev_byte_size = bytes_read;
746         m_prev_format_options = m_format_options;
747         m_prev_memory_options = m_memory_options;
748         m_prev_outfile_options = m_outfile_options;
749         m_prev_varobj_options = m_varobj_options;
750         m_prev_clang_ast_type = clang_ast_type;
751 
752         StreamFile outfile_stream;
753         Stream *output_stream = NULL;
754         const FileSpec &outfile_spec = m_outfile_options.GetFile().GetCurrentValue();
755         if (outfile_spec)
756         {
757             char path[PATH_MAX];
758             outfile_spec.GetPath (path, sizeof(path));
759 
760             uint32_t open_options = File::eOpenOptionWrite | File::eOpenOptionCanCreate;
761             const bool append = m_outfile_options.GetAppend().GetCurrentValue();
762             if (append)
763                 open_options |= File::eOpenOptionAppend;
764 
765             if (outfile_stream.GetFile ().Open (path, open_options).Success())
766             {
767                 if (m_memory_options.m_output_as_binary)
768                 {
769                     const size_t bytes_written = outfile_stream.Write (data_sp->GetBytes(), bytes_read);
770                     if (bytes_written > 0)
771                     {
772                         result.GetOutputStream().Printf ("%zi bytes %s to '%s'\n",
773                                                          bytes_written,
774                                                          append ? "appended" : "written",
775                                                          path);
776                         return true;
777                     }
778                     else
779                     {
780                         result.AppendErrorWithFormat("Failed to write %" PRIu64 " bytes to '%s'.\n", (uint64_t)bytes_read, path);
781                         result.SetStatus(eReturnStatusFailed);
782                         return false;
783                     }
784                 }
785                 else
786                 {
787                     // We are going to write ASCII to the file just point the
788                     // output_stream to our outfile_stream...
789                     output_stream = &outfile_stream;
790                 }
791             }
792             else
793             {
794                 result.AppendErrorWithFormat("Failed to open file '%s' for %s.\n", path, append ? "append" : "write");
795                 result.SetStatus(eReturnStatusFailed);
796                 return false;
797             }
798         }
799         else
800         {
801             output_stream = &result.GetOutputStream();
802         }
803 
804 
805         ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
806         if (clang_ast_type.GetOpaqueQualType())
807         {
808             for (uint32_t i = 0; i<item_count; ++i)
809             {
810                 addr_t item_addr = addr + (i * item_byte_size);
811                 Address address (item_addr);
812                 StreamString name_strm;
813                 name_strm.Printf ("0x%" PRIx64, item_addr);
814                 ValueObjectSP valobj_sp (ValueObjectMemory::Create (exe_scope,
815                                                                     name_strm.GetString().c_str(),
816                                                                     address,
817                                                                     clang_ast_type));
818                 if (valobj_sp)
819                 {
820                     Format format = m_format_options.GetFormat();
821                     if (format != eFormatDefault)
822                         valobj_sp->SetFormat (format);
823 
824                     ValueObject::DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(false,format));
825 
826                     ValueObject::DumpValueObject (*output_stream,
827                                                   valobj_sp.get(),
828                                                   options);
829                 }
830                 else
831                 {
832                     result.AppendErrorWithFormat ("failed to create a value object for: (%s) %s\n",
833                                                   view_as_type_cstr,
834                                                   name_strm.GetString().c_str());
835                     result.SetStatus(eReturnStatusFailed);
836                     return false;
837                 }
838             }
839             return true;
840         }
841 
842         result.SetStatus(eReturnStatusSuccessFinishResult);
843         DataExtractor data (data_sp,
844                             target->GetArchitecture().GetByteOrder(),
845                             target->GetArchitecture().GetAddressByteSize());
846 
847         Format format = m_format_options.GetFormat();
848         if ( ( (format == eFormatChar) || (format == eFormatCharPrintable) )
849             && (item_byte_size != 1)
850             && (item_count == 1))
851         {
852             // this turns requests such as
853             // memory read -fc -s10 -c1 *charPtrPtr
854             // which make no sense (what is a char of size 10?)
855             // into a request for fetching 10 chars of size 1 from the same memory location
856             format = eFormatCharArray;
857             item_count = item_byte_size;
858             item_byte_size = 1;
859         }
860 
861         assert (output_stream);
862         size_t bytes_dumped = data.Dump (output_stream,
863                                          0,
864                                          format,
865                                          item_byte_size,
866                                          item_count,
867                                          num_per_line,
868                                          addr,
869                                          0,
870                                          0,
871                                          exe_scope);
872         m_next_addr = addr + bytes_dumped;
873         output_stream->EOL();
874         return true;
875     }
876 
877     OptionGroupOptions m_option_group;
878     OptionGroupFormat m_format_options;
879     OptionGroupReadMemory m_memory_options;
880     OptionGroupOutputFile m_outfile_options;
881     OptionGroupValueObjectDisplay m_varobj_options;
882     lldb::addr_t m_next_addr;
883     lldb::addr_t m_prev_byte_size;
884     OptionGroupFormat m_prev_format_options;
885     OptionGroupReadMemory m_prev_memory_options;
886     OptionGroupOutputFile m_prev_outfile_options;
887     OptionGroupValueObjectDisplay m_prev_varobj_options;
888     ClangASTType m_prev_clang_ast_type;
889 };
890 
891 
892 OptionDefinition
893 g_memory_write_option_table[] =
894 {
895 { LLDB_OPT_SET_1, true,  "infile", 'i', required_argument, NULL, 0, eArgTypeFilename, "Write memory using the contents of a file."},
896 { LLDB_OPT_SET_1, false, "offset", 'o', required_argument, NULL, 0, eArgTypeOffset,   "Start writng bytes from an offset within the input file."},
897 };
898 
899 
900 //----------------------------------------------------------------------
901 // Write memory to the inferior process
902 //----------------------------------------------------------------------
903 class CommandObjectMemoryWrite : public CommandObjectParsed
904 {
905 public:
906 
907     class OptionGroupWriteMemory : public OptionGroup
908     {
909     public:
910         OptionGroupWriteMemory () :
911             OptionGroup()
912         {
913         }
914 
915         virtual
916         ~OptionGroupWriteMemory ()
917         {
918         }
919 
920         virtual uint32_t
921         GetNumDefinitions ()
922         {
923             return sizeof (g_memory_write_option_table) / sizeof (OptionDefinition);
924         }
925 
926         virtual const OptionDefinition*
927         GetDefinitions ()
928         {
929             return g_memory_write_option_table;
930         }
931 
932         virtual Error
933         SetOptionValue (CommandInterpreter &interpreter,
934                         uint32_t option_idx,
935                         const char *option_arg)
936         {
937             Error error;
938             const int short_option = g_memory_write_option_table[option_idx].short_option;
939 
940             switch (short_option)
941             {
942                 case 'i':
943                     m_infile.SetFile (option_arg, true);
944                     if (!m_infile.Exists())
945                     {
946                         m_infile.Clear();
947                         error.SetErrorStringWithFormat("input file does not exist: '%s'", option_arg);
948                     }
949                     break;
950 
951                 case 'o':
952                     {
953                         bool success;
954                         m_infile_offset = Args::StringToUInt64(option_arg, 0, 0, &success);
955                         if (!success)
956                         {
957                             error.SetErrorStringWithFormat("invalid offset string '%s'", option_arg);
958                         }
959                     }
960                     break;
961 
962                 default:
963                     error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option);
964                     break;
965             }
966             return error;
967         }
968 
969         virtual void
970         OptionParsingStarting (CommandInterpreter &interpreter)
971         {
972             m_infile.Clear();
973             m_infile_offset = 0;
974         }
975 
976         FileSpec m_infile;
977         off_t m_infile_offset;
978     };
979 
980     CommandObjectMemoryWrite (CommandInterpreter &interpreter) :
981         CommandObjectParsed (interpreter,
982                              "memory write",
983                              "Write to the memory of the process being debugged.",
984                              NULL,
985                              eFlagRequiresProcess | eFlagProcessMustBeLaunched),
986         m_option_group (interpreter),
987         m_format_options (eFormatBytes, 1, UINT64_MAX),
988         m_memory_options ()
989     {
990         CommandArgumentEntry arg1;
991         CommandArgumentEntry arg2;
992         CommandArgumentData addr_arg;
993         CommandArgumentData value_arg;
994 
995         // Define the first (and only) variant of this arg.
996         addr_arg.arg_type = eArgTypeAddress;
997         addr_arg.arg_repetition = eArgRepeatPlain;
998 
999         // There is only one variant this argument could be; put it into the argument entry.
1000         arg1.push_back (addr_arg);
1001 
1002         // Define the first (and only) variant of this arg.
1003         value_arg.arg_type = eArgTypeValue;
1004         value_arg.arg_repetition = eArgRepeatPlus;
1005 
1006         // There is only one variant this argument could be; put it into the argument entry.
1007         arg2.push_back (value_arg);
1008 
1009         // Push the data for the first argument into the m_arguments vector.
1010         m_arguments.push_back (arg1);
1011         m_arguments.push_back (arg2);
1012 
1013         m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT, LLDB_OPT_SET_1);
1014         m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_SIZE  , LLDB_OPT_SET_1|LLDB_OPT_SET_2);
1015         m_option_group.Append (&m_memory_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_2);
1016         m_option_group.Finalize();
1017 
1018     }
1019 
1020     virtual
1021     ~CommandObjectMemoryWrite ()
1022     {
1023     }
1024 
1025     Options *
1026     GetOptions ()
1027     {
1028         return &m_option_group;
1029     }
1030 
1031     bool
1032     UIntValueIsValidForSize (uint64_t uval64, size_t total_byte_size)
1033     {
1034         if (total_byte_size > 8)
1035             return false;
1036 
1037         if (total_byte_size == 8)
1038             return true;
1039 
1040         const uint64_t max = ((uint64_t)1 << (uint64_t)(total_byte_size * 8)) - 1;
1041         return uval64 <= max;
1042     }
1043 
1044     bool
1045     SIntValueIsValidForSize (int64_t sval64, size_t total_byte_size)
1046     {
1047         if (total_byte_size > 8)
1048             return false;
1049 
1050         if (total_byte_size == 8)
1051             return true;
1052 
1053         const int64_t max = ((int64_t)1 << (uint64_t)(total_byte_size * 8 - 1)) - 1;
1054         const int64_t min = ~(max);
1055         return min <= sval64 && sval64 <= max;
1056     }
1057 
1058 protected:
1059     virtual bool
1060     DoExecute (Args& command, CommandReturnObject &result)
1061     {
1062         // No need to check "process" for validity as eFlagRequiresProcess ensures it is valid
1063         Process *process = m_exe_ctx.GetProcessPtr();
1064 
1065         const size_t argc = command.GetArgumentCount();
1066 
1067         if (m_memory_options.m_infile)
1068         {
1069             if (argc < 1)
1070             {
1071                 result.AppendErrorWithFormat ("%s takes a destination address when writing file contents.\n", m_cmd_name.c_str());
1072                 result.SetStatus(eReturnStatusFailed);
1073                 return false;
1074             }
1075         }
1076         else if (argc < 2)
1077         {
1078             result.AppendErrorWithFormat ("%s takes a destination address and at least one value.\n", m_cmd_name.c_str());
1079             result.SetStatus(eReturnStatusFailed);
1080             return false;
1081         }
1082 
1083         StreamString buffer (Stream::eBinary,
1084                              process->GetTarget().GetArchitecture().GetAddressByteSize(),
1085                              process->GetTarget().GetArchitecture().GetByteOrder());
1086 
1087         OptionValueUInt64 &byte_size_value = m_format_options.GetByteSizeValue();
1088         size_t item_byte_size = byte_size_value.GetCurrentValue();
1089 
1090         Error error;
1091         lldb::addr_t addr = Args::StringToAddress (&m_exe_ctx,
1092                                                    command.GetArgumentAtIndex(0),
1093                                                    LLDB_INVALID_ADDRESS,
1094                                                    &error);
1095 
1096         if (addr == LLDB_INVALID_ADDRESS)
1097         {
1098             result.AppendError("invalid address expression\n");
1099             result.AppendError(error.AsCString());
1100             result.SetStatus(eReturnStatusFailed);
1101             return false;
1102         }
1103 
1104         if (m_memory_options.m_infile)
1105         {
1106             size_t length = SIZE_MAX;
1107             if (item_byte_size > 0)
1108                 length = item_byte_size;
1109             lldb::DataBufferSP data_sp (m_memory_options.m_infile.ReadFileContents (m_memory_options.m_infile_offset, length));
1110             if (data_sp)
1111             {
1112                 length = data_sp->GetByteSize();
1113                 if (length > 0)
1114                 {
1115                     Error error;
1116                     size_t bytes_written = process->WriteMemory (addr, data_sp->GetBytes(), length, error);
1117 
1118                     if (bytes_written == length)
1119                     {
1120                         // All bytes written
1121                         result.GetOutputStream().Printf("%" PRIu64 " bytes were written to 0x%" PRIx64 "\n", (uint64_t)bytes_written, addr);
1122                         result.SetStatus(eReturnStatusSuccessFinishResult);
1123                     }
1124                     else if (bytes_written > 0)
1125                     {
1126                         // Some byte written
1127                         result.GetOutputStream().Printf("%" PRIu64 " bytes of %" PRIu64 " requested were written to 0x%" PRIx64 "\n", (uint64_t)bytes_written, (uint64_t)length, addr);
1128                         result.SetStatus(eReturnStatusSuccessFinishResult);
1129                     }
1130                     else
1131                     {
1132                         result.AppendErrorWithFormat ("Memory write to 0x%" PRIx64 " failed: %s.\n", addr, error.AsCString());
1133                         result.SetStatus(eReturnStatusFailed);
1134                     }
1135                 }
1136             }
1137             else
1138             {
1139                 result.AppendErrorWithFormat ("Unable to read contents of file.\n");
1140                 result.SetStatus(eReturnStatusFailed);
1141             }
1142             return result.Succeeded();
1143         }
1144         else if (item_byte_size == 0)
1145         {
1146             if (m_format_options.GetFormat() == eFormatPointer)
1147                 item_byte_size = buffer.GetAddressByteSize();
1148             else
1149                 item_byte_size = 1;
1150         }
1151 
1152         command.Shift(); // shift off the address argument
1153         uint64_t uval64;
1154         int64_t sval64;
1155         bool success = false;
1156         const size_t num_value_args = command.GetArgumentCount();
1157         for (size_t i=0; i<num_value_args; ++i)
1158         {
1159             const char *value_str = command.GetArgumentAtIndex(i);
1160 
1161             switch (m_format_options.GetFormat())
1162             {
1163             case kNumFormats:
1164             case eFormatFloat:  // TODO: add support for floats soon
1165             case eFormatCharPrintable:
1166             case eFormatBytesWithASCII:
1167             case eFormatComplex:
1168             case eFormatEnum:
1169             case eFormatUnicode16:
1170             case eFormatUnicode32:
1171             case eFormatVectorOfChar:
1172             case eFormatVectorOfSInt8:
1173             case eFormatVectorOfUInt8:
1174             case eFormatVectorOfSInt16:
1175             case eFormatVectorOfUInt16:
1176             case eFormatVectorOfSInt32:
1177             case eFormatVectorOfUInt32:
1178             case eFormatVectorOfSInt64:
1179             case eFormatVectorOfUInt64:
1180             case eFormatVectorOfFloat32:
1181             case eFormatVectorOfFloat64:
1182             case eFormatVectorOfUInt128:
1183             case eFormatOSType:
1184             case eFormatComplexInteger:
1185             case eFormatAddressInfo:
1186             case eFormatHexFloat:
1187             case eFormatInstruction:
1188             case eFormatVoid:
1189                 result.AppendError("unsupported format for writing memory");
1190                 result.SetStatus(eReturnStatusFailed);
1191                 return false;
1192 
1193             case eFormatDefault:
1194             case eFormatBytes:
1195             case eFormatHex:
1196             case eFormatHexUppercase:
1197             case eFormatPointer:
1198 
1199                 // Decode hex bytes
1200                 uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 16, &success);
1201                 if (!success)
1202                 {
1203                     result.AppendErrorWithFormat ("'%s' is not a valid hex string value.\n", value_str);
1204                     result.SetStatus(eReturnStatusFailed);
1205                     return false;
1206                 }
1207                 else if (!UIntValueIsValidForSize (uval64, item_byte_size))
1208                 {
1209                     result.AppendErrorWithFormat ("Value 0x%" PRIx64 " is too large to fit in a %lu byte unsigned integer value.\n", uval64, item_byte_size);
1210                     result.SetStatus(eReturnStatusFailed);
1211                     return false;
1212                 }
1213                 buffer.PutMaxHex64 (uval64, item_byte_size);
1214                 break;
1215 
1216             case eFormatBoolean:
1217                 uval64 = Args::StringToBoolean(value_str, false, &success);
1218                 if (!success)
1219                 {
1220                     result.AppendErrorWithFormat ("'%s' is not a valid boolean string value.\n", value_str);
1221                     result.SetStatus(eReturnStatusFailed);
1222                     return false;
1223                 }
1224                 buffer.PutMaxHex64 (uval64, item_byte_size);
1225                 break;
1226 
1227             case eFormatBinary:
1228                 uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 2, &success);
1229                 if (!success)
1230                 {
1231                     result.AppendErrorWithFormat ("'%s' is not a valid binary string value.\n", value_str);
1232                     result.SetStatus(eReturnStatusFailed);
1233                     return false;
1234                 }
1235                 else if (!UIntValueIsValidForSize (uval64, item_byte_size))
1236                 {
1237                     result.AppendErrorWithFormat ("Value 0x%" PRIx64 " is too large to fit in a %lu byte unsigned integer value.\n", uval64, item_byte_size);
1238                     result.SetStatus(eReturnStatusFailed);
1239                     return false;
1240                 }
1241                 buffer.PutMaxHex64 (uval64, item_byte_size);
1242                 break;
1243 
1244             case eFormatCharArray:
1245             case eFormatChar:
1246             case eFormatCString:
1247                 if (value_str[0])
1248                 {
1249                     size_t len = strlen (value_str);
1250                     // Include the NULL for C strings...
1251                     if (m_format_options.GetFormat() == eFormatCString)
1252                         ++len;
1253                     Error error;
1254                     if (process->WriteMemory (addr, value_str, len, error) == len)
1255                     {
1256                         addr += len;
1257                     }
1258                     else
1259                     {
1260                         result.AppendErrorWithFormat ("Memory write to 0x%" PRIx64 " failed: %s.\n", addr, error.AsCString());
1261                         result.SetStatus(eReturnStatusFailed);
1262                         return false;
1263                     }
1264                 }
1265                 break;
1266 
1267             case eFormatDecimal:
1268                 sval64 = Args::StringToSInt64(value_str, INT64_MAX, 0, &success);
1269                 if (!success)
1270                 {
1271                     result.AppendErrorWithFormat ("'%s' is not a valid signed decimal value.\n", value_str);
1272                     result.SetStatus(eReturnStatusFailed);
1273                     return false;
1274                 }
1275                 else if (!SIntValueIsValidForSize (sval64, item_byte_size))
1276                 {
1277                     result.AppendErrorWithFormat ("Value %" PRIi64 " is too large or small to fit in a %lu byte signed integer value.\n", sval64, item_byte_size);
1278                     result.SetStatus(eReturnStatusFailed);
1279                     return false;
1280                 }
1281                 buffer.PutMaxHex64 (sval64, item_byte_size);
1282                 break;
1283 
1284             case eFormatUnsigned:
1285                 uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 0, &success);
1286                 if (!success)
1287                 {
1288                     result.AppendErrorWithFormat ("'%s' is not a valid unsigned decimal string value.\n", value_str);
1289                     result.SetStatus(eReturnStatusFailed);
1290                     return false;
1291                 }
1292                 else if (!UIntValueIsValidForSize (uval64, item_byte_size))
1293                 {
1294                     result.AppendErrorWithFormat ("Value %" PRIu64 " is too large to fit in a %lu byte unsigned integer value.\n", uval64, item_byte_size);
1295                     result.SetStatus(eReturnStatusFailed);
1296                     return false;
1297                 }
1298                 buffer.PutMaxHex64 (uval64, item_byte_size);
1299                 break;
1300 
1301             case eFormatOctal:
1302                 uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 8, &success);
1303                 if (!success)
1304                 {
1305                     result.AppendErrorWithFormat ("'%s' is not a valid octal string value.\n", value_str);
1306                     result.SetStatus(eReturnStatusFailed);
1307                     return false;
1308                 }
1309                 else if (!UIntValueIsValidForSize (uval64, item_byte_size))
1310                 {
1311                     result.AppendErrorWithFormat ("Value %" PRIo64 " is too large to fit in a %lu byte unsigned integer value.\n", uval64, item_byte_size);
1312                     result.SetStatus(eReturnStatusFailed);
1313                     return false;
1314                 }
1315                 buffer.PutMaxHex64 (uval64, item_byte_size);
1316                 break;
1317             }
1318         }
1319 
1320         if (!buffer.GetString().empty())
1321         {
1322             Error error;
1323             if (process->WriteMemory (addr, buffer.GetString().c_str(), buffer.GetString().size(), error) == buffer.GetString().size())
1324                 return true;
1325             else
1326             {
1327                 result.AppendErrorWithFormat ("Memory write to 0x%" PRIx64 " failed: %s.\n", addr, error.AsCString());
1328                 result.SetStatus(eReturnStatusFailed);
1329                 return false;
1330             }
1331         }
1332         return true;
1333     }
1334 
1335     OptionGroupOptions m_option_group;
1336     OptionGroupFormat m_format_options;
1337     OptionGroupWriteMemory m_memory_options;
1338 };
1339 
1340 
1341 //-------------------------------------------------------------------------
1342 // CommandObjectMemory
1343 //-------------------------------------------------------------------------
1344 
1345 CommandObjectMemory::CommandObjectMemory (CommandInterpreter &interpreter) :
1346     CommandObjectMultiword (interpreter,
1347                             "memory",
1348                             "A set of commands for operating on memory.",
1349                             "memory <subcommand> [<subcommand-options>]")
1350 {
1351     LoadSubCommand ("read",  CommandObjectSP (new CommandObjectMemoryRead (interpreter)));
1352     LoadSubCommand ("write", CommandObjectSP (new CommandObjectMemoryWrite (interpreter)));
1353 }
1354 
1355 CommandObjectMemory::~CommandObjectMemory ()
1356 {
1357 }
1358