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