diff --git a/gdb/remote.c b/gdb/remote.c
index 1d5e098e91f..e2d21c65b70 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1396,6 +1396,9 @@ class extended_remote_target final : public remote_target
 
   void post_attach (int) override;
   bool supports_disable_randomization () override;
+
+private:
+  std::string get_exec_file_for_create_inferior (const char *exec_file);
 };
 
 struct stop_reply : public notif_event
@@ -11031,6 +11034,51 @@ directory: %s"),
     }
 }
 
+/* Return the path for the executable that GDB should ask the remote target
+   to use when starting an inferior.  EXEC_FILE is GDB's idea of the
+   current inferior (e.g. set with the 'file' command), however, this is
+   often not the right thing for a remote target to use, which is why GDB
+   also has the 'set remote exec-file' command.
+
+   If the user has 'set remote exec-file', then this function returns the
+   path the user set.  Otherwise, if it is possible that GDB and the remote
+   target can see the same filesystem, this function will return
+   EXEC_FILE.  */
+
+std::string
+extended_remote_target::get_exec_file_for_create_inferior
+  (const char *exec_file)
+{
+  const remote_exec_file_info &info
+    = get_remote_exec_file_info (current_program_space);
+
+  /* If INFO.SECOND is remote_exec_source::DEFAULT_VALUE, then this
+     indicates that the remote target has failed to inform us if it has a
+     default-executable set or not, maybe the remote doesn't support the
+     qDefaultExecAndArgs packet?  In this case, we pass the empty string to
+     the remote and expect it to use the default executable (if one is
+     set).  */
+  if (exec_file != nullptr
+      && info.second == remote_exec_source::UNSET_VALUE)
+    {
+      /* If the sysroot has been set to something that does not have a
+	 'target:' prefix then GDB will not be trying to fetch files from
+	 the remote anyway, so we can assume that the executable is visible
+	 to both the remote and GDB.
+
+	 Otherwise, if GDB is able to determine that the remote filesystem
+	 is actually local, then we are still OK to use the local
+	 executable.  */
+      if (!is_target_filename (gdb_sysroot)
+	  || target_filesystem_is_local ())
+	return exec_file;
+    }
+
+  /* The user has set the remote exec-file, or GDB doesn't think the remote
+     target and GDB can see the same filesystem.  */
+  return info.first;
+}
+
 /* 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
@@ -11045,7 +11093,9 @@ extended_remote_target::create_inferior (const char *exec_file,
   int run_worked;
   char *stop_reply;
   struct remote_state *rs = get_remote_state ();
-  const std::string &remote_exec_file = get_remote_exec_file ();
+
+  std::string remote_exec_file
+    = get_exec_file_for_create_inferior (exec_file);
 
   /* If running asynchronously, register the target file descriptor
      with the event loop.  */
diff --git a/gdb/testsuite/gdb.server/ext-run.exp b/gdb/testsuite/gdb.server/ext-run.exp
index 59ead666d7b..3a4bf3e7eb1 100644
--- a/gdb/testsuite/gdb.server/ext-run.exp
+++ b/gdb/testsuite/gdb.server/ext-run.exp
@@ -30,43 +30,118 @@ if {[build_executable $testfile.exp $testfile $srcfile debug] == -1} {
 # allow_xml_test must be called while gdb is not running.
 set do_xml_test [allow_xml_test]
 
-save_vars { GDBFLAGS } {
-    # If GDB and GDBserver are both running locally, set the sysroot to avoid
-    # reading files via the remote protocol.
-    if { ![is_remote host] && ![is_remote target] } {
-	set GDBFLAGS "$GDBFLAGS -ex \"set sysroot\""
+# This is used as an override function.
+proc do_nothing {} { return 0 }
+
+# Start an exetended-remote gdbserver, connect to it, and then use
+# 'run' to start an inferior.
+#
+# If CLEAR_SYSROOT is true then the 'set sysroot' command is issued,
+# clearing the sysroot, otherwise the sysroot is left unchanged.
+#
+# If SET_REMOTE_EXEC is true then the 'set remote-exec ...' command is
+# issued to point GDB at the executable on the target (after copying
+# the executable over).  Otherwise, we rely on GDB and gdbserver being
+# able to see the same filesystem, remote exec-file is not set, and
+# GDB will just use the path to the executable.
+proc do_test { clear_sysroot set_remote_exec fetch_exec_and_args } {
+    save_vars { ::GDBFLAGS } {
+	if { $clear_sysroot } {
+	    set ::GDBFLAGS "$::GDBFLAGS -ex \"set sysroot\""
+	}
+
+	clean_restart $::binfile
     }
 
-    clean_restart $binfile
-}
+    # Disable, or enable, use of the qDefaultExecAndArgs packet.
+    gdb_test "set remote fetch-exec-and-args-packet ${fetch_exec_and_args}" \
+	".*"
 
-# Make sure we're disconnected, in case we're testing with an
-# extended-remote board, therefore already connected.
-gdb_test "disconnect" ".*"
+    # Make sure we're disconnected, in case we're testing with an
+    # extended-remote board, therefore already connected.
+    gdb_test "disconnect" ".*"
 
-set target_exec [gdbserver_download_current_prog]
-gdbserver_start_extended
+    gdbserver_start_extended
 
-gdb_test_no_output "set remote exec-file $target_exec" "set remote exec-file"
+    gdb_test "show remote exec-file" \
+	"The remote exec-file is \"\"\[^\r\n\]*" \
+	"check remote exec-file is unset"
 
-gdb_breakpoint main
-gdb_test "run" "Breakpoint.* main .*" "continue to main"
+    if { $set_remote_exec } {
+	set target_exec [gdbserver_download_current_prog]
+	gdb_test_no_output "set remote exec-file $target_exec" \
+	    "set remote exec-file"
+    }
 
-if { [istarget *-*-linux*] } {
-    # On Linux, gdbserver can also report the list of processes.
-    # But only if xml support is compiled in.
-    if { $do_xml_test } {
-	# This is done in a way to avoid the timeout that can occur from
-	# applying .* regexp to large output.
-	gdb_test_sequence "info os processes" "get process list" \
-	    { "pid +user +command" "1 +root +\[/a-z\]*(init|systemd)" }
+    gdb_breakpoint main
+    gdb_test_multiple "run" "continue to main" {
+	-re -wrap "Breakpoint.* main .*" {
+	    pass $gdb_test_name
+	}
+	-re -wrap "Running the default executable on the remote target failed; try \"set remote exec-file\"." {
+
+	    # If 'set remote exec-file' has been used then we should
+	    # not get here.
+	    gdb_assert {!$set_remote_exec} \
+		"confirm remote exec-file is not set"
+
+	    if {!$fetch_exec_and_args} {
+		# We deliberately disabled GDB's ability to know that
+		# the remote doesn't have a default executable set (by
+		# disabling the qDefaultExecAndArgs packet).  We got
+		# the result we expected, but the inferior is not
+		# running, so we're done with this phase of testing.
+		pass $gdb_test_name
+		return
+	    } else {
+		# This means that the qMachineId packet is not supported,
+		# and GDB could not tell if it is running on the same host
+		# as gdbserver.  Confirm that qMachineId is showing as
+		# disabled, and then move onto the next testing mode.
+		gdb_test "show remote fetch-machine-id-packet" \
+		    "Support for the 'qMachineId' packet on the current remote target is \"auto\", currently disabled\\." \
+		    "confirm qMachineId packet is not supported"
+		return
+	    }
+	}
+    }
+
+    if { [istarget *-*-linux*] } {
+	# On Linux, gdbserver can also report the list of processes.
+	# But only if xml support is compiled in.
+	if { $::do_xml_test } {
+	    # This is done in a way to avoid the timeout that can occur from
+	    # applying .* regexp to large output.
+	    gdb_test_sequence "info os processes" "get process list" \
+		{ "pid +user +command" "1 +root +\[/a-z\]*(init|systemd)" }
+	}
     }
-}
 
-gdb_test "kill" "" "kill" "Kill the program being debugged. .y or n. " "y"
+    gdb_test "kill" "" "kill" "Kill the program being debugged. .y or n. " "y"
 
-gdb_load $binfile
-gdb_test "monitor help" "The following monitor commands.*" \
+    gdb_load $::binfile
+    gdb_test "monitor help" "The following monitor commands.*" \
         "load new file without any gdbserver inferior"
 
-gdb_test_no_output "monitor exit"
+    gdb_test_no_output "monitor exit"
+}
+
+set clear_sysroot_modes { false }
+set set_remote_exec_modes { true }
+if {![is_remote target] && ![is_remote host]} {
+    lappend set_remote_exec_modes false
+    lappend clear_sysroot_modes true
+}
+
+# This override prevents GDB from automatically setting the 'remote
+# exec-file' when using the extended-remote protocol.  If we want the
+# exec-file set, then this test takes care of it.
+with_override extended_gdbserver_load_last_file do_nothing {
+    foreach_with_prefix clear_sysroot $clear_sysroot_modes {
+	foreach_with_prefix set_remote_exec $set_remote_exec_modes {
+	    foreach_with_prefix fetch_exec_and_args { on off } {
+		do_test $clear_sysroot $set_remote_exec $fetch_exec_and_args
+	    }
+	}
+    }
+}
