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