From f19c7ff839d7a32ebb48482ae7d318fb46ca823d Mon Sep 17 00:00:00 2001 From: John Darrington Date: Sat, 22 Sep 2018 12:35:41 +0200 Subject: [PATCH] GDBSERVER: Listen on a unix domain (instead of TCP) socket if requested. When invoking gdbserver, if the COMM parameter takes the form "unix::/path/name" then a local (unix) domain socket will be created with that name and gdbserver will listen for connections on that. gdb/ * NEWS: Mention new feature. gdb/gdbserver/ * configure.ac (AC_CHECK_HEADERS): Add sys/un.h. * configure: Regenerate. * remote-utils.c (remote_prepare): Create a local socket if requested. (remote_open): Don't attempt to open a file if it's a socket. (handle_accept_event): Display the name of the socket on connection. gdb/common/ * netstuff.c (parse_connection_spec)[prefixes]: New member for local domain sockets. --- gdb/NEWS | 4 + gdb/common/netstuff.c | 8 ++ gdb/gdbserver/configure.ac | 2 +- gdb/gdbserver/remote-utils.c | 159 +++++++++++++++++++++++++---------- 4 files changed, 126 insertions(+), 47 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 1f311a42d8d..0208ae76fc8 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/common/netstuff.c b/gdb/common/netstuff.c index c1c401ccb2a..11182c1e87e 100644 --- a/gdb/common/netstuff.c +++ b/gdb/common/netstuff.c @@ -56,6 +56,7 @@ parse_connection_spec_without_prefix (std::string spec, struct addrinfo *hint) && (spec[0] == '[' || std::count (spec.begin (), spec.end (), ':') > 1))); + bool is_unix = hint->ai_family == AF_UNIX; if (is_ipv6) { @@ -109,6 +110,12 @@ parse_connection_spec_without_prefix (std::string spec, struct addrinfo *hint) if (ret.host_str.empty ()) ret.host_str = "localhost"; + if (is_unix && ret.host_str != "localhost") + error (_("The host name must be empty or 'localhost' for a unix domain socket.")); + + if (is_unix && ret.port_str.empty ()) + error (_("A path name must be specified for a unix domain socket.")); + return ret; } @@ -138,6 +145,7 @@ parse_connection_spec (const char *spec, struct addrinfo *hint) { "tcp4:", AF_INET, SOCK_STREAM }, { "udp6:", AF_INET6, SOCK_DGRAM }, { "tcp6:", AF_INET6, SOCK_STREAM }, + { "unix:", AF_LOCAL, SOCK_STREAM }, }; for (const host_prefix prefix : prefixes) diff --git a/gdb/gdbserver/configure.ac b/gdb/gdbserver/configure.ac index f1dfa4546cc..1bc3631999d 100644 --- a/gdb/gdbserver/configure.ac +++ b/gdb/gdbserver/configure.ac @@ -96,7 +96,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 9199a9c7add..f968e554a52 100644 --- a/gdb/gdbserver/remote-utils.c +++ b/gdb/gdbserver/remote-utils.c @@ -38,6 +38,9 @@ #if HAVE_NETINET_IN_H #include #endif +#if HAVE_SYS_UN_H +#include +#endif #if HAVE_SYS_SOCKET_H #include #endif @@ -193,20 +196,36 @@ 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]; +#if HAVE_SYS_UN_H + if (sockaddr.ss_family == AF_UNIX) + { + struct sockaddr_un su; + socklen_t socklen = sizeof (su); - int r = getnameinfo ((struct sockaddr *) &sockaddr, len, - orig_host, sizeof (orig_host), - orig_port, sizeof (orig_port), - NI_NUMERICHOST | NI_NUMERICSERV); + if (getsockname (listen_desc, (struct sockaddr *) &su, &socklen) != 0) + 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); +#endif + { + /* 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 +269,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. */ @@ -258,7 +280,7 @@ remote_prepare (const char *name) hint.ai_protocol = IPPROTO_TCP; parsed_connection_spec parsed - = parse_connection_spec_without_prefix (name, &hint); + = parse_connection_spec (name, &hint); if (parsed.port_str.empty ()) { @@ -276,47 +298,92 @@ remote_prepare (const char *name) } #endif - int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (), - &hint, &ainfo); +#if HAVE_SYS_UN_H + 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 (hint.ai_family == AF_LOCAL) + { + const char *sock_name = parsed.port_str.c_str (); + if (parsed.port_str.length () > UNIX_PATH_MAX - 1) + { + error + (_("%s is too long. Socket names may be no longer than %s bytes."), + sock_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"); - struct addrinfo *iter; + memset (&unix_addr, 0, sizeof (unix_addr)); + unix_addr.sun_family = AF_UNIX; + strncpy (unix_addr.sun_path, sock_name, UNIX_PATH_MAX - 1); - for (iter = ainfo; iter != NULL; iter = iter->ai_next) - { - listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype, - iter->ai_protocol); + struct stat statbuf; + int stat_result = stat (sock_name, &statbuf); - if (listen_desc >= 0) - break; + if (stat_result == 0 + && S_ISSOCK (statbuf.st_mode)) + unlink (sock_name); + + addr = (struct sockaddr *) &unix_addr; + addrlen = sizeof (unix_addr); } + else +#endif + { + struct addrinfo *iter; - if (iter == NULL) - perror_with_name ("Can't open socket"); + int r = getaddrinfo (parsed.host_str.c_str (), + parsed.port_str.c_str (), + &hint, &ainfo); - /* Allow rapid reuse of this port. */ - tmp = 1; - setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, - sizeof (tmp)); + if (r != 0) + error (_("%s: cannot resolve name: %s"), name, gai_strerror (r)); - 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); + scoped_free_addrinfo freeaddrinfo (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 (listen_desc >= 0) + break; + } + + 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)); + + if (iter->ai_socktype == SOCK_DGRAM) + error (_("Only stream orientated protocols are currently supported.")); + + 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) @@ -354,11 +421,11 @@ remote_open (const char *name) } #ifndef USE_WIN32API else if (port_str == NULL) - { + { struct stat statbuf; if (stat (name, &statbuf) == 0 - && (S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode))) + && (S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode))) remote_desc = open (name, O_RDWR); else { @@ -1153,7 +1220,7 @@ prepare_resume_reply (char *buf, ptid_t ptid, struct regcache *regcache; if ((status->kind == TARGET_WAITKIND_FORKED && cs.report_fork_events) - || (status->kind == TARGET_WAITKIND_VFORKED + || (status->kind == TARGET_WAITKIND_VFORKED && cs.report_vfork_events)) { enum gdb_signal signal = GDB_SIGNAL_TRAP; @@ -1165,7 +1232,7 @@ prepare_resume_reply (char *buf, ptid_t ptid, buf = write_ptid (buf, status->value.related_pid); strcat (buf, ";"); } - else if (status->kind == TARGET_WAITKIND_VFORK_DONE + else if (status->kind == TARGET_WAITKIND_VFORK_DONE && cs.report_vfork_events) { enum gdb_signal signal = GDB_SIGNAL_TRAP; -- 2.30.2