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

Message ID 20170830043811.776-1-sergiodj@redhat.com
State New, archived
Headers

Commit Message

Sergio Durigan Junior Aug. 30, 2017, 4:38 a.m. UTC
  [ 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
  

Comments

Eli Zaretskii Aug. 30, 2017, 2:28 p.m. UTC | #1
> From: Sergio Durigan Junior <sergiodj@redhat.com>
> Cc: Pedro Alves <palves@redhat.com>,
> 	Eli Zaretskii <eliz@gnu.org>,
> 	Sergio Durigan Junior <sergiodj@redhat.com>
> Date: Wed, 30 Aug 2017 00:38:11 -0400
> 
> --- 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.

Can you tell why this is limited to Unix systems?  The chdir function
is available much wider than just on Unix.

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

Couldn't GDB do this "cd ." step under the hood, without bothering
users with that?

> +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 "@code{cd .}" part should be @kbd{cd .}, and I'd take it in @w{..}
for a good measure, to prevent it from being broken between 2 lines.

> +This packet is only transmitted when the user issues a @code{cd}
> +command in @value{GDBN} (@xref{Working Directory, ,Your Program's
> +Working Directory}).

The @xref should be @pxref, as the former is not appropriate in
parentheses.

The documentation parts are okay with these nits fixed.

Thanks.
  
Sergio Durigan Junior Aug. 31, 2017, 9:40 p.m. UTC | #2
On Wednesday, August 30 2017, Eli Zaretskii wrote:

>> From: Sergio Durigan Junior <sergiodj@redhat.com>
>> Cc: Pedro Alves <palves@redhat.com>,
>> 	Eli Zaretskii <eliz@gnu.org>,
>> 	Sergio Durigan Junior <sergiodj@redhat.com>
>> Date: Wed, 30 Aug 2017 00:38:11 -0400
>> 
>> --- 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.
>
> Can you tell why this is limited to Unix systems?  The chdir function
> is available much wider than just on Unix.

No particular reason.  This is the same text I've been using in my
latest patches for gdbserver, so I chose to use it again.  I'll rewrite
it to remove this part.

>> +     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.
>
> Couldn't GDB do this "cd ." step under the hood, without bothering
> users with that?

The problem is that we don't really know if the user will want to change
gdbserver's current directory or not.  If we always assume so, this will
lead to many breakages as the directory tree will not be always the same
on host and target.  That's why 'user_set_cwd' is initially false.
However, there's the case when the user may want to change gdbserver's
directory to the same directory GDB is in.  That's why I included this
explanation in the docs.

The more I think about this, the less I'm satisfied with the current
solution.  But I can't really think of a better alternative that doesn't
involve having a separate command to manipulate gdbserver's cwd.

>> +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 "@code{cd .}" part should be @kbd{cd .}, and I'd take it in @w{..}
> for a good measure, to prevent it from being broken between 2 lines.

Thanks, fixed.

>> +This packet is only transmitted when the user issues a @code{cd}
>> +command in @value{GDBN} (@xref{Working Directory, ,Your Program's
>> +Working Directory}).
>
> The @xref should be @pxref, as the former is not appropriate in
> parentheses.

Fixed.

> The documentation parts are okay with these nits fixed.

Thanks.
  
Pedro Alves Aug. 31, 2017, 10:01 p.m. UTC | #3
On 08/30/2017 06:38 AM, Sergio Durigan Junior wrote:
> 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.

I'm not sure this is the right approach.  I'd like to have a
better understanding of what are the use cases "cd" is used for.
Beyond affecting the inferior's cwd when it is started, what
else is/can "cd" used for?  Or IOW, what else does GDB's
current working directory affect?

Thanks,
Pedro Alves
  
John Baldwin Aug. 31, 2017, 10:36 p.m. UTC | #4
On Friday, September 01, 2017 12:01:51 AM Pedro Alves wrote:
> On 08/30/2017 06:38 AM, Sergio Durigan Junior wrote:
> > 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.
> 
> I'm not sure this is the right approach.  I'd like to have a
> better understanding of what are the use cases "cd" is used for.
> Beyond affecting the inferior's cwd when it is started, what
> else is/can "cd" used for?  Or IOW, what else does GDB's
> current working directory affect?

I often use 'cd' to source gdb script files that themselves source
additional files.  In particular, I have a set of scripts I use for kernel
debugging on FreeBSD that live in a 'gdb6' top-level script that itself
sources machine-dependent scripts (e.g. 'gdb6.amd64').  To make this work
I do 'cd /path/to/scripts' before 'source gdb6'.  That may be a bit of an
odd ball case, but it would not be relevant to a remote target.
  
Philippe Waroquiers Sept. 1, 2017, 12:33 p.m. UTC | #5
On Thu, 2017-08-31 at 17:40 -0400, Sergio Durigan Junior wrote:
> >> +     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.
> >
> > Couldn't GDB do this "cd ." step under the hood, without bothering
> > users with that?
> 
> The problem is that we don't really know if the user will want to change
> gdbserver's current directory or not.  If we always assume so, this will
> lead to many breakages as the directory tree will not be always the same
> on host and target.  That's why 'user_set_cwd' is initially false.
> However, there's the case when the user may want to change gdbserver's
> directory to the same directory GDB is in.  That's why I included this
> explanation in the docs.
> 
> The more I think about this, the less I'm satisfied with the current
> solution.  But I can't really think of a better alternative that doesn't
> involve having a separate command to manipulate gdbserver's cwd.
Why not have options such as:
    cd -s xxxx 
         only change the gdb server directory 
    cd -g xxxx
         only change the gdb directory
    cd xxxx
         change both gdb and gdb server directory
       (synonym of cd -s -g xxxx)


Philippe
  
Sergio Durigan Junior Sept. 1, 2017, 6:39 p.m. UTC | #6
On Friday, September 01 2017, Philippe Waroquiers wrote:

> On Thu, 2017-08-31 at 17:40 -0400, Sergio Durigan Junior wrote:
>> >> +     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.
>> >
>> > Couldn't GDB do this "cd ." step under the hood, without bothering
>> > users with that?
>> 
>> The problem is that we don't really know if the user will want to change
>> gdbserver's current directory or not.  If we always assume so, this will
>> lead to many breakages as the directory tree will not be always the same
>> on host and target.  That's why 'user_set_cwd' is initially false.
>> However, there's the case when the user may want to change gdbserver's
>> directory to the same directory GDB is in.  That's why I included this
>> explanation in the docs.
>> 
>> The more I think about this, the less I'm satisfied with the current
>> solution.  But I can't really think of a better alternative that doesn't
>> involve having a separate command to manipulate gdbserver's cwd.
> Why not have options such as:
>     cd -s xxxx 
>          only change the gdb server directory 
>     cd -g xxxx
>          only change the gdb directory
>     cd xxxx
>          change both gdb and gdb server directory
>        (synonym of cd -s -g xxxx)

That'd be almost similar to having a "set remote cwd" or something.
We'd like to avoid that because the goal here is to unify the
interfaces/features of GDB and gdbserver.  If we have two commands, one
for each, it then becomes necessary to treat things differently
internally which ultimately leads to two different versions of the
interfaces.

Not sure if it's clear enough; I can expand more if needed.

Thanks,
  
Sergio Durigan Junior Sept. 5, 2017, 5:45 p.m. UTC | #7
On Thursday, August 31 2017, Pedro Alves wrote:

> On 08/30/2017 06:38 AM, Sergio Durigan Junior wrote:
>> 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.
>
> I'm not sure this is the right approach.  I'd like to have a
> better understanding of what are the use cases "cd" is used for.
> Beyond affecting the inferior's cwd when it is started, what
> else is/can "cd" used for?  Or IOW, what else does GDB's
> current working directory affect?

Good, I was also not 100% this was the right approach either.

I gave this all a thought yesterday and, before I answer your questions,
I'd like to know if I'm understanding the goal correctly.  We want to be
able to instruct gdbserver to change the current working directory
before starting the inferior, correct?  I had the impression that this
was the only goal to achieve, but I'm afraid I'm not seeing the entire
picture here.

As for your questions.  I looked at the code to find places where the
"current_directory" variable was being used.  This is the variable that
ultimately gets changed when "cd" is used.

Aside from impacting the inferior's cwd, current_directory is also used
on the ".gdb_history" machinery.

    tmpenv = getenv ("GDBHISTFILE");
    if (tmpenv)
      history_filename = xstrdup (tmpenv);
    else if (!history_filename)
      {
        /* We include the current directory so that if the user changes
           directories the file written will be the same as the one
           that was read.  */
  #ifdef __MSDOS__
        /* No leading dots in file names are allowed on MSDOS.  */
        history_filename = concat (current_directory, "/_gdb_history",
                                   (char *)NULL);
  #else
        history_filename = concat (current_directory, "/.gdb_history",
                                   (char *)NULL);
  #endif
      }
    read_history (history_filename);

As John Baldwin also mentioned, 'cd' has an effect when loading GDB
scripts.  And probably has an effect when loading other stuff.

Since gdbserver doesn't really support loading scripts and also doesn't
use .gdb_history, I don't think they are relevant in this case.

Having said that, I'd like to discuss more about the ultimate goal, so
that I know I'm pursuing the right things here.

Thanks,
  
Pedro Alves Sept. 6, 2017, 2:20 p.m. UTC | #8
On 09/05/2017 06:45 PM, Sergio Durigan Junior wrote:
> On Thursday, August 31 2017, Pedro Alves wrote:
> 
>> On 08/30/2017 06:38 AM, Sergio Durigan Junior wrote:
>>> 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.
>>
>> I'm not sure this is the right approach.  I'd like to have a
>> better understanding of what are the use cases "cd" is used for.
>> Beyond affecting the inferior's cwd when it is started, what
>> else is/can "cd" used for?  Or IOW, what else does GDB's
>> current working directory affect?
> 
> Good, I was also not 100% this was the right approach either.
> 
> I gave this all a thought yesterday and, before I answer your questions,
> I'd like to know if I'm understanding the goal correctly.  We want to be
> able to instruct gdbserver to change the current working directory
> before starting the inferior, correct?  I had the impression that this
> was the only goal to achieve, but I'm afraid I'm not seeing the entire
> picture here.

That's part of the goal, yes.  However, another goal we should have
is  to also try to be sure that we end up with a coherent user
command line interface.  The drawbacks you mention in the proposed
commit log look like deal breakers to me, off hand.

For native debugging, things were easy -- the inferior always inherits
GDB's current working directory. But with remote debugging in the picture,
we have a new variable axis -- there's no relation between gdb's filesystem,
and the target's.  So I worry that trying to fit all use cases through the
same UI might not be the best approach.  Some use cases are clearly
concerned with GDB's current working directory, while other use cases
are not.  I'd like to pick them apart to be sure to end up with something
that makes sense, isn't confusing to users, and doesn't result in awkward
uses in some scenarios.

> 
> As for your questions.  I looked at the code to find places where the
> "current_directory" variable was being used.  This is the variable that
> ultimately gets changed when "cd" is used.
> 
> Aside from impacting the inferior's cwd, current_directory is also used
> on the ".gdb_history" machinery.
> 
>     tmpenv = getenv ("GDBHISTFILE");
>     if (tmpenv)
>       history_filename = xstrdup (tmpenv);
>     else if (!history_filename)
>       {
>         /* We include the current directory so that if the user changes
>            directories the file written will be the same as the one
>            that was read.  */
>   #ifdef __MSDOS__
>         /* No leading dots in file names are allowed on MSDOS.  */
>         history_filename = concat (current_directory, "/_gdb_history",
>                                    (char *)NULL);
>   #else
>         history_filename = concat (current_directory, "/.gdb_history",
>                                    (char *)NULL);
>   #endif
>       }
>     read_history (history_filename);
> 
> As John Baldwin also mentioned, 'cd' has an effect when loading GDB
> scripts.  And probably has an effect when loading other stuff.
> 
> Since gdbserver doesn't really support loading scripts and also doesn't
> use .gdb_history, I don't think they are relevant in this case.

Well, that's where I disagree -- I think we need to take a step back
and look at the wider picture.

For example, these settings are per-inferior:

(gdb) set environment
(gdb) set args

E.g.,:

(gdb) inferior 1
(gdb) show args
"foo"
(gdb) inferior 2
(gdb) show args 
"bar"
(gdb) inferior 1
(gdb) show args
"foo"

This allows preparing multiple independent inferior
environments, before starting all inferiors.

From that perspective, the inferior's current working directory
looks to me exactly the same kind of variable as "args" or
"environment".  So from that angle, it'd seem to make sense to
make the current working directory that "cd" affects be a
per-inferior setting.  However, that may conflict with the use
cases that expect "cd" to affect where GDB loads scripts
from [a host setting], which seems unrelated to the inferior's
current working directory [which is a target setting and may
resolve to a path in a different machine].

To address that, it'd seem natural to add a "set cwd" command
(to go with set args/environment) that would set up a per-inferior
current working directory, and leave "cd" for gdb's own current working
directory, which affects other things like loading scripts.

With that approach, if "show cwd" is empty, then the inferior
would inherit gdb's current directory, so it'd end up being
a backward compatible extension.

Making the setting be per-inferior instead of adding a
remote-specific "set remote directory" still addresses
local/remote parity, because the interface/feature ends up
working the same way independently of native vs remote target.

Consider this a strawman proposal, to get the discussion going.
I'm not exactly sure it's the best interface either.

I also wonder whether "set sysroot" should affect this setting
in some way.  Maybe.  Maybe not.  I haven't thought about that.

> 
> Having said that, I'd like to discuss more about the ultimate goal, so
> that I know I'm pursuing the right things here.
> 
> Thanks,
> 

Thanks,
Pedro Alves
  
Sergio Durigan Junior Sept. 6, 2017, 6:17 p.m. UTC | #9
On Wednesday, September 06 2017, Pedro Alves wrote:

> On 09/05/2017 06:45 PM, Sergio Durigan Junior wrote:
>> On Thursday, August 31 2017, Pedro Alves wrote:
>> 
>> As for your questions.  I looked at the code to find places where the
>> "current_directory" variable was being used.  This is the variable that
>> ultimately gets changed when "cd" is used.
>> 
>> Aside from impacting the inferior's cwd, current_directory is also used
>> on the ".gdb_history" machinery.
>> 
>>     tmpenv = getenv ("GDBHISTFILE");
>>     if (tmpenv)
>>       history_filename = xstrdup (tmpenv);
>>     else if (!history_filename)
>>       {
>>         /* We include the current directory so that if the user changes
>>            directories the file written will be the same as the one
>>            that was read.  */
>>   #ifdef __MSDOS__
>>         /* No leading dots in file names are allowed on MSDOS.  */
>>         history_filename = concat (current_directory, "/_gdb_history",
>>                                    (char *)NULL);
>>   #else
>>         history_filename = concat (current_directory, "/.gdb_history",
>>                                    (char *)NULL);
>>   #endif
>>       }
>>     read_history (history_filename);
>> 
>> As John Baldwin also mentioned, 'cd' has an effect when loading GDB
>> scripts.  And probably has an effect when loading other stuff.
>> 
>> Since gdbserver doesn't really support loading scripts and also doesn't
>> use .gdb_history, I don't think they are relevant in this case.
>
> Well, that's where I disagree -- I think we need to take a step back
> and look at the wider picture.
>
> For example, these settings are per-inferior:
>
> (gdb) set environment
> (gdb) set args
>
> E.g.,:
>
> (gdb) inferior 1
> (gdb) show args
> "foo"
> (gdb) inferior 2
> (gdb) show args 
> "bar"
> (gdb) inferior 1
> (gdb) show args
> "foo"
>
> This allows preparing multiple independent inferior
> environments, before starting all inferiors.
>
> From that perspective, the inferior's current working directory
> looks to me exactly the same kind of variable as "args" or
> "environment".  So from that angle, it'd seem to make sense to
> make the current working directory that "cd" affects be a
> per-inferior setting.  However, that may conflict with the use
> cases that expect "cd" to affect where GDB loads scripts
> from [a host setting], which seems unrelated to the inferior's
> current working directory [which is a target setting and may
> resolve to a path in a different machine].
>
> To address that, it'd seem natural to add a "set cwd" command
> (to go with set args/environment) that would set up a per-inferior
> current working directory, and leave "cd" for gdb's own current working
> directory, which affects other things like loading scripts.

This makes sense to me.  I confess I hadn't thought about how the "cd"
command is used for other things inside GDB; I was just tackling the
problem of the inferior's cwd, as is obvious from my patch.

Now I understand why you said my approach was not the best.  And I
agree: it works when you consider the inferior's cwd only, but it can
break other use cases that we obviously want to maintain.

> With that approach, if "show cwd" is empty, then the inferior
> would inherit gdb's current directory, so it'd end up being
> a backward compatible extension.

Right, that makes sense.  And perhaps we could print a (temporary)
warning when "cd" is used, saying that the "right" way to change the
inferior's cwd is to use "set cwd".  But these are just implementation
details.

> Making the setting be per-inferior instead of adding a
> remote-specific "set remote directory" still addresses
> local/remote parity, because the interface/feature ends up
> working the same way independently of native vs remote target.

Right.

> Consider this a strawman proposal, to get the discussion going.
> I'm not exactly sure it's the best interface either.

OK.  This proposal is better than my RFC, and I have the impression that
it is the right way forward, even if we have to make adjustments.  What
I can do right now is prepare a patch to simply implement the new
command, without worrying about the integration with gdbserver.

> I also wonder whether "set sysroot" should affect this setting
> in some way.  Maybe.  Maybe not.  I haven't thought about that.

I want to say that "set sysroot" and "set cwd" are two different (though
correlated) commands, but I'm not 100% sure.

IIUC "set sysroot" is used only for calculating the absolute paths of
shared libraries, and mostly in the context of a remote debugging.
Currently, it doesn't seem that "set sysroot" affects anything related
to the "cd" command, which makes sense to me.  But I may be missing some
context here.

In any case, as a first step of this task I think it makes sense to go
ahead and decouple the concept of setting an inferior's cwd from the
"cd" command, creating the "set cwd" command, as you proposed.  WDYT?
  

Patch

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