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