1/* Dump-to-file commands, for GDB, the GNU debugger.
2
3   Copyright (c) 2002, 2005, 2007, 2008, 2009, 2010, 2011
4   Free Software Foundation, Inc.
5
6   Contributed by Red Hat.
7
8   This file is part of GDB.
9
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 3 of the License, or
13   (at your option) any later version.
14
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19
20   You should have received a copy of the GNU General Public License
21   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
22
23#include "defs.h"
24#include "gdb_string.h"
25#include "cli/cli-decode.h"
26#include "cli/cli-cmds.h"
27#include "value.h"
28#include "completer.h"
29#include "cli/cli-dump.h"
30#include "gdb_assert.h"
31#include <ctype.h>
32#include "target.h"
33#include "readline/readline.h"
34#include "gdbcore.h"
35#include "cli/cli-utils.h"
36
37#define XMALLOC(TYPE) ((TYPE*) xmalloc (sizeof (TYPE)))
38
39
40char *
41scan_expression_with_cleanup (char **cmd, const char *def)
42{
43  if ((*cmd) == NULL || (**cmd) == '\0')
44    {
45      char *exp = xstrdup (def);
46
47      make_cleanup (xfree, exp);
48      return exp;
49    }
50  else
51    {
52      char *exp;
53      char *end;
54
55      end = (*cmd) + strcspn (*cmd, " \t");
56      exp = savestring ((*cmd), end - (*cmd));
57      make_cleanup (xfree, exp);
58      (*cmd) = skip_spaces (end);
59      return exp;
60    }
61}
62
63
64char *
65scan_filename_with_cleanup (char **cmd, const char *defname)
66{
67  char *filename;
68  char *fullname;
69
70  /* FIXME: Need to get the ``/a(ppend)'' flag from somewhere.  */
71
72  /* File.  */
73  if ((*cmd) == NULL)
74    {
75      if (defname == NULL)
76	error (_("Missing filename."));
77      filename = xstrdup (defname);
78      make_cleanup (xfree, filename);
79    }
80  else
81    {
82      /* FIXME: should parse a possibly quoted string.  */
83      char *end;
84
85      (*cmd) = skip_spaces (*cmd);
86      end = *cmd + strcspn (*cmd, " \t");
87      filename = savestring ((*cmd), end - (*cmd));
88      make_cleanup (xfree, filename);
89      (*cmd) = skip_spaces (end);
90    }
91  gdb_assert (filename != NULL);
92
93  fullname = tilde_expand (filename);
94  make_cleanup (xfree, fullname);
95
96  return fullname;
97}
98
99FILE *
100fopen_with_cleanup (const char *filename, const char *mode)
101{
102  FILE *file = fopen (filename, mode);
103
104  if (file == NULL)
105    perror_with_name (filename);
106  make_cleanup_fclose (file);
107  return file;
108}
109
110static bfd *
111bfd_openr_with_cleanup (const char *filename, const char *target)
112{
113  bfd *ibfd;
114
115  ibfd = bfd_openr (filename, target);
116  if (ibfd == NULL)
117    error (_("Failed to open %s: %s."), filename,
118	   bfd_errmsg (bfd_get_error ()));
119
120  make_cleanup_bfd_close (ibfd);
121  if (!bfd_check_format (ibfd, bfd_object))
122    error (_("'%s' is not a recognized file format."), filename);
123
124  return ibfd;
125}
126
127static bfd *
128bfd_openw_with_cleanup (const char *filename, const char *target,
129			const char *mode)
130{
131  bfd *obfd;
132
133  if (*mode == 'w')	/* Write: create new file */
134    {
135      obfd = bfd_openw (filename, target);
136      if (obfd == NULL)
137	error (_("Failed to open %s: %s."), filename,
138	       bfd_errmsg (bfd_get_error ()));
139      make_cleanup_bfd_close (obfd);
140      if (!bfd_set_format (obfd, bfd_object))
141	error (_("bfd_openw_with_cleanup: %s."), bfd_errmsg (bfd_get_error ()));
142    }
143  else if (*mode == 'a')	/* Append to existing file.  */
144    {	/* FIXME -- doesn't work...  */
145      error (_("bfd_openw does not work with append."));
146    }
147  else
148    error (_("bfd_openw_with_cleanup: unknown mode %s."), mode);
149
150  return obfd;
151}
152
153struct cmd_list_element *dump_cmdlist;
154struct cmd_list_element *append_cmdlist;
155struct cmd_list_element *srec_cmdlist;
156struct cmd_list_element *ihex_cmdlist;
157struct cmd_list_element *tekhex_cmdlist;
158struct cmd_list_element *binary_dump_cmdlist;
159struct cmd_list_element *binary_append_cmdlist;
160
161static void
162dump_command (char *cmd, int from_tty)
163{
164  printf_unfiltered (_("\"dump\" must be followed by a subcommand.\n\n"));
165  help_list (dump_cmdlist, "dump ", -1, gdb_stdout);
166}
167
168static void
169append_command (char *cmd, int from_tty)
170{
171  printf_unfiltered (_("\"append\" must be followed by a subcommand.\n\n"));
172  help_list (dump_cmdlist, "append ", -1, gdb_stdout);
173}
174
175static void
176dump_binary_file (const char *filename, const char *mode,
177		  const bfd_byte *buf, int len)
178{
179  FILE *file;
180  int status;
181
182  file = fopen_with_cleanup (filename, mode);
183  status = fwrite (buf, len, 1, file);
184  if (status != 1)
185    perror_with_name (filename);
186}
187
188static void
189dump_bfd_file (const char *filename, const char *mode,
190	       const char *target, CORE_ADDR vaddr,
191	       const bfd_byte *buf, int len)
192{
193  bfd *obfd;
194  asection *osection;
195
196  obfd = bfd_openw_with_cleanup (filename, target, mode);
197  osection = bfd_make_section_anyway (obfd, ".newsec");
198  bfd_set_section_size (obfd, osection, len);
199  bfd_set_section_vma (obfd, osection, vaddr);
200  bfd_set_section_alignment (obfd, osection, 0);
201  bfd_set_section_flags (obfd, osection, (SEC_HAS_CONTENTS
202					  | SEC_ALLOC
203					  | SEC_LOAD));
204  osection->entsize = 0;
205  if (!bfd_set_section_contents (obfd, osection, buf, 0, len))
206    warning (_("writing dump file '%s' (%s)"), filename,
207	     bfd_errmsg (bfd_get_error ()));
208}
209
210static void
211dump_memory_to_file (char *cmd, char *mode, char *file_format)
212{
213  struct cleanup *old_cleanups = make_cleanup (null_cleanup, NULL);
214  CORE_ADDR lo;
215  CORE_ADDR hi;
216  ULONGEST count;
217  char *filename;
218  void *buf;
219  char *lo_exp;
220  char *hi_exp;
221
222  /* Open the file.  */
223  filename = scan_filename_with_cleanup (&cmd, NULL);
224
225  /* Find the low address.  */
226  if (cmd == NULL || *cmd == '\0')
227    error (_("Missing start address."));
228  lo_exp = scan_expression_with_cleanup (&cmd, NULL);
229
230  /* Find the second address - rest of line.  */
231  if (cmd == NULL || *cmd == '\0')
232    error (_("Missing stop address."));
233  hi_exp = cmd;
234
235  lo = parse_and_eval_address (lo_exp);
236  hi = parse_and_eval_address (hi_exp);
237  if (hi <= lo)
238    error (_("Invalid memory address range (start >= end)."));
239  count = hi - lo;
240
241  /* FIXME: Should use read_memory_partial() and a magic blocking
242     value.  */
243  buf = xmalloc (count);
244  make_cleanup (xfree, buf);
245  read_memory (lo, buf, count);
246
247  /* Have everything.  Open/write the data.  */
248  if (file_format == NULL || strcmp (file_format, "binary") == 0)
249    {
250      dump_binary_file (filename, mode, buf, count);
251    }
252  else
253    {
254      dump_bfd_file (filename, mode, file_format, lo, buf, count);
255    }
256
257  do_cleanups (old_cleanups);
258}
259
260static void
261dump_memory_command (char *cmd, char *mode)
262{
263  dump_memory_to_file (cmd, mode, "binary");
264}
265
266static void
267dump_value_to_file (char *cmd, char *mode, char *file_format)
268{
269  struct cleanup *old_cleanups = make_cleanup (null_cleanup, NULL);
270  struct value *val;
271  char *filename;
272
273  /* Open the file.  */
274  filename = scan_filename_with_cleanup (&cmd, NULL);
275
276  /* Find the value.  */
277  if (cmd == NULL || *cmd == '\0')
278    error (_("No value to %s."), *mode == 'a' ? "append" : "dump");
279  val = parse_and_eval (cmd);
280  if (val == NULL)
281    error (_("Invalid expression."));
282
283  /* Have everything.  Open/write the data.  */
284  if (file_format == NULL || strcmp (file_format, "binary") == 0)
285    {
286      dump_binary_file (filename, mode, value_contents (val),
287			TYPE_LENGTH (value_type (val)));
288    }
289  else
290    {
291      CORE_ADDR vaddr;
292
293      if (VALUE_LVAL (val))
294	{
295	  vaddr = value_address (val);
296	}
297      else
298	{
299	  vaddr = 0;
300	  warning (_("value is not an lval: address assumed to be zero"));
301	}
302
303      dump_bfd_file (filename, mode, file_format, vaddr,
304		     value_contents (val),
305		     TYPE_LENGTH (value_type (val)));
306    }
307
308  do_cleanups (old_cleanups);
309}
310
311static void
312dump_value_command (char *cmd, char *mode)
313{
314  dump_value_to_file (cmd, mode, "binary");
315}
316
317static void
318dump_srec_memory (char *args, int from_tty)
319{
320  dump_memory_to_file (args, FOPEN_WB, "srec");
321}
322
323static void
324dump_srec_value (char *args, int from_tty)
325{
326  dump_value_to_file (args, FOPEN_WB, "srec");
327}
328
329static void
330dump_ihex_memory (char *args, int from_tty)
331{
332  dump_memory_to_file (args, FOPEN_WB, "ihex");
333}
334
335static void
336dump_ihex_value (char *args, int from_tty)
337{
338  dump_value_to_file (args, FOPEN_WB, "ihex");
339}
340
341static void
342dump_tekhex_memory (char *args, int from_tty)
343{
344  dump_memory_to_file (args, FOPEN_WB, "tekhex");
345}
346
347static void
348dump_tekhex_value (char *args, int from_tty)
349{
350  dump_value_to_file (args, FOPEN_WB, "tekhex");
351}
352
353static void
354dump_binary_memory (char *args, int from_tty)
355{
356  dump_memory_to_file (args, FOPEN_WB, "binary");
357}
358
359static void
360dump_binary_value (char *args, int from_tty)
361{
362  dump_value_to_file (args, FOPEN_WB, "binary");
363}
364
365static void
366append_binary_memory (char *args, int from_tty)
367{
368  dump_memory_to_file (args, FOPEN_AB, "binary");
369}
370
371static void
372append_binary_value (char *args, int from_tty)
373{
374  dump_value_to_file (args, FOPEN_AB, "binary");
375}
376
377struct dump_context
378{
379  void (*func) (char *cmd, char *mode);
380  char *mode;
381};
382
383static void
384call_dump_func (struct cmd_list_element *c, char *args, int from_tty)
385{
386  struct dump_context *d = get_cmd_context (c);
387
388  d->func (args, d->mode);
389}
390
391void
392add_dump_command (char *name, void (*func) (char *args, char *mode),
393		  char *descr)
394
395{
396  struct cmd_list_element *c;
397  struct dump_context *d;
398
399  c = add_cmd (name, all_commands, NULL, descr, &dump_cmdlist);
400  c->completer =  filename_completer;
401  d = XMALLOC (struct dump_context);
402  d->func = func;
403  d->mode = FOPEN_WB;
404  set_cmd_context (c, d);
405  c->func = call_dump_func;
406
407  c = add_cmd (name, all_commands, NULL, descr, &append_cmdlist);
408  c->completer =  filename_completer;
409  d = XMALLOC (struct dump_context);
410  d->func = func;
411  d->mode = FOPEN_AB;
412  set_cmd_context (c, d);
413  c->func = call_dump_func;
414
415  /* Replace "Dump " at start of docstring with "Append " (borrowed
416     from [deleted] deprecated_add_show_from_set).  */
417  if (   c->doc[0] == 'W'
418      && c->doc[1] == 'r'
419      && c->doc[2] == 'i'
420      && c->doc[3] == 't'
421      && c->doc[4] == 'e'
422      && c->doc[5] == ' ')
423    c->doc = concat ("Append ", c->doc + 6, (char *)NULL);
424}
425
426/* Opaque data for restore_section_callback.  */
427struct callback_data {
428  CORE_ADDR load_offset;
429  CORE_ADDR load_start;
430  CORE_ADDR load_end;
431};
432
433/* Function: restore_section_callback.
434
435   Callback function for bfd_map_over_sections.
436   Selectively loads the sections into memory.  */
437
438static void
439restore_section_callback (bfd *ibfd, asection *isec, void *args)
440{
441  struct callback_data *data = args;
442  bfd_vma sec_start  = bfd_section_vma (ibfd, isec);
443  bfd_size_type size = bfd_section_size (ibfd, isec);
444  bfd_vma sec_end    = sec_start + size;
445  bfd_size_type sec_offset = 0;
446  bfd_size_type sec_load_count = size;
447  struct cleanup *old_chain;
448  gdb_byte *buf;
449  int ret;
450
451  /* Ignore non-loadable sections, eg. from elf files.  */
452  if (!(bfd_get_section_flags (ibfd, isec) & SEC_LOAD))
453    return;
454
455  /* Does the section overlap with the desired restore range? */
456  if (sec_end <= data->load_start
457      || (data->load_end > 0 && sec_start >= data->load_end))
458    {
459      /* No, no useable data in this section.  */
460      printf_filtered (_("skipping section %s...\n"),
461		       bfd_section_name (ibfd, isec));
462      return;
463    }
464
465  /* Compare section address range with user-requested
466     address range (if any).  Compute where the actual
467     transfer should start and end.  */
468  if (sec_start < data->load_start)
469    sec_offset = data->load_start - sec_start;
470  /* Size of a partial transfer.  */
471  sec_load_count -= sec_offset;
472  if (data->load_end > 0 && sec_end > data->load_end)
473    sec_load_count -= sec_end - data->load_end;
474
475  /* Get the data.  */
476  buf = xmalloc (size);
477  old_chain = make_cleanup (xfree, buf);
478  if (!bfd_get_section_contents (ibfd, isec, buf, 0, size))
479    error (_("Failed to read bfd file %s: '%s'."), bfd_get_filename (ibfd),
480	   bfd_errmsg (bfd_get_error ()));
481
482  printf_filtered ("Restoring section %s (0x%lx to 0x%lx)",
483		   bfd_section_name (ibfd, isec),
484		   (unsigned long) sec_start,
485		   (unsigned long) sec_end);
486
487  if (data->load_offset != 0 || data->load_start != 0 || data->load_end != 0)
488    printf_filtered (" into memory (%s to %s)\n",
489		     paddress (target_gdbarch,
490			       (unsigned long) sec_start
491			       + sec_offset + data->load_offset),
492		     paddress (target_gdbarch,
493			       (unsigned long) sec_start + sec_offset
494				+ data->load_offset + sec_load_count));
495  else
496    puts_filtered ("\n");
497
498  /* Write the data.  */
499  ret = target_write_memory (sec_start + sec_offset + data->load_offset,
500			     buf + sec_offset, sec_load_count);
501  if (ret != 0)
502    warning (_("restore: memory write failed (%s)."), safe_strerror (ret));
503  do_cleanups (old_chain);
504  return;
505}
506
507static void
508restore_binary_file (char *filename, struct callback_data *data)
509{
510  FILE *file = fopen_with_cleanup (filename, FOPEN_RB);
511  gdb_byte *buf;
512  long len;
513
514  /* Get the file size for reading.  */
515  if (fseek (file, 0, SEEK_END) == 0)
516    {
517      len = ftell (file);
518      if (len < 0)
519	perror_with_name (filename);
520    }
521  else
522    perror_with_name (filename);
523
524  if (len <= data->load_start)
525    error (_("Start address is greater than length of binary file %s."),
526	   filename);
527
528  /* Chop off "len" if it exceeds the requested load_end addr.  */
529  if (data->load_end != 0 && data->load_end < len)
530    len = data->load_end;
531  /* Chop off "len" if the requested load_start addr skips some bytes.  */
532  if (data->load_start > 0)
533    len -= data->load_start;
534
535  printf_filtered
536    ("Restoring binary file %s into memory (0x%lx to 0x%lx)\n",
537     filename,
538     (unsigned long) (data->load_start + data->load_offset),
539     (unsigned long) (data->load_start + data->load_offset + len));
540
541  /* Now set the file pos to the requested load start pos.  */
542  if (fseek (file, data->load_start, SEEK_SET) != 0)
543    perror_with_name (filename);
544
545  /* Now allocate a buffer and read the file contents.  */
546  buf = xmalloc (len);
547  make_cleanup (xfree, buf);
548  if (fread (buf, 1, len, file) != len)
549    perror_with_name (filename);
550
551  /* Now write the buffer into target memory.  */
552  len = target_write_memory (data->load_start + data->load_offset, buf, len);
553  if (len != 0)
554    warning (_("restore: memory write failed (%s)."), safe_strerror (len));
555  return;
556}
557
558static void
559restore_command (char *args, int from_tty)
560{
561  char *filename;
562  struct callback_data data;
563  bfd *ibfd;
564  int binary_flag = 0;
565
566  if (!target_has_execution)
567    noprocess ();
568
569  data.load_offset = 0;
570  data.load_start  = 0;
571  data.load_end    = 0;
572
573  /* Parse the input arguments.  First is filename (required).  */
574  filename = scan_filename_with_cleanup (&args, NULL);
575  if (args != NULL && *args != '\0')
576    {
577      char *binary_string = "binary";
578
579      /* Look for optional "binary" flag.  */
580      if (strncmp (args, binary_string, strlen (binary_string)) == 0)
581	{
582	  binary_flag = 1;
583	  args += strlen (binary_string);
584	  args = skip_spaces (args);
585	}
586      /* Parse offset (optional).  */
587      if (args != NULL && *args != '\0')
588      data.load_offset =
589	parse_and_eval_address (scan_expression_with_cleanup (&args, NULL));
590      if (args != NULL && *args != '\0')
591	{
592	  /* Parse start address (optional).  */
593	  data.load_start =
594	    parse_and_eval_long (scan_expression_with_cleanup (&args, NULL));
595	  if (args != NULL && *args != '\0')
596	    {
597	      /* Parse end address (optional).  */
598	      data.load_end = parse_and_eval_long (args);
599	      if (data.load_end <= data.load_start)
600		error (_("Start must be less than end."));
601	    }
602	}
603    }
604
605  if (info_verbose)
606    printf_filtered ("Restore file %s offset 0x%lx start 0x%lx end 0x%lx\n",
607		     filename, (unsigned long) data.load_offset,
608		     (unsigned long) data.load_start,
609		     (unsigned long) data.load_end);
610
611  if (binary_flag)
612    {
613      restore_binary_file (filename, &data);
614    }
615  else
616    {
617      /* Open the file for loading.  */
618      ibfd = bfd_openr_with_cleanup (filename, NULL);
619
620      /* Process the sections.  */
621      bfd_map_over_sections (ibfd, restore_section_callback, &data);
622    }
623  return;
624}
625
626static void
627srec_dump_command (char *cmd, int from_tty)
628{
629  printf_unfiltered ("\"dump srec\" must be followed by a subcommand.\n");
630  help_list (srec_cmdlist, "dump srec ", -1, gdb_stdout);
631}
632
633static void
634ihex_dump_command (char *cmd, int from_tty)
635{
636  printf_unfiltered ("\"dump ihex\" must be followed by a subcommand.\n");
637  help_list (ihex_cmdlist, "dump ihex ", -1, gdb_stdout);
638}
639
640static void
641tekhex_dump_command (char *cmd, int from_tty)
642{
643  printf_unfiltered ("\"dump tekhex\" must be followed by a subcommand.\n");
644  help_list (tekhex_cmdlist, "dump tekhex ", -1, gdb_stdout);
645}
646
647static void
648binary_dump_command (char *cmd, int from_tty)
649{
650  printf_unfiltered ("\"dump binary\" must be followed by a subcommand.\n");
651  help_list (binary_dump_cmdlist, "dump binary ", -1, gdb_stdout);
652}
653
654static void
655binary_append_command (char *cmd, int from_tty)
656{
657  printf_unfiltered ("\"append binary\" must be followed by a subcommand.\n");
658  help_list (binary_append_cmdlist, "append binary ", -1, gdb_stdout);
659}
660
661extern initialize_file_ftype _initialize_cli_dump; /* -Wmissing-prototypes */
662
663void
664_initialize_cli_dump (void)
665{
666  struct cmd_list_element *c;
667
668  add_prefix_cmd ("dump", class_vars, dump_command,
669		  _("Dump target code/data to a local file."),
670		  &dump_cmdlist, "dump ",
671		  0/*allow-unknown*/,
672		  &cmdlist);
673  add_prefix_cmd ("append", class_vars, append_command,
674		  _("Append target code/data to a local file."),
675		  &append_cmdlist, "append ",
676		  0/*allow-unknown*/,
677		  &cmdlist);
678
679  add_dump_command ("memory", dump_memory_command, "\
680Write contents of memory to a raw binary file.\n\
681Arguments are FILE START STOP.  Writes the contents of memory within the\n\
682range [START .. STOP) to the specifed FILE in raw target ordered bytes.");
683
684  add_dump_command ("value", dump_value_command, "\
685Write the value of an expression to a raw binary file.\n\
686Arguments are FILE EXPRESSION.  Writes the value of EXPRESSION to\n\
687the specified FILE in raw target ordered bytes.");
688
689  add_prefix_cmd ("srec", all_commands, srec_dump_command,
690		  _("Write target code/data to an srec file."),
691		  &srec_cmdlist, "dump srec ",
692		  0 /*allow-unknown*/,
693		  &dump_cmdlist);
694
695  add_prefix_cmd ("ihex", all_commands, ihex_dump_command,
696		  _("Write target code/data to an intel hex file."),
697		  &ihex_cmdlist, "dump ihex ",
698		  0 /*allow-unknown*/,
699		  &dump_cmdlist);
700
701  add_prefix_cmd ("tekhex", all_commands, tekhex_dump_command,
702		  _("Write target code/data to a tekhex file."),
703		  &tekhex_cmdlist, "dump tekhex ",
704		  0 /*allow-unknown*/,
705		  &dump_cmdlist);
706
707  add_prefix_cmd ("binary", all_commands, binary_dump_command,
708		  _("Write target code/data to a raw binary file."),
709		  &binary_dump_cmdlist, "dump binary ",
710		  0 /*allow-unknown*/,
711		  &dump_cmdlist);
712
713  add_prefix_cmd ("binary", all_commands, binary_append_command,
714		  _("Append target code/data to a raw binary file."),
715		  &binary_append_cmdlist, "append binary ",
716		  0 /*allow-unknown*/,
717		  &append_cmdlist);
718
719  add_cmd ("memory", all_commands, dump_srec_memory, _("\
720Write contents of memory to an srec file.\n\
721Arguments are FILE START STOP.  Writes the contents of memory\n\
722within the range [START .. STOP) to the specifed FILE in srec format."),
723	   &srec_cmdlist);
724
725  add_cmd ("value", all_commands, dump_srec_value, _("\
726Write the value of an expression to an srec file.\n\
727Arguments are FILE EXPRESSION.  Writes the value of EXPRESSION\n\
728to the specified FILE in srec format."),
729	   &srec_cmdlist);
730
731  add_cmd ("memory", all_commands, dump_ihex_memory, _("\
732Write contents of memory to an ihex file.\n\
733Arguments are FILE START STOP.  Writes the contents of memory within\n\
734the range [START .. STOP) to the specifed FILE in intel hex format."),
735	   &ihex_cmdlist);
736
737  add_cmd ("value", all_commands, dump_ihex_value, _("\
738Write the value of an expression to an ihex file.\n\
739Arguments are FILE EXPRESSION.  Writes the value of EXPRESSION\n\
740to the specified FILE in intel hex format."),
741	   &ihex_cmdlist);
742
743  add_cmd ("memory", all_commands, dump_tekhex_memory, _("\
744Write contents of memory to a tekhex file.\n\
745Arguments are FILE START STOP.  Writes the contents of memory\n\
746within the range [START .. STOP) to the specifed FILE in tekhex format."),
747	   &tekhex_cmdlist);
748
749  add_cmd ("value", all_commands, dump_tekhex_value, _("\
750Write the value of an expression to a tekhex file.\n\
751Arguments are FILE EXPRESSION.  Writes the value of EXPRESSION\n\
752to the specified FILE in tekhex format."),
753	   &tekhex_cmdlist);
754
755  add_cmd ("memory", all_commands, dump_binary_memory, _("\
756Write contents of memory to a raw binary file.\n\
757Arguments are FILE START STOP.  Writes the contents of memory\n\
758within the range [START .. STOP) to the specifed FILE in binary format."),
759	   &binary_dump_cmdlist);
760
761  add_cmd ("value", all_commands, dump_binary_value, _("\
762Write the value of an expression to a raw binary file.\n\
763Arguments are FILE EXPRESSION.  Writes the value of EXPRESSION\n\
764to the specified FILE in raw target ordered bytes."),
765	   &binary_dump_cmdlist);
766
767  add_cmd ("memory", all_commands, append_binary_memory, _("\
768Append contents of memory to a raw binary file.\n\
769Arguments are FILE START STOP.  Writes the contents of memory within the\n\
770range [START .. STOP) to the specifed FILE in raw target ordered bytes."),
771	   &binary_append_cmdlist);
772
773  add_cmd ("value", all_commands, append_binary_value, _("\
774Append the value of an expression to a raw binary file.\n\
775Arguments are FILE EXPRESSION.  Writes the value of EXPRESSION\n\
776to the specified FILE in raw target ordered bytes."),
777	   &binary_append_cmdlist);
778
779  c = add_com ("restore", class_vars, restore_command, _("\
780Restore the contents of FILE to target memory.\n\
781Arguments are FILE OFFSET START END where all except FILE are optional.\n\
782OFFSET will be added to the base address of the file (default zero).\n\
783If START and END are given, only the file contents within that range\n\
784(file relative) will be restored to target memory."));
785  c->completer = filename_completer;
786  /* FIXME: completers for other commands.  */
787}
788