diff --git a/gdb/NEWS b/gdb/NEWS
index 81c21b82cc..fcf454833f 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -67,6 +67,9 @@ QStartupWithShell
 
 * New commands
 
+set|show cwd
+  Set and show the current working directory for the inferior.
+
 set|show compile-gcc
   Set and show compilation command used for compiling and injecting code
   with the 'compile' commands.
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index dde67ee2b3..dad5ffa42e 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -1727,9 +1727,11 @@ The commands below can be used to select other frames by number or address."),
 Print working directory.  This is used for your program as well."));
 
   c = add_cmd ("cd", class_files, cd_command, _("\
-Set working directory to DIR for debugger and program being debugged.\n\
-The change does not take effect for the program being debugged\n\
-until the next time it is started."), &cmdlist);
+Set working directory to DIR for debugger.\n\
+The debugger's current working directory specifies where scripts and other\n\
+files that can be loaded by GDB are located.\n\
+In order to change the inferior's current working directory, the recommended\n\
+way is to use the \"set cwd\" command."), &cmdlist);
   set_cmd_completer (c, filename_completer);
 
   add_com ("echo", class_support, echo_command, _("\
diff --git a/gdb/common/common-inferior.h b/gdb/common/common-inferior.h
index 87c13009ed..515a8c0f4e 100644
--- a/gdb/common/common-inferior.h
+++ b/gdb/common/common-inferior.h
@@ -30,4 +30,8 @@ extern const char *get_exec_wrapper ();
    otherwise return 0 in that case.  */
 extern char *get_exec_file (int err);
 
+/* Return the inferior's current working directory.  If nothing has
+   been set, then return NULL.  */
+extern const char *get_inferior_cwd ();
+
 #endif /* ! COMMON_INFERIOR_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9905ff6513..0d059603cf 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -2057,8 +2057,9 @@ environment} to change parts of the environment that affect
 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}.
+You can set your program's working directory with the command
+@kbd{set cwd}.  If you do not set any working directory with this
+command, your program will inherit @value{GDBN}'s working directory.
 @xref{Working Directory, ,Your Program's Working Directory}.
 
 @item The @emph{standard input and output.}
@@ -2434,19 +2435,43 @@ variables to files that are only run when you sign on, such as
 @section Your Program's Working Directory
 
 @cindex working directory (of your program)
-Each time you start your program with @code{run}, it inherits its
-working directory from the current working directory of @value{GDBN}.
-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.
+Each time you start your program with @code{run}, the inferior will be
+initialized with the current working directory specified by the
+@kbd{set cwd} command.  If no directory has been specified by this
+command, then the inferior will inherit @value{GDBN}'s current working
+directory as its working directory.
+
+You can also change @value{GDBN}'s current working directory by using
+the @code{cd} command.
 
 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 set cwd
+@cindex change inferior's working directory
+@item set cwd @r{[}@var{directory}@r{]}
+Set the inferior's working directory to @var{directory}, which will be
+@code{glob}-expanded in order to resolve tildes (@file{~}).  If no
+argument has been specified, the command clears the setting and resets
+it to an empty state.  This setting has no effect on @value{GDBN}'s
+working directory, and it only takes effect the next time you start
+the inferior.  The @file{~} in @var{directory} is a short for the
+@dfn{home directory}, usually pointed to by the @env{HOME} environment
+variable.  On MS-Windows, if @env{HOME} is not defined, @value{GDBN}
+uses the concatenation of @env{HOMEDRIVE} and @env{HOMEPATH} as
+fallback.
+
+@kindex show cwd
+@cindex show inferior's working directory
+@item show cwd
+Show the inferior's working directory.  If no directory has been
+specified by @kbd{set cwd}, then the default inferior's working
+directory is the same as @value{GDBN}'s working directory.
+
 @kindex cd
-@cindex change working directory
+@cindex change @value{GDBN}'s working directory
 @item cd @r{[}@var{directory}@r{]}
 Set the @value{GDBN} working directory to @var{directory}.  If not
 given, @var{directory} uses @file{'~'}.
diff --git a/gdb/gdbserver/inferiors.c b/gdb/gdbserver/inferiors.c
index 3a45959000..57d9956ebb 100644
--- a/gdb/gdbserver/inferiors.c
+++ b/gdb/gdbserver/inferiors.c
@@ -29,6 +29,9 @@ struct thread_info *current_thread;
 
 #define get_thread(inf) ((struct thread_info *)(inf))
 
+/* The current working directory used to start the inferior.  */
+static const char *current_inferior_cwd = NULL;
+
 void
 add_inferior_to_list (struct inferior_list *list,
 		      struct inferior_list_entry *new_inferior)
@@ -445,3 +448,11 @@ switch_to_thread (ptid_t ptid)
   gdb_assert (ptid != minus_one_ptid);
   current_thread = find_thread_ptid (ptid);
 }
+
+/* See common/common-inferior.h.  */
+
+const char *
+get_inferior_cwd ()
+{
+  return current_inferior_cwd;
+}
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index da16f5ed6d..dbdf273ca5 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -103,6 +103,10 @@ static void run_command (char *, int);
 
 static char *inferior_args_scratch;
 
+/* Scratch area where the new cwd will be stored by 'set cwd'.  */
+
+static char *inferior_cwd_scratch;
+
 /* Scratch area where 'set inferior-tty' will store user-provided value.
    We'll immediate copy it into per-inferior storage.  */
 
@@ -238,6 +242,60 @@ show_args_command (struct ui_file *file, int from_tty,
   deprecated_show_value_hack (file, from_tty, c, get_inferior_args ());
 }
 
+/* Set the inferior current working directory.  If CWD is NULL, unset
+   the directory.  */
+
+static void
+set_inferior_cwd (const char *cwd)
+{
+  struct inferior *inf = current_inferior ();
+
+  gdb_assert (inf != NULL);
+
+  if (cwd == NULL)
+    inf->cwd.reset ();
+  else
+    inf->cwd.reset (xstrdup (cwd));
+}
+
+/* See common/common-inferior.h.  */
+
+const char *
+get_inferior_cwd ()
+{
+  return current_inferior ()->cwd.get ();
+}
+
+/* Handle the 'set cwd' command.  */
+
+static void
+set_cwd_command (char *args, int from_tty, struct cmd_list_element *c)
+{
+  if (*inferior_cwd_scratch == '\0')
+    set_inferior_cwd (NULL);
+  else
+    set_inferior_cwd (inferior_cwd_scratch);
+}
+
+/* Handle the 'show cwd' command.  */
+
+static void
+show_cwd_command (struct ui_file *file, int from_tty,
+		  struct cmd_list_element *c, const char *value)
+{
+  const char *cwd = get_inferior_cwd ();
+
+  if (cwd == NULL)
+    fprintf_filtered (gdb_stdout,
+		      _("\
+You have not set the inferior's current working directory.\n\
+The inferior will inherit GDB's cwd.\n"));
+  else
+    fprintf_filtered (gdb_stdout,
+		      _("Current working directory that will be used "
+			"when starting the inferior is \"%s\".\n"), cwd);
+}
+
 
 /* Compute command-line string given argument vector.  This does the
    same shell processing as fork_inferior.  */
@@ -3253,6 +3311,25 @@ Follow this command with any number of args, to be passed to the program."),
   gdb_assert (c != NULL);
   set_cmd_completer (c, filename_completer);
 
+  cmd_name = "cwd";
+  add_setshow_string_noescape_cmd (cmd_name, class_run,
+				   &inferior_cwd_scratch, _("\
+Set the current working directory to be used when the inferior is started.\n\
+Changing this setting does not have any effect on inferiors that are\n\
+already running."),
+				   _("\
+Show the current working directory that is used when the inferior is started."),
+				   _("\
+Use this command to change the current working directory that will be used\n\
+when the inferior is started.  This setting does not affect GDB's current\n\
+working directory."),
+				   set_cwd_command,
+				   show_cwd_command,
+				   &setlist, &showlist);
+  c = lookup_cmd (&cmd_name, setlist, "", -1, 1);
+  gdb_assert (c != NULL);
+  set_cmd_completer (c, filename_completer);
+
   c = add_cmd ("environment", no_class, environment_info, _("\
 The environment to give the program, or one variable's value.\n\
 With an argument VAR, prints the value of environment variable VAR to\n\
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 7f2d53e5b3..498d74706a 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -355,6 +355,10 @@ public:
      should never be freed.  */
   char **argv = NULL;
 
+  /* The current working directory that will be used when starting
+     this inferior.  */
+  gdb::unique_xmalloc_ptr<char> cwd;
+
   /* The name of terminal device to use for I/O.  */
   char *terminal = NULL;
 
diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index 6ff119768c..1480be4961 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -25,6 +25,7 @@
 #include "common-inferior.h"
 #include "common-gdbthread.h"
 #include "signals-state-save-restore.h"
+#include "gdb_tilde_expand.h"
 #include <vector>
 
 extern char **environ;
@@ -298,6 +299,8 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
   char **save_our_env;
   int i;
   int save_errno;
+  const char *inferior_cwd;
+  std::string expanded_inferior_cwd;
 
   /* If no exec file handed to us, get it from the exec-file command
      -- with a good, common error message if none is specified.  */
@@ -339,6 +342,18 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
      the parent and child flushing the same data after the fork.  */
   gdb_flush_out_err ();
 
+  /* Check if the user wants to set a different working directory for
+     the inferior.  */
+  inferior_cwd = get_inferior_cwd ();
+
+  if (inferior_cwd != NULL)
+    {
+      /* Expand before forking because between fork and exec, the child
+	 process may only execute async-signal-safe operations.  */
+      expanded_inferior_cwd = gdb_tilde_expand (inferior_cwd);
+      inferior_cwd = expanded_inferior_cwd.c_str ();
+    }
+
   /* If there's any initialization of the target layers that must
      happen to prepare to handle the child we're about fork, do it
      now...  */
@@ -374,6 +389,14 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
 	 UIs.  */
       close_most_fds ();
 
+      /* Change to the requested working directory if the user
+	 requested it.  */
+      if (inferior_cwd != NULL)
+	{
+	  if (chdir (inferior_cwd) < 0)
+	    trace_start_error_with_name (expanded_inferior_cwd.c_str ());
+	}
+
       if (debug_fork)
 	sleep (debug_fork);
 
diff --git a/gdb/testsuite/gdb.base/set-cwd.c b/gdb/testsuite/gdb.base/set-cwd.c
new file mode 100644
index 0000000000..58738830d6
--- /dev/null
+++ b/gdb/testsuite/gdb.base/set-cwd.c
@@ -0,0 +1,31 @@
+/* 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/>.  */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+static char dir[4096];
+
+int
+main (int argc, char *argv[])
+{
+  const char *home = getenv ("HOME");
+
+  getcwd (dir, 4096);
+
+  return 0; /* break-here */
+}
diff --git a/gdb/testsuite/gdb.base/set-cwd.exp b/gdb/testsuite/gdb.base/set-cwd.exp
new file mode 100644
index 0000000000..bbb8d006a4
--- /dev/null
+++ b/gdb/testsuite/gdb.base/set-cwd.exp
@@ -0,0 +1,198 @@
+# 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/>.
+
+if { [use_gdb_stub] || [target_info gdb_protocol] == "extended-remote" } {
+    untested "not implemented on remote servers"
+    return
+}
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } {
+    return -1
+}
+
+# Test that tilde expansion works fine.
+
+proc_with_prefix test_tilde_expansion { } {
+    global decimal gdb_prompt hex
+
+    gdb_test_no_output "set cwd ~/" "set inferior cwd to ~/ dir"
+
+    if { ![runto_main] } {
+	untested "could not run to main"
+	return -1
+    }
+
+    gdb_breakpoint [gdb_get_line_number "break-here"]
+    gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+
+    set home ""
+    set test "print home var"
+    gdb_test_multiple "print home" $test {
+    	-re "\\\$$decimal = $hex \"\(.+\)\"\r\n$gdb_prompt $" {
+    	    set home $expect_out(1,string)
+    	    pass $test
+    	}
+    }
+
+    if { $home == "" } {
+    	untested "could not retrieve home var"
+    	return
+    }
+
+    set curdir ""
+    set test "print dir var"
+    gdb_test_multiple "print dir" $test {
+	-re "\\\$$decimal = \"\(.+\)\"\(, .*repeats.*\)?\r\n$gdb_prompt $" {
+	    set curdir $expect_out(1,string)
+	    pass $test
+	}
+    }
+
+    if { $curdir == "" } {
+	untested "could not retrieve dir var"
+	return
+    }
+
+    gdb_assert [string equal $curdir $home] \
+	"successfully chdir'd into home"
+}
+
+# The temporary directory that we will use to start the inferior.
+set tmpdir [standard_output_file ""]
+
+# Test that when we "set cwd" the inferior will be started under the
+# correct working directory and GDB will not be affected by this.
+
+proc_with_prefix test_cd_into_dir { } {
+    global decimal gdb_prompt tmpdir
+
+    set gdb_cwd_before_run ""
+    set test "pwd before run"
+    gdb_test_multiple "pwd" $test {
+	-re "Working directory \(.*\)\.\r\n$gdb_prompt $" {
+	    set gdb_cwd_before_run $expect_out(1,string)
+	    pass $test
+	}
+    }
+
+    if { $gdb_cwd_before_run == "" } {
+	untested "could not obtain GDB cwd before run"
+	return
+    }
+
+    # This test only makes sense if $tmpdir != $gdb_cwd_before_run
+    if { ![gdb_assert ![string equal $tmpdir $gdb_cwd_before_run] \
+	       "make sure that tmpdir and GDB's cwd are different"] } {
+	return -1
+    }
+
+    gdb_test_no_output "set cwd $tmpdir" "set inferior cwd to temp dir"
+
+    if { ![runto_main] } {
+	untested "could not run to main"
+	return -1
+    }
+
+    gdb_breakpoint [gdb_get_line_number "break-here"]
+    gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+
+    gdb_test "print dir" "\\\$$decimal = \"$tmpdir\", .*" \
+	"inferior cwd is correctly set"
+
+    set gdb_cwd_after_run ""
+    set test "pwd after run"
+    gdb_test_multiple "pwd" $test {
+	-re "Working directory \(.*\)\.\r\n$gdb_prompt $" {
+	    set gdb_cwd_after_run $expect_out(1,string)
+	    pass $test
+	}
+    }
+
+    if { $gdb_cwd_after_run == "" } {
+	untested "could not obtain GDB cwd after run"
+	return
+    }
+
+    gdb_assert [string equal $gdb_cwd_before_run $gdb_cwd_after_run] \
+	"GDB cwd is unchanged after running inferior"
+}
+
+# Test that executing "set cwd" without arguments will reset the
+# inferior's cwd setting to its previous state.
+
+proc_with_prefix test_cwd_reset { } {
+    global decimal gdb_prompt tmpdir
+
+    gdb_test_multiple "pwd" "GDB cwd" {
+	-re "Working directory \(.*\)\.\r\n$gdb_prompt $" {
+	    set gdb_cwd $expect_out(1,string)
+	}
+	-re ".*$gdb_prompt $" {
+	    fail "failed to obtain GDB cwd before run"
+	    return -1
+	}
+	default {
+	    fail "failed to obtain GDB cwd before run"
+	    return -1
+	}
+    }
+
+    # This test only makes sense if $tmpdir != $gdb_cwd.
+    if { ![gdb_assert ![string equal $tmpdir $gdb_cwd] \
+	       "make sure that tmpdir and GDB's cwd are different"] } {
+	return -1
+    }
+
+    gdb_test_no_output "set cwd $tmpdir" "set inferior cwd to temp dir"
+
+    with_test_prefix "running with set cwd" {
+	if { ![runto_main] } {
+	    untested "could not run to main"
+	    return -1
+	}
+    }
+
+    gdb_breakpoint [gdb_get_line_number "break-here"]
+    gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+
+    gdb_test "print dir" "\\\$$decimal = \"$tmpdir\", .*" \
+	"inferior cwd is correctly set"
+
+    # Reset the inferior's cwd.
+    gdb_test_no_output "set cwd" "resetting inferior cwd"
+
+    with_test_prefix "running without set cwd" {
+	if { ![runto_main] } {
+	    untested "could not run to main"
+	    return -1
+	}
+    }
+
+    gdb_breakpoint [gdb_get_line_number "break-here"]
+    gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+
+    gdb_test "print dir" "\\\$$decimal = \"$gdb_cwd\", .*" \
+	"inferior cwd got reset correctly"
+}
+
+test_cd_into_dir
+clean_restart $binfile
+test_tilde_expansion
+clean_restart $binfile
+test_cwd_reset
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 3e1894410d..6b590fdca5 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -66,6 +66,7 @@
 #include "x86-nat.h"
 #include "complaints.h"
 #include "inf-child.h"
+#include "gdb_tilde_expand.h"
 
 #define AdjustTokenPrivileges		dyn_AdjustTokenPrivileges
 #define DebugActiveProcessStop		dyn_DebugActiveProcessStop
@@ -2428,6 +2429,7 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file,
 #ifdef __CYGWIN__
   cygwin_buf_t real_path[__PMAX];
   cygwin_buf_t shell[__PMAX]; /* Path to shell */
+  cygwin_buf_t infcwd[__PMAX];
   const char *sh;
   cygwin_buf_t *toexec;
   cygwin_buf_t *cygallargs;
@@ -2465,6 +2467,17 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file,
   if (!exec_file)
     error (_("No executable specified, use `target exec'."));
 
+  const char *inferior_cwd = get_inferior_cwd ();
+  std::string expanded_infcwd;
+  if (inferior_cwd != NULL)
+    {
+      expanded_infcwd = gdb_tilde_expand (inferior_cwd);
+      /* Mirror slashes on inferior's cwd.  */
+      std::replace (expanded_infcwd.begin (), expanded_infcwd.end (),
+		    '/', '\\');
+      inferior_cwd = expanded_infcwd.c_str ();
+    }
+
   memset (&si, 0, sizeof (si));
   si.cb = sizeof (si);
 
@@ -2514,6 +2527,10 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file,
       flags |= DEBUG_PROCESS;
     }
 
+  if (cygwin_conv_path (CCP_POSIX_TO_WIN_W, inferior_cwd,
+			infcwd, strlen (inferior_cwd)) < 0)
+    error (_("Error converting inferior cwd: %d"), errno);
+
 #ifdef __USEWIDE
   args = (cygwin_buf_t *) alloca ((wcslen (toexec) + wcslen (cygallargs) + 2)
 				  * sizeof (wchar_t));
@@ -2574,7 +2591,7 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file,
 		       TRUE,	/* inherit handles */
 		       flags,	/* start flags */
 		       w32_env,	/* environment */
-		       NULL,	/* current directory */
+		       infcwd,	/* current directory */
 		       &si,
 		       &pi);
   if (w32_env)
@@ -2697,7 +2714,7 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file,
 			TRUE,	/* inherit handles */
 			flags,	/* start flags */
 			w32env,	/* environment */
-			NULL,	/* current directory */
+			inferior_cwd, /* current directory */
 			&si,
 			&pi);
   if (tty != INVALID_HANDLE_VALUE)
