summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Jacobowitz <dan@debian.org>2006-05-23 21:44:20 +0000
committerDaniel Jacobowitz <dan@debian.org>2006-05-23 21:44:20 +0000
commit42f054a2222834580f3236b1b3cbcbacd545eeaa (patch)
treecb086f2aa78a28ff0e6ccbdaf50a36de050b0cf0
parent7c1c5e0c8377375c8267ff24166663c14e507698 (diff)
downloadgdb-42f054a2222834580f3236b1b3cbcbacd545eeaa.tar.gz
* gdb/remote.c: Include "gdb/fileio.h".
(PACKET_Fopen, PACKET_Fread, PACKET_Fwrite, PACKET_Fclose): New enums. (remote_escape_output, remote_unescape_input): New functions. (remote_write_bytes): Use remote_escape_output. (readchar): Do not mask off the high bit. (read_frame): Print binary packets correctly. (getpkt_sane): Likewise. Return the number of bytes read or -1. (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_write, remote_hostio_read) (remote_hostio_close, remote_fileio_errno_to_host) (remote_hostio_error, fclose_cleanup, remote_download_command) (remote_upload_command): New functions. (_initialize_remote): Register new packets and remote-upload and remote-download commands. * gdb/doc/gdb.texinfo (File Transfer): New section. (Remote Configuration): Document Host I/O packet commands. (Host I/O Packets): New section. (Packets): Mention overloading of F prefix. * gdb/gdbserver/Makefile.in (OBS): Add hostio.o. (hostio.o): New rule. * gdb/gdbserver/hostio.c: New file. * gdb/gdbserver/remote-utils.c (putpkt_binary): New function, broken out from putpkt. (putpkt): Use it. (readchar): Do not mask the high bits. Correct the buffer type. * gdb/gdbserver/server.c (main): Leave space for a trailing zero in the packet buffer. Save the length of the received packet. Handle Host I/O requests. Optionally call putpkt_binary. * gdb/gdbserver/server.h (handle_f_hostio, putpkt_binary): New prototypes.
-rw-r--r--ChangeLog.csl41
-rw-r--r--gdb/doc/gdb.texinfo132
-rw-r--r--gdb/gdbserver/Makefile.in3
-rw-r--r--gdb/gdbserver/hostio.c480
-rw-r--r--gdb/gdbserver/remote-utils.c22
-rw-r--r--gdb/gdbserver/server.c25
-rw-r--r--gdb/gdbserver/server.h4
-rw-r--r--gdb/remote.c655
8 files changed, 1326 insertions, 36 deletions
diff --git a/ChangeLog.csl b/ChangeLog.csl
index 5cf4205ac0d..ba29d73ac3b 100644
--- a/ChangeLog.csl
+++ b/ChangeLog.csl
@@ -1,3 +1,44 @@
+2006-05-23 Daniel Jacobowitz <dan@codesourcery.com>
+
+ * gdb/remote.c: Include "gdb/fileio.h".
+ (PACKET_Fopen, PACKET_Fread, PACKET_Fwrite, PACKET_Fclose): New
+ enums.
+ (remote_escape_output, remote_unescape_input): New functions.
+ (remote_write_bytes): Use remote_escape_output.
+ (readchar): Do not mask off the high bit.
+ (read_frame): Print binary packets correctly.
+ (getpkt_sane): Likewise. Return the number of bytes read
+ or -1.
+ (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_write, remote_hostio_read)
+ (remote_hostio_close, remote_fileio_errno_to_host)
+ (remote_hostio_error, fclose_cleanup, remote_download_command)
+ (remote_upload_command): New functions.
+ (_initialize_remote): Register new packets and remote-upload
+ and remote-download commands.
+
+ * gdb/doc/gdb.texinfo (File Transfer): New section.
+ (Remote Configuration): Document Host I/O packet commands.
+ (Host I/O Packets): New section.
+ (Packets): Mention overloading of F prefix.
+
+ * gdb/gdbserver/Makefile.in (OBS): Add hostio.o.
+ (hostio.o): New rule.
+ * gdb/gdbserver/hostio.c: New file.
+ * gdb/gdbserver/remote-utils.c (putpkt_binary): New function,
+ broken out from putpkt.
+ (putpkt): Use it.
+ (readchar): Do not mask the high bits. Correct the buffer
+ type.
+ * gdb/gdbserver/server.c (main): Leave space for a trailing
+ zero in the packet buffer. Save the length of the
+ received packet. Handle Host I/O requests. Optionally call
+ putpkt_binary.
+ * gdb/gdbserver/server.h (handle_f_hostio, putpkt_binary): New
+ prototypes.
+
2006-05-10 Daniel Jacobowitz <dan@codesourcery.com>
* gdb/frame.c (get_prev_frame): Move check for pc == 0 ...
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 12f6b3932eb..6dbb66c010e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -12131,6 +12131,7 @@ want to try.
@menu
* Connecting:: Connecting to a remote target
+* File Transfer:: Sending files to a remote system
* Server:: Using the gdbserver program
* NetWare:: Using the gdbserve.nlm program
* Remote configuration:: Remote configuration
@@ -12248,6 +12249,29 @@ can add new commands that only the external monitor will understand
and implement.
@end table
+@node File Transfer
+@section Sending files to a remote system
+
+Some remote targets offer the ability to transfer files over the same
+connection used to communicate with @value{GDBN}. For targets
+accessible through other means, e.g.@: a GNU/Linux system with a
+network interface, this offers added convenience. For other
+targets, e.g.@: embedded devices with only a single serial port,
+this may be the only way to upload or download files.
+
+Not all remote targets support these commands.
+
+@table @code
+@item remote-download @var{hostfile} @var{targetfile}
+Copy @var{hostfile} from the host system (the machine running
+@value{GDBN}) to the path @var{targetfile} on the target system.
+
+@item remote-upload @var{targetfile} @var{hostfile}
+Copy @var{targetfile} from the target system to @var{hostfile}
+on the host system.
+
+@end table
+
@node Server
@section Using the @code{gdbserver} program
@@ -12625,6 +12649,21 @@ packet.
@kindex show remote get-thread-local-storage-address
Show the current setting of @samp{qGetTLSAddr} packet usage.
+@item set remote hostio-close-packet
+@itemx set remote hostio-open-packet
+@itemx set remote hostio-read-packet
+@itemx set remote hostio-write-packet
+@cindex remote host I/O packets
+Set use of the remote host I/O packets, used to upload and
+download files to a remote system. @xref{Host I/O Packets},
+for more details about these packets.
+
+@item show remote hostio-close-packet
+@itemx show remote hostio-open-packet
+@itemx show remote hostio-read-packet
+@itemx show remote hostio-write-packet
+Show the current settings of the remote host I/O packets.
+
@item set remote load-offsets
@kindex set remote load-offsets
@cindex load offsets of remote targets
@@ -22406,6 +22445,7 @@ Show the current setting of the target wait timeout.
* General Query Packets::
* Register Packet Format::
* Tracepoint Packets::
+* Host I/O Packets::
* Interrupts::
* Examples::
* File-I/O remote protocol extension::
@@ -22636,6 +22676,9 @@ A reply from @value{GDBN} to an @samp{F} packet sent by the target.
This is part of the File-I/O protocol extension. @xref{File-I/O
remote protocol extension}, for the specification.
+The @samp{F} prefix is also used for @xref{Host I/O Packets}. This use
+is ambiguous and should be corrected.
+
@item g
@anchor{read registers packet}
@cindex @samp{g} packet
@@ -23702,6 +23745,95 @@ There is a trace experiment running.
@end table
+@node Host I/O Packets
+@section Host I/O Packets
+@cindex Host I/O (remote protocol)
+@cindex File Transfer (remote protocol)
+
+The @dfn{Host I/O} packets allow @value{GDBN} to perform I/O
+operations on the far side of a remote link. For example, Host I/O is
+used to upload and download files to a remote target with its own
+filesystem. The protocol has some common features with the File-I/O
+protocol, e.g.@: all constants and data structures are encoded in the
+same way. However, the packets are structured differently, because
+requests are initiated by @value{GDBN}, and the target's memory is not
+involved. @xref{File-I/O remote protocol extension}, for more details
+on the target-initiated protocol.
+
+The Host I/O request packets all encode a single operation along with
+its arguments. They have this format:
+
+@table @samp
+
+@item F @var{operation}, @var{parameter}@dots{}
+@var{operation} is the name of the particular request; the target
+should compare the entire packet name up to the comma when checking
+for a supported operation. The format of @var{parameter} depends on
+the operation. Numbers are always passed in hexadecimal, and strings
+(e.g.@: filenames) are encoded as a series of hexadecimal bytes.
+The last argument to a system call may be a binary buffer, which will
+be escaped in the same way as the X packet (@pxref{X packet}).
+
+@end table
+
+The valid responses to Host I/O packets are:
+
+@table @samp
+
+@item F @var{result} [, @var{errno}] [; @var{attachment}]
+@var{result} is the integer value returned by this operation, usually
+non-negative for success and -1 for errors. If an error has occured,
+@var{errno} will be included in the result. @var{errno} will have a
+value defined by the File-I/O protocol (@pxref{Errno values}). For
+operations which return data, @var{attachment} will be provided a
+binary buffer. Binary buffers in response packets should be encoded
+in the same way as the @samp{X} packet (@pxref{X packet}), with one
+exception: the @samp{*} character must also be escaped, or it will be
+interpreted as a run-length encoding marker by the remote protocol.
+See the individual packet documentation for the interpretation of
+@var{result} and @var{attachment}.
+
+@item
+An empty response indicates that this operation is not recognized.
+
+@end table
+
+These are the supported Host I/O operations:
+
+@table @samp
+@item Fopen, @var{pathname}, @var{flags}, @var{mode}
+Open a file at @var{pathname} and return a file descriptor for it, or
+return -1 if an error occurs. @var{pathname} is a string,
+@var{flags} is an integer indicating a mask of open flags
+(@pxref{Open flags}), and @var{mode} is an integer indicating a mask
+of mode bits to use if the file is created (@pxref{mode_t values}).
+
+@item Fclose, @var{fd}
+Close the open file corresponding to @var{fd} and return 0, or
+-1 if an error occurs.
+
+@item Fread, @var{fd}, @var{count}
+Read data from the open file corresponding to @var{fd}. Up to
+@var{count} bytes will be read from the file. The target may
+read fewer bytes; common reasons include packet size limits
+and an end-of-file condition. The number of bytes read is
+returned. Zero should only be returned for a successful
+read at the end of the file, or if @var{count} was zero.
+
+The data read should be returned as a binary attachment on success,
+even if zero bytes were read. The return value is the number of
+target bytes read; the binary attachment may be longer if some
+characters were escaped.
+
+@item Fwrite, @var{fd}, @var{data}
+Write @var{data} (a binary buffer) to the open file corresponding
+to @var{fd}. Unlike many @code{write} system calls, there is no
+separate @var{count} argument; the length of @var{data} in the
+packet is used. @samp{Fwrite} returns the number of bytes written,
+which may be shorter than the length of @var{data}, or -1 if an
+error occurred.
+@end table
+
@node Interrupts
@section Interrupts
@cindex interrupts (remote protocol)
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index d13e465bc2c..40622c27bcf 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -136,7 +136,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 \
$(DEPFILES)
GDBSERVER_LIBS = @GDBSERVER_LIBS@
@@ -249,6 +249,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..d1f13615163
--- /dev/null
+++ b/gdb/gdbserver/hostio.c
@@ -0,0 +1,480 @@
+/* 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_fopen (char *own_buf)
+{
+ char filename[PATH_MAX];
+ char *p;
+ int fileio_flags, mode, flags, fd;
+ struct fd_list *new_fd;
+
+ p = own_buf + strlen ("Fopen,");
+
+ 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_fread (char *own_buf, int *new_packet_len)
+{
+ int fd, ret, len, bytes_sent;
+ char *p, *data;
+
+ p = own_buf + strlen ("Fread,");
+
+ if (require_int (&p, &fd)
+ || require_comma (&p)
+ || require_valid_fd (fd)
+ || require_int (&p, &len)
+ || require_end (p))
+ {
+ hostio_packet_error (own_buf);
+ return;
+ }
+
+ data = malloc (len);
+ ret = read (fd, data, len);
+
+ 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 all the data could not fit in the reply, back up the file
+ pointer for the next read. */
+ if (bytes_sent < ret)
+ lseek (fd, bytes_sent - ret, SEEK_CUR);
+
+ free (data);
+}
+
+static void
+handle_fwrite (char *own_buf, int packet_len)
+{
+ int fd, ret, len;
+ char *p, *data;
+
+ p = own_buf + strlen ("Fwrite,");
+
+ if (require_int (&p, &fd)
+ || require_comma (&p)
+ || require_valid_fd (fd)
+ || require_data (p, packet_len - (p - own_buf), &data, &len))
+ {
+ hostio_packet_error (own_buf);
+ return;
+ }
+
+ ret = write (fd, data, len);
+
+ if (ret == -1)
+ {
+ hostio_error (own_buf, errno);
+ free (data);
+ return;
+ }
+
+ hostio_reply (own_buf, ret);
+ free (data);
+}
+
+static void
+handle_fclose (char *own_buf)
+{
+ int fd, ret;
+ char *p;
+ struct fd_list **open_fd_p, *old_fd;
+
+ p = own_buf + strlen ("Fclose,");
+
+ 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);
+}
+
+/* Handle all the 'F' file transfer packets. */
+
+int
+handle_f_hostio (char *own_buf, int packet_len, int *new_packet_len)
+{
+ if (strncmp (own_buf, "Fopen,", 6) == 0)
+ handle_fopen (own_buf);
+ else if (strncmp (own_buf, "Fread,", 6) == 0)
+ handle_fread (own_buf, new_packet_len);
+ else if (strncmp (own_buf, "Fwrite,", 7) == 0)
+ handle_fwrite (own_buf, packet_len);
+ else if (strncmp (own_buf, "Fclose,", 7) == 0)
+ handle_fclose (own_buf);
+ else
+ return 0;
+
+ return 1;
+}
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index f0e0d4b7195..844dad16182 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -273,16 +273,16 @@ hexify (char *hex, const char *bin, int count)
}
/* Send a packet to the remote machine, with error checking.
- The data of the packet is in BUF. Returns >= 0 on success, -1 otherwise. */
+ The data of the packet is in BUF, and the length of the
+ packet is in CNT. Returns >= 0 on success, -1 otherwise. */
int
-putpkt (char *buf)
+putpkt_binary (char *buf, int cnt)
{
int i;
unsigned char csum = 0;
char *buf2;
char buf3[1];
- int cnt = strlen (buf);
char *p;
buf2 = malloc (PBUFSIZ);
@@ -349,6 +349,16 @@ putpkt (char *buf)
return 1; /* Success! */
}
+/* Send a packet to the remote machine, with error checking. The data
+ of the packet is in BUF, and the packet should be a NUL-terminated
+ string. Returns >= 0 on success, -1 otherwise. */
+
+int
+putpkt (char *buf)
+{
+ return putpkt_binary (buf, strlen (buf));
+}
+
/* Come here when we get an input interrupt from the remote side. This
interrupt should only be active while we are waiting for the child to do
something. About the only thing that should come through is a ^C, which
@@ -441,12 +451,12 @@ disable_async_io (void)
static int
readchar (void)
{
- static char buf[BUFSIZ];
+ static unsigned char buf[BUFSIZ];
static int bufcnt = 0;
- static char *bufp;
+ static unsigned char *bufp;
if (bufcnt-- > 0)
- return *bufp++ & 0x7f;
+ return *bufp++;
bufcnt = read (remote_desc, buf, sizeof (buf));
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index b9de99a5982..ffc94e7063f 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -717,7 +717,7 @@ main (int argc, char *argv[])
initialize_low ();
- own_buf = malloc (PBUFSIZ);
+ own_buf = malloc (PBUFSIZ + 1);
mem_buf = malloc (PBUFSIZ);
if (pid == 0)
@@ -748,9 +748,15 @@ main (int argc, char *argv[])
restart:
setjmp (toplevel);
- while (!exit_requested && getpkt (own_buf) > 0)
+ while (!exit_requested)
{
unsigned char sig;
+ int new_packet_len = -1;
+ int packet_len;
+
+ packet_len = getpkt (own_buf);
+ if (packet_len <= 0)
+ break;
if (running == 0)
{
@@ -1056,6 +1062,16 @@ main (int argc, char *argv[])
/* Extended (long) request. */
handle_v_requests (own_buf, &status, &signal);
break;
+ case 'F':
+ if (handle_f_hostio (own_buf, packet_len, &new_packet_len))
+ break;
+
+ /* It is a request we don't understand. Respond with an
+ empty packet so that gdb knows that we don't support this
+ request. */
+ own_buf[0] = '\0';
+ 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
@@ -1065,7 +1081,10 @@ main (int argc, char *argv[])
}
done_packet:
- putpkt (own_buf);
+ if (new_packet_len != -1)
+ putpkt_binary (own_buf, new_packet_len);
+ else
+ putpkt (own_buf);
if (running && (status == 'W' || status == 'X'))
{
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 88d35d04136..49b8e0fb0c6 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -127,9 +127,13 @@ extern int server_waiting;
extern jmp_buf toplevel;
+/* Functions from hostio.c. */
+extern int handle_f_hostio (char *, int, int *);
+
/* Functions from remote-utils.c */
int putpkt (char *buf);
+int putpkt_binary (char *buf, int len);
int getpkt (char *buf);
void remote_open (char *name);
void remote_close (void);
diff --git a/gdb/remote.c b/gdb/remote.c
index 2e8b5ecd515..53471207ee2 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -62,6 +62,8 @@
#include "remote-fileio.h"
+#include "gdb/fileio.h"
+
/* Prototypes for local functions. */
static void cleanup_sigint_signal_handler (void *dummy);
static void initialize_sigint_signal_handler (void);
@@ -823,6 +825,10 @@ enum {
PACKET_qPart_auxv,
PACKET_qPart_features,
PACKET_qGetTLSAddr,
+ PACKET_Fopen,
+ PACKET_Fread,
+ PACKET_Fwrite,
+ PACKET_Fclose,
PACKET_vAttach,
PACKET_vRun,
PACKET_qfDllInfo,
@@ -4086,6 +4092,87 @@ check_binary_download (CORE_ADDR addr)
}
}
+/* Convert BUFFER, binary data at least LEN bytes long, into escaped
+ binary data in OUT_BUF. Set *OUT_LEN to the length of the data
+ encoded in OUT_BUF, and return the number of bytes in OUT_BUF
+ (which may be more than *OUT_LEN due to escape characters). The
+ total number of bytes in the output buffer will be at most
+ OUT_MAXLEN. */
+
+static int
+remote_escape_output (const gdb_byte *buffer, int len,
+ gdb_byte *out_buf, int *out_len,
+ int out_maxlen)
+{
+ int input_index, output_index;
+
+ output_index = 0;
+ for (input_index = 0; input_index < len; input_index++)
+ {
+ gdb_byte b = buffer[input_index];
+
+ if (b == '$' || b == '#' || b == '}')
+ {
+ /* These must be escaped. */
+ if (output_index + 2 > out_maxlen)
+ break;
+ out_buf[output_index++] = '}';
+ out_buf[output_index++] = b ^ 0x20;
+ }
+ else
+ {
+ if (output_index + 1 > out_maxlen)
+ break;
+ out_buf[output_index++] = b;
+ }
+ }
+
+ *out_len = input_index;
+ return output_index;
+}
+
+/* Convert BUFFER, escaped data LEN bytes long, into binary data
+ in OUT_BUF. Return the number of bytes written to OUT_BUF.
+ Raise an error if the total number of bytes exceeds OUT_MAXLEN.
+
+ This function reverses remote_escape_output. It allows more
+ escaped characters than that function does, in particular because
+ '*' must be escaped to avoid the run-length encoding processing
+ in reading packets. */
+
+static int
+remote_unescape_input (const gdb_byte *buffer, int len,
+ gdb_byte *out_buf, int out_maxlen)
+{
+ int input_index, output_index;
+ int escaped;
+
+ output_index = 0;
+ escaped = 0;
+ for (input_index = 0; input_index < len; input_index++)
+ {
+ gdb_byte b = buffer[input_index];
+
+ if (output_index + 1 > out_maxlen)
+ error (_("Received too much data from the target."));
+
+ if (escaped)
+ {
+ out_buf[output_index++] = b ^ 0x20;
+ escaped = 0;
+ }
+ else if (b == '}')
+ escaped = 1;
+ else
+ out_buf[output_index++] = b;
+ }
+
+ if (escaped)
+ error (_("Unmatched escape character in target response."));
+
+ return output_index;
+}
+
/* Write memory data directly to the remote machine.
This does not inform the data cache; the data cache uses this.
MEMADDR is the address in the remote memory space.
@@ -4179,24 +4266,7 @@ remote_write_bytes (CORE_ADDR memaddr, gdb_byte *myaddr, int len)
/* Binary mode. Send target system values byte by byte, in
increasing byte addresses. Only escape certain critical
characters. */
- for (nr_bytes = 0;
- (nr_bytes < todo) && (p - payload_start) < payload_size;
- nr_bytes++)
- {
- switch (myaddr[nr_bytes] & 0xff)
- {
- case '$':
- case '#':
- case 0x7d:
- /* These must be escaped. */
- *p++ = 0x7d;
- *p++ = (myaddr[nr_bytes] & 0xff) ^ 0x20;
- break;
- default:
- *p++ = myaddr[nr_bytes] & 0xff;
- break;
- }
- }
+ p += remote_escape_output (myaddr, todo, p, &nr_bytes, payload_size);
if (nr_bytes < todo)
{
/* Escape chars have filled up the buffer prematurely,
@@ -4358,8 +4428,7 @@ remote_files_info (struct target_ops *ignore)
/* Stuff for dealing with the packets which are part of this protocol.
See comment at top of file for details. */
-/* Read a single character from the remote end, masking it down to 7
- bits. */
+/* Read a single character from the remote end. */
static int
readchar (int timeout)
@@ -4369,7 +4438,7 @@ readchar (int timeout)
ch = serial_readchar (remote_desc, timeout);
if (ch >= 0)
- return (ch & 0x7f);
+ return ch;
switch ((enum serial_rc) ch)
{
@@ -4654,7 +4723,7 @@ read_frame (char **buf_p,
fprintf_filtered (gdb_stdlog,
"Bad checksum, sentsum=0x%x, csum=0x%x, buf=",
pktcsum, csum);
- fputs_filtered (buf, gdb_stdlog);
+ fputstrn_unfiltered (buf, bc, 0, gdb_stdlog);
fputs_filtered ("\n", gdb_stdlog);
}
/* Number of characters in buffer ignoring trailing
@@ -4733,7 +4802,8 @@ getpkt (char **buf,
rather than timing out; this is used (in synchronous mode) to wait
for a target that is is executing user code to stop. If FOREVER ==
0, this function is allowed to time out gracefully and return an
- indication of this to the caller. */
+ indication of this (-1) to the caller. Otherwise return the number
+ of bytes read. */
static int
getpkt_sane (char **buf, long *sizeof_buf, int forever)
{
@@ -4794,11 +4864,11 @@ getpkt_sane (char **buf, long *sizeof_buf, int forever)
if (remote_debug)
{
fprintf_unfiltered (gdb_stdlog, "Packet received: ");
- fputstr_unfiltered (*buf, 0, gdb_stdlog);
+ fputstrn_unfiltered (*buf, val, 0, gdb_stdlog);
fprintf_unfiltered (gdb_stdlog, "\n");
}
serial_write (remote_desc, "+", 1);
- return 0;
+ return val;
}
/* Try the whole thing again. */
@@ -4811,7 +4881,7 @@ getpkt_sane (char **buf, long *sizeof_buf, int forever)
printf_unfiltered (_("Ignoring packet error, continuing...\n"));
serial_write (remote_desc, "+", 1);
- return 1;
+ return -1;
}
static void
@@ -5957,6 +6027,501 @@ remote_get_thread_local_address (ptid_t ptid, CORE_ADDR lm, CORE_ADDR offset)
return 0;
}
+/* 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;
+
+ *retcode = strtol (&buffer[1], &p, 16);
+ if (p == &buffer[1])
+ return -1;
+
+ /* Check for ",errno". */
+ if (*p == ',')
+ {
+ *remote_errno = strtol (p + 1, &p2, 16);
+ if (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 = rs->remote_packet_size - 1;
+
+ remote_buffer_add_string (&p, &left, "Fopen,");
+
+ 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_Fopen,
+ 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_write (int fd, const gdb_byte *write_buf, int len,
+ int *remote_errno)
+{
+ struct remote_state *rs = get_remote_state ();
+ char *p = rs->buf;
+ int left = rs->remote_packet_size;
+ int out_len;
+
+ remote_buffer_add_string (&p, &left, "Fwrite,");
+
+ remote_buffer_add_int (&p, &left, fd);
+ remote_buffer_add_string (&p, &left, ",");
+
+ p += remote_escape_output (write_buf, len, p, &out_len,
+ rs->remote_packet_size - strlen (p));
+
+ return remote_hostio_send_command (p - rs->buf, PACKET_Fwrite,
+ 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_read (int fd, gdb_byte *read_buf, int len,
+ int *remote_errno)
+{
+ struct remote_state *rs = get_remote_state ();
+ char *p = rs->buf;
+ char *attachment;
+ int left = rs->remote_packet_size;
+ int ret, attachment_len;
+ int read_len;
+
+ remote_buffer_add_string (&p, &left, "Fread,");
+
+ remote_buffer_add_int (&p, &left, fd);
+ remote_buffer_add_string (&p, &left, ",");
+
+ remote_buffer_add_int (&p, &left, len);
+
+ ret = remote_hostio_send_command (p - rs->buf, PACKET_Fwrite,
+ 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 = rs->remote_packet_size - 1;
+
+ remote_buffer_add_string (&p, &left, "Fclose,");
+
+ remote_buffer_add_int (&p, &left, fd);
+
+ return remote_hostio_send_command (p - rs->buf, PACKET_Fclose,
+ remote_errno, NULL, NULL);
+}
+
+static int
+remote_fileio_errno_to_host (int error)
+{
+ switch (error)
+ {
+ 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 error)
+{
+ int host_error = remote_fileio_errno_to_host (error);
+
+ if (host_error == -1)
+ error (_("Unknown remote I/O error %d"), error);
+ else
+ error (_("Remote I/O error: %s"), safe_strerror (host_error));
+}
+
+static void
+fclose_cleanup (void *file)
+{
+ fclose (file);
+}
+
+static void
+remote_download_command (char *args, int from_tty)
+{
+ struct cleanup *cleanups;
+ char **argv;
+ int retcode, fd, remote_errno, bytes;
+ FILE *file;
+ gdb_byte *buffer;
+ int bytes_in_buffer;
+
+ if (!remote_desc)
+ error (_("command can only be used with remote target"));
+
+ argv = buildargv (args);
+ if (argv == NULL)
+ nomem (0);
+ cleanups = make_cleanup_freeargv (argv);
+ if (argv[0] == NULL || argv[1] == NULL || argv[2] != NULL)
+ error (_("Invalid parameters to remote-download"));
+
+ file = fopen (argv[0], "rb");
+ if (file == NULL)
+ perror_with_name (argv[0]);
+ make_cleanup (fclose_cleanup, file);
+
+ fd = remote_hostio_open (argv[1], FILEIO_O_WRONLY | FILEIO_O_CREAT, 0700,
+ &remote_errno);
+ if (fd == -1)
+ remote_hostio_error (remote_errno);
+
+ /* FIXME: Adjust to the requested packet and memory transfer size,
+ batch I/O better. */
+ buffer = xmalloc (1024);
+ make_cleanup (xfree, buffer);
+
+ bytes_in_buffer = 0;
+ while (1)
+ {
+ bytes = fread (buffer, 1, 1024 - bytes_in_buffer, file);
+ if (bytes == 0)
+ {
+ if (ferror (file))
+ error (_("Error reading %s."), argv[0]);
+ else
+ /* EOF */
+ break;
+ }
+
+ bytes += bytes_in_buffer;
+ bytes_in_buffer = 0;
+
+ retcode = remote_hostio_write (fd, buffer, bytes, &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);
+ }
+ }
+
+ if (remote_hostio_close (fd, &remote_errno))
+ remote_hostio_error (remote_errno);
+
+ do_cleanups (cleanups);
+}
+
+static void
+remote_upload_command (char *args, int from_tty)
+{
+ struct cleanup *cleanups;
+ char **argv;
+ int retcode, fd, remote_errno, bytes;
+ FILE *file;
+ gdb_byte *buffer;
+
+ if (!remote_desc)
+ error (_("command can only be used with remote target"));
+
+ argv = buildargv (args);
+ if (argv == NULL)
+ nomem (0);
+ cleanups = make_cleanup_freeargv (argv);
+ if (argv[0] == NULL || argv[1] == NULL || argv[2] != NULL)
+ error (_("Invalid parameters to remote-upload"));
+
+ fd = remote_hostio_open (argv[0], FILEIO_O_RDONLY, 0, &remote_errno);
+ if (fd == -1)
+ remote_hostio_error (remote_errno);
+
+ file = fopen (argv[1], "wb");
+ if (file == NULL)
+ perror_with_name (argv[1]);
+ make_cleanup (fclose_cleanup, file);
+
+ /* FIXME: Adjust to the requested packet and memory transfer size,
+ batch I/O better. */
+ buffer = xmalloc (1024);
+ make_cleanup (xfree, buffer);
+
+ while (1)
+ {
+ bytes = remote_hostio_read (fd, buffer, 1024, &remote_errno);
+ if (bytes == 0)
+ /* Success, but no bytes, means end-of-file. */
+ break;
+ if (bytes == -1)
+ remote_hostio_error (remote_errno);
+
+ bytes = fwrite (buffer, 1, bytes, file);
+ if (bytes == 0)
+ perror_with_name (argv[1]);
+ }
+
+ if (remote_hostio_close (fd, &remote_errno))
+ remote_hostio_error (remote_errno);
+
+ do_cleanups (cleanups);
+}
+
static void
init_remote_ops (void)
{
@@ -6392,6 +6957,34 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_qfDllInfo],
"qfDllInfo", "dll-info", 0, 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_Fopen],
+ "Fopen", "hostio-open",
+ set_remote_protocol_packet_cmd,
+ show_remote_protocol_packet_cmd,
+ &remote_set_cmdlist, &remote_show_cmdlist,
+ 0);
+
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_Fread],
+ "Fread", "hostio-read",
+ set_remote_protocol_packet_cmd,
+ show_remote_protocol_packet_cmd,
+ &remote_set_cmdlist, &remote_show_cmdlist,
+ 0);
+
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_Fwrite],
+ "Fwrite", "hostio-write",
+ set_remote_protocol_packet_cmd,
+ show_remote_protocol_packet_cmd,
+ &remote_set_cmdlist, &remote_show_cmdlist,
+ 0);
+
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_Fclose],
+ "Fclose", "hostio-close",
+ set_remote_protocol_packet_cmd,
+ show_remote_protocol_packet_cmd,
+ &remote_set_cmdlist, &remote_show_cmdlist,
+ 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
@@ -6413,6 +7006,16 @@ Set the remote pathname for \"run\""), _("\
Show the remote pathname for \"run\""), NULL, NULL, NULL,
&remote_set_cmdlist, &remote_show_cmdlist);
+ add_cmd ("remote-download", class_support /* ??? */,
+ remote_download_command,
+ _("Copy a local file to the remote system."),
+ &cmdlist);
+
+ add_cmd ("remote-upload", class_support /* ??? */,
+ remote_upload_command,
+ _("Copy a remote file to the local system."),
+ &cmdlist);
+
/* Eventually initialize fileio. See fileio.c */
initialize_remote_fileio (remote_set_cmdlist, remote_show_cmdlist);
}