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