This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH/RFC] Implement the ability to set the current working directory in GDBserver


[ Disclaimer: this patch depends on the hex2str/bin2hex functions
  present at the share-env-with-gdbserver patch.  ]

Currently, gdbserver doesn't offer the user the ability to change the
current working directory before starting the remote inferior.  This
patch implements this.

The idea is to use GDB's "cd" command to identify when the user wants
to actively change the cwd, and reflect this change in gdbserver as
well.  This means that if the user doesn't use the "cd" command, then
nothing will happen and things will work as before.  Otherwise, GDB
will send a new QSetWorkingDir packet to gdbserver containing the
hex-encoded version of the directory into which gdbserver must be
before starting the remote inferior.

I didn't want to implement a gdbserver-specific command (e.g., "set
remote directory"), which means that my approach has some drawbacks.
For example, if you want gdbserver to cd to "/abc", but "/abc" doesn't
exist in the host, then you still won't be able to do this, because
GDB obviously won't allow you to "cd" into a non-existing dir.  So you
will have to have the same directory structure in both host and target
if you want to do that.

Another "drawback" is this: suppose you started your GDB on "/abc" and
your gdbserver on "/xyz".  Now, suppose you want to tell gdbserver to
cd into "/abc" before starting the inferior.  You will have to do a
dummy "cd ." in order to avoid changing GDB's cwd while at the same
time instruct it to actually send the QSetWorkingDir to gdbserver.

Other than that, the feature works great and is one small step towards
having the local-remote feature parity project completed.

Built and regtested on buildbot, no regressions.

Thoughts?

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* NEWS (Changes since GDB 8.0): Add entry about new
	'set-cwd-on-gdbserver' feature.
	(New remote packets): Add entry for QSetWorkingDir.
	* cli/cli-cmds.c (user_set_cwd): New variable.
	(cd_command): Set 'user_set_cwd' variable.
	* cli/cli-cmds.h (user_set_cwd): New variable.
	* remote.c: Include "cli/cli-cmds.h".  Add PACKET_QSetWorkingDir.
	(remote_protocol_features) <QSetWorkingDir>: New entry for
	PACKET_QSetWorkingDir.
	(extended_remote_handle_inferior_cwd): New function.
	(extended_remote_create_inferior): Call
	"extended_remote_handle_inferior_cwd".
	(_initialize_remote): Call "add_packet_config_cmd" for
	QSetWorkingDir.

gdb/gdbserver/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* server.c (handle_general_set): Handle QSetWorkingDir packet.
	(handle_query): Inform that QSetWorkingDir is supported.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

gdb/doc/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* gdb.texinfo (Starting your Program) <The working directory>:
	Mention that working directory change will be reflected in
	GDBserver.
	(Your Program's Working Directory): Likewise.  Add @anchor on "cd
	command".
	(Connecting) <Remote Packet>: Add "set-working-dir"
	and "QSetWorkingDir" to the table.
	(Remote Protocol) <QSetWorkingDir>: New item, explaining the
	packet.
---
 gdb/NEWS                                          | 11 ++++
 gdb/cli/cli-cmds.c                                |  6 +++
 gdb/cli/cli-cmds.h                                |  4 ++
 gdb/common/rsp-low.c                              | 39 +++++++++++++++
 gdb/common/rsp-low.h                              |  8 +++
 gdb/doc/gdb.texinfo                               | 53 ++++++++++++++++++++
 gdb/gdbserver/server.c                            | 27 +++++++++-
 gdb/remote.c                                      | 36 +++++++++++++
 gdb/testsuite/gdb.server/set-cwd-on-gdbserver.exp | 61 +++++++++++++++++++++++
 9 files changed, 244 insertions(+), 1 deletion(-)
 create mode 100644 gdb/testsuite/gdb.server/set-cwd-on-gdbserver.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 735415495e..0074f4446b 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -17,11 +17,22 @@
      "target remote", you can disable the startup with shell by using the
      new "--no-startup-with-shell" GDBserver command line option.
 
+  ** On Unix systems, GDBserver is now able to enter a directory
+     before starting an inferior.
+
+     This is done by using the "cd" command in GDB, which instructs it
+     to tell GDBserver about this directory change the next time an
+     inferior is run.  If you want to make GDBserver enter the
+     directory your GDB is currently in, you can do a "cd ." in GDB.
+
 * New remote packets
 
 QStartupWithShell
   Indicates whether the inferior must be started with a shell or not.
 
+QSetWorkingDir
+  Tell GDBserver to enter another directory before starting the inferior.
+
 * The "maintenance print c-tdesc" command now takes an optional
   argument which is the file name of XML target description.
 
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index d4dc53904c..fb4bba9ae0 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -203,6 +203,10 @@ static const char *const script_ext_enums[] = {
 };
 
 static const char *script_ext_mode = script_ext_soft;
+
+/* This variable is set to true when the user has set the cwd, i.e.,
+   when 'cd_command' has been invoked.  Otherwise, it's false.  */
+bool user_set_cwd = false;
 
 /* Utility used everywhere when at least one argument is needed and
    none is supplied.  */
@@ -490,6 +494,8 @@ cd_command (char *dir, int from_tty)
   if (from_tty)
     pwd_command ((char *) 0, 1);
 
+  user_set_cwd = true;
+
   do_cleanups (cleanup);
 }
 
diff --git a/gdb/cli/cli-cmds.h b/gdb/cli/cli-cmds.h
index 1122a97b34..5175d7d0eb 100644
--- a/gdb/cli/cli-cmds.h
+++ b/gdb/cli/cli-cmds.h
@@ -100,6 +100,10 @@ extern struct cmd_list_element *setchecklist;
 
 extern struct cmd_list_element *showchecklist;
 
+/* This variable is set to true when the user has set the cwd, i.e.,
+   when 'cd_command' has been invoked.  Otherwise, it's false.  */
+extern bool user_set_cwd;
+
 /* Exported to gdb/top.c */
 
 void init_cmd_lists (void);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index d977b234d0..bcf4ead572 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -2059,6 +2059,10 @@ your program.  @xref{Environment, ,Your Program's Environment}.
 @item The @emph{working directory.}
 Your program inherits its working directory from @value{GDBN}.  You can set
 the @value{GDBN} working directory with the @code{cd} command in @value{GDBN}.
+
+If you are doing remote debugging (@pxref{Remote Debugging}) and
+change the working directory in @value{GDBN}, this will be reflected
+in @command{gdbserver} the next time you run the inferior.
 @xref{Working Directory, ,Your Program's Working Directory}.
 
 @item The @emph{standard input and output.}
@@ -2420,12 +2424,22 @@ The @value{GDBN} working directory is initially whatever it inherited
 from its parent process (typically the shell), but you can specify a new
 working directory in @value{GDBN} with the @code{cd} command.
 
+Whenever you specify a new working directory in @value{GDBN}, and if
+you are performing a remote debug (@pxref{Remote Debugging}), this
+change will be reflected in @command{gdbserver} the next time you run
+the program being debugged (@pxref{QSetWorkingDir packet}).  Sometimes
+you may want to make @command{gdbserver} enter a directory in which
+@value{GDBN} is already in; in this case, you can perform a @code{cd
+.} which will not change the current directory in @value{GDBN} but
+will force @command{gdbserver} to enter it.
+
 The @value{GDBN} working directory also serves as a default for the commands
 that specify files for @value{GDBN} to operate on.  @xref{Files, ,Commands to
 Specify Files}.
 
 @table @code
 @kindex cd
+@anchor{cd command}
 @cindex change working directory
 @item cd @r{[}@var{directory}@r{]}
 Set the @value{GDBN} working directory to @var{directory}.  If not
@@ -20849,6 +20863,10 @@ are:
 @tab @code{QStartupWithShell}
 @tab @code{set startup-with-shell}
 
+@item @code{set-working-dir}
+@tab @code{QSetWorkingDir}
+@tab @code{cd}
+
 @item @code{conditional-breakpoints-packet}
 @tab @code{Z0 and Z1}
 @tab @code{Support for target-side breakpoint condition evaluation}
@@ -36604,6 +36622,41 @@ actually support starting the inferior using a shell.
 Use of this packet is controlled by the @code{set startup-with-shell}
 command; @pxref{set startup-with-shell}.
 
+@item QSetWorkingDir:@var{hex-value}
+@anchor{QSetWorkingDir packet}
+@cindex set working directory, remote request
+@cindex @samp{QSetWorkingDir} packet
+On UNIX-like targets, it is possible to set the current working
+directory that @command{gdbserver} will enter before starting the
+inferior.  This packet is used to inform @command{gdbserver} of a
+directory into which it should enter during the startup process.
+
+The packet is composed by @var{hex-value}, an hex encoded
+representation of the directory to be entered by @command{gdbserver}.
+
+This packet is only transmitted when the user issues a @code{cd}
+command in @value{GDBN} (@xref{Working Directory, ,Your Program's
+Working Directory}).
+
+This packet is only available in extended mode (@pxref{extended
+mode}).
+
+Reply:
+@table @samp
+@item OK
+The request succeeded.
+
+@item E @var{nn}
+An error occurred.  The error number @var{nn} is given as hex digits.
+@end table
+
+This packet is not probed by default; the remote stub must request it,
+by supplying an appropriate @samp{qSupported} response
+(@pxref{qSupported}).
+
+Use of this packet is controlled by the @code{cd} command; @pxref{cd
+command}.
+
 @item qfThreadInfo
 @itemx qsThreadInfo
 @cindex list active threads, remote request
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 8200aa17c8..8dee2d5688 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -794,6 +794,30 @@ handle_general_set (char *own_buf)
       return;
     }
 
+  if (startswith (own_buf, "QSetWorkingDir:"))
+    {
+      const char *p = own_buf + sizeof ("QSetWorkingDir:") - 1;
+      std::string path = hex2str (p);
+
+      if (chdir (path.c_str ()) < 0)
+	{
+	  int saved_errno = errno;
+
+	  fprintf (stderr, _("Could not change to directory '%s': %s"),
+		   path.c_str (), safe_strerror (saved_errno));
+	  write_enn (own_buf);
+	}
+      else
+	{
+	  if (remote_debug)
+	    debug_printf (_("[Changed current directory to %s]\n"),
+			  path.c_str ());
+	  write_ok (own_buf);
+	}
+
+      return;
+    }
+
   /* Otherwise we didn't know what packet it was.  Say we didn't
      understand it.  */
   own_buf[0] = 0;
@@ -2230,7 +2254,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 	}
 
       sprintf (own_buf,
-	       "PacketSize=%x;QPassSignals+;QProgramSignals+;QStartupWithShell+",
+	       "PacketSize=%x;QPassSignals+;QProgramSignals+;QStartupWithShell+;"
+	       "QSetWorkingDir+",
 	       PBUFSIZ - 1);
 
       if (target_supports_catch_syscall ())
diff --git a/gdb/remote.c b/gdb/remote.c
index 2249533258..9807107032 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -40,6 +40,7 @@
 #include "solib.h"
 #include "cli/cli-decode.h"
 #include "cli/cli-setshow.h"
+#include "cli/cli-cmds.h"
 #include "target-descriptions.h"
 #include "gdb_bfd.h"
 #include "filestuff.h"
@@ -1429,6 +1430,7 @@ enum {
   PACKET_QPassSignals,
   PACKET_QCatchSyscalls,
   PACKET_QProgramSignals,
+  PACKET_QSetWorkingDir,
   PACKET_QStartupWithShell,
   PACKET_qCRC,
   PACKET_qSearch_memory,
@@ -4635,6 +4637,8 @@ static const struct protocol_feature remote_protocol_features[] = {
     PACKET_QCatchSyscalls },
   { "QProgramSignals", PACKET_DISABLE, remote_supported_packet,
     PACKET_QProgramSignals },
+  { "QSetWorkingDir", PACKET_DISABLE, remote_supported_packet,
+    PACKET_QSetWorkingDir },
   { "QStartupWithShell", PACKET_DISABLE, remote_supported_packet,
     PACKET_QStartupWithShell },
   { "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
@@ -9556,6 +9560,33 @@ extended_remote_run (const std::string &args)
     }
 }
 
+/* Helper function to handle the change of the current working
+   directory in the remote.  */
+
+static void
+extended_remote_handle_inferior_cwd (struct remote_state *rs)
+{
+  if (packet_support (PACKET_QSetWorkingDir) != PACKET_DISABLE)
+    {
+      /* Check if the user has explicitly told us to change the remote
+	 cwd.  */
+      if (user_set_cwd)
+	{
+	  std::string hexpath = bin2hex ((const gdb_byte *) current_directory,
+					 strlen (current_directory));
+
+	  xsnprintf (rs->buf, get_remote_packet_size (),
+		     "QSetWorkingDir:%s", hexpath.c_str ());
+	  putpkt (rs->buf);
+	  getpkt (&rs->buf, &rs->buf_size, 0);
+	  if (strcmp (rs->buf, "OK") != 0)
+	    error (_("\
+Remote replied unexpectedly while changing working directory: %s"),
+		   rs->buf);
+	}
+    }
+}
+
 /* In the extended protocol we want to be able to do things like
    "run" and have them basically work as expected.  So we need
    a special create_inferior function.  We support changing the
@@ -9596,6 +9627,8 @@ Remote replied unexpectedly while setting startup-with-shell: %s"),
 	       rs->buf);
     }
 
+  extended_remote_handle_inferior_cwd (rs);
+
   /* Now restart the remote server.  */
   run_worked = extended_remote_run (args) != -1;
   if (!run_worked)
@@ -14064,6 +14097,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_QProgramSignals],
 			 "QProgramSignals", "program-signals", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_QSetWorkingDir],
+			 "QSetWorkingDir", "set-working-dir", 0);
+
   add_packet_config_cmd (&remote_protocol_packets[PACKET_QStartupWithShell],
 			 "QStartupWithShell", "startup-with-shell", 0);
 
diff --git a/gdb/testsuite/gdb.server/set-cwd-on-gdbserver.exp b/gdb/testsuite/gdb.server/set-cwd-on-gdbserver.exp
new file mode 100644
index 0000000000..50eb8c2d20
--- /dev/null
+++ b/gdb/testsuite/gdb.server/set-cwd-on-gdbserver.exp
@@ -0,0 +1,61 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2017 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/>.
+
+load_lib gdbserver-support.exp
+
+if {[skip_gdbserver_tests]} {
+    return
+}
+
+standard_testfile normal.c
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } {
+    return -1
+}
+
+set tmpdir [standard_temp_file ""]
+set curdir [pwd]
+
+# Start gdbserver inside the temporary directory.  This is needed
+# because gdbserver cannot be able to find the binary.
+cd $tmpdir
+# Make sure we're disconnected, in case we're testing with an
+# extended-remote board, therefore already connected.
+gdb_test "disconnect" ".*" "disconnect"
+gdbserver_start_extended
+cd $curdir
+
+# Set the remote exec-file to the local binary.  We don't use the full
+# pathname because we want gdbserver to look for the file in the
+# current dir.
+gdb_test_no_output "set remote exec-file ./[file tail $binfile]" \
+    "set remote exec-file to the local binary"
+
+# Now, tell GDB to cd to the binfile directory.  This will make GDB
+# send a packet to gdbserver instructing it to also change to the
+# specified directory, which is needed to run the binary.
+gdb_test "cd [file dirname $binfile]" \
+    "Working directory [file dirname $binfile]." \
+    "cd to binfile dir before executing"
+
+# And now run it.
+gdb_test "run" \
+    "\\\[Inferior $decimal \\\(process $decimal\\\) exited normally\\\]" \
+    "run works normally"
+
+# Delete the temporary directory.
+file delete -force $tmpdir
-- 
2.13.3


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]