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
