Patchwork GDBSERVER: Listen on a unix domain (instead of TCP) socket if requested.

login
register
mail settings
Submitter John Darrington
Date Oct. 9, 2018, 5:32 p.m.
Message ID <20181009173257.11250-2-john@darrington.wattle.id.au>
Download mbox | patch
Permalink /patch/29682/
State New
Headers show

Comments

John Darrington - Oct. 9, 2018, 5:32 p.m.
When invoking gdbserver, if the COMM parameter does not include a colon (:) and
is not the name of an existing character device, then a local (unix) domain
socket will be created with that name and gdbserver will listen for connections
on that.

    gdb/doc/
    * gdb.texinfo (Server): Describe connection over a local domain socket.

    gdb/gdbserver/
    * NEWS: Mention new feature.
    * configure.ac (AC_CHECK_HEADERS): Add sys/un.h.
    * remote-utils.c (remote_prepare): Create a local socket if requested.
    * remote-utils.c (remote_open):  Don't attempt to open a file if it's a socket.
    * remote-utils.c (handle_accept_event): Display the name of the socket on connection.
---
 gdb/NEWS                     |   4 ++
 gdb/doc/gdb.texinfo          |  26 +++++--
 gdb/gdbserver/configure.ac   |   2 +-
 gdb/gdbserver/remote-utils.c | 164 ++++++++++++++++++++++++++++++-------------
 4 files changed, 141 insertions(+), 55 deletions(-)
Eli Zaretskii - Oct. 9, 2018, 5:56 p.m.
> From: John Darrington <john@darrington.wattle.id.au>
> Cc: John Darrington <john@darrington.wattle.id.au>
> Date: Tue,  9 Oct 2018 19:32:57 +0200
> 
> When invoking gdbserver, if the COMM parameter does not include a colon (:) and
> is not the name of an existing character device, then a local (unix) domain
> socket will be created with that name and gdbserver will listen for connections
> on that.
> 
>     gdb/doc/
>     * gdb.texinfo (Server): Describe connection over a local domain socket.
> 
>     gdb/gdbserver/
>     * NEWS: Mention new feature.
>     * configure.ac (AC_CHECK_HEADERS): Add sys/un.h.
>     * remote-utils.c (remote_prepare): Create a local socket if requested.
>     * remote-utils.c (remote_open):  Don't attempt to open a file if it's a socket.
>     * remote-utils.c (handle_accept_event): Display the name of the socket on connection.

Thanks.  The documentation parts are approved, but I think we should
say somewhere that this feature is only available on systems that
support Unix domain sockets.
Pedro Alves - Oct. 9, 2018, 6:02 p.m.
On 10/09/2018 06:32 PM, John Darrington wrote:
> When invoking gdbserver, if the COMM parameter does not include a colon (:) and
> is not the name of an existing character device, then a local (unix) domain
> socket will be created with that name and gdbserver will listen for connections
> on that.

Is that "colon/no-colon" magic something that tools frequently do?
Off hand it doesn't seem like a good idea to me, because if you typo a
character device name or unix tcp host name, you end up creating a
unix socket.  I'd think an explicit option would be better.  E.g.,
reuse gdb's connection specs, i.e., a prefix, like:

 gdbserver unix:/tmp/some/path 

which can be extended just like gdb's:

 gdbserver tcp:host:port
 gdbserver tcp6:host:port

etc.

That would suggest extending parse_connection_spec to support "unix" specs.

> 
>     gdb/doc/
>     * gdb.texinfo (Server): Describe connection over a local domain socket.
> 
>     gdb/gdbserver/
>     * NEWS: Mention new feature.

NEWS is under gdb/, not gdb/gdbserver/.

>     * configure.ac (AC_CHECK_HEADERS): Add sys/un.h.
>     * remote-utils.c (remote_prepare): Create a local socket if requested.
>     * remote-utils.c (remote_open):  Don't attempt to open a file if it's a socket.
>     * remote-utils.c (handle_accept_event): Display the name of the socket on connection.

Don't repeat "* remote-utils.c" for the second and third functions.

Add "* configure: Regenerate.".


> ---
>  gdb/NEWS                     |   4 ++
>  gdb/doc/gdb.texinfo          |  26 +++++--
>  gdb/gdbserver/configure.ac   |   2 +-
>  gdb/gdbserver/remote-utils.c | 164 ++++++++++++++++++++++++++++++-------------
>  4 files changed, 141 insertions(+), 55 deletions(-)
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 78d20713a8..95c5083d9b 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -15,6 +15,10 @@
>    can be passed using the '[ADDRESS]:PORT' notation, or the regular
>    'ADDRESS:PORT' method.
>  
> +* GDB and GDBserver now support local domain socket connections.  The
> +  name of a local domain socket may be provided instead of the
> +  [ADDRESS]:PORT notation.
> +
>  * DWARF index cache: GDB can now automatically save indices of DWARF
>    symbols on disk to speed up further loading of the same binaries.
>  
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index efd6dffb1e..44b319ebc7 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -21075,9 +21075,13 @@ syntax is:
>  target> gdbserver @var{comm} @var{program} [ @var{args} @dots{} ]
>  @end smallexample
>  
> -@var{comm} is either a device name (to use a serial line), or a TCP
> +@var{comm} is either an existing device name (to use a serial line), or a TCP
>  hostname and portnumber, or @code{-} or @code{stdio} to use
>  stdin/stdout of @code{gdbserver}.
> +If @var{comm} is none of the above, then a local domain socket
> +will be created with that name.
> +@cindex local socket
> +@cindex Unix domain socket
>  For example, to debug Emacs with the argument
>  @samp{foo.txt} and communicate with @value{GDBN} over the serial port
>  @file{/dev/com1}:
> @@ -21107,6 +21111,20 @@ conflicts with another service, @code{gdbserver} prints an error message
>  and exits.}  You must use the same port number with the host @value{GDBN}
>  @code{target remote} command.
>  
> +If the target and local machine are one and the same, then you can
> +use local domain socket instead of a TCP connection:
> +
> +@smallexample
> +target> gdbserver /tmp/local-socket emacs foo.txt
> +@end smallexample
> +
> +A local domain socket called @file{/tmp/local-socket} will be created
> +in the filesystem.
> +If there is already a local domain socket with this name it will be removed.
> +@smallexample
> +(gdb) target remote /tmp/local-socket
> +@end smallexample
> +
>  The @code{stdio} connection is useful when starting @code{gdbserver}
>  with ssh:
>  
> @@ -21155,10 +21173,10 @@ In case more than one copy of @var{program} is running, or @var{program}
>  has multiple threads, most versions of @code{pidof} support the
>  @code{-s} option to only return the first process ID.
>  
> -@subsubsection TCP port allocation lifecycle of @code{gdbserver}
> +@subsubsection Socket lifecycle of @code{gdbserver}
>  
>  This section applies only when @code{gdbserver} is run to listen on a TCP
> -port.
> +port or a unix domain socket.
>  
>  @code{gdbserver} normally terminates after all of its debugged processes have
>  terminated in @kbd{target remote} mode.  On the other hand, for @kbd{target
> @@ -21174,7 +21192,7 @@ Such reconnecting is useful for features like @ref{disconnected tracing}.  For
>  completeness, at most one @value{GDBN} can be connected at a time.
>  
>  @cindex @option{--once}, @code{gdbserver} option
> -By default, @code{gdbserver} keeps the listening TCP port open, so that
> +By default, @code{gdbserver} keeps the listening socket open, so that
>  subsequent connections are possible.  However, if you start @code{gdbserver}
>  with the @option{--once} option, it will stop listening for any further
>  connection attempts after connecting to the first @value{GDBN} session.  This
> diff --git a/gdb/gdbserver/configure.ac b/gdb/gdbserver/configure.ac
> index fa3ca53efd..de7e7d4103 100644
> --- a/gdb/gdbserver/configure.ac
> +++ b/gdb/gdbserver/configure.ac
> @@ -98,7 +98,7 @@ ACX_CONFIGURE_DIR(["../../libiberty"], ["build-libiberty-gdbserver"])
>  AC_CHECK_HEADERS(termios.h sys/reg.h string.h dnl
>  		 proc_service.h sys/procfs.h linux/elf.h dnl
>  		 fcntl.h signal.h sys/file.h dnl
> -		 sys/ioctl.h netinet/in.h sys/socket.h netdb.h dnl
> +		 sys/ioctl.h netinet/in.h sys/socket.h sys/un.h netdb.h dnl
>  		 netinet/tcp.h arpa/inet.h)
>  AC_FUNC_FORK
>  AC_CHECK_FUNCS(getauxval pread pwrite pread64 setns)
> diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
> index 9199a9c7ad..00e3340156 100644
> --- a/gdb/gdbserver/remote-utils.c
> +++ b/gdb/gdbserver/remote-utils.c
> @@ -38,6 +38,9 @@
>  #if HAVE_NETINET_IN_H
>  #include <netinet/in.h>
>  #endif
> +#if HAVE_SYS_UN_H
> +#include <sys/un.h>
> +#endif
>  #if HAVE_SYS_SOCKET_H
>  #include <sys/socket.h>
>  #endif
> @@ -193,20 +196,35 @@ handle_accept_event (int err, gdb_client_data client_data)
>       descriptor open for add_file_handler to wait for a new connection.  */
>    delete_file_handler (listen_desc);
>  
> -  /* Convert IP address to string.  */
> -  char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
> -
> -  int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
> -		       orig_host, sizeof (orig_host),
> -		       orig_port, sizeof (orig_port),
> -		       NI_NUMERICHOST | NI_NUMERICSERV);
> +  if (sockaddr.ss_family == AF_UNIX)
> +    {
> +      struct sockaddr_un su;
> +      socklen_t len;
> +      if (0 != getsockname (listen_desc, (struct sockaddr *) &su, &len))

We don't use that "constant on lhs style".  Put the 0 != on the rhs.

> +	{
> +	  perror (_("Could not obtain remote address"));
> +	}

Remove unnecessary curly braces.

>  
> -  if (r != 0)
> -    fprintf (stderr, _("Could not obtain remote address: %s\n"),
> -	     gai_strerror (r));
> +      fprintf (stderr, _("Remote debugging on local socket bound to %s\n"),
> +	       su.sun_path);
> +    }
>    else
> -    fprintf (stderr, _("Remote debugging from host %s, port %s\n"),
> -	     orig_host, orig_port);
> +    {
> +      /* Convert IP address to string.  */
> +      char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
> +
> +      int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
> +			   orig_host, sizeof (orig_host),
> +			   orig_port, sizeof (orig_port),
> +			   NI_NUMERICHOST | NI_NUMERICSERV);
> +
> +      if (r != 0)
> +	fprintf (stderr, _("Could not obtain remote address: %s\n"),
> +		 gai_strerror (r));
> +      else
> +	fprintf (stderr, _("Remote debugging from host %s, port %s\n"),
> +		 orig_host, orig_port);
> +    }
>  
>    enable_async_notification (remote_desc);
>  
> @@ -250,6 +268,9 @@ remote_prepare (const char *name)
>    struct addrinfo hint;
>    struct addrinfo *ainfo;
>  
> +  struct sockaddr *addr;
> +  socklen_t addrlen;
> +
>    memset (&hint, 0, sizeof (hint));
>    /* Assume no prefix will be passed, therefore we should use
>       AF_UNSPEC.  */
> @@ -260,10 +281,17 @@ remote_prepare (const char *name)
>    parsed_connection_spec parsed
>      = parse_connection_spec_without_prefix (name, &hint);
>  
> +  struct stat statbuf;
> +  int stat_result = stat (name, &statbuf);
> +
>    if (parsed.port_str.empty ())
>      {
> -      cs.transport_is_reliable = 0;
> -      return;
> +      if (stat_result == 0
> +	  && (S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode)))
> +	{
> +	  cs.transport_is_reliable = 0;
> +	  return;
> +	}
>      }
>  
>  #ifdef USE_WIN32API
> @@ -276,47 +304,82 @@ remote_prepare (const char *name)
>      }
>  #endif
>  
> -  int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
> -		       &hint, &ainfo);
> +  struct sockaddr_un unix_addr;
>  
> -  if (r != 0)
> -    error (_("%s: cannot resolve name: %s"), name, gai_strerror (r));
> +#ifndef UNIX_PATH_MAX
> +#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) NULL)->sun_path)
> +#endif
>  
> -  scoped_free_addrinfo freeaddrinfo (ainfo);
> +  if (parsed.port_str.empty ())
> +    {
> +      if (strlen (name) > UNIX_PATH_MAX - 1)
> +	{
> +	  error
> +	    (_("%s is too long.  Socket names may be no longer than %s bytes."),
> +	     name, pulongest (UNIX_PATH_MAX - 1));
> +	  return;
> +	}
> +      listen_desc = socket (AF_UNIX,  SOCK_STREAM,  0);
> +      if (listen_desc < 0)
> +	perror_with_name ("Can't open socket");
> +
> +      memset (&unix_addr, 0, sizeof (unix_addr));
> +      unix_addr.sun_family = AF_UNIX;
> +      strncpy (unix_addr.sun_path, parsed.host_str.c_str(), UNIX_PATH_MAX - 1);
> +      if (stat_result == 0
> +	  && (S_IFSOCK & statbuf.st_mode))
> +	unlink (name);
> +
> +      addr = (struct sockaddr *) &unix_addr;
> +      addrlen = sizeof (unix_addr);
> +    }
> +  else
> +    {
> +      struct addrinfo *iter;
>  
> -  struct addrinfo *iter;
> +      int r = getaddrinfo (parsed.host_str.c_str (),
> +			   parsed.port_str.c_str (),
> +			   &hint, &ainfo);
>  
> -  for (iter = ainfo; iter != NULL; iter = iter->ai_next)
> -    {
> -      listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype,
> -					iter->ai_protocol);
> +      if (r != 0)
> +	error (_("%s: cannot resolve name: %s"), name, gai_strerror (r));
>  
> -      if (listen_desc >= 0)
> -	break;
> -    }
> +      scoped_free_addrinfo freeaddrinfo (ainfo);
>  
> -  if (iter == NULL)
> -    perror_with_name ("Can't open socket");
> +      for (iter = ainfo; iter != NULL; iter = iter->ai_next)
> +	{
> +	  listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype,
> +					    iter->ai_protocol);
>  
> -  /* Allow rapid reuse of this port. */
> -  tmp = 1;
> -  setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
> -	      sizeof (tmp));
> +	  if (listen_desc >= 0)
> +	    break;
> +	}
>  
> -  switch (iter->ai_family)
> -    {
> -    case AF_INET:
> -      ((struct sockaddr_in *) iter->ai_addr)->sin_addr.s_addr = INADDR_ANY;
> -      break;
> -    case AF_INET6:
> -      ((struct sockaddr_in6 *) iter->ai_addr)->sin6_addr = in6addr_any;
> -      break;
> -    default:
> -      internal_error (__FILE__, __LINE__,
> -		      _("Invalid 'ai_family' %d\n"), iter->ai_family);
> +      if (iter == NULL)
> +	perror_with_name ("Can't open socket");
> +
> +      /* Allow rapid reuse of this port. */
> +      tmp = 1;
> +      setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
> +		  sizeof (tmp));
> +
> +      switch (iter->ai_family)
> +	{
> +	case AF_INET:
> +	  ((struct sockaddr_in *) iter->ai_addr)->sin_addr.s_addr = INADDR_ANY;
> +	  break;
> +	case AF_INET6:
> +	  ((struct sockaddr_in6 *) iter->ai_addr)->sin6_addr = in6addr_any;
> +	  break;
> +	default:
> +	  internal_error (__FILE__, __LINE__,
> +			  _("Invalid 'ai_family' %d\n"), iter->ai_family);
> +	}
> +      addr = iter->ai_addr;
> +      addrlen = iter->ai_addrlen;
>      }
>  
> -  if (bind (listen_desc, iter->ai_addr, iter->ai_addrlen) != 0)
> +  if (bind (listen_desc, addr, addrlen) != 0)
>      perror_with_name ("Can't bind address");
>  
>    if (listen (listen_desc, 1) != 0)
> @@ -334,6 +397,9 @@ remote_open (const char *name)
>    const char *port_str;
>  
>    port_str = strchr (name, ':');
> +  struct stat statbuf;
> +  int stat_result = stat (name, &statbuf);
> +
>  #ifdef USE_WIN32API
>    if (port_str == NULL)
>      error ("Only HOST:PORT is supported on this platform.");
> @@ -353,12 +419,10 @@ remote_open (const char *name)
>        add_file_handler (remote_desc, handle_serial_event, NULL);
>      }
>  #ifndef USE_WIN32API
> -  else if (port_str == NULL)
> +  else if (port_str == NULL && stat_result == 0
> +	   && 0 == (S_IFSOCK & statbuf.st_mode))

Constant on rhs.

>      {
> -      struct stat statbuf;
> -
> -      if (stat (name, &statbuf) == 0
> -	  && (S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode)))
> +      if (S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode))
>  	remote_desc = open (name, O_RDWR);
>        else
>  	{
> 

Thanks,
Pedro Alves
John Darrington - Oct. 9, 2018, 6:41 p.m.
On Tue, Oct 09, 2018 at 07:02:14PM +0100, Pedro Alves wrote:
     On 10/09/2018 06:32 PM, John Darrington wrote:
     > When invoking gdbserver, if the COMM parameter does not include a colon (:) and
     > is not the name of an existing character device, then a local (unix) domain
     > socket will be created with that name and gdbserver will listen for connections
     > on that.
     
     Is that "colon/no-colon" magic something that tools frequently do?

Not exactly.  Tools with which I'm familiar with work as follows:

:1234           Creates a unix domain socket on the local host called 1234
localhost:1234  Listens on TCP port 1234

which is the way I think gdb ought to work, but this would be
inconsistent with it's current behaviour and cause confusion if somebody
used an old version of gdb with a new version of gdbserver or
vici-versa.

J'
Pedro Alves - Oct. 9, 2018, 6:53 p.m.
On 10/09/2018 07:41 PM, John Darrington wrote:
> On Tue, Oct 09, 2018 at 07:02:14PM +0100, Pedro Alves wrote:
>      On 10/09/2018 06:32 PM, John Darrington wrote:
>      > When invoking gdbserver, if the COMM parameter does not include a colon (:) and
>      > is not the name of an existing character device, then a local (unix) domain
>      > socket will be created with that name and gdbserver will listen for connections
>      > on that.
>      
>      Is that "colon/no-colon" magic something that tools frequently do?
> 
> Not exactly.  Tools with which I'm familiar with work as follows:
> 
> :1234           Creates a unix domain socket on the local host called 1234
> localhost:1234  Listens on TCP port 1234
> 
> which is the way I think gdb ought to work, but this would be
> inconsistent with it's current behaviour and cause confusion if somebody
> used an old version of gdb with a new version of gdbserver or
> vici-versa.

In that example you didn't even pass a path to a unix domain socket.
You let the tool create it, I suppose.  It doesn't feel like
apples to apples.

In those tools you know, how would you pass the path to the local
socket then?

Thanks,
Pedro Alves
John Darrington - Oct. 9, 2018, 7 p.m.
On Tue, Oct 09, 2018 at 07:53:55PM +0100, Pedro Alves wrote:
     On 10/09/2018 07:41 PM, John Darrington wrote:
     > On Tue, Oct 09, 2018 at 07:02:14PM +0100, Pedro Alves wrote:
     >      On 10/09/2018 06:32 PM, John Darrington wrote:
     >      > When invoking gdbserver, if the COMM parameter does not include a colon (:) and
     >      > is not the name of an existing character device, then a local (unix) domain
     >      > socket will be created with that name and gdbserver will listen for connections
     >      > on that.
     >      
     >      Is that "colon/no-colon" magic something that tools frequently do?
     > 
     > Not exactly.  Tools with which I'm familiar with work as follows:
     > 
     > :1234           Creates a unix domain socket on the local host called 1234
     > localhost:1234  Listens on TCP port 1234
     > 
     > which is the way I think gdb ought to work, but this would be
     > inconsistent with it's current behaviour and cause confusion if somebody
     > used an old version of gdb with a new version of gdbserver or
     > vici-versa.
     
     In that example you didn't even pass a path to a unix domain socket.
     You let the tool create it, I suppose.  It doesn't feel like
     apples to apples.
     
     In those tools you know, how would you pass the path to the local
     socket then?
     

If you give no path, it'll refer to a socket in the current working
directory.  An example with a path would be  :/tmp/this/socket
Pedro Alves - Oct. 9, 2018, 7:06 p.m.

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 78d20713a8..95c5083d9b 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -15,6 +15,10 @@ 
   can be passed using the '[ADDRESS]:PORT' notation, or the regular
   'ADDRESS:PORT' method.
 
+* GDB and GDBserver now support local domain socket connections.  The
+  name of a local domain socket may be provided instead of the
+  [ADDRESS]:PORT notation.
+
 * DWARF index cache: GDB can now automatically save indices of DWARF
   symbols on disk to speed up further loading of the same binaries.
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index efd6dffb1e..44b319ebc7 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21075,9 +21075,13 @@  syntax is:
 target> gdbserver @var{comm} @var{program} [ @var{args} @dots{} ]
 @end smallexample
 
-@var{comm} is either a device name (to use a serial line), or a TCP
+@var{comm} is either an existing device name (to use a serial line), or a TCP
 hostname and portnumber, or @code{-} or @code{stdio} to use
 stdin/stdout of @code{gdbserver}.
+If @var{comm} is none of the above, then a local domain socket
+will be created with that name.
+@cindex local socket
+@cindex Unix domain socket
 For example, to debug Emacs with the argument
 @samp{foo.txt} and communicate with @value{GDBN} over the serial port
 @file{/dev/com1}:
@@ -21107,6 +21111,20 @@  conflicts with another service, @code{gdbserver} prints an error message
 and exits.}  You must use the same port number with the host @value{GDBN}
 @code{target remote} command.
 
+If the target and local machine are one and the same, then you can
+use local domain socket instead of a TCP connection:
+
+@smallexample
+target> gdbserver /tmp/local-socket emacs foo.txt
+@end smallexample
+
+A local domain socket called @file{/tmp/local-socket} will be created
+in the filesystem.
+If there is already a local domain socket with this name it will be removed.
+@smallexample
+(gdb) target remote /tmp/local-socket
+@end smallexample
+
 The @code{stdio} connection is useful when starting @code{gdbserver}
 with ssh:
 
@@ -21155,10 +21173,10 @@  In case more than one copy of @var{program} is running, or @var{program}
 has multiple threads, most versions of @code{pidof} support the
 @code{-s} option to only return the first process ID.
 
-@subsubsection TCP port allocation lifecycle of @code{gdbserver}
+@subsubsection Socket lifecycle of @code{gdbserver}
 
 This section applies only when @code{gdbserver} is run to listen on a TCP
-port.
+port or a unix domain socket.
 
 @code{gdbserver} normally terminates after all of its debugged processes have
 terminated in @kbd{target remote} mode.  On the other hand, for @kbd{target
@@ -21174,7 +21192,7 @@  Such reconnecting is useful for features like @ref{disconnected tracing}.  For
 completeness, at most one @value{GDBN} can be connected at a time.
 
 @cindex @option{--once}, @code{gdbserver} option
-By default, @code{gdbserver} keeps the listening TCP port open, so that
+By default, @code{gdbserver} keeps the listening socket open, so that
 subsequent connections are possible.  However, if you start @code{gdbserver}
 with the @option{--once} option, it will stop listening for any further
 connection attempts after connecting to the first @value{GDBN} session.  This
diff --git a/gdb/gdbserver/configure.ac b/gdb/gdbserver/configure.ac
index fa3ca53efd..de7e7d4103 100644
--- a/gdb/gdbserver/configure.ac
+++ b/gdb/gdbserver/configure.ac
@@ -98,7 +98,7 @@  ACX_CONFIGURE_DIR(["../../libiberty"], ["build-libiberty-gdbserver"])
 AC_CHECK_HEADERS(termios.h sys/reg.h string.h dnl
 		 proc_service.h sys/procfs.h linux/elf.h dnl
 		 fcntl.h signal.h sys/file.h dnl
-		 sys/ioctl.h netinet/in.h sys/socket.h netdb.h dnl
+		 sys/ioctl.h netinet/in.h sys/socket.h sys/un.h netdb.h dnl
 		 netinet/tcp.h arpa/inet.h)
 AC_FUNC_FORK
 AC_CHECK_FUNCS(getauxval pread pwrite pread64 setns)
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 9199a9c7ad..00e3340156 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -38,6 +38,9 @@ 
 #if HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #endif
+#if HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
 #if HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
 #endif
@@ -193,20 +196,35 @@  handle_accept_event (int err, gdb_client_data client_data)
      descriptor open for add_file_handler to wait for a new connection.  */
   delete_file_handler (listen_desc);
 
-  /* Convert IP address to string.  */
-  char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
-
-  int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
-		       orig_host, sizeof (orig_host),
-		       orig_port, sizeof (orig_port),
-		       NI_NUMERICHOST | NI_NUMERICSERV);
+  if (sockaddr.ss_family == AF_UNIX)
+    {
+      struct sockaddr_un su;
+      socklen_t len;
+      if (0 != getsockname (listen_desc, (struct sockaddr *) &su, &len))
+	{
+	  perror (_("Could not obtain remote address"));
+	}
 
-  if (r != 0)
-    fprintf (stderr, _("Could not obtain remote address: %s\n"),
-	     gai_strerror (r));
+      fprintf (stderr, _("Remote debugging on local socket bound to %s\n"),
+	       su.sun_path);
+    }
   else
-    fprintf (stderr, _("Remote debugging from host %s, port %s\n"),
-	     orig_host, orig_port);
+    {
+      /* Convert IP address to string.  */
+      char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
+
+      int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
+			   orig_host, sizeof (orig_host),
+			   orig_port, sizeof (orig_port),
+			   NI_NUMERICHOST | NI_NUMERICSERV);
+
+      if (r != 0)
+	fprintf (stderr, _("Could not obtain remote address: %s\n"),
+		 gai_strerror (r));
+      else
+	fprintf (stderr, _("Remote debugging from host %s, port %s\n"),
+		 orig_host, orig_port);
+    }
 
   enable_async_notification (remote_desc);
 
@@ -250,6 +268,9 @@  remote_prepare (const char *name)
   struct addrinfo hint;
   struct addrinfo *ainfo;
 
+  struct sockaddr *addr;
+  socklen_t addrlen;
+
   memset (&hint, 0, sizeof (hint));
   /* Assume no prefix will be passed, therefore we should use
      AF_UNSPEC.  */
@@ -260,10 +281,17 @@  remote_prepare (const char *name)
   parsed_connection_spec parsed
     = parse_connection_spec_without_prefix (name, &hint);
 
+  struct stat statbuf;
+  int stat_result = stat (name, &statbuf);
+
   if (parsed.port_str.empty ())
     {
-      cs.transport_is_reliable = 0;
-      return;
+      if (stat_result == 0
+	  && (S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode)))
+	{
+	  cs.transport_is_reliable = 0;
+	  return;
+	}
     }
 
 #ifdef USE_WIN32API
@@ -276,47 +304,82 @@  remote_prepare (const char *name)
     }
 #endif
 
-  int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
-		       &hint, &ainfo);
+  struct sockaddr_un unix_addr;
 
-  if (r != 0)
-    error (_("%s: cannot resolve name: %s"), name, gai_strerror (r));
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) NULL)->sun_path)
+#endif
 
-  scoped_free_addrinfo freeaddrinfo (ainfo);
+  if (parsed.port_str.empty ())
+    {
+      if (strlen (name) > UNIX_PATH_MAX - 1)
+	{
+	  error
+	    (_("%s is too long.  Socket names may be no longer than %s bytes."),
+	     name, pulongest (UNIX_PATH_MAX - 1));
+	  return;
+	}
+      listen_desc = socket (AF_UNIX,  SOCK_STREAM,  0);
+      if (listen_desc < 0)
+	perror_with_name ("Can't open socket");
+
+      memset (&unix_addr, 0, sizeof (unix_addr));
+      unix_addr.sun_family = AF_UNIX;
+      strncpy (unix_addr.sun_path, parsed.host_str.c_str(), UNIX_PATH_MAX - 1);
+      if (stat_result == 0
+	  && (S_IFSOCK & statbuf.st_mode))
+	unlink (name);
+
+      addr = (struct sockaddr *) &unix_addr;
+      addrlen = sizeof (unix_addr);
+    }
+  else
+    {
+      struct addrinfo *iter;
 
-  struct addrinfo *iter;
+      int r = getaddrinfo (parsed.host_str.c_str (),
+			   parsed.port_str.c_str (),
+			   &hint, &ainfo);
 
-  for (iter = ainfo; iter != NULL; iter = iter->ai_next)
-    {
-      listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype,
-					iter->ai_protocol);
+      if (r != 0)
+	error (_("%s: cannot resolve name: %s"), name, gai_strerror (r));
 
-      if (listen_desc >= 0)
-	break;
-    }
+      scoped_free_addrinfo freeaddrinfo (ainfo);
 
-  if (iter == NULL)
-    perror_with_name ("Can't open socket");
+      for (iter = ainfo; iter != NULL; iter = iter->ai_next)
+	{
+	  listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype,
+					    iter->ai_protocol);
 
-  /* Allow rapid reuse of this port. */
-  tmp = 1;
-  setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
-	      sizeof (tmp));
+	  if (listen_desc >= 0)
+	    break;
+	}
 
-  switch (iter->ai_family)
-    {
-    case AF_INET:
-      ((struct sockaddr_in *) iter->ai_addr)->sin_addr.s_addr = INADDR_ANY;
-      break;
-    case AF_INET6:
-      ((struct sockaddr_in6 *) iter->ai_addr)->sin6_addr = in6addr_any;
-      break;
-    default:
-      internal_error (__FILE__, __LINE__,
-		      _("Invalid 'ai_family' %d\n"), iter->ai_family);
+      if (iter == NULL)
+	perror_with_name ("Can't open socket");
+
+      /* Allow rapid reuse of this port. */
+      tmp = 1;
+      setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
+		  sizeof (tmp));
+
+      switch (iter->ai_family)
+	{
+	case AF_INET:
+	  ((struct sockaddr_in *) iter->ai_addr)->sin_addr.s_addr = INADDR_ANY;
+	  break;
+	case AF_INET6:
+	  ((struct sockaddr_in6 *) iter->ai_addr)->sin6_addr = in6addr_any;
+	  break;
+	default:
+	  internal_error (__FILE__, __LINE__,
+			  _("Invalid 'ai_family' %d\n"), iter->ai_family);
+	}
+      addr = iter->ai_addr;
+      addrlen = iter->ai_addrlen;
     }
 
-  if (bind (listen_desc, iter->ai_addr, iter->ai_addrlen) != 0)
+  if (bind (listen_desc, addr, addrlen) != 0)
     perror_with_name ("Can't bind address");
 
   if (listen (listen_desc, 1) != 0)
@@ -334,6 +397,9 @@  remote_open (const char *name)
   const char *port_str;
 
   port_str = strchr (name, ':');
+  struct stat statbuf;
+  int stat_result = stat (name, &statbuf);
+
 #ifdef USE_WIN32API
   if (port_str == NULL)
     error ("Only HOST:PORT is supported on this platform.");
@@ -353,12 +419,10 @@  remote_open (const char *name)
       add_file_handler (remote_desc, handle_serial_event, NULL);
     }
 #ifndef USE_WIN32API
-  else if (port_str == NULL)
+  else if (port_str == NULL && stat_result == 0
+	   && 0 == (S_IFSOCK & statbuf.st_mode))
     {
-      struct stat statbuf;
-
-      if (stat (name, &statbuf) == 0
-	  && (S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode)))
+      if (S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode))
 	remote_desc = open (name, O_RDWR);
       else
 	{