diff options
author | Daniel Jacobowitz <drow@false.org> | 2007-11-30 21:50:19 +0000 |
---|---|---|
committer | Daniel Jacobowitz <drow@false.org> | 2007-11-30 21:50:19 +0000 |
commit | a6b151f18761ed51bf9bfc7460a6e4023dd63449 (patch) | |
tree | b4986879576fca170a28587609156febb1a7f52d /gdb/remote.c | |
parent | fba57f8f38639af470bbe7a9b81a9dfcaba5d855 (diff) | |
download | gdb-a6b151f18761ed51bf9bfc7460a6e4023dd63449.zip gdb-a6b151f18761ed51bf9bfc7460a6e4023dd63449.tar.gz gdb-a6b151f18761ed51bf9bfc7460a6e4023dd63449.tar.bz2 |
* remote.c (remote_cmdlist): New variable.
(PACKET_vFile_open, PACKET_vFile_pread, PACKET_vFile_pwrite)
(PACKET_vFile_close, PACKET_vFile_unlink): New constants.
(remote_buffer_add_string, remote_buffer_add_bytes)
(remote_buffer_add_int, remote_hostio_parse_result)
(remote_hostio_send_command, remote_hostio_open, remote_hostio_pwrite)
(remote_hostio_pread, remote_hostio_close, remote_hostio_unlink)
(remote_fileio_errno_to_host, remote_hostio_error, fclose_cleanup)
(remote_hostio_close_cleanup, remote_file_put, remote_file_get)
(remote_file_delete, remote_put_command, remote_get_command)
(remote_delete_command, remote_command): New functions.
(_initialize_remote): Register new packets and commands.
* Makefile.in (gdb_fileio_h): New variable.
(remote.o): Update.
(SUBDIR_MI_OBS): Add mi-cmd-target.o.
(SUBDIR_MI_SRCS): Add mi/mi-cmd-target.c.
(mi-cmd-target.o): New rule.
* mi/mi-cmd-target.c: New file.
* mi/mi-cmds.c (mi_cmds): Add target-file-delete, target-file-get,
and target-file-put.
* mi/mi-cmds.h (mi_cmd_target_file_get, mi_cmd_target_file_put)
(mi_cmd_target_file_delete): Declare.
* remote.h (remote_file_put, remote_file_get, remote_file_delete):
Declare.
* NEWS: Describe new file transfer support.
* gdb.texinfo (Debugging Programs with Multiple Processes): Correct
formatting.
(Remote Debugging): Add File Transfer section.
(Remote Configuration): Document Host I/O packets.
(GDB/MI): Add GDB/MI File Transfer Commands section.
(Remote Protocol): Add Host I/O Packets section.
(Packets): Add vFile.
* Makefile.in (OBS): Add hostio.o.
(hostio.o): New rule.
* server.h (handle_vFile): Declare.
* hostio.c: New file.
* server.c (handle_v_requests): Take packet_len and new_packet_len
for binary packets. Call handle_vFile.
(main): Update call to handle_v_requests.
* gdb.server/file-transfer.exp, gdb.server/transfer.txt,
gdb.mi/mi-file-transfer.exp: New.
Diffstat (limited to 'gdb/remote.c')
-rw-r--r-- | gdb/remote.c | 668 |
1 files changed, 668 insertions, 0 deletions
diff --git a/gdb/remote.c b/gdb/remote.c index 2d8a88b..ec7044e 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -58,6 +58,7 @@ #include "gdbcore.h" /* for exec_bfd */ #include "remote-fileio.h" +#include "gdb/fileio.h" #include "memory-map.h" @@ -207,6 +208,10 @@ static void show_remote_protocol_packet_cmd (struct ui_file *file, void _initialize_remote (void); +/* For "remote". */ + +static struct cmd_list_element *remote_cmdlist; + /* For "set remote" and "show remote". */ static struct cmd_list_element *remote_set_cmdlist; @@ -901,6 +906,11 @@ enum { PACKET_Z2, PACKET_Z3, PACKET_Z4, + PACKET_vFile_open, + PACKET_vFile_pread, + PACKET_vFile_pwrite, + PACKET_vFile_close, + PACKET_vFile_unlink, PACKET_qXfer_auxv, PACKET_qXfer_features, PACKET_qXfer_libraries, @@ -6276,6 +6286,631 @@ remote_read_description (struct target_ops *target) return NULL; } +/* Remote file transfer support. This is host-initiated I/O, not + target-initiated; for target-initiated, see remote-fileio.c. */ + +/* If *LEFT is at least the length of STRING, copy STRING to + *BUFFER, update *BUFFER to point to the new end of the buffer, and + decrease *LEFT. Otherwise raise an error. */ + +static void +remote_buffer_add_string (char **buffer, int *left, char *string) +{ + int len = strlen (string); + + if (len > *left) + error (_("Packet too long for target.")); + + memcpy (*buffer, string, len); + *buffer += len; + *left -= len; + + /* NUL-terminate the buffer as a convenience, if there is + room. */ + if (*left) + **buffer = '\0'; +} + +/* If *LEFT is large enough, hex encode LEN bytes from BYTES into + *BUFFER, update *BUFFER to point to the new end of the buffer, and + decrease *LEFT. Otherwise raise an error. */ + +static void +remote_buffer_add_bytes (char **buffer, int *left, const gdb_byte *bytes, + int len) +{ + if (2 * len > *left) + error (_("Packet too long for target.")); + + bin2hex (bytes, *buffer, len); + *buffer += 2 * len; + *left -= 2 * len; + + /* NUL-terminate the buffer as a convenience, if there is + room. */ + if (*left) + **buffer = '\0'; +} + +/* If *LEFT is large enough, convert VALUE to hex and add it to + *BUFFER, update *BUFFER to point to the new end of the buffer, and + decrease *LEFT. Otherwise raise an error. */ + +static void +remote_buffer_add_int (char **buffer, int *left, ULONGEST value) +{ + int len = hexnumlen (value); + + if (len > *left) + error (_("Packet too long for target.")); + + hexnumstr (*buffer, value); + *buffer += len; + *left -= len; + + /* NUL-terminate the buffer as a convenience, if there is + room. */ + if (*left) + **buffer = '\0'; +} + +/* Parse an I/O result packet from BUFFER. Set RETCODE to the return + value, *REMOTE_ERRNO to the remote error number or zero if none + was included, and *ATTACHMENT to point to the start of the annex + if any. The length of the packet isn't needed here; there may + be NUL bytes in BUFFER, but they will be after *ATTACHMENT. + + Return 0 if the packet could be parsed, -1 if it could not. If + -1 is returned, the other variables may not be initialized. */ + +static int +remote_hostio_parse_result (char *buffer, int *retcode, + int *remote_errno, char **attachment) +{ + char *p, *p2; + + *remote_errno = 0; + *attachment = NULL; + + if (buffer[0] != 'F') + return -1; + + errno = 0; + *retcode = strtol (&buffer[1], &p, 16); + if (errno != 0 || p == &buffer[1]) + return -1; + + /* Check for ",errno". */ + if (*p == ',') + { + errno = 0; + *remote_errno = strtol (p + 1, &p2, 16); + if (errno != 0 || p + 1 == p2) + return -1; + p = p2; + } + + /* Check for ";attachment". If there is no attachment, the + packet should end here. */ + if (*p == ';') + { + *attachment = p + 1; + return 0; + } + else if (*p == '\0') + return 0; + else + return -1; +} + +/* Send a prepared I/O packet to the target and read its response. + The prepared packet is in the global RS->BUF before this function + is called, and the answer is there when we return. + + COMMAND_BYTES is the length of the request to send, which may include + binary data. WHICH_PACKET is the packet configuration to check + before attempting a packet. If an error occurs, *REMOTE_ERRNO + is set to the error number and -1 is returned. Otherwise the value + returned by the function is returned. + + ATTACHMENT and ATTACHMENT_LEN should be non-NULL if and only if an + attachment is expected; an error will be reported if there's a + mismatch. If one is found, *ATTACHMENT will be set to point into + the packet buffer and *ATTACHMENT_LEN will be set to the + attachment's length. */ + +static int +remote_hostio_send_command (int command_bytes, int which_packet, + int *remote_errno, char **attachment, + int *attachment_len) +{ + struct remote_state *rs = get_remote_state (); + int ret, bytes_read; + char *attachment_tmp; + + if (remote_protocol_packets[which_packet].support == PACKET_DISABLE) + { + *remote_errno = FILEIO_ENOSYS; + return -1; + } + + putpkt_binary (rs->buf, command_bytes); + bytes_read = getpkt_sane (&rs->buf, &rs->buf_size, 0); + + /* If it timed out, something is wrong. Don't try to parse the + buffer. */ + if (bytes_read < 0) + { + *remote_errno = FILEIO_EINVAL; + return -1; + } + + switch (packet_ok (rs->buf, &remote_protocol_packets[which_packet])) + { + case PACKET_ERROR: + *remote_errno = FILEIO_EINVAL; + return -1; + case PACKET_UNKNOWN: + *remote_errno = FILEIO_ENOSYS; + return -1; + case PACKET_OK: + break; + } + + if (remote_hostio_parse_result (rs->buf, &ret, remote_errno, + &attachment_tmp)) + { + *remote_errno = FILEIO_EINVAL; + return -1; + } + + /* Make sure we saw an attachment if and only if we expected one. */ + if ((attachment_tmp == NULL && attachment != NULL) + || (attachment_tmp != NULL && attachment == NULL)) + { + *remote_errno = FILEIO_EINVAL; + return -1; + } + + /* If an attachment was found, it must point into the packet buffer; + work out how many bytes there were. */ + if (attachment_tmp != NULL) + { + *attachment = attachment_tmp; + *attachment_len = bytes_read - (*attachment - rs->buf); + } + + return ret; +} + +/* Open FILENAME on the remote target, using FLAGS and MODE. Return a + remote file descriptor, or -1 if an error occurs (and set + *REMOTE_ERRNO). */ + +static int +remote_hostio_open (const char *filename, int flags, int mode, + int *remote_errno) +{ + struct remote_state *rs = get_remote_state (); + char *p = rs->buf; + int left = get_remote_packet_size () - 1; + + remote_buffer_add_string (&p, &left, "vFile:open:"); + + remote_buffer_add_bytes (&p, &left, (const gdb_byte *) filename, + strlen (filename)); + remote_buffer_add_string (&p, &left, ","); + + remote_buffer_add_int (&p, &left, flags); + remote_buffer_add_string (&p, &left, ","); + + remote_buffer_add_int (&p, &left, mode); + + return remote_hostio_send_command (p - rs->buf, PACKET_vFile_open, + remote_errno, NULL, NULL); +} + +/* Write up to LEN bytes from WRITE_BUF to FD on the remote target. + Return the number of bytes written, or -1 if an error occurs (and + set *REMOTE_ERRNO). */ + +static int +remote_hostio_pwrite (int fd, const gdb_byte *write_buf, int len, + ULONGEST offset, int *remote_errno) +{ + struct remote_state *rs = get_remote_state (); + char *p = rs->buf; + int left = get_remote_packet_size (); + int out_len; + + remote_buffer_add_string (&p, &left, "vFile:pwrite:"); + + remote_buffer_add_int (&p, &left, fd); + remote_buffer_add_string (&p, &left, ","); + + remote_buffer_add_int (&p, &left, offset); + remote_buffer_add_string (&p, &left, ","); + + p += remote_escape_output (write_buf, len, p, &out_len, + get_remote_packet_size () - (p - rs->buf)); + + return remote_hostio_send_command (p - rs->buf, PACKET_vFile_pwrite, + remote_errno, NULL, NULL); +} + +/* Read up to LEN bytes FD on the remote target into READ_BUF + Return the number of bytes read, or -1 if an error occurs (and + set *REMOTE_ERRNO). */ + +static int +remote_hostio_pread (int fd, gdb_byte *read_buf, int len, + ULONGEST offset, int *remote_errno) +{ + struct remote_state *rs = get_remote_state (); + char *p = rs->buf; + char *attachment; + int left = get_remote_packet_size (); + int ret, attachment_len; + int read_len; + + remote_buffer_add_string (&p, &left, "vFile:pread:"); + + remote_buffer_add_int (&p, &left, fd); + remote_buffer_add_string (&p, &left, ","); + + remote_buffer_add_int (&p, &left, len); + remote_buffer_add_string (&p, &left, ","); + + remote_buffer_add_int (&p, &left, offset); + + ret = remote_hostio_send_command (p - rs->buf, PACKET_vFile_pread, + remote_errno, &attachment, + &attachment_len); + + if (ret < 0) + return ret; + + read_len = remote_unescape_input (attachment, attachment_len, + read_buf, len); + if (read_len != ret) + error (_("Read returned %d, but %d bytes."), ret, (int) read_len); + + return ret; +} + +/* Close FD on the remote target. Return 0, or -1 if an error occurs + (and set *REMOTE_ERRNO). */ + +static int +remote_hostio_close (int fd, int *remote_errno) +{ + struct remote_state *rs = get_remote_state (); + char *p = rs->buf; + int left = get_remote_packet_size () - 1; + + remote_buffer_add_string (&p, &left, "vFile:close:"); + + remote_buffer_add_int (&p, &left, fd); + + return remote_hostio_send_command (p - rs->buf, PACKET_vFile_close, + remote_errno, NULL, NULL); +} + +/* Unlink FILENAME on the remote target. Return 0, or -1 if an error + occurs (and set *REMOTE_ERRNO). */ + +static int +remote_hostio_unlink (const char *filename, int *remote_errno) +{ + struct remote_state *rs = get_remote_state (); + char *p = rs->buf; + int left = get_remote_packet_size () - 1; + + remote_buffer_add_string (&p, &left, "vFile:unlink:"); + + remote_buffer_add_bytes (&p, &left, (const gdb_byte *) filename, + strlen (filename)); + + return remote_hostio_send_command (p - rs->buf, PACKET_vFile_unlink, + remote_errno, NULL, NULL); +} + +static int +remote_fileio_errno_to_host (int errnum) +{ + switch (errnum) + { + case FILEIO_EPERM: + return EPERM; + case FILEIO_ENOENT: + return ENOENT; + case FILEIO_EINTR: + return EINTR; + case FILEIO_EIO: + return EIO; + case FILEIO_EBADF: + return EBADF; + case FILEIO_EACCES: + return EACCES; + case FILEIO_EFAULT: + return EFAULT; + case FILEIO_EBUSY: + return EBUSY; + case FILEIO_EEXIST: + return EEXIST; + case FILEIO_ENODEV: + return ENODEV; + case FILEIO_ENOTDIR: + return ENOTDIR; + case FILEIO_EISDIR: + return EISDIR; + case FILEIO_EINVAL: + return EINVAL; + case FILEIO_ENFILE: + return ENFILE; + case FILEIO_EMFILE: + return EMFILE; + case FILEIO_EFBIG: + return EFBIG; + case FILEIO_ENOSPC: + return ENOSPC; + case FILEIO_ESPIPE: + return ESPIPE; + case FILEIO_EROFS: + return EROFS; + case FILEIO_ENOSYS: + return ENOSYS; + case FILEIO_ENAMETOOLONG: + return ENAMETOOLONG; + } + return -1; +} + +static char * +remote_hostio_error (int errnum) +{ + int host_error = remote_fileio_errno_to_host (errnum); + + if (host_error == -1) + error (_("Unknown remote I/O error %d"), errnum); + else + error (_("Remote I/O error: %s"), safe_strerror (host_error)); +} + +static void +fclose_cleanup (void *file) +{ + fclose (file); +} + +static void +remote_hostio_close_cleanup (void *opaque) +{ + int fd = *(int *) opaque; + int remote_errno; + + remote_hostio_close (fd, &remote_errno); +} + +void +remote_file_put (const char *local_file, const char *remote_file, int from_tty) +{ + struct cleanup *back_to, *close_cleanup; + int retcode, fd, remote_errno, bytes, io_size; + FILE *file; + gdb_byte *buffer; + int bytes_in_buffer; + int saw_eof; + ULONGEST offset; + + if (!remote_desc) + error (_("command can only be used with remote target")); + + file = fopen (local_file, "rb"); + if (file == NULL) + perror_with_name (local_file); + back_to = make_cleanup (fclose_cleanup, file); + + fd = remote_hostio_open (remote_file, (FILEIO_O_WRONLY | FILEIO_O_CREAT + | FILEIO_O_TRUNC), + 0700, &remote_errno); + if (fd == -1) + remote_hostio_error (remote_errno); + + /* Send up to this many bytes at once. They won't all fit in the + remote packet limit, so we'll transfer slightly fewer. */ + io_size = get_remote_packet_size (); + buffer = xmalloc (io_size); + make_cleanup (xfree, buffer); + + close_cleanup = make_cleanup (remote_hostio_close_cleanup, &fd); + + bytes_in_buffer = 0; + saw_eof = 0; + offset = 0; + while (bytes_in_buffer || !saw_eof) + { + if (!saw_eof) + { + bytes = fread (buffer + bytes_in_buffer, 1, io_size - bytes_in_buffer, + file); + if (bytes == 0) + { + if (ferror (file)) + error (_("Error reading %s."), local_file); + else + { + /* EOF. Unless there is something still in the + buffer from the last iteration, we are done. */ + saw_eof = 1; + if (bytes_in_buffer == 0) + break; + } + } + } + else + bytes = 0; + + bytes += bytes_in_buffer; + bytes_in_buffer = 0; + + retcode = remote_hostio_pwrite (fd, buffer, bytes, offset, &remote_errno); + + if (retcode < 0) + remote_hostio_error (remote_errno); + else if (retcode == 0) + error (_("Remote write of %d bytes returned 0!"), bytes); + else if (retcode < bytes) + { + /* Short write. Save the rest of the read data for the next + write. */ + bytes_in_buffer = bytes - retcode; + memmove (buffer, buffer + retcode, bytes_in_buffer); + } + + offset += retcode; + } + + discard_cleanups (close_cleanup); + if (remote_hostio_close (fd, &remote_errno)) + remote_hostio_error (remote_errno); + + if (from_tty) + printf_filtered (_("Successfully sent file \"%s\".\n"), local_file); + do_cleanups (back_to); +} + +void +remote_file_get (const char *remote_file, const char *local_file, int from_tty) +{ + struct cleanup *back_to, *close_cleanup; + int retcode, fd, remote_errno, bytes, io_size; + FILE *file; + gdb_byte *buffer; + ULONGEST offset; + + if (!remote_desc) + error (_("command can only be used with remote target")); + + fd = remote_hostio_open (remote_file, FILEIO_O_RDONLY, 0, &remote_errno); + if (fd == -1) + remote_hostio_error (remote_errno); + + file = fopen (local_file, "wb"); + if (file == NULL) + perror_with_name (local_file); + back_to = make_cleanup (fclose_cleanup, file); + + /* Send up to this many bytes at once. They won't all fit in the + remote packet limit, so we'll transfer slightly fewer. */ + io_size = get_remote_packet_size (); + buffer = xmalloc (io_size); + make_cleanup (xfree, buffer); + + close_cleanup = make_cleanup (remote_hostio_close_cleanup, &fd); + + offset = 0; + while (1) + { + bytes = remote_hostio_pread (fd, buffer, io_size, offset, &remote_errno); + if (bytes == 0) + /* Success, but no bytes, means end-of-file. */ + break; + if (bytes == -1) + remote_hostio_error (remote_errno); + + offset += bytes; + + bytes = fwrite (buffer, 1, bytes, file); + if (bytes == 0) + perror_with_name (local_file); + } + + discard_cleanups (close_cleanup); + if (remote_hostio_close (fd, &remote_errno)) + remote_hostio_error (remote_errno); + + if (from_tty) + printf_filtered (_("Successfully fetched file \"%s\".\n"), remote_file); + do_cleanups (back_to); +} + +void +remote_file_delete (const char *remote_file, int from_tty) +{ + int retcode, remote_errno; + + if (!remote_desc) + error (_("command can only be used with remote target")); + + retcode = remote_hostio_unlink (remote_file, &remote_errno); + if (retcode == -1) + remote_hostio_error (remote_errno); + + if (from_tty) + printf_filtered (_("Successfully deleted file \"%s\".\n"), remote_file); +} + +static void +remote_put_command (char *args, int from_tty) +{ + struct cleanup *back_to; + char **argv; + + argv = buildargv (args); + if (argv == NULL) + nomem (0); + back_to = make_cleanup_freeargv (argv); + if (argv[0] == NULL || argv[1] == NULL || argv[2] != NULL) + error (_("Invalid parameters to remote put")); + + remote_file_put (argv[0], argv[1], from_tty); + + do_cleanups (back_to); +} + +static void +remote_get_command (char *args, int from_tty) +{ + struct cleanup *back_to; + char **argv; + + argv = buildargv (args); + if (argv == NULL) + nomem (0); + back_to = make_cleanup_freeargv (argv); + if (argv[0] == NULL || argv[1] == NULL || argv[2] != NULL) + error (_("Invalid parameters to remote get")); + + remote_file_get (argv[0], argv[1], from_tty); + + do_cleanups (back_to); +} + +static void +remote_delete_command (char *args, int from_tty) +{ + struct cleanup *back_to; + char **argv; + + argv = buildargv (args); + if (argv == NULL) + nomem (0); + back_to = make_cleanup_freeargv (argv); + if (argv[0] == NULL || argv[1] != NULL) + error (_("Invalid parameters to remote delete")); + + remote_file_delete (argv[0], from_tty); + + do_cleanups (back_to); +} + +static void +remote_command (char *args, int from_tty) +{ + help_list (remote_cmdlist, "remote ", -1, gdb_stdout); +} + static void init_remote_ops (void) { @@ -6719,6 +7354,21 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL, add_packet_config_cmd (&remote_protocol_packets[PACKET_qSupported], "qSupported", "supported-packets", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_open], + "vFile:open", "hostio-open", 0); + + add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_pread], + "vFile:pread", "hostio-pread", 0); + + add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_pwrite], + "vFile:pwrite", "hostio-pwrite", 0); + + add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_close], + "vFile:close", "hostio-close", 0); + + add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_unlink], + "vFile:unlink", "hostio-unlink", 0); + /* Keep the old ``set remote Z-packet ...'' working. Each individual Z sub-packet has its own set and show commands, but users may have sets to this variable in their .gdbinit files (or in their @@ -6733,6 +7383,24 @@ packets."), show_remote_protocol_Z_packet_cmd, /* FIXME: i18n: Use of remote protocol `Z' packets is %s. */ &remote_set_cmdlist, &remote_show_cmdlist); + add_prefix_cmd ("remote", class_files, remote_command, _("\ +Manipulate files on the remote system\n\ +Transfer files to and from the remote target system."), + &remote_cmdlist, "remote ", + 0 /* allow-unknown */, &cmdlist); + + add_cmd ("put", class_files, remote_put_command, + _("Copy a local file to the remote system."), + &remote_cmdlist); + + add_cmd ("get", class_files, remote_get_command, + _("Copy a remote file to the local system."), + &remote_cmdlist); + + add_cmd ("delete", class_files, remote_delete_command, + _("Delete a remote file."), + &remote_cmdlist); + /* Eventually initialize fileio. See fileio.c */ initialize_remote_fileio (remote_set_cmdlist, remote_show_cmdlist); } |