* 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.
This commit is contained in:
Daniel Jacobowitz
2007-11-30 21:50:19 +00:00
parent fba57f8f38
commit a6b151f187
19 changed files with 1828 additions and 7 deletions

View File

@ -1,3 +1,31 @@
2007-11-30 Daniel Jacobowitz <dan@codesourcery.com>
* 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.
2007-11-30 Vladimir Prus <vladimir@codesourcery.com>
* infrun.c (handle_inferior_event): Don't

View File

@ -184,7 +184,7 @@ SUBDIR_CLI_CFLAGS=
SUBDIR_MI_OBS = \
mi-out.o mi-console.o \
mi-cmds.o mi-cmd-env.o mi-cmd-var.o mi-cmd-break.o mi-cmd-stack.o \
mi-cmd-file.o mi-cmd-disas.o mi-symbol-cmds.o \
mi-cmd-file.o mi-cmd-disas.o mi-symbol-cmds.o mi-cmd-target.o \
mi-interp.o \
mi-main.o mi-parse.o mi-getopt.o mi-common.o
SUBDIR_MI_SRCS = \
@ -192,7 +192,7 @@ SUBDIR_MI_SRCS = \
mi/mi-cmds.c mi/mi-cmd-env.c \
mi/mi-cmd-var.c mi/mi-cmd-break.c mi/mi-cmd-stack.c \
mi/mi-cmd-file.c mi/mi-cmd-disas.c mi/mi-symbol-cmds.c \
mi/mi-interp.c \
mi/mi-cmd-target.c mi/mi-interp.c \
mi/mi-main.c mi/mi-parse.c mi/mi-getopt.c mi/mi-common.c
SUBDIR_MI_DEPS =
SUBDIR_MI_LDFLAGS=
@ -675,6 +675,7 @@ mep_desc_h = $(OPCODES_SRC)/mep-desc.h
mep_opc_h = $(OPCODES_SRC)/mep-opc.h
sh_opc_h = $(OPCODES_SRC)/sh-opc.h
gdb_callback_h = $(INCLUDE_DIR)/gdb/callback.h
gdb_fileio_h = $(INCLUDE_DIR)/gdb/fileio.h
gdb_sim_arm_h = $(INCLUDE_DIR)/gdb/sim-arm.h
gdb_sim_frv_h = $(INCLUDE_DIR)/gdb/sim-frv.h
gdb_sim_m32c_h = $(INCLUDE_DIR)/gdb/sim-m32c.h
@ -2602,7 +2603,8 @@ remote.o: remote.c $(defs_h) $(gdb_string_h) $(inferior_h) $(bfd_h) \
$(gdb_stabs_h) $(gdbthread_h) $(remote_h) $(regcache_h) $(value_h) \
$(gdb_assert_h) $(event_loop_h) $(event_top_h) $(inf_loop_h) \
$(serial_h) $(gdbcore_h) $(remote_fileio_h) $(solib_h) $(observer_h) \
$(cli_decode_h) $(cli_setshow_h) $(memory_map_h) $(target_descriptions_h)
$(cli_decode_h) $(cli_setshow_h) $(memory_map_h) \
$(target_descriptions_h) $(gdb_fileio_h)
remote-fileio.o: remote-fileio.c $(defs_h) $(gdb_string_h) $(gdbcmd_h) \
$(remote_h) $(gdb_fileio_h) $(gdb_wait_h) $(gdb_stat_h) \
$(exceptions_h) $(remote_fileio_h)
@ -3186,6 +3188,9 @@ mi-cmd-stack.o: $(srcdir)/mi/mi-cmd-stack.c $(defs_h) $(target_h) $(frame_h) \
$(value_h) $(mi_cmds_h) $(ui_out_h) $(symtab_h) $(block_h) \
$(stack_h) $(dictionary_h) $(gdb_string_h)
$(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/mi/mi-cmd-stack.c
mi-cmd-target.o: $(srcdir)/mi/mi-cmd-target.c $(defs_h) $(mi_cmds_h) \
$(mi_getopt_h) $(remote_h)
$(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/mi/mi-cmd-target.c
mi-cmd-var.o: $(srcdir)/mi/mi-cmd-var.c $(defs_h) $(mi_cmds_h) $(ui_out_h) \
$(mi_out_h) $(varobj_h) $(value_h) $(gdb_string_h)
$(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/mi/mi-cmd-var.c

View File

@ -28,6 +28,9 @@ registers on PowerPC targets.
* The GDB remote stub, gdbserver, now supports thread debugging on GNU/Linux
targets even when the libthread_db library is not available.
* The GDB remote stub, gdbserver, now supports the new file transfer
commands (remote put, remote get, and remote delete).
* hppa*64*-*-hpux11* target broken
The debugger is unable to start a program and fails with the following
error: "Error trying to get information about dynamic linker".
@ -37,6 +40,28 @@ targets even when the libthread_db library is not available.
building a single GDB executable that supports multiple remote
target architectures.
* New commands
remote put
remote get
remote delete
Transfer files to and from a remote target, and delete remote files.
* New MI commands
-target-file-put
-target-file-get
-target-file-delete
Transfer files to and from a remote target, and delete remote files.
* New remote packets
vFile:open:
vFile:close:
vFile:pread:
vFile:pwrite:
vFile:unlink:
Open, close, read, write, and delete files on the remote system.
*** Changes in GDB 6.7

View File

@ -1,3 +1,13 @@
2007-11-30 Daniel Jacobowitz <dan@codesourcery.com>
* 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.
2007-11-17 Eli Zaretskii <eliz@gnu.org>
* gdb.texinfo (Set Breaks, Disabling): Clarify behavior of

View File

@ -2480,7 +2480,7 @@ the child process just like any other process which you attached to.
On some systems, @value{GDBN} provides support for debugging programs that
create additional processes using the @code{fork} or @code{vfork} functions.
Currently, the only platforms with this feature are HP-UX (11.x and later
only?) and GNU/Linux (kernel version 2.5.60 and later).
only?) and @sc{gnu}/Linux (kernel version 2.5.60 and later).
By default, when a program forks, @value{GDBN} will continue to debug
the parent process and the child process will run unimpeded.
@ -12670,6 +12670,7 @@ configuration of @value{GDBN}; use @code{help target} to list them.
@menu
* Connecting:: Connecting to a remote target
* File Transfer:: Sending files to a remote system
* Server:: Using the gdbserver program
* Remote Configuration:: Remote configuration
* Remote Stub:: Implementing a remote stub
@ -12817,6 +12818,38 @@ 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
@cindex remote target, file transfer
@cindex file transfer
@cindex sending files to remote systems
Some remote targets offer the ability to transfer files over the same
connection used to communicate with @value{GDBN}. This is convenient
for targets accessible through other means, e.g.@: @sc{gnu}/Linux systems
running @code{gdbserver} over a network interface. 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
@kindex remote put
@item remote put @var{hostfile} @var{targetfile}
Copy file @var{hostfile} from the host system (the machine running
@value{GDBN}) to @var{targetfile} on the target system.
@kindex remote get
@item remote get @var{targetfile} @var{hostfile}
Copy file @var{targetfile} from the target system to @var{hostfile}
on the host system.
@kindex remote delete
@item remote delete @var{targetfile}
Delete @var{targetfile} from the target system.
@end table
@node Server
@section Using the @code{gdbserver} Program
@ -13153,6 +13186,25 @@ are:
@tab @code{QPassSignals}
@tab @code{handle @var{signal}}
@item @code{hostio-close-packet}
@tab @code{vFile:close}
@tab @code{remote get}, @code{remote put}
@item @code{hostio-open-packet}
@tab @code{vFile:open}
@tab @code{remote get}, @code{remote put}
@item @code{hostio-pread-packet}
@tab @code{vFile:pread}
@tab @code{remote get}, @code{remote put}
@item @code{hostio-pwrite-packet}
@tab @code{vFile:pwrite}
@tab @code{remote get}, @code{remote put}
@item @code{hostio-unlink-packet}
@tab @code{vFile:unlink}
@tab @code{remote delete}
@end multitable
@node Remote Stub
@ -17349,6 +17401,7 @@ may repeat one or more times.
* GDB/MI Signal Handling Commands::
@end ignore
* GDB/MI Target Manipulation::
* GDB/MI File Transfer Commands::
* GDB/MI Miscellaneous Commands::
@end menu
@ -21295,6 +21348,88 @@ The corresponding @value{GDBN} command is @samp{target}.
(gdb)
@end smallexample
@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@node GDB/MI File Transfer Commands
@section @sc{gdb/mi} File Transfer Commands
@subheading The @code{-target-file-put} Command
@findex -target-file-put
@subsubheading Synopsis
@smallexample
-target-file-put @var{hostfile} @var{targetfile}
@end smallexample
Copy file @var{hostfile} from the host system (the machine running
@value{GDBN}) to @var{targetfile} on the target system.
@subsubheading @value{GDBN} Command
The corresponding @value{GDBN} command is @samp{remote put}.
@subsubheading Example
@smallexample
(gdb)
-target-file-put localfile remotefile
^done
(gdb)
@end smallexample
@subheading The @code{-target-file-put} Command
@findex -target-file-get
@subsubheading Synopsis
@smallexample
-target-file-get @var{targetfile} @var{hostfile}
@end smallexample
Copy file @var{targetfile} from the target system to @var{hostfile}
on the host system.
@subsubheading @value{GDBN} Command
The corresponding @value{GDBN} command is @samp{remote get}.
@subsubheading Example
@smallexample
(gdb)
-target-file-get remotefile localfile
^done
(gdb)
@end smallexample
@subheading The @code{-target-file-delete} Command
@findex -target-file-delete
@subsubheading Synopsis
@smallexample
-target-file-delete @var{targetfile}
@end smallexample
Delete @var{targetfile} from the target system.
@subsubheading @value{GDBN} Command
The corresponding @value{GDBN} command is @samp{remote delete}.
@subsubheading Example
@smallexample
(gdb)
-target-file-delete remotefile
^done
(gdb)
@end smallexample
@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@node GDB/MI Miscellaneous Commands
@section Miscellaneous @sc{gdb/mi} Commands
@ -22920,6 +23055,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::
@ -23423,6 +23559,11 @@ command in the @samp{vCont} packet.
The @samp{vCont} packet is not supported.
@end table
@item vFile:@var{operation}:@var{parameter}@dots{}
@cindex @samp{vFile} packet
Perform a file operation on the target system. For details,
see @ref{Host I/O Packets}.
@item vFlashErase:@var{addr},@var{length}
@cindex @samp{vFlashErase} packet
Direct the stub to erase @var{length} bytes of flash starting at
@ -24572,6 +24713,104 @@ 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. Host I/O uses the same constant values and data structure
layout as the target-initiated File-I/O protocol. However, the
Host I/O packets are structured differently. The target-initiated
protocol relies on target memory to store parameters and buffers.
Host I/O 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 vFile:@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 second colon when checking
for a supported operation. The format of @var{parameter} depends on
the operation. Numbers are always passed in hexadecimal. Negative
numbers have an explicit minus sign (i.e.@: two's complement is not
used). Strings (e.g.@: filenames) are encoded as a series of
hexadecimal bytes. The last argument to a system call may be a
buffer of escaped binary data (@pxref{Binary Data}).
@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} supplies the data as a
binary buffer. Binary buffers in response packets are escaped in the
normal way (@pxref{Binary Data}). 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 vFile:open: @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}).
@xref{open} for details of the open flags and mode values.
@item vFile:close: @var{fd}
Close the open file corresponding to @var{fd} and return 0, or
-1 if an error occurs.
@item vFile:pread: @var{fd}, @var{count}, @var{offset}
Read data from the open file corresponding to @var{fd}. Up to
@var{count} bytes will be read from the file, starting at @var{offset}
relative to the start of 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.
If zero bytes were read, the response should include an empty binary
attachment (i.e.@: a trailing semicolon). The return value is the
number of target bytes read; the binary attachment may be longer if
some characters were escaped.
@item vFile:pwrite: @var{fd}, @var{offset}, @var{data}
Write @var{data} (a binary buffer) to the open file corresponding
to @var{fd}. Start the write at @var{offset} from the start of the
file. Unlike many @code{write} system calls, there is no
separate @var{count} argument; the length of @var{data} in the
packet is used. @samp{vFile:write} returns the number of bytes written,
which may be shorter than the length of @var{data}, or -1 if an
error occurred.
@item vFile:unlink: @var{pathname}
Delete the file at @var{pathname} on the target. Return 0,
or -1 if an error occurs. @var{pathname} is a string.
@end table
@node Interrupts
@section Interrupts
@cindex interrupts (remote protocol)

View File

@ -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>.

View File

@ -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)

517
gdb/gdbserver/hostio.c Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -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;

100
gdb/mi/mi-cmd-target.c Normal file
View File

@ -0,0 +1,100 @@
/* MI Command Set - target commands.
Copyright (C) 2007 Free Software Foundation, Inc.
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 3 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, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "mi-cmds.h"
#include "mi-getopt.h"
#include "remote.h"
/* Get a file from the target. */
enum mi_cmd_result
mi_cmd_target_file_get (char *command, char **argv, int argc)
{
int optind = 0;
char *optarg;
const char *remote_file, *local_file;
static struct mi_opt opts[] =
{
{ 0, 0, 0 }
};
static const char *prefix = "mi_cmd_target_file_get";
if (mi_getopt (prefix, argc, argv, opts, &optind, &optarg) != -1
|| optind != argc - 2)
error (_("mi_cmd_target_file_get: Usage: REMOTE_FILE LOCAL_FILE"));
remote_file = argv[optind];
local_file = argv[optind + 1];
remote_file_get (remote_file, local_file, 0);
return MI_CMD_DONE;
}
/* Send a file to the target. */
enum mi_cmd_result
mi_cmd_target_file_put (char *command, char **argv, int argc)
{
int optind = 0;
char *optarg;
const char *remote_file, *local_file;
static struct mi_opt opts[] =
{
{ 0, 0, 0 }
};
static const char *prefix = "mi_cmd_target_file_put";
if (mi_getopt (prefix, argc, argv, opts, &optind, &optarg) != -1
|| optind != argc - 2)
error (_("mi_cmd_target_file_put: Usage: LOCAL_FILE REMOTE_FILE"));
local_file = argv[optind];
remote_file = argv[optind + 1];
remote_file_put (local_file, remote_file, 0);
return MI_CMD_DONE;
}
/* Delete a file on the target. */
enum mi_cmd_result
mi_cmd_target_file_delete (char *command, char **argv, int argc)
{
int optind = 0;
char *optarg;
const char *remote_file, *local_file;
static struct mi_opt opts[] =
{
{ 0, 0, 0 }
};
static const char *prefix = "mi_cmd_target_file_delete";
if (mi_getopt (prefix, argc, argv, opts, &optind, &optarg) != -1
|| optind != argc - 1)
error (_("mi_cmd_target_file_delete: Usage: REMOTE_FILE"));
remote_file = argv[optind];
remote_file_delete (remote_file, 0);
return MI_CMD_DONE;
}

View File

@ -123,6 +123,9 @@ struct mi_cmd mi_cmds[] =
{ "target-disconnect", { "disconnect", 0 }, 0 },
{ "target-download", { NULL, 0 }, mi_cmd_target_download},
{ "target-exec-status", { NULL, 0 }, NULL, NULL },
{ "target-file-delete", { NULL, 0 }, NULL, mi_cmd_target_file_delete },
{ "target-file-get", { NULL, 0 }, NULL, mi_cmd_target_file_get },
{ "target-file-put", { NULL, 0 }, NULL, mi_cmd_target_file_put },
{ "target-list-available-targets", { NULL, 0 }, NULL, NULL },
{ "target-list-current-targets", { NULL, 0 }, NULL, NULL },
{ "target-list-parameters", { NULL, 0 }, NULL, NULL },

View File

@ -100,6 +100,9 @@ extern mi_cmd_argv_ftype mi_cmd_stack_list_locals;
extern mi_cmd_argv_ftype mi_cmd_stack_select_frame;
extern mi_cmd_argv_ftype mi_cmd_symbol_list_lines;
extern mi_cmd_args_ftype mi_cmd_target_download;
extern mi_cmd_argv_ftype mi_cmd_target_file_get;
extern mi_cmd_argv_ftype mi_cmd_target_file_put;
extern mi_cmd_argv_ftype mi_cmd_target_file_delete;
extern mi_cmd_args_ftype mi_cmd_target_select;
extern mi_cmd_argv_ftype mi_cmd_thread_list_ids;
extern mi_cmd_argv_ftype mi_cmd_thread_select;

View File

@ -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);
}

View File

@ -67,4 +67,10 @@ extern void (*deprecated_target_wait_loop_hook) (void);
void register_remote_g_packet_guess (struct gdbarch *gdbarch, int bytes,
const struct target_desc *tdesc);
void remote_file_put (const char *local_file, const char *remote_file,
int from_tty);
void remote_file_get (const char *remote_file, const char *local_file,
int from_tty);
void remote_file_delete (const char *remote_file, int from_tty);
#endif

View File

@ -1,3 +1,8 @@
2007-11-30 Daniel Jacobowitz <dan@codesourcery.com>
* gdb.server/file-transfer.exp, gdb.server/transfer.txt,
gdb.mi/mi-file-transfer.exp: New.
2007-11-25 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.dwarf2/dw2-ranges.S, gdb.dwarf2/dw2-ranges.exp: New files.

View File

@ -0,0 +1,99 @@
# This testcase is part of GDB, the GNU debugger.
# Copyright 2007 Free Software Foundation, Inc.
# 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 3 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, see <http://www.gnu.org/licenses/>.
# Test gdbserver monitor commands.
load_lib gdbserver-support.exp
load_lib mi-support.exp
set MIFLAGS "-i=mi"
if { [skip_gdbserver_tests] } {
return 0
}
set testfile "basics"
set srcfile ${testfile}.c
set binfile ${objdir}/${subdir}/${testfile}
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
untested mi-file-transfer.exp
return -1
}
gdb_exit
if [mi_gdb_start] {
continue
}
mi_delete_breakpoints
mi_gdb_reinitialize_dir $srcdir/$subdir
mi_gdb_file_cmd ${binfile}
proc mi_gdbserver_run { } {
mi_gdb_test "kill" ".*" ""
set res [gdbserver_spawn ""]
set protocol [lindex $res 0]
set gdbport [lindex $res 1]
if { [mi_gdb_target_cmd $protocol $gdbport] != 0 } {
return -1
}
return 0
}
proc test_file_transfer { filename description } {
mi_gdb_test "-target-file-put \"$filename\" \"down-server\"" \
"\\^done" "put $description"
mi_gdb_test "-target-file-get \"down-server\" \"up-server\"" \
"\\^done" "get $description"
if { ![is_remote target] } {
# If we can check the target copy of the file, do that too.
# This should catch symmetric errors in upload and download.
set result [remote_exec host "cmp -s $filename down-server"]
if { [lindex $result 0] == 0 } {
pass "compare intermediate $description"
} else {
fail "compare intermediate $description"
}
}
set result [remote_exec host "cmp -s $filename up-server"]
if { [lindex $result 0] == 0 } {
pass "compare $description"
} else {
fail "compare $description"
}
mi_gdb_test "-target-file-delete \"down-server\"" \
"\\^done" "deleted $description"
if { ![is_remote target] } {
if { ! [remote_file target exists down-server] } {
pass "verified deleted $description"
} else {
fail "verified deleted $description"
}
}
catch { file delete up-server }
}
mi_gdbserver_run
test_file_transfer "$binfile" "binary file"
mi_gdb_exit

View File

@ -0,0 +1,80 @@
# This testcase is part of GDB, the GNU debugger.
# Copyright 2007 Free Software Foundation, Inc.
# 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 3 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, see <http://www.gnu.org/licenses/>.
# Test gdbserver monitor commands.
load_lib gdbserver-support.exp
set testfile "server"
set srcfile ${testfile}.c
set binfile ${objdir}/${subdir}/${testfile}
if { [skip_gdbserver_tests] } {
return 0
}
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
untested file-transfer.exp
return -1
}
gdb_exit
gdb_start
gdb_load $binfile
gdb_reinitialize_dir $srcdir/$subdir
gdbserver_run ""
proc test_file_transfer { filename description } {
gdb_test "remote put \"$filename\" down-server" \
"Successfully sent .*" "put $description"
gdb_test "remote get down-server up-server" \
"Successfully fetched .*" "get $description"
if { ![is_remote target] } {
# If we can check the target copy of the file, do that too.
# This should catch symmetric errors in upload and download.
set result [remote_exec host "cmp -s $filename down-server"]
if { [lindex $result 0] == 0 } {
pass "compare intermediate $description"
} else {
fail "compare intermediate $description"
}
}
set result [remote_exec host "cmp -s $filename up-server"]
if { [lindex $result 0] == 0 } {
pass "compare $description"
} else {
fail "compare $description"
}
gdb_test "remote delete down-server" \
"Successfully deleted .*" "deleted $description"
if { ![is_remote target] } {
if { ! [remote_file target exists down-server] } {
pass "verified deleted $description"
} else {
fail "verified deleted $description"
}
}
catch { file delete up-server }
}
test_file_transfer "$binfile" "binary file"
test_file_transfer "$srcdir/$subdir/transfer.txt" "text file"

View File

@ -0,0 +1,12 @@
This text file is a test input for GDB's file transfer commands. It
contains some characters which need to be escaped in remote protocol
packets, like "*" and "}" and "$" and "#". Actually, it contains
a good sampling of printable characters:
!"#$%&'()*+,-./0123456789:;<=>?@
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
abcdefghijklmnopqrstuvwxyz{|}~
!"#$%&'()*+,-./0123456789:;<=>?@
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
abcdefghijklmnopqrstuvwxyz{|}~