diff options
author | Daniel Jacobowitz <dan@debian.org> | 2007-11-30 21:50:19 +0000 |
---|---|---|
committer | Daniel Jacobowitz <dan@debian.org> | 2007-11-30 21:50:19 +0000 |
commit | be7ded9a130360a39eb10e5771933518d99531f1 (patch) | |
tree | 021064d0ffdfcf3f24b1aaa518e83c0eb89bfa85 /gdb/gdbserver | |
parent | b519ad14c51825e08db6bfa452261e4335bc5572 (diff) | |
download | gdb-be7ded9a130360a39eb10e5771933518d99531f1.tar.gz |
* 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/gdbserver')
-rw-r--r-- | gdb/gdbserver/ChangeLog | 10 | ||||
-rw-r--r-- | gdb/gdbserver/Makefile.in | 3 | ||||
-rw-r--r-- | gdb/gdbserver/hostio.c | 517 | ||||
-rw-r--r-- | gdb/gdbserver/server.c | 11 | ||||
-rw-r--r-- | gdb/gdbserver/server.h | 3 |
5 files changed, 541 insertions, 3 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 245163bc37c..aa1661fec66 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,13 @@ +2007-11-30 Daniel Jacobowitz <dan@codesourcery.com> + + * 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. + 2007-11-05 Daniel Jacobowitz <dan@codesourcery.com> * linux-low.c: Include <sched.h>. diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index 9ac7c443a99..9658ce36402 100644 --- a/gdb/gdbserver/Makefile.in +++ b/gdb/gdbserver/Makefile.in @@ -139,7 +139,7 @@ TAGFILES = $(SOURCES) ${HFILES} ${ALLPARAM} ${POSSLIBS} OBS = inferiors.o regcache.o remote-utils.o server.o signals.o target.o \ utils.o version.o \ - mem-break.o \ + mem-break.o hostio.o \ $(XML_BUILTIN) \ $(DEPFILES) GDBSERVER_LIBS = @GDBSERVER_LIBS@ @@ -277,6 +277,7 @@ regcache_h = $(srcdir)/regcache.h server_h = $(srcdir)/server.h $(regcache_h) config.h $(srcdir)/target.h \ $(srcdir)/mem-break.h +hostio.o: hostio.c $(server_h) inferiors.o: inferiors.c $(server_h) mem-break.o: mem-break.c $(server_h) proc-service.o: proc-service.c $(server_h) $(gdb_proc_service_h) diff --git a/gdb/gdbserver/hostio.c b/gdb/gdbserver/hostio.c new file mode 100644 index 00000000000..5160ba9194e --- /dev/null +++ b/gdb/gdbserver/hostio.c @@ -0,0 +1,517 @@ +/* Host file transfer support for gdbserver. + Copyright (C) 2006 + Free Software Foundation, Inc. + + Contributed by CodeSourcery. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" +#include "gdb/fileio.h" + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <unistd.h> + +extern int remote_debug; + +struct fd_list +{ + int fd; + struct fd_list *next; +}; + +static struct fd_list *open_fds; + +static int +safe_fromhex (char a, int *nibble) +{ + if (a >= '0' && a <= '9') + *nibble = a - '0'; + else if (a >= 'a' && a <= 'f') + *nibble = a - 'a' + 10; + else if (a >= 'A' && a <= 'F') + *nibble = a - 'A' + 10; + else + return -1; + + return 0; +} + +static int +require_filename (char **pp, char *filename) +{ + int count; + char *p; + + p = *pp; + count = 0; + + while (*p && *p != ',') + { + int nib1, nib2; + + /* Don't allow overflow. */ + if (count >= PATH_MAX - 1) + return -1; + + if (safe_fromhex (p[0], &nib1) + || safe_fromhex (p[1], &nib2)) + return -1; + + filename[count++] = nib1 * 16 + nib2; + p += 2; + } + + filename[count] = '\0'; + *pp = p; + return 0; +} + +static int +require_int (char **pp, int *value) +{ + char *p; + int count; + + p = *pp; + *value = 0; + count = 0; + + while (*p && *p != ',') + { + int nib; + + /* Don't allow overflow. */ + if (count >= 7) + return -1; + + if (safe_fromhex (p[0], &nib)) + return -1; + *value = *value * 16 + nib; + p++; + count++; + } + + *pp = p; + return 0; +} + +static int +require_data (char *p, int p_len, char **data, int *data_len) +{ + int input_index, output_index, escaped; + + *data = malloc (p_len); + + output_index = 0; + escaped = 0; + for (input_index = 0; input_index < p_len; input_index++) + { + char b = p[input_index]; + + if (escaped) + { + (*data)[output_index++] = b ^ 0x20; + escaped = 0; + } + else if (b == '}') + escaped = 1; + else + (*data)[output_index++] = b; + } + + if (escaped) + return -1; + + *data_len = output_index; + return 0; +} + +static int +require_comma (char **pp) +{ + if (**pp == ',') + { + (*pp)++; + return 0; + } + else + return -1; +} + +static int +require_end (char *p) +{ + if (*p == '\0') + return 0; + else + return -1; +} + +static int +require_valid_fd (int fd) +{ + struct fd_list *fd_ptr; + + for (fd_ptr = open_fds; fd_ptr != NULL; fd_ptr = fd_ptr->next) + if (fd_ptr->fd == fd) + return 0; + + return -1; +} + +static int +errno_to_fileio_errno (int error) +{ + switch (error) + { + case EPERM: + return FILEIO_EPERM; + case ENOENT: + return FILEIO_ENOENT; + case EINTR: + return FILEIO_EINTR; + case EIO: + return FILEIO_EIO; + case EBADF: + return FILEIO_EBADF; + case EACCES: + return FILEIO_EACCES; + case EFAULT: + return FILEIO_EFAULT; + case EBUSY: + return FILEIO_EBUSY; + case EEXIST: + return FILEIO_EEXIST; + case ENODEV: + return FILEIO_ENODEV; + case ENOTDIR: + return FILEIO_ENOTDIR; + case EISDIR: + return FILEIO_EISDIR; + case EINVAL: + return FILEIO_EINVAL; + case ENFILE: + return FILEIO_ENFILE; + case EMFILE: + return FILEIO_EMFILE; + case EFBIG: + return FILEIO_EFBIG; + case ENOSPC: + return FILEIO_ENOSPC; + case ESPIPE: + return FILEIO_ESPIPE; + case EROFS: + return FILEIO_EROFS; + case ENOSYS: + return FILEIO_ENOSYS; + case ENAMETOOLONG: + return FILEIO_ENAMETOOLONG; + } + return FILEIO_EUNKNOWN; +} + +static void +hostio_error (char *own_buf, int error) +{ + int fileio_error = errno_to_fileio_errno (error); + + sprintf (own_buf, "F-1,%x", fileio_error); +} + +static void +hostio_packet_error (char *own_buf) +{ + hostio_error (own_buf, EINVAL); +} + +static void +hostio_reply (char *own_buf, int result) +{ + sprintf (own_buf, "F%x", result); +} + +static int +hostio_reply_with_data (char *own_buf, char *buffer, int len, + int *new_packet_len) +{ + int input_index, output_index, out_maxlen; + + sprintf (own_buf, "F%x;", len); + output_index = strlen (own_buf); + + out_maxlen = PBUFSIZ; + + for (input_index = 0; input_index < len; input_index++) + { + char b = buffer[input_index]; + + if (b == '$' || b == '#' || b == '}' || b == '*') + { + /* These must be escaped. */ + if (output_index + 2 > out_maxlen) + break; + own_buf[output_index++] = '}'; + own_buf[output_index++] = b ^ 0x20; + } + else + { + if (output_index + 1 > out_maxlen) + break; + own_buf[output_index++] = b; + } + } + + *new_packet_len = output_index; + return input_index; +} + +static int +fileio_open_flags_to_host (int fileio_open_flags, int *open_flags_p) +{ + int open_flags = 0; + + if (fileio_open_flags & ~FILEIO_O_SUPPORTED) + return -1; + + if (fileio_open_flags & FILEIO_O_CREAT) + open_flags |= O_CREAT; + if (fileio_open_flags & FILEIO_O_EXCL) + open_flags |= O_EXCL; + if (fileio_open_flags & FILEIO_O_TRUNC) + open_flags |= O_TRUNC; + if (fileio_open_flags & FILEIO_O_APPEND) + open_flags |= O_APPEND; + if (fileio_open_flags & FILEIO_O_RDONLY) + open_flags |= O_RDONLY; + if (fileio_open_flags & FILEIO_O_WRONLY) + open_flags |= O_WRONLY; + if (fileio_open_flags & FILEIO_O_RDWR) + open_flags |= O_RDWR; +/* On systems supporting binary and text mode, always open files in + binary mode. */ +#ifdef O_BINARY + open_flags |= O_BINARY; +#endif + + *open_flags_p = open_flags; + return 0; +} + +static void +handle_open (char *own_buf) +{ + char filename[PATH_MAX]; + char *p; + int fileio_flags, mode, flags, fd; + struct fd_list *new_fd; + + p = own_buf + strlen ("vFile:open:"); + + if (require_filename (&p, filename) + || require_comma (&p) + || require_int (&p, &fileio_flags) + || require_comma (&p) + || require_int (&p, &mode) + || require_end (p) + || fileio_open_flags_to_host (fileio_flags, &flags)) + { + hostio_packet_error (own_buf); + return; + } + + /* We do not need to convert MODE, since the fileio protocol + uses the standard values. */ + fd = open (filename, flags, mode); + + if (fd == -1) + { + hostio_error (own_buf, errno); + return; + } + + /* Record the new file descriptor. */ + new_fd = malloc (sizeof (struct fd_list)); + new_fd->fd = fd; + new_fd->next = open_fds; + open_fds = new_fd; + + hostio_reply (own_buf, fd); +} + +static void +handle_pread (char *own_buf, int *new_packet_len) +{ + int fd, ret, len, offset, bytes_sent; + char *p, *data; + + p = own_buf + strlen ("vFile:pread:"); + + if (require_int (&p, &fd) + || require_comma (&p) + || require_valid_fd (fd) + || require_int (&p, &len) + || require_comma (&p) + || require_int (&p, &offset) + || require_end (p)) + { + hostio_packet_error (own_buf); + return; + } + + data = malloc (len); + ret = pread (fd, data, len, offset); + + if (ret == -1) + { + hostio_error (own_buf, errno); + free (data); + return; + } + + bytes_sent = hostio_reply_with_data (own_buf, data, ret, new_packet_len); + + /* If we were using read, and the data did not all fit in the reply, + we would have to back up using lseek here. With pread it does + not matter. But we still have a problem; the return value in the + packet might be wrong, so we must fix it. This time it will + definitely fit. */ + if (bytes_sent < ret) + bytes_sent = hostio_reply_with_data (own_buf, data, bytes_sent, + new_packet_len); + + free (data); +} + +static void +handle_pwrite (char *own_buf, int packet_len) +{ + int fd, ret, len, offset; + char *p, *data; + + p = own_buf + strlen ("vFile:pwrite:"); + + if (require_int (&p, &fd) + || require_comma (&p) + || require_valid_fd (fd) + || require_int (&p, &offset) + || require_comma (&p) + || require_data (p, packet_len - (p - own_buf), &data, &len)) + { + hostio_packet_error (own_buf); + return; + } + + ret = pwrite (fd, data, len, offset); + + if (ret == -1) + { + hostio_error (own_buf, errno); + free (data); + return; + } + + hostio_reply (own_buf, ret); + free (data); +} + +static void +handle_close (char *own_buf) +{ + int fd, ret; + char *p; + struct fd_list **open_fd_p, *old_fd; + + p = own_buf + strlen ("vFile:close:"); + + if (require_int (&p, &fd) + || require_valid_fd (fd) + || require_end (p)) + { + hostio_packet_error (own_buf); + return; + } + + ret = close (fd); + + if (ret == -1) + { + hostio_error (own_buf, errno); + return; + } + + open_fd_p = &open_fds; + while (*open_fd_p && (*open_fd_p)->fd != fd) + open_fd_p = &(*open_fd_p)->next; + + old_fd = *open_fd_p; + *open_fd_p = (*open_fd_p)->next; + free (old_fd); + + hostio_reply (own_buf, ret); +} + +static void +handle_unlink (char *own_buf) +{ + char filename[PATH_MAX]; + char *p; + int ret; + + p = own_buf + strlen ("vFile:unlink:"); + + if (require_filename (&p, filename) + || require_end (p)) + { + hostio_packet_error (own_buf); + return; + } + + ret = unlink (filename); + + if (ret == -1) + { + hostio_error (own_buf, errno); + return; + } + + hostio_reply (own_buf, ret); +} + +/* Handle all the 'F' file transfer packets. */ + +int +handle_vFile (char *own_buf, int packet_len, int *new_packet_len) +{ + if (strncmp (own_buf, "vFile:open:", 11) == 0) + handle_open (own_buf); + else if (strncmp (own_buf, "vFile:pread:", 11) == 0) + handle_pread (own_buf, new_packet_len); + else if (strncmp (own_buf, "vFile:pwrite:", 12) == 0) + handle_pwrite (own_buf, packet_len); + else if (strncmp (own_buf, "vFile:close:", 12) == 0) + handle_close (own_buf); + else if (strncmp (own_buf, "vFile:unlink:", 13) == 0) + handle_unlink (own_buf); + else + return 0; + + return 1; +} diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 991ed15caf1..0261e6bdf3a 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -772,7 +772,8 @@ err: /* Handle all of the extended 'v' packets. */ void -handle_v_requests (char *own_buf, char *status, int *signal) +handle_v_requests (char *own_buf, char *status, int *signal, + int packet_len, int *new_packet_len) { if (strncmp (own_buf, "vCont;", 6) == 0) { @@ -786,6 +787,10 @@ handle_v_requests (char *own_buf, char *status, int *signal) return; } + if (strncmp (own_buf, "vFile:", 6) == 0 + && handle_vFile (own_buf, packet_len, new_packet_len)) + return; + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; @@ -1218,8 +1223,10 @@ main (int argc, char *argv[]) } case 'v': /* Extended (long) request. */ - handle_v_requests (own_buf, &status, &signal); + handle_v_requests (own_buf, &status, &signal, + packet_len, &new_packet_len); break; + default: /* It is a request we don't understand. Respond with an empty packet so that gdb knows that we don't support this diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index d350f441657..54ba614fd9b 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -156,6 +156,9 @@ extern int pass_signals[]; extern jmp_buf toplevel; +/* Functions from hostio.c. */ +extern int handle_vFile (char *, int, int *); + /* From remote-utils.c */ extern int remote_debug; |