[2/9,v2] Introduce nat/linux-namespaces.[ch]

Message ID 1430395542-16017-3-git-send-email-gbenson@redhat.com
State New, archived
Headers

Commit Message

Gary Benson April 30, 2015, 12:05 p.m. UTC
  This commit introduces new shared files nat/linux-namespaces.[ch]
containing code to support Linux namespaces that will be used by
both GDB and gdbserver.

gdb/ChangeLog:

	* configure.ac (AC_CHECK_FUNCS): Add setns.
	* config.in: Regenerate.
	* configure: Likewise.
	* nat/linux-namespaces.h: New file.
	* nat/linux-namespaces.c: Likewise.
	* Makefile.in (HFILES_NO_SRCDIR): Add nat/linux-namespaces.h.
	(linux-namespaces.o): New rule.
	* config/aarch64/linux.mh (NATDEPFILES): Add linux-namespaces.o.
	* config/alpha/alpha-linux.mh (NATDEPFILES): Likewise.
	* config/arm/linux.mh (NATDEPFILES): Likewise.
	* config/i386/linux.mh (NATDEPFILES): Likewise.
	* config/i386/linux64.mh (NATDEPFILES): Likewise.
	* config/ia64/linux.mh (NATDEPFILES): Likewise.
	* config/m32r/linux.mh (NATDEPFILES): Likewise.
	* config/m68k/linux.mh (NATDEPFILES): Likewise.
	* config/mips/linux.mh (NATDEPFILES): Likewise.
	* config/pa/linux.mh (NATDEPFILES): Likewise.
	* config/powerpc/linux.mh (NATDEPFILES): Likewise.
	* config/powerpc/ppc64-linux.mh (NATDEPFILES): Likewise.
	* config/powerpc/spu-linux.mh (NATDEPFILES): Likewise.
	* config/s390/linux.mh (NATDEPFILES): Likewise.
	* config/sparc/linux.mh (NATDEPFILES): Likewise.
	* config/sparc/linux64.mh (NATDEPFILES): Likewise.
	* config/tilegx/linux.mh (NATDEPFILES): Likewise.
	* config/xtensa/linux.mh (NATDEPFILES): Likewise.

gdb/gdbserver/ChangeLog:

	* configure.ac (AC_CHECK_FUNCS): Add setns.
	* config.in: Regenerate.
	* configure: Likewise.
	* Makefile.in (SFILES): Add nat/linux-namespaces.c.
	(linux-namespaces.o): New rule.
	* configure.srv (srv_linux_obj): Add linux-namespaces.o.
---
 gdb/ChangeLog                     |   28 +
 gdb/Makefile.in                   |    6 +-
 gdb/config.in                     |    3 +
 gdb/config/aarch64/linux.mh       |    2 +-
 gdb/config/alpha/alpha-linux.mh   |    2 +-
 gdb/config/arm/linux.mh           |    2 +-
 gdb/config/i386/linux.mh          |    2 +-
 gdb/config/i386/linux64.mh        |    2 +-
 gdb/config/ia64/linux.mh          |    3 +-
 gdb/config/m32r/linux.mh          |    2 +-
 gdb/config/m68k/linux.mh          |    2 +-
 gdb/config/mips/linux.mh          |    2 +-
 gdb/config/pa/linux.mh            |    2 +-
 gdb/config/powerpc/linux.mh       |    2 +-
 gdb/config/powerpc/ppc64-linux.mh |    3 +-
 gdb/config/powerpc/spu-linux.mh   |    4 +-
 gdb/config/s390/linux.mh          |    2 +-
 gdb/config/sparc/linux.mh         |    2 +-
 gdb/config/sparc/linux64.mh       |    2 +-
 gdb/config/tilegx/linux.mh        |    2 +-
 gdb/config/xtensa/linux.mh        |    2 +-
 gdb/configure                     |    2 +-
 gdb/configure.ac                  |    2 +-
 gdb/gdbserver/ChangeLog           |    9 +
 gdb/gdbserver/Makefile.in         |    6 +-
 gdb/gdbserver/config.in           |    3 +
 gdb/gdbserver/configure           |    2 +-
 gdb/gdbserver/configure.ac        |    2 +-
 gdb/gdbserver/configure.srv       |    2 +-
 gdb/nat/linux-namespaces.c        | 1042 +++++++++++++++++++++++++++++++++++++
 gdb/nat/linux-namespaces.h        |   76 +++
 31 files changed, 1197 insertions(+), 26 deletions(-)
 create mode 100644 gdb/nat/linux-namespaces.c
 create mode 100644 gdb/nat/linux-namespaces.h
  

Comments

Gary Benson May 1, 2015, 9:28 a.m. UTC | #1
Alban Crequy wrote:
> On Thu, Apr 30, 2015 at 2:05 PM, Gary Benson <gbenson@redhat.com> wrote:
> > This commit introduces new shared files nat/linux-namespaces.[ch]
> > containing code to support Linux namespaces that will be used by
> > both GDB and gdbserver.
> 
> Thanks for working on this!
> 
> > +/* We need to use setns(2) to handle filesystem access in mount
> > +   namespaces other than our own, but this isn't permitted for
> > +   multithreaded processes.  GDB is multithreaded when compiled
> > +   with Guile support, and may become multithreaded if compiled
> > +   with Python support.  We deal with this by spawning a single-
> > +   threaded helper process to access mount namespaces other than
> > +   our own.
> 
> setns() needs CAP_SYS_CHROOT and CAP_SYS_ADMIN to change the mnt
> namespace.  So users will need to run gdb as root...

As root, or with those privileges yes.  But if you're attaching to
a process in a container, it's not running as the same UID as you;
you have to have CAP_SYS_PTRACE, for example, to even get to the
point where GDB wants to access the files.

The scenario I'm targeting is that you have an application you want
to debug running in a container.  You don't want to run GDB as root
on the container host, so you start a second container with just the
privileges you need and run GDB from there.

I've been testing this with Docker, which has --cap-add and --cap-drop
options to adjust what privileges containers are granted when they are
created with "docker run".  I'm assuming other containering systems
have something similar.

> Would accessing the files directly through
> /proc/<pid_of_traced_process>/root/usr/lib/debug/... work, without
> needing the superuser? If it works, it would also remove the
> requirement for the single-threaded helper process.

No, you need to be root (or have CAP_DAC_OVERRIDE or something) to
access /proc/PID/root.  There's also issues with symlinks pointing
back to / if you access the files that way.  I investigated it back
in February and I don't think you can handle paths with symlinks in
a way that's 100% race-free.  (I've not been overly concerned with
security but I didn't want to introduce a way for a hostile app to
exploit GDB to break out of its container.)

Cheers,
Gary
  
Alban Crequy May 1, 2015, 1:18 p.m. UTC | #2
On Fri, May 1, 2015 at 11:28 AM, Gary Benson <gbenson@redhat.com> wrote:
>
> Alban Crequy wrote:
> > On Thu, Apr 30, 2015 at 2:05 PM, Gary Benson <gbenson@redhat.com> wrote:
> > > This commit introduces new shared files nat/linux-namespaces.[ch]
> > > containing code to support Linux namespaces that will be used by
> > > both GDB and gdbserver.
> >
> > Thanks for working on this!
> >
> > > +/* We need to use setns(2) to handle filesystem access in mount
> > > +   namespaces other than our own, but this isn't permitted for
> > > +   multithreaded processes.  GDB is multithreaded when compiled
> > > +   with Guile support, and may become multithreaded if compiled
> > > +   with Python support.  We deal with this by spawning a single-
> > > +   threaded helper process to access mount namespaces other than
> > > +   our own.
> >
> > setns() needs CAP_SYS_CHROOT and CAP_SYS_ADMIN to change the mnt
> > namespace.  So users will need to run gdb as root...
>
> As root, or with those privileges yes.  But if you're attaching to
> a process in a container, it's not running as the same UID as you;
> you have to have CAP_SYS_PTRACE, for example, to even get to the
> point where GDB wants to access the files.

Ok, I understand the scenario.

I had tried only with a non-root process in the container and gdb on
the host with the same uid, so I didn't need CAP_SYS_PTRACE. But
that's probably not a good use case.

> The scenario I'm targeting is that you have an application you want
> to debug running in a container.  You don't want to run GDB as root
> on the container host, so you start a second container with just the
> privileges you need and run GDB from there.

How do you get the pid of the process to give as a parameter to "gdb
-p" if gdb is running in a different pid namespace than the process to
debug?

> I've been testing this with Docker, which has --cap-add and --cap-drop
> options to adjust what privileges containers are granted when they are
> created with "docker run".  I'm assuming other containering systems
> have something similar.
>
> > Would accessing the files directly through
> > /proc/<pid_of_traced_process>/root/usr/lib/debug/... work, without
> > needing the superuser? If it works, it would also remove the
> > requirement for the single-threaded helper process.
>
> No, you need to be root (or have CAP_DAC_OVERRIDE or something) to
> access /proc/PID/root.  There's also issues with symlinks pointing
> back to / if you access the files that way.  I investigated it back
> in February and I don't think you can handle paths with symlinks in
> a way that's 100% race-free.  (I've not been overly concerned with
> security but I didn't want to introduce a way for a hostile app to
> exploit GDB to break out of its container.)

Thanks for the explanation.

Cheers,
Alban

> Cheers,
> Gary
>
> --
> http://gbenson.net/
  
Gary Benson May 1, 2015, 8:29 p.m. UTC | #3
Alban Crequy wrote:
> On Fri, May 1, 2015 at 11:28 AM, Gary Benson <gbenson@redhat.com> wrote:
> > Alban Crequy wrote:
> > > On Thu, Apr 30, 2015 at 2:05 PM, Gary Benson <gbenson@redhat.com> wrote:
> > > > This commit introduces new shared files nat/linux-namespaces.[ch]
> > > > containing code to support Linux namespaces that will be used by
> > > > both GDB and gdbserver.
> > >
> > > Thanks for working on this!
> > >
> > > > +/* We need to use setns(2) to handle filesystem access in mount
> > > > +   namespaces other than our own, but this isn't permitted for
> > > > +   multithreaded processes.  GDB is multithreaded when compiled
> > > > +   with Guile support, and may become multithreaded if compiled
> > > > +   with Python support.  We deal with this by spawning a single-
> > > > +   threaded helper process to access mount namespaces other than
> > > > +   our own.
> > >
> > > setns() needs CAP_SYS_CHROOT and CAP_SYS_ADMIN to change the mnt
> > > namespace.  So users will need to run gdb as root...
> >
> > As root, or with those privileges yes.  But if you're attaching to
> > a process in a container, it's not running as the same UID as you;
> > you have to have CAP_SYS_PTRACE, for example, to even get to the
> > point where GDB wants to access the files.
> 
> Ok, I understand the scenario.
> 
> I had tried only with a non-root process in the container and gdb on
> the host with the same uid, so I didn't need CAP_SYS_PTRACE. But
> that's probably not a good use case.

Interesting... how'd you get the user IDs to line up?

> > The scenario I'm targeting is that you have an application you want
> > to debug running in a container.  You don't want to run GDB as root
> > on the container host, so you start a second container with just the
> > privileges you need and run GDB from there.
> 
> How do you get the pid of the process to give as a parameter to "gdb
> -p" if gdb is running in a different pid namespace than the process
> to debug?

It's the PID on the host, I've just been finding them with ps.  I'm
not sure how well that'd work if you had hundreds or thousands of
containers running though...  It'd be nice to have a command like ps
but that was limited to one container and that listed both inner and
outer PIDs, but I don't think any exist.  I don't think the kernel
even exports any way to translates PIDs from one PID namespace to
another... there were some patches back in November but I don't think
they got in.

> > > Would accessing the files directly through
> > > /proc/<pid_of_traced_process>/root/usr/lib/debug/... work, without
> > > needing the superuser? If it works, it would also remove the
> > > requirement for the single-threaded helper process.
> >
> > No, you need to be root (or have CAP_DAC_OVERRIDE or something) to
> > access /proc/PID/root.  There's also issues with symlinks pointing
> > back to / if you access the files that way.  I investigated it back
> > in February and I don't think you can handle paths with symlinks in
> > a way that's 100% race-free.  (I've not been overly concerned with
> > security but I didn't want to introduce a way for a hostile app to
> > exploit GDB to break out of its container.)
> 
> Thanks for the explanation.

No worries.  FWIW I expect you probably can access /proc/PID/root if
you're running with the same UID like you seem to be, but the symlink
issue is still there.

Cheers,
Gary
  
Alban Crequy May 6, 2015, 6:54 p.m. UTC | #4
On Fri, May 1, 2015 at 1:29 PM, Gary Benson <gbenson@redhat.com> wrote:
> Alban Crequy wrote:
>> On Fri, May 1, 2015 at 11:28 AM, Gary Benson <gbenson@redhat.com> wrote:
>> > Alban Crequy wrote:
>> > > On Thu, Apr 30, 2015 at 2:05 PM, Gary Benson <gbenson@redhat.com> wrote:
>> > > > This commit introduces new shared files nat/linux-namespaces.[ch]
>> > > > containing code to support Linux namespaces that will be used by
>> > > > both GDB and gdbserver.
>> > >
>> > > Thanks for working on this!
>> > >
>> > > > +/* We need to use setns(2) to handle filesystem access in mount
>> > > > +   namespaces other than our own, but this isn't permitted for
>> > > > +   multithreaded processes.  GDB is multithreaded when compiled
>> > > > +   with Guile support, and may become multithreaded if compiled
>> > > > +   with Python support.  We deal with this by spawning a single-
>> > > > +   threaded helper process to access mount namespaces other than
>> > > > +   our own.
>> > >
>> > > setns() needs CAP_SYS_CHROOT and CAP_SYS_ADMIN to change the mnt
>> > > namespace.  So users will need to run gdb as root...
>> >
>> > As root, or with those privileges yes.  But if you're attaching to
>> > a process in a container, it's not running as the same UID as you;
>> > you have to have CAP_SYS_PTRACE, for example, to even get to the
>> > point where GDB wants to access the files.
>>
>> Ok, I understand the scenario.
>>
>> I had tried only with a non-root process in the container and gdb on
>> the host with the same uid, so I didn't need CAP_SYS_PTRACE. But
>> that's probably not a good use case.
>
> Interesting... how'd you get the user IDs to line up?

I just tested by manually writing in /etc/passwd in the container.

>> > The scenario I'm targeting is that you have an application you want
>> > to debug running in a container.  You don't want to run GDB as root
>> > on the container host, so you start a second container with just the
>> > privileges you need and run GDB from there.
>>
>> How do you get the pid of the process to give as a parameter to "gdb
>> -p" if gdb is running in a different pid namespace than the process
>> to debug?
>
> It's the PID on the host, I've just been finding them with ps.

But if you get the pid of the process to debug on the host, that pid
is not meaningful in the container where GDB is running. How can tell
gdb to attach to the pid if that pid is not visible in gdb's
container?

> I'm
> not sure how well that'd work if you had hundreds or thousands of
> containers running though...  It'd be nice to have a command like ps
> but that was limited to one container and that listed both inner and
> outer PIDs, but I don't think any exist.  I don't think the kernel
> even exports any way to translates PIDs from one PID namespace to
> another... there were some patches back in November but I don't think
> they got in.
>
>> > > Would accessing the files directly through
>> > > /proc/<pid_of_traced_process>/root/usr/lib/debug/... work, without
>> > > needing the superuser? If it works, it would also remove the
>> > > requirement for the single-threaded helper process.
>> >
>> > No, you need to be root (or have CAP_DAC_OVERRIDE or something) to
>> > access /proc/PID/root.  There's also issues with symlinks pointing
>> > back to / if you access the files that way.  I investigated it back
>> > in February and I don't think you can handle paths with symlinks in
>> > a way that's 100% race-free.  (I've not been overly concerned with
>> > security but I didn't want to introduce a way for a hostile app to
>> > exploit GDB to break out of its container.)
>>
>> Thanks for the explanation.
>
> No worries.  FWIW I expect you probably can access /proc/PID/root if
> you're running with the same UID like you seem to be, but the symlink
> issue is still there.
>
> Cheers,
> Gary
>
> --
> http://gbenson.net/
  
Gary Benson May 7, 2015, 8:41 a.m. UTC | #5
Alban Crequy wrote:
> On Fri, May 1, 2015 at 1:29 PM, Gary Benson <gbenson@redhat.com> wrote:
> > Alban Crequy wrote:
> > > On Fri, May 1, 2015 at 11:28 AM, Gary Benson <gbenson@redhat.com> wrote:
> > > > The scenario I'm targeting is that you have an application you
> > > > want to debug running in a container.  You don't want to run
> > > > GDB as root on the container host, so you start a second
> > > > container with just the privileges you need and run GDB from
> > > > there.
> > >
> > > How do you get the pid of the process to give as a parameter to
> > > "gdb -p" if gdb is running in a different pid namespace than the
> > > process to debug?
> >
> > It's the PID on the host, I've just been finding them with ps.
> 
> But if you get the pid of the process to debug on the host, that pid
> is not meaningful in the container where GDB is running. How can
> tell gdb to attach to the pid if that pid is not visible in gdb's
> container?

Docker has an option --pid=host that starts a container without
creating a PID namespace for it.  I'm starting the debugger container
with that option so it can see the host PID namespace.

Cheers,
Gary
  
Gary Benson May 7, 2015, 10:38 a.m. UTC | #6
Gary Benson wrote:
> Alban Crequy wrote:
> > On Fri, May 1, 2015 at 11:28 AM, Gary Benson <gbenson@redhat.com> wrote:
> > > Alban Crequy wrote:
> > > > On Thu, Apr 30, 2015 at 2:05 PM, Gary Benson <gbenson@redhat.com> wrote:
> > > The scenario I'm targeting is that you have an application you
> > > want to debug running in a container.  You don't want to run GDB
> > > as root on the container host, so you start a second container
> > > with just the privileges you need and run GDB from there.
> > 
> > How do you get the pid of the process to give as a parameter to
> > "gdb -p" if gdb is running in a different pid namespace than the
> > process to debug?
> 
> It's the PID on the host, I've just been finding them with ps.  I'm
> not sure how well that'd work if you had hundreds or thousands of
> containers running though...  It'd be nice to have a command like ps
> but that was limited to one container and that listed both inner and
> outer PIDs [snip]

FWIW the command I was looking for is "docker top":

  bash-4.2# docker ps
  CONTAINER ID  IMAGE    COMMAND  CREATED         STATUS         PORTS  NAMES
  615386bf33ac  rhel7:0  "bash"   11 minutes ago  Up 11 minutes         rhel7

  bash-4.2# docker top 615386bf33ac
  UID   PID   PPID  C  STIME  TTY    TIME      CMD
  root  2209  939   0  11:18  pts/1  00:00:00  bash
  root  2595  939   0  11:23  ?      00:00:00  nsenter-exec --nspid 2209 -- gdb -p 1
  root  2596  2595  0  11:23  ?      00:00:00  gdb -p 1
  root  2653  939   0  11:24  pts/4  00:00:00  nsenter-exec --nspid 2209 --console /dev/pts/4 -- bash
  root  2654  2653  0  11:24  pts/4  00:00:00  bash
  root  2674  2654  0  11:26  pts/4  00:00:01  /usr/bin/python /usr/bin/debuginfo-install gdb-7.6.1-64.el7.x86_64
  root  2675  2674  7  11:26  pts/4  00:00:22  /usr/bin/python /usr/libexec/urlgrabber-ext-down
  root  2676  2674  1  11:26  pts/4  00:00:03  /usr/bin/python /usr/libexec/urlgrabber-ext-down

Cheers,
Gary
  
Pedro Alves May 21, 2015, 2:56 p.m. UTC | #7
On 04/30/2015 01:05 PM, Gary Benson wrote:

...

> +   For avoidance of doubt, if the helper process receives a
> +   message it doesn't handle it will reply with MNSH_MSG_ERROR.
> +   If the main process receives MNSH_MSG_ERROR at any time then
> +   it will call internal_error.  If internal_error causes the
> +   main process to exit, the helper will notice this and also
> +   exit.  The helper will not exit until the main process
> +   terminates, so if the user continues through internal_error
> +   the helper will still be there awaiting requests from the
> +   main process.

...

> +/* Mount namespace helper message types.  */
> +
> +enum mnsh_msg_type
> +  {
> +    /* An unrecoverable communication error occurred.

I think "unrecoverable" here sounds a bit confusing, as it
contradicts the comment above that explains that the helper
is still awaiting requests if the user decides to continue
after internal_error.

> +       Receipt of this message by either end will cause
> +       an assertion failure in the main process.  */
> +    MNSH_MSG_ERROR,


> +    /* A request that the helper call unlink.  The single
> +       argument (the filename) should be passed in BUF, and
> +       should include a terminating NUL character.  Helper
> +       should respond with a MNSH_RET_INT.  */
> +    MNSH_REQ_UNLINK,
> +
> +    /* A request that the helper call readlink.  The single
> +       argument (the filename) should be passed in BUF, and
> +       should include a terminating NUL character. The helper

Missing double-space.

> +static void
> +mnsh_main (int sock)
> +{
> +  while (1)
> +    {
> +      enum mnsh_msg_type type;
> +      int fd, int1, int2;
> +      char buf[PATH_MAX];
> +      ssize_t size, response = -1;
> +
> +      size = mnsh_recv_message (sock, &type,
> +				&fd, &int1, &int2,
> +				buf, sizeof (buf));
> +
> +      if (size >= 0 && size < sizeof (buf))
> +	{
> +	  switch (type)
> +	    {
> +	    case MNSH_REQ_SETNS:
> +	      if (fd > 0)
> +		response = mnsh_handle_setns (sock, fd, int1);
> +	      break;
> +
> +	    case MNSH_REQ_OPEN:
> +	      if (buf[size - 1] == '\0')

Why these  == '\0' checks?  To protect against bugs?  In
that case, I guess this should be:

	      if (size > 0 && buf[size - 1] == '\0')

> +		response = mnsh_handle_open (sock, buf, int1, int2);
> +	      break;


And that's it.  Really all looks good to me.  :-)

Thanks,
Pedro Alves
  
Gary Benson May 27, 2015, 10:14 a.m. UTC | #8
Pedro Alves wrote:
> On 04/30/2015 01:05 PM, Gary Benson wrote:
> ...
> > +   For avoidance of doubt, if the helper process receives a
> > +   message it doesn't handle it will reply with MNSH_MSG_ERROR.
> > +   If the main process receives MNSH_MSG_ERROR at any time then
> > +   it will call internal_error.  If internal_error causes the
> > +   main process to exit, the helper will notice this and also
> > +   exit.  The helper will not exit until the main process
> > +   terminates, so if the user continues through internal_error
> > +   the helper will still be there awaiting requests from the
> > +   main process.
> ...
> > +/* Mount namespace helper message types.  */
> > +
> > +enum mnsh_msg_type
> > +  {
> > +    /* An unrecoverable communication error occurred.
> 
> I think "unrecoverable" here sounds a bit confusing, as it
> contradicts the comment above that explains that the helper
> is still awaiting requests if the user decides to continue
> after internal_error.

Ok, I'll remove the "unrecoverable" and leave the rest of the
message alone.

> > +    /* A request that the helper call readlink.  The single
> > +       argument (the filename) should be passed in BUF, and
> > +       should include a terminating NUL character. The helper
> 
> Missing double-space.

Ok.

> > +static void
> > +mnsh_main (int sock)
> > +{
> > +  while (1)
> > +    {
> > +      enum mnsh_msg_type type;
> > +      int fd, int1, int2;
> > +      char buf[PATH_MAX];
> > +      ssize_t size, response = -1;
> > +
> > +      size = mnsh_recv_message (sock, &type,
> > +				&fd, &int1, &int2,
> > +				buf, sizeof (buf));
> > +
> > +      if (size >= 0 && size < sizeof (buf))
> > +	{
> > +	  switch (type)
> > +	    {
> > +	    case MNSH_REQ_SETNS:
> > +	      if (fd > 0)
> > +		response = mnsh_handle_setns (sock, fd, int1);
> > +	      break;
> > +
> > +	    case MNSH_REQ_OPEN:
> > +	      if (buf[size - 1] == '\0')
> 
> Why these  == '\0' checks?  To protect against bugs?  In
> that case, I guess this should be:

> 	      if (size > 0 && buf[size - 1] == '\0')
> 
> > +		response = mnsh_handle_open (sock, buf, int1, int2);
> > +	      break;

Yeah, that's better, I'll change it.

> And that's it.  Really all looks good to me.  :-)

Sweet :)

Thanks,
Gary
  
James Greenhalgh June 11, 2015, 8:40 a.m. UTC | #9
Hi,

This patch broke a -Werror native build on ARM for me as so:

---
.../src/binutils-gdb/gdb/gdbserver/../nat/linux-namespaces.c: In function 'mnsh_send_message':
.../src/binutils-gdb/gdb/gdbserver/../nat/linux-namespaces.c:377:28: error: format '%ld' expects argument of type 'long int', but argument 2 has type 'ssize_t {aka int}' [-Werror=format=]
       debug_printf (" -> %ld\n", size);
                            ^
.../src/binutils-gdb/gdb/gdbserver/../nat/linux-namespaces.c: In function 'mnsh_recv_message':
.../src/binutils-gdb/gdb/gdbserver/../nat/linux-namespaces.c:429:50: error: format '%ld' expects argument of type 'long int', but argument 2 has type 'ssize_t {aka int}' [-Werror=format=]
  debug_printf ("namespace-helper: recv failed (%ld)\n", size);
                                                  ^
.../src/binutils-gdb/gdb/gdbserver/../nat/linux-namespaces.c:440:53: error: format '%ld' expects argument of type 'long int', but argument 2 has type 'ssize_t {aka int}' [-Werror=format=]
  debug_printf ("namespace-helper: recv truncated (%ld 0x%x)\n",
                                                   ^
---

I would expect that to mean this is broken for any
"long int != ssize_t" target. I'm not sure which C standards GDB requires
compatability with, but I believe the portable C99 way of printing a
ssize_t would be "%zd".

Thanks,
James
  
Pedro Alves June 11, 2015, 11:04 a.m. UTC | #10
On 06/11/2015 09:40 AM, James Greenhalgh wrote:

> I would expect that to mean this is broken for any
> "long int != ssize_t" target. I'm not sure which C standards GDB requires
> compatability with, but I believe the portable C99 way of printing a
> ssize_t would be "%zd".

GDB is written in C90 currently.

The usual way to handle this is to use plongest (with %s).  As this
is native GNU/Linux code, casting to long (thus keep %ld) would
be fine too.

Thanks,
Pedro Alves
  
Michael Eager June 15, 2015, 3:02 p.m. UTC | #11
On 04/30/2015 05:05 AM, Gary Benson wrote:
> This commit introduces new shared files nat/linux-namespaces.[ch]
> containing code to support Linux namespaces that will be used by
> both GDB and gdbserver.
>
> gdb/ChangeLog:
>
> 	* configure.ac (AC_CHECK_FUNCS): Add setns.
> 	* config.in: Regenerate.
> 	* configure: Likewise.
> 	* nat/linux-namespaces.h: New file.
> 	* nat/linux-namespaces.c: Likewise.
> 	* Makefile.in (HFILES_NO_SRCDIR): Add nat/linux-namespaces.h.
> 	(linux-namespaces.o): New rule.
> 	* config/aarch64/linux.mh (NATDEPFILES): Add linux-namespaces.o.
> 	* config/alpha/alpha-linux.mh (NATDEPFILES): Likewise.
> 	* config/arm/linux.mh (NATDEPFILES): Likewise.
> 	* config/i386/linux.mh (NATDEPFILES): Likewise.
> 	* config/i386/linux64.mh (NATDEPFILES): Likewise.
> 	* config/ia64/linux.mh (NATDEPFILES): Likewise.
> 	* config/m32r/linux.mh (NATDEPFILES): Likewise.
> 	* config/m68k/linux.mh (NATDEPFILES): Likewise.
> 	* config/mips/linux.mh (NATDEPFILES): Likewise.
> 	* config/pa/linux.mh (NATDEPFILES): Likewise.
> 	* config/powerpc/linux.mh (NATDEPFILES): Likewise.
> 	* config/powerpc/ppc64-linux.mh (NATDEPFILES): Likewise.
> 	* config/powerpc/spu-linux.mh (NATDEPFILES): Likewise.
> 	* config/s390/linux.mh (NATDEPFILES): Likewise.
> 	* config/sparc/linux.mh (NATDEPFILES): Likewise.
> 	* config/sparc/linux64.mh (NATDEPFILES): Likewise.
> 	* config/tilegx/linux.mh (NATDEPFILES): Likewise.
> 	* config/xtensa/linux.mh (NATDEPFILES): Likewise.

This patch causes a build failure in nat/linux-namespaces.c with
glibc-2.5, which does not define MSG_CMSG_CLOEXEC.  It looks like
this symbol was introduced in glib-2.7.
  
Yao Qi Jan. 8, 2016, 10:49 a.m. UTC | #12
Gary Benson <gbenson@redhat.com> writes:

Hi Gary,

> +/* Return an object representing the mount namespace helper process.
> +   If no mount namespace helper process has been started then start
> +   one.  Return NULL if no mount namespace helper process could be
> +   started.  */
> +
> +static struct linux_mnsh *
> +linux_mntns_get_helper (void)
> +{
> +  static struct linux_mnsh *helper = NULL;
> +
> +  if (helper == NULL)
> +    {
> +      static struct linux_mnsh h;
> +      struct linux_ns *ns;
> +      pid_t helper_creator = getpid ();
> +      int sv[2];
> +
> +      ns = linux_ns_get_namespace (LINUX_NS_MNT);
> +      if (ns == NULL)
> +	return NULL;
> +
> +      if (gdb_socketpair_cloexec (AF_UNIX, SOCK_STREAM, 0, sv) < 0)
> +	return NULL;
> +
> +      h.pid = fork ();
> +      if (h.pid < 0)

Function fork isn't available on uclinux target, so it causes a
compilation error like this below.  Since my work touches
linux-bfin-low.c, I build GDBserver for bfin-uclinux, and the error is found.

cc1: warnings being treated as errors
gdb/gdbserver/../nat/linux-namespaces.c: In function ‘linux_mntns_get_helper’:
gdb/gdbserver/../nat/linux-namespaces.c:647: error: implicit declaration of function ‘fork’

I don't know much about linux namespace stuff, so I didn't give a fix
here.  Could you have a look?
  
Gary Benson Jan. 11, 2016, 4:40 p.m. UTC | #13
Hi Yao,

Yao Qi wrote:
> Gary Benson <gbenson@redhat.com> writes:
> > +/* Return an object representing the mount namespace helper process.
> > +   If no mount namespace helper process has been started then start
> > +   one.  Return NULL if no mount namespace helper process could be
> > +   started.  */
> > +
> > +static struct linux_mnsh *
> > +linux_mntns_get_helper (void)
> > +{
> > +  static struct linux_mnsh *helper = NULL;
> > +
> > +  if (helper == NULL)
> > +    {
> > +      static struct linux_mnsh h;
> > +      struct linux_ns *ns;
> > +      pid_t helper_creator = getpid ();
> > +      int sv[2];
> > +
> > +      ns = linux_ns_get_namespace (LINUX_NS_MNT);
> > +      if (ns == NULL)
> > +	return NULL;
> > +
> > +      if (gdb_socketpair_cloexec (AF_UNIX, SOCK_STREAM, 0, sv) < 0)
> > +	return NULL;
> > +
> > +      h.pid = fork ();
> > +      if (h.pid < 0)
> 
> Function fork isn't available on uclinux target, so it causes a
> compilation error like this below.  Since my work touches
> linux-bfin-low.c, I build GDBserver for bfin-uclinux, and the error
> is found.
[snip]
> I don't know much about linux namespace stuff, so I didn't give a
> fix here.  Could you have a look?

It probably needs a configure check, I'll add one.

Cheers,
Gary
  

Patch

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 47b216a..9921dcb 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -980,7 +980,7 @@  common/common-debug.h common/cleanups.h common/gdb_setjmp.h \
 common/common-exceptions.h target/target.h common/symbol.h \
 common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \
 common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h \
-common/cleanup-utils.h
+common/cleanup-utils.h nat/linux-namespaces.h
 
 # Header files that already have srcdir in them, or which are in objdir.
 
@@ -2325,6 +2325,10 @@  x86-linux-dregs.o: ${srcdir}/nat/x86-linux-dregs.c
 	$(COMPILE) $(srcdir)/nat/x86-linux-dregs.c
 	$(POSTCOMPILE)
 
+linux-namespaces.o: ${srcdir}/nat/linux-namespaces.c
+	$(COMPILE) $(srcdir)/nat/linux-namespaces.c
+	$(POSTCOMPILE)
+
 #
 # gdb/tui/ dependencies
 #
diff --git a/gdb/config.in b/gdb/config.in
index d41e6cf..5b6dbbb 100644
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -390,6 +390,9 @@ 
 /* Define to 1 if you have the `setlocale' function. */
 #undef HAVE_SETLOCALE
 
+/* Define to 1 if you have the `setns' function. */
+#undef HAVE_SETNS
+
 /* Define to 1 if you have the `setpgid' function. */
 #undef HAVE_SETPGID
 
diff --git a/gdb/config/aarch64/linux.mh b/gdb/config/aarch64/linux.mh
index 7f96e4d..6a8aa7d 100644
--- a/gdb/config/aarch64/linux.mh
+++ b/gdb/config/aarch64/linux.mh
@@ -22,7 +22,7 @@  NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o aarch64-linux-nat.o \
 	proc-service.o linux-thread-db.o linux-nat.o linux-fork.o \
 	linux-procfs.o linux-ptrace.o linux-osdata.o linux-waitpid.o \
-	linux-personality.o
+	linux-personality.o linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES= -ldl $(RDYNAMIC)
diff --git a/gdb/config/alpha/alpha-linux.mh b/gdb/config/alpha/alpha-linux.mh
index 2ea02a1..4991dd2 100644
--- a/gdb/config/alpha/alpha-linux.mh
+++ b/gdb/config/alpha/alpha-linux.mh
@@ -3,7 +3,7 @@  NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o alpha-linux-nat.o \
 	fork-child.o proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
-	linux-waitpid.o linux-personality.o
+	linux-waitpid.o linux-personality.o linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 # The dynamically loaded libthread_db needs access to symbols in the
diff --git a/gdb/config/arm/linux.mh b/gdb/config/arm/linux.mh
index 549bf42..f0c5967 100644
--- a/gdb/config/arm/linux.mh
+++ b/gdb/config/arm/linux.mh
@@ -4,7 +4,7 @@  NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o arm-linux-nat.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
-	linux-waitpid.o linux-personality.o
+	linux-waitpid.o linux-personality.o linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES= -ldl $(RDYNAMIC)
diff --git a/gdb/config/i386/linux.mh b/gdb/config/i386/linux.mh
index 9be2c5f..421c56f 100644
--- a/gdb/config/i386/linux.mh
+++ b/gdb/config/i386/linux.mh
@@ -6,7 +6,7 @@  NATDEPFILES= inf-ptrace.o fork-child.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
 	linux-btrace.o linux-waitpid.o linux-personality.o x86-linux.o \
-	x86-linux-dregs.o
+	x86-linux-dregs.o linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 # The dynamically loaded libthread_db needs access to symbols in the
diff --git a/gdb/config/i386/linux64.mh b/gdb/config/i386/linux64.mh
index 224a2a9..04cbb95 100644
--- a/gdb/config/i386/linux64.mh
+++ b/gdb/config/i386/linux64.mh
@@ -6,7 +6,7 @@  NATDEPFILES= inf-ptrace.o fork-child.o \
 	proc-service.o linux-thread-db.o linux-fork.o \
 	linux-procfs.o linux-ptrace.o linux-btrace.o \
 	linux-waitpid.o linux-personality.o x86-linux.o \
-	x86-linux-dregs.o
+	x86-linux-dregs.o linux-namespaces.o
 NAT_FILE= config/nm-linux.h
 NAT_CDEPS = $(srcdir)/proc-service.list
 
diff --git a/gdb/config/ia64/linux.mh b/gdb/config/ia64/linux.mh
index 9dce22b..b05f834 100644
--- a/gdb/config/ia64/linux.mh
+++ b/gdb/config/ia64/linux.mh
@@ -6,7 +6,8 @@  NATDEPFILES= inf-ptrace.o fork-child.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
 	linux-personality.o \
-	linux-procfs.o linux-ptrace.o linux-waitpid.o
+	linux-procfs.o linux-ptrace.o linux-waitpid.o \
+	linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES = -ldl $(RDYNAMIC)
diff --git a/gdb/config/m32r/linux.mh b/gdb/config/m32r/linux.mh
index 6b810e6..277d8bd 100644
--- a/gdb/config/m32r/linux.mh
+++ b/gdb/config/m32r/linux.mh
@@ -4,7 +4,7 @@  NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o				\
 	m32r-linux-nat.o proc-service.o linux-thread-db.o	\
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
-	linux-waitpid.o linux-personality.o
+	linux-waitpid.o linux-personality.o linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES= -ldl $(RDYNAMIC)
diff --git a/gdb/config/m68k/linux.mh b/gdb/config/m68k/linux.mh
index f3b3baa..60d5ce4 100644
--- a/gdb/config/m68k/linux.mh
+++ b/gdb/config/m68k/linux.mh
@@ -6,7 +6,7 @@  NATDEPFILES= inf-ptrace.o fork-child.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
 	linux-personality.o \
-	linux-waitpid.o
+	linux-waitpid.o linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 # The dynamically loaded libthread_db needs access to symbols in the
diff --git a/gdb/config/mips/linux.mh b/gdb/config/mips/linux.mh
index d6a802f..11ff903 100644
--- a/gdb/config/mips/linux.mh
+++ b/gdb/config/mips/linux.mh
@@ -5,7 +5,7 @@  NATDEPFILES= inf-ptrace.o fork-child.o mips-linux-nat.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
 	linux-procfs.o linux-ptrace.o linux-waitpid.o \
 	linux-personality.o \
-	mips-linux-watch.o
+	mips-linux-watch.o linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES = -ldl $(RDYNAMIC)
diff --git a/gdb/config/pa/linux.mh b/gdb/config/pa/linux.mh
index 9539b64..1b73ecd 100644
--- a/gdb/config/pa/linux.mh
+++ b/gdb/config/pa/linux.mh
@@ -4,7 +4,7 @@  NATDEPFILES= inf-ptrace.o fork-child.o \
 	hppa-linux-nat.o proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
 	linux-procfs.o linux-ptrace.o linux-waitpid.o \
-	linux-personality.o
+	linux-personality.o linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES = -ldl $(RDYNAMIC)
diff --git a/gdb/config/powerpc/linux.mh b/gdb/config/powerpc/linux.mh
index 76e62c0..f4a52c3 100644
--- a/gdb/config/powerpc/linux.mh
+++ b/gdb/config/powerpc/linux.mh
@@ -6,7 +6,7 @@  NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o \
 	ppc-linux-nat.o proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
-	linux-waitpid.o linux-personality.o
+	linux-waitpid.o linux-personality.o linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES = -ldl $(RDYNAMIC)
diff --git a/gdb/config/powerpc/ppc64-linux.mh b/gdb/config/powerpc/ppc64-linux.mh
index 7eb6507..580eb1f 100644
--- a/gdb/config/powerpc/ppc64-linux.mh
+++ b/gdb/config/powerpc/ppc64-linux.mh
@@ -6,7 +6,8 @@  NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o \
 	ppc-linux-nat.o proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
-	linux-waitpid.o ppc-linux.o linux-personality.o
+	linux-waitpid.o ppc-linux.o linux-personality.o \
+	linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 # The PowerPC has severe limitations on TOC size, and uses them even
diff --git a/gdb/config/powerpc/spu-linux.mh b/gdb/config/powerpc/spu-linux.mh
index d44aeeb..9205b62 100644
--- a/gdb/config/powerpc/spu-linux.mh
+++ b/gdb/config/powerpc/spu-linux.mh
@@ -4,5 +4,5 @@ 
 # PPU side of the Cell BE and debugging the SPU side.
 
 NATDEPFILES = spu-linux-nat.o fork-child.o inf-ptrace.o \
-	      linux-procfs.o linux-ptrace.o linux-waitpid.o linux-personality.o
-
+	      linux-procfs.o linux-ptrace.o linux-waitpid.o \
+	      linux-personality.o linux-namespaces.o
diff --git a/gdb/config/s390/linux.mh b/gdb/config/s390/linux.mh
index e1ad899..4a137cd 100644
--- a/gdb/config/s390/linux.mh
+++ b/gdb/config/s390/linux.mh
@@ -4,6 +4,6 @@  NATDEPFILES= inf-ptrace.o fork-child.o s390-linux-nat.o \
 	linux-thread-db.o proc-service.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
 	linux-personality.o \
-	linux-waitpid.o
+	linux-waitpid.o linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 LOADLIBES = -ldl $(RDYNAMIC)
diff --git a/gdb/config/sparc/linux.mh b/gdb/config/sparc/linux.mh
index bd7fc86..385f640 100644
--- a/gdb/config/sparc/linux.mh
+++ b/gdb/config/sparc/linux.mh
@@ -5,7 +5,7 @@  NATDEPFILES= sparc-nat.o sparc-linux-nat.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
 	linux-procfs.o linux-ptrace.o linux-waitpid.o \
-	linux-personality.o
+	linux-personality.o linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 # The dynamically loaded libthread_db needs access to symbols in the
diff --git a/gdb/config/sparc/linux64.mh b/gdb/config/sparc/linux64.mh
index 86f984f..8df0de1 100644
--- a/gdb/config/sparc/linux64.mh
+++ b/gdb/config/sparc/linux64.mh
@@ -5,7 +5,7 @@  NATDEPFILES= sparc-nat.o sparc64-nat.o sparc64-linux-nat.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
 	linux-procfs.o linux-ptrace.o linux-waitpid.o \
-	linux-personality.o
+	linux-personality.o linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 # The dynamically loaded libthread_db needs access to symbols in the
diff --git a/gdb/config/tilegx/linux.mh b/gdb/config/tilegx/linux.mh
index b5edcd4..ec648d3 100644
--- a/gdb/config/tilegx/linux.mh
+++ b/gdb/config/tilegx/linux.mh
@@ -6,7 +6,7 @@  NATDEPFILES= inf-ptrace.o fork-child.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
 	linux-procfs.o linux-ptrace.o linux-waitpid.o \
-	linux-personality.o
+	linux-personality.o linux-namespaces.o
 
 # The dynamically loaded libthread_db needs access to symbols in the
 # gdb executable.
diff --git a/gdb/config/xtensa/linux.mh b/gdb/config/xtensa/linux.mh
index b4e59b3..d5b8f91 100644
--- a/gdb/config/xtensa/linux.mh
+++ b/gdb/config/xtensa/linux.mh
@@ -5,7 +5,7 @@  NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o xtensa-linux-nat.o \
 	linux-thread-db.o proc-service.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
-	linux-waitpid.o linux-personality.o
+	linux-waitpid.o linux-personality.o linux-namespaces.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES = -ldl $(RDYNAMIC)
diff --git a/gdb/configure b/gdb/configure
index 2baf6dd..5100921 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -10957,7 +10957,7 @@  for ac_func in getauxval getrusage getuid getgid \
 		sigaction sigprocmask sigsetmask socketpair \
 		ttrace wborder wresize setlocale iconvlist libiconvlist btowc \
 		setrlimit getrlimit posix_madvise waitpid \
-		ptrace64 sigaltstack mkdtemp
+		ptrace64 sigaltstack mkdtemp setns
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
diff --git a/gdb/configure.ac b/gdb/configure.ac
index c703e35..2035a37 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -1331,7 +1331,7 @@  AC_CHECK_FUNCS([getauxval getrusage getuid getgid \
 		sigaction sigprocmask sigsetmask socketpair \
 		ttrace wborder wresize setlocale iconvlist libiconvlist btowc \
 		setrlimit getrlimit posix_madvise waitpid \
-		ptrace64 sigaltstack mkdtemp])
+		ptrace64 sigaltstack mkdtemp setns])
 AM_LANGINFO_CODESET
 GDB_AC_COMMON
 
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 8ba88d4..4179cd6 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -180,7 +180,8 @@  SFILES=	$(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
 	$(srcdir)/common/common-debug.c $(srcdir)/common/cleanups.c \
 	$(srcdir)/common/common-exceptions.c $(srcdir)/symbol.c \
 	$(srcdir)/common/btrace-common.c \
-	$(srcdir)/common/fileio.c $(srcdir)/common/cleanup-utils.c
+	$(srcdir)/common/fileio.c $(srcdir)/common/cleanup-utils.c \
+	$(srcdir)/nat/linux-namespaces.c
 
 DEPFILES = @GDBSERVER_DEPFILES@
 
@@ -619,6 +620,9 @@  x86-linux-dregs.o: ../nat/x86-linux-dregs.c
 cleanup-utils.o: ../common/cleanup-utils.c
 	$(COMPILE) $<
 	$(POSTCOMPILE)
+linux-namespaces.o: ../nat/linux-namespaces.c
+	$(COMPILE) $<
+	$(POSTCOMPILE)
 
 aarch64.c : $(srcdir)/../regformats/aarch64.dat $(regdat_sh)
 	$(SHELL) $(regdat_sh) $(srcdir)/../regformats/aarch64.dat aarch64.c
diff --git a/gdb/gdbserver/config.in b/gdb/gdbserver/config.in
index 3c3bfca..893aaab 100644
--- a/gdb/gdbserver/config.in
+++ b/gdb/gdbserver/config.in
@@ -183,6 +183,9 @@ 
 /* Define to 1 if you have the `pwrite' function. */
 #undef HAVE_PWRITE
 
+/* Define to 1 if you have the `setns' function. */
+#undef HAVE_SETNS
+
 /* Define to 1 if you have the <sgtty.h> header file. */
 #undef HAVE_SGTTY_H
 
diff --git a/gdb/gdbserver/configure b/gdb/gdbserver/configure
index 75860dd..bcb3f2e 100755
--- a/gdb/gdbserver/configure
+++ b/gdb/gdbserver/configure
@@ -5310,7 +5310,7 @@  fi
 
 done
 
-for ac_func in getauxval pread pwrite pread64
+for ac_func in getauxval pread pwrite pread64 setns
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
diff --git a/gdb/gdbserver/configure.ac b/gdb/gdbserver/configure.ac
index 34af269..2aef2b4 100644
--- a/gdb/gdbserver/configure.ac
+++ b/gdb/gdbserver/configure.ac
@@ -94,7 +94,7 @@  AC_CHECK_HEADERS(sgtty.h termio.h termios.h sys/reg.h string.h dnl
 		 fcntl.h signal.h sys/file.h dnl
 		 sys/ioctl.h netinet/in.h sys/socket.h netdb.h dnl
 		 netinet/tcp.h arpa/inet.h)
-AC_CHECK_FUNCS(getauxval pread pwrite pread64)
+AC_CHECK_FUNCS(getauxval pread pwrite pread64 setns)
 
 GDB_AC_COMMON
 
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index 81dd235..7f89f2f 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -42,7 +42,7 @@  srv_amd64_linux_xmlfiles="i386/amd64-linux.xml i386/amd64-avx-linux.xml i386/amd
 
 # Linux object files.  This is so we don't have to repeat
 # these files over and over again.
-srv_linux_obj="linux-low.o linux-osdata.o linux-procfs.o linux-ptrace.o linux-waitpid.o linux-personality.o"
+srv_linux_obj="linux-low.o linux-osdata.o linux-procfs.o linux-ptrace.o linux-waitpid.o linux-personality.o linux-namespaces.o"
 
 # Input is taken from the "${target}" variable.
 
diff --git a/gdb/nat/linux-namespaces.c b/gdb/nat/linux-namespaces.c
new file mode 100644
index 0000000..3603733
--- /dev/null
+++ b/gdb/nat/linux-namespaces.c
@@ -0,0 +1,1042 @@ 
+/* Linux namespaces(7) support.
+
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 "common-defs.h"
+#include "nat/linux-namespaces.h"
+#include "filestuff.h"
+#include <fcntl.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <sched.h>
+
+/* See nat/linux-namespaces.h.  */
+int debug_linux_namespaces;
+
+/* Handle systems without setns.  */
+
+#ifndef HAVE_SETNS
+static int
+setns (int fd, int nstype)
+{
+#ifdef __NR_setns
+  return syscall (__NR_setns, fd, nstype);
+#else
+  errno = ENOSYS;
+  return -1;
+#endif
+}
+#endif
+
+/* A Linux namespace.  */
+
+struct linux_ns
+{
+  /* Filename of this namespace's entries in /proc/PID/ns.  */
+  const char *filename;
+
+  /* Nonzero if this object has been initialized.  */
+  int initialized;
+
+  /* Nonzero if this namespace is supported on this system.  */
+  int supported;
+
+  /* ID of the namespace the calling process is in, used to
+     see if other processes share the namespace.  The code in
+     this file assumes that the calling process never changes
+     namespace.  */
+  ino_t id;
+};
+
+/* Return the absolute filename of process PID's /proc/PID/ns
+   entry for namespace NS.  The returned value persists until
+   this function is next called.  */
+
+static const char *
+linux_ns_filename (struct linux_ns *ns, int pid)
+{
+  static char filename[PATH_MAX];
+
+  gdb_assert (pid > 0);
+  xsnprintf (filename, sizeof (filename), "/proc/%d/ns/%s", pid,
+	     ns->filename);
+
+  return filename;
+}
+
+/* Return a representation of the caller's TYPE namespace, or
+   NULL if TYPE namespaces are not supported on this system.  */
+
+static struct linux_ns *
+linux_ns_get_namespace (enum linux_ns_type type)
+{
+  static struct linux_ns namespaces[NUM_LINUX_NS_TYPES] =
+    {
+      { "ipc" },
+      { "mnt" },
+      { "net" },
+      { "pid" },
+      { "user" },
+      { "uts" },
+    };
+  struct linux_ns *ns;
+
+  gdb_assert (type >= 0 && type < NUM_LINUX_NS_TYPES);
+  ns = &namespaces[type];
+
+  if (!ns->initialized)
+    {
+      struct stat sb;
+
+      if (stat (linux_ns_filename (ns, getpid ()), &sb) == 0)
+	{
+	  ns->id = sb.st_ino;
+
+	  ns->supported = 1;
+	}
+
+      ns->initialized = 1;
+    }
+
+  return ns->supported ? ns : NULL;
+}
+
+/* See nat/linux-namespaces.h.  */
+
+int
+linux_ns_same (pid_t pid, enum linux_ns_type type)
+{
+  struct linux_ns *ns = linux_ns_get_namespace (type);
+  const char *filename;
+  struct stat sb;
+
+  /* If the kernel does not support TYPE namespaces then there's
+     effectively only one TYPE namespace that all processes on
+     the system share.  */
+  if (ns == NULL)
+    return 1;
+
+  /* Stat PID's TYPE namespace entry to get the namespace ID.  This
+     might fail if the process died, or if we don't have the right
+     permissions (though we should be attached by this time so this
+     seems unlikely).  In any event, we can't make any decisions and
+     must throw.  */
+  filename = linux_ns_filename (ns, pid);
+  if (stat (filename, &sb) != 0)
+    perror_with_name (filename);
+
+  return sb.st_ino == ns->id;
+}
+
+/* We need to use setns(2) to handle filesystem access in mount
+   namespaces other than our own, but this isn't permitted for
+   multithreaded processes.  GDB is multithreaded when compiled
+   with Guile support, and may become multithreaded if compiled
+   with Python support.  We deal with this by spawning a single-
+   threaded helper process to access mount namespaces other than
+   our own.
+
+   The helper process is started the first time a call to setns
+   is required.  The main process (GDB or gdbserver) communicates
+   with the helper via sockets, passing file descriptors where
+   necessary using SCM_RIGHTS.  Once started the helper process
+   runs until the main process terminates; when this happens the
+   helper will receive socket errors, notice that its parent died,
+   and exit accordingly (see mnsh_maybe_mourn_peer).
+
+   The protocol is that the main process sends a request in a
+   single message, and the helper replies to every message it
+   receives with a single-message response.  If the helper
+   receives a message it does not understand it will reply with
+   a MNSH_MSG_ERROR message.  The main process checks all
+   responses it receives with gdb_assert, so if the main process
+   receives something unexpected (which includes MNSH_MSG_ERROR)
+   the main process will call internal_error.
+
+   For avoidance of doubt, if the helper process receives a
+   message it doesn't handle it will reply with MNSH_MSG_ERROR.
+   If the main process receives MNSH_MSG_ERROR at any time then
+   it will call internal_error.  If internal_error causes the
+   main process to exit, the helper will notice this and also
+   exit.  The helper will not exit until the main process
+   terminates, so if the user continues through internal_error
+   the helper will still be there awaiting requests from the
+   main process.
+
+   Messages in both directions have the following payload:
+
+   - TYPE (enum mnsh_msg_type, always sent) - the message type.
+   - INT1 and
+   - INT2 (int, always sent, though not always used) - two
+           values whose meaning is message-type-dependent.
+	   See enum mnsh_msg_type documentation below.
+   - FD (int, optional, sent using SCM_RIGHTS) - an open file
+         descriptor.
+   - BUF (unstructured data, optional) - some data with message-
+          type-dependent meaning.
+
+   Note that the helper process is the child of a call to fork,
+   so all code in the helper must be async-signal-safe.  */
+
+/* Mount namespace helper message types.  */
+
+enum mnsh_msg_type
+  {
+    /* An unrecoverable communication error occurred.
+       Receipt of this message by either end will cause
+       an assertion failure in the main process.  */
+    MNSH_MSG_ERROR,
+
+    /* Requests, sent from the main process to the helper.  */
+
+    /* A request that the helper call setns.  Arguments should
+       be passed in FD and INT1.  Helper should respond with a
+       MNSH_RET_INT.  */
+    MNSH_REQ_SETNS,
+
+    /* A request that the helper call open.  Arguments should
+       be passed in BUF, INT1 and INT2.  The filename (in BUF)
+       should include a terminating NUL character.  The helper
+       should respond with a MNSH_RET_FD.  */
+    MNSH_REQ_OPEN,
+
+    /* A request that the helper call unlink.  The single
+       argument (the filename) should be passed in BUF, and
+       should include a terminating NUL character.  Helper
+       should respond with a MNSH_RET_INT.  */
+    MNSH_REQ_UNLINK,
+
+    /* A request that the helper call readlink.  The single
+       argument (the filename) should be passed in BUF, and
+       should include a terminating NUL character. The helper
+       should respond with a MNSH_RET_INTSTR.  */
+    MNSH_REQ_READLINK,
+
+    /* Responses, sent to the main process from the helper.  */
+
+    /* Return an integer in INT1 and errno in INT2.  */
+    MNSH_RET_INT,
+
+    /* Return a file descriptor in FD if one was opened or an
+       integer in INT1 otherwise.  Return errno in INT2.  */
+    MNSH_RET_FD,
+
+    /* Return an integer in INT1, errno in INT2, and optionally
+       some data in BUF.  */
+    MNSH_RET_INTSTR,
+  };
+
+/* Print a string representation of a message using debug_printf.
+   This function is not async-signal-safe so should never be
+   called from the helper.  */
+
+static void
+mnsh_debug_print_message (enum mnsh_msg_type type,
+			  int fd, int int1, int int2,
+			  const void *buf, int bufsiz)
+{
+  gdb_byte *c = (gdb_byte *) buf;
+  gdb_byte *cl = c + bufsiz;
+
+  switch (type)
+    {
+    case MNSH_MSG_ERROR:
+      debug_printf ("ERROR");
+      break;
+
+    case MNSH_REQ_SETNS:
+      debug_printf ("SETNS");
+      break;
+
+    case MNSH_REQ_OPEN:
+      debug_printf ("OPEN");
+      break;
+
+    case MNSH_REQ_UNLINK:
+      debug_printf ("UNLINK");
+      break;
+
+    case MNSH_REQ_READLINK:
+      debug_printf ("READLINK");
+      break;
+
+    case MNSH_RET_INT:
+      debug_printf ("INT");
+      break;
+
+    case MNSH_RET_FD:
+      debug_printf ("FD");
+      break;
+
+    case MNSH_RET_INTSTR:
+      debug_printf ("INTSTR");
+      break;
+
+    default:
+      debug_printf ("unknown-packet-%d", type);
+    }
+
+  debug_printf (" %d %d %d \"", fd, int1, int2);
+
+  for (; c < cl; c++)
+    debug_printf (*c >= ' ' && *c <= '~' ? "%c" : "\\%o", *c);
+
+  debug_printf ("\"");
+}
+
+/* Forward declaration.  */
+
+static void mnsh_maybe_mourn_peer (void);
+
+/* Send a message.  The argument SOCK is the file descriptor of the
+   sending socket, the other arguments are the payload to send.
+   Return the number of bytes sent on success.  Return -1 on failure
+   and set errno appropriately.  This function is called by both the
+   main process and the helper so must be async-signal-safe.  */
+
+static ssize_t
+mnsh_send_message (int sock, enum mnsh_msg_type type,
+		   int fd, int int1, int int2,
+		   const void *buf, int bufsiz)
+{
+  struct msghdr msg;
+  struct iovec iov[4];
+  char fdbuf[CMSG_SPACE (sizeof (fd))];
+  ssize_t size;
+
+  /* Build the basic TYPE, INT1, INT2 message.  */
+  memset (&msg, 0, sizeof (msg));
+  msg.msg_iov = iov;
+
+  iov[0].iov_base = &type;
+  iov[0].iov_len = sizeof (type);
+  iov[1].iov_base = &int1;
+  iov[1].iov_len = sizeof (int1);
+  iov[2].iov_base = &int2;
+  iov[2].iov_len = sizeof (int2);
+
+  msg.msg_iovlen = 3;
+
+  /* Append BUF if supplied.  */
+  if (buf != NULL && bufsiz > 0)
+    {
+      iov[3].iov_base = alloca (bufsiz);
+      memcpy (iov[3].iov_base, buf, bufsiz);
+      iov[3].iov_len = bufsiz;
+
+      msg.msg_iovlen ++;
+    }
+
+  /* Attach FD if supplied.  */
+  if (fd >= 0)
+    {
+      struct cmsghdr *cmsg;
+
+      msg.msg_control = fdbuf;
+      msg.msg_controllen = sizeof (fdbuf);
+
+      cmsg = CMSG_FIRSTHDR (&msg);
+      cmsg->cmsg_level = SOL_SOCKET;
+      cmsg->cmsg_type = SCM_RIGHTS;
+      cmsg->cmsg_len = CMSG_LEN (sizeof (int));
+
+      *((int *) CMSG_DATA (cmsg)) = fd;
+
+      msg.msg_controllen = cmsg->cmsg_len;
+    }
+
+  /* Send the message.  */
+  size = sendmsg (sock, &msg, 0);
+
+  if (size < 0)
+    mnsh_maybe_mourn_peer ();
+
+  if (debug_linux_namespaces)
+    {
+      debug_printf ("mnsh: send: ");
+      mnsh_debug_print_message (type, fd, int1, int2, buf, bufsiz);
+      debug_printf (" -> %ld\n", size);
+    }
+
+  return size;
+}
+
+/* Receive a message.  The argument SOCK is the file descriptor of
+   the receiving socket, the other arguments point to storage for
+   the received payload.  Returns the number of bytes stored into
+   BUF on success, which may be zero in the event no BUF was sent.
+   Return -1 on failure and set errno appropriately.  This function
+   is called from both the main process and the helper and must be
+   async-signal-safe.  */
+
+static ssize_t
+mnsh_recv_message (int sock, enum mnsh_msg_type *type,
+		   int *fd, int *int1, int *int2,
+		   void *buf, int bufsiz)
+{
+  struct msghdr msg;
+  struct iovec iov[4];
+  char fdbuf[CMSG_SPACE (sizeof (*fd))];
+  struct cmsghdr *cmsg;
+  ssize_t size, fixed_size;
+  int i;
+
+  /* Build the message to receive data into.  */
+  memset (&msg, 0, sizeof (msg));
+  msg.msg_iov = iov;
+
+  iov[0].iov_base = type;
+  iov[0].iov_len = sizeof (*type);
+  iov[1].iov_base = int1;
+  iov[1].iov_len = sizeof (*int1);
+  iov[2].iov_base = int2;
+  iov[2].iov_len = sizeof (*int2);
+  iov[3].iov_base = buf;
+  iov[3].iov_len = bufsiz;
+
+  msg.msg_iovlen = 4;
+
+  for (fixed_size = i = 0; i < msg.msg_iovlen - 1; i++)
+    fixed_size += iov[i].iov_len;
+
+  msg.msg_control = fdbuf;
+  msg.msg_controllen = sizeof (fdbuf);
+
+  /* Receive the message.  */
+  size = recvmsg (sock, &msg, MSG_CMSG_CLOEXEC);
+  if (size < 0)
+    {
+      if (debug_linux_namespaces)
+	debug_printf ("namespace-helper: recv failed (%ld)\n", size);
+
+      mnsh_maybe_mourn_peer ();
+
+      return size;
+    }
+
+  /* Check for truncation.  */
+  if (size < fixed_size || (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)))
+    {
+      if (debug_linux_namespaces)
+	debug_printf ("namespace-helper: recv truncated (%ld 0x%x)\n",
+		      size, msg.msg_flags);
+
+      mnsh_maybe_mourn_peer ();
+
+      errno = EBADMSG;
+      return -1;
+    }
+
+  /* Unpack the file descriptor if supplied.  */
+  cmsg = CMSG_FIRSTHDR (&msg);
+  if (cmsg != NULL
+      && cmsg->cmsg_len == CMSG_LEN (sizeof (int))
+      && cmsg->cmsg_level == SOL_SOCKET
+      && cmsg->cmsg_type == SCM_RIGHTS)
+    *fd = *((int *) CMSG_DATA (cmsg));
+  else
+    *fd = -1;
+
+  if (debug_linux_namespaces)
+    {
+      debug_printf ("mnsh: recv: ");
+      mnsh_debug_print_message (*type, *fd, *int1, *int2, buf,
+				size - fixed_size);
+      debug_printf ("\n");
+    }
+
+  /* Return the number of bytes of data in BUF.  */
+  return size - fixed_size;
+}
+
+/* Shortcuts for returning results from the helper.  */
+
+#define mnsh_return_int(sock, result, error) \
+  mnsh_send_message (sock, MNSH_RET_INT, -1, result, error, NULL, 0)
+
+#define mnsh_return_fd(sock, fd, error) \
+  mnsh_send_message (sock, MNSH_RET_FD, \
+		     (fd) < 0 ? -1 : (fd), \
+		     (fd) < 0 ? (fd) : 0, \
+		     error, NULL, 0)
+
+#define mnsh_return_intstr(sock, result, buf, bufsiz, error) \
+  mnsh_send_message (sock, MNSH_RET_INTSTR, -1, result, error, \
+		     buf, bufsiz)
+
+/* Handle a MNSH_REQ_SETNS message.  Must be async-signal-safe.  */
+
+static ssize_t
+mnsh_handle_setns (int sock, int fd, int nstype)
+{
+  int result = setns (fd, nstype);
+
+  return mnsh_return_int (sock, result, errno);
+}
+
+/* Handle a MNSH_REQ_OPEN message.  Must be async-signal-safe.  */
+
+static ssize_t
+mnsh_handle_open (int sock, const char *filename,
+		  int flags, mode_t mode)
+{
+  int fd = gdb_open_cloexec (filename, flags, mode);
+  ssize_t result = mnsh_return_fd (sock, fd, errno);
+
+  if (fd >= 0)
+    close (fd);
+
+  return result;
+}
+
+/* Handle a MNSH_REQ_UNLINK message.  Must be async-signal-safe.  */
+
+static ssize_t
+mnsh_handle_unlink (int sock, const char *filename)
+{
+  int result = unlink (filename);
+
+  return mnsh_return_int (sock, result, errno);
+}
+
+/* Handle a MNSH_REQ_READLINK message.  Must be async-signal-safe.  */
+
+static ssize_t
+mnsh_handle_readlink (int sock, const char *filename)
+{
+  char buf[PATH_MAX];
+  int len = readlink (filename, buf, sizeof (buf));
+
+  return mnsh_return_intstr (sock, len,
+			     buf, len < 0 ? 0 : len,
+			     errno);
+}
+
+/* The helper process.  Never returns.  Must be async-signal-safe.  */
+
+static void mnsh_main (int sock) ATTRIBUTE_NORETURN;
+
+static void
+mnsh_main (int sock)
+{
+  while (1)
+    {
+      enum mnsh_msg_type type;
+      int fd, int1, int2;
+      char buf[PATH_MAX];
+      ssize_t size, response = -1;
+
+      size = mnsh_recv_message (sock, &type,
+				&fd, &int1, &int2,
+				buf, sizeof (buf));
+
+      if (size >= 0 && size < sizeof (buf))
+	{
+	  switch (type)
+	    {
+	    case MNSH_REQ_SETNS:
+	      if (fd > 0)
+		response = mnsh_handle_setns (sock, fd, int1);
+	      break;
+
+	    case MNSH_REQ_OPEN:
+	      if (buf[size - 1] == '\0')
+		response = mnsh_handle_open (sock, buf, int1, int2);
+	      break;
+
+	    case MNSH_REQ_UNLINK:
+	      if (buf[size - 1] == '\0')
+		response = mnsh_handle_unlink (sock, buf);
+	      break;
+
+	    case MNSH_REQ_READLINK:
+	      if (buf[size - 1] == '\0')
+		response = mnsh_handle_readlink (sock, buf);
+	      break;
+
+	    default:
+	      break; /* Handled below.  */
+	    }
+	}
+
+      /* Close any file descriptors we were passed.  */
+      if (fd >= 0)
+	close (fd);
+
+      /* Can't handle this message, bounce it back.  */
+      if (response < 0)
+	{
+	  if (size < 0)
+	    size = 0;
+
+	  mnsh_send_message (sock, MNSH_MSG_ERROR,
+			     -1, int1, int2, buf, size);
+	}
+    }
+}
+
+/* The mount namespace helper process.  */
+
+struct linux_mnsh
+{
+  /* PID of helper.  */
+  pid_t pid;
+
+  /* Socket for communication.  */
+  int sock;
+
+  /* ID of the mount namespace the helper is currently in.  */
+  ino_t nsid;
+};
+
+/* In the helper process this is set to the PID of the process that
+   created the helper (i.e. GDB or gdbserver).  In the main process
+   this is set to zero.  Used by mnsh_maybe_mourn_peer.  */
+static int mnsh_creator_pid = 0;
+
+/* Return an object representing the mount namespace helper process.
+   If no mount namespace helper process has been started then start
+   one.  Return NULL if no mount namespace helper process could be
+   started.  */
+
+static struct linux_mnsh *
+linux_mntns_get_helper (void)
+{
+  static struct linux_mnsh *helper = NULL;
+
+  if (helper == NULL)
+    {
+      static struct linux_mnsh h;
+      struct linux_ns *ns;
+      pid_t helper_creator = getpid ();
+      int sv[2];
+
+      ns = linux_ns_get_namespace (LINUX_NS_MNT);
+      if (ns == NULL)
+	return NULL;
+
+      if (gdb_socketpair_cloexec (AF_UNIX, SOCK_STREAM, 0, sv) < 0)
+	return NULL;
+
+      h.pid = fork ();
+      if (h.pid < 0)
+	{
+	  int saved_errno = errno;
+
+	  close (sv[0]);
+	  close (sv[1]);
+
+	  errno = saved_errno;
+	  return NULL;
+	}
+
+      if (h.pid == 0)
+	{
+	  /* Child process.  */
+	  close (sv[0]);
+
+	  mnsh_creator_pid = helper_creator;
+
+	  /* Debug printing isn't async-signal-safe.  */
+	  debug_linux_namespaces = 0;
+
+	  mnsh_main (sv[1]);
+	}
+
+      /* Parent process.  */
+      close (sv[1]);
+
+      helper = &h;
+      helper->sock = sv[0];
+      helper->nsid = ns->id;
+
+      if (debug_linux_namespaces)
+	debug_printf ("Started mount namespace helper process %d\n",
+		      helper->pid);
+    }
+
+  return helper;
+}
+
+/* Check whether the other process died and act accordingly.  Called
+   whenever a socket error occurs, from both the main process and the
+   helper.  Must be async-signal-safe when called from the helper.  */
+
+static void
+mnsh_maybe_mourn_peer (void)
+{
+  if (mnsh_creator_pid != 0)
+    {
+      /* We're in the helper.  Check if our current parent is the
+	 process that started us.  If it isn't, then our original
+	 parent died and we've been reparented.  Exit immediately
+	 if that's the case.  */
+      if (getppid () != mnsh_creator_pid)
+	_exit (0);
+    }
+  else
+    {
+      /* We're in the main process.  */
+
+      struct linux_mnsh *helper = linux_mntns_get_helper ();
+      int status;
+      pid_t pid;
+
+      if (helper->pid < 0)
+	{
+	  /* We already mourned it.  */
+	  return;
+	}
+
+      pid = waitpid (helper->pid, &status, WNOHANG);
+      if (pid == 0)
+	{
+	  /* The helper is still alive.  */
+	  return;
+	}
+      else if (pid == -1)
+	{
+	  if (errno == ECHILD)
+	    warning (_("mount namespace helper vanished?"));
+	  else
+	    internal_warning (__FILE__, __LINE__,
+			      _("unhandled error %d"), errno);
+	}
+      else if (pid == helper->pid)
+	{
+	  if (WIFEXITED (status))
+	    warning (_("mount namespace helper exited with status %d"),
+		     WEXITSTATUS (status));
+	  else if (WIFSIGNALED (status))
+	    warning (_("mount namespace helper killed by signal %d"),
+		     WTERMSIG (status));
+	  else
+	    internal_warning (__FILE__, __LINE__,
+			      _("unhandled status %d"), status);
+	}
+      else
+	internal_warning (__FILE__, __LINE__,
+			  _("unknown pid %d"), pid);
+
+      /* Something unrecoverable happened.  */
+      helper->pid = -1;
+    }
+}
+
+/* Shortcuts for sending messages to the helper.  */
+
+#define mnsh_send_setns(helper, fd, nstype) \
+  mnsh_send_message (helper->sock, MNSH_REQ_SETNS, fd, nstype, 0, \
+		     NULL, 0)
+
+#define mnsh_send_open(helper, filename, flags, mode) \
+  mnsh_send_message (helper->sock, MNSH_REQ_OPEN, -1, flags, mode, \
+    		     filename, strlen (filename) + 1)
+
+#define mnsh_send_unlink(helper, filename) \
+  mnsh_send_message (helper->sock, MNSH_REQ_UNLINK, -1, 0, 0, \
+    		     filename, strlen (filename) + 1)
+
+#define mnsh_send_readlink(helper, filename) \
+  mnsh_send_message (helper->sock, MNSH_REQ_READLINK, -1, 0, 0, \
+    		     filename, strlen (filename) + 1)
+
+/* Receive a message from the helper.  Issue an assertion failure if
+   the message isn't a correctly-formatted MNSH_RET_INT.  Set RESULT
+   and ERROR and return 0 on success.  Set errno and return -1 on
+   failure.  */
+
+static int
+mnsh_recv_int (struct linux_mnsh *helper, int *result, int *error)
+{
+  enum mnsh_msg_type type;
+  char buf[PATH_MAX];
+  ssize_t size;
+  int fd;
+
+  size = mnsh_recv_message (helper->sock, &type, &fd,
+			    result, error,
+			    buf, sizeof (buf));
+  if (size < 0)
+    return -1;
+
+  gdb_assert (type == MNSH_RET_INT);
+  gdb_assert (fd == -1);
+  gdb_assert (size == 0);
+
+  return 0;
+}
+
+/* Receive a message from the helper.  Issue an assertion failure if
+   the message isn't a correctly-formatted MNSH_RET_FD.  Set FD and
+   ERROR and return 0 on success.  Set errno and return -1 on
+   failure.  */
+
+static int
+mnsh_recv_fd (struct linux_mnsh *helper, int *fd, int *error)
+{
+  enum mnsh_msg_type type;
+  char buf[PATH_MAX];
+  ssize_t size;
+  int result;
+
+  size = mnsh_recv_message (helper->sock, &type, fd,
+			    &result, error,
+			    buf, sizeof (buf));
+  if (size < 0)
+    return -1;
+
+  gdb_assert (type == MNSH_RET_FD);
+  gdb_assert (size == 0);
+
+  if (*fd < 0)
+    {
+      gdb_assert (result < 0);
+      *fd = result;
+    }
+
+  return 0;
+}
+
+/* Receive a message from the helper.  Issue an assertion failure if
+   the message isn't a correctly-formatted MNSH_RET_INTSTR.  Set
+   RESULT and ERROR and optionally store data in BUF, then return
+   the number of bytes stored in BUF on success (this may be zero).
+   Set errno and return -1 on error.  */
+
+static ssize_t
+mnsh_recv_intstr (struct linux_mnsh *helper,
+		  int *result, int *error,
+		  void *buf, int bufsiz)
+{
+  enum mnsh_msg_type type;
+  ssize_t size;
+  int fd;
+
+  size = mnsh_recv_message (helper->sock, &type, &fd,
+			    result, error,
+			    buf, bufsiz);
+
+  if (size < 0)
+    return -1;
+
+  gdb_assert (type == MNSH_RET_INTSTR);
+  gdb_assert (fd == -1);
+
+  return size;
+}
+
+/* Return values for linux_mntns_access_fs.  */
+
+enum mnsh_fs_code
+  {
+    /* Something went wrong, errno is set.  */
+    MNSH_FS_ERROR = -1,
+
+    /* The main process is in the correct mount namespace.
+       The caller should access the filesystem directly.  */
+    MNSH_FS_DIRECT,
+
+    /* The helper is in the correct mount namespace.
+       The caller should access the filesystem via the helper.  */
+    MNSH_FS_HELPER
+  };
+
+/* Return a value indicating how the caller should access the
+   mount namespace of process PID.  */
+
+static enum mnsh_fs_code
+linux_mntns_access_fs (pid_t pid)
+{
+  struct cleanup *old_chain;
+  struct linux_ns *ns;
+  struct stat sb;
+  struct linux_mnsh *helper;
+  ssize_t size;
+  int fd, saved_errno;
+
+  if (pid == getpid ())
+    return MNSH_FS_DIRECT;
+
+  ns = linux_ns_get_namespace (LINUX_NS_MNT);
+  if (ns == NULL)
+    return MNSH_FS_DIRECT;
+
+  old_chain = make_cleanup (null_cleanup, NULL);
+
+  fd = gdb_open_cloexec (linux_ns_filename (ns, pid), O_RDONLY, 0);
+  if (fd < 0)
+    goto error;
+
+  old_chain = make_cleanup_close (fd);
+
+  if (fstat (fd, &sb) != 0)
+    goto error;
+
+  if (sb.st_ino == ns->id)
+    {
+      do_cleanups (old_chain);
+
+      return MNSH_FS_DIRECT;
+    }
+
+  helper = linux_mntns_get_helper ();
+  if (helper == NULL)
+    goto error;
+
+  if (sb.st_ino != helper->nsid)
+    {
+      int result, error;
+
+      size = mnsh_send_setns (helper, fd, 0);
+      if (size < 0)
+	goto error;
+
+      if (mnsh_recv_int (helper, &result, &error) != 0)
+	goto error;
+
+      if (result != 0)
+	{
+	  errno = error;
+	  goto error;
+	}
+
+      helper->nsid = sb.st_ino;
+    }
+
+  do_cleanups (old_chain);
+
+  return MNSH_FS_HELPER;
+
+error:
+  saved_errno = errno;
+
+  do_cleanups (old_chain);
+
+  errno = saved_errno;
+  return MNSH_FS_ERROR;
+}
+
+/* See nat/linux-namespaces.h.  */
+
+int
+linux_mntns_open_cloexec (pid_t pid, const char *filename,
+			  int flags, mode_t mode)
+{
+  enum mnsh_fs_code access = linux_mntns_access_fs (pid);
+  struct linux_mnsh *helper;
+  int fd, error;
+  ssize_t size;
+
+  if (access == MNSH_FS_ERROR)
+    return -1;
+
+  if (access == MNSH_FS_DIRECT)
+    return gdb_open_cloexec (filename, flags, mode);
+
+  gdb_assert (access == MNSH_FS_HELPER);
+
+  helper = linux_mntns_get_helper ();
+
+  size = mnsh_send_open (helper, filename, flags, mode);
+  if (size < 0)
+    return -1;
+
+  if (mnsh_recv_fd (helper, &fd, &error) != 0)
+    return -1;
+
+  if (fd < 0)
+    errno = error;
+
+  return fd;
+}
+
+/* See nat/linux-namespaces.h.  */
+
+int
+linux_mntns_unlink (pid_t pid, const char *filename)
+{
+  enum mnsh_fs_code access = linux_mntns_access_fs (pid);
+  struct linux_mnsh *helper;
+  int ret, error;
+  ssize_t size;
+
+  if (access == MNSH_FS_ERROR)
+    return -1;
+
+  if (access == MNSH_FS_DIRECT)
+    return unlink (filename);
+
+  gdb_assert (access == MNSH_FS_HELPER);
+
+  helper = linux_mntns_get_helper ();
+
+  size = mnsh_send_unlink (helper, filename);
+  if (size < 0)
+    return -1;
+
+  if (mnsh_recv_int (helper, &ret, &error) != 0)
+    return -1;
+
+  if (ret != 0)
+    errno = error;
+
+  return ret;
+}
+
+/* See nat/linux-namespaces.h.  */
+
+ssize_t
+linux_mntns_readlink (pid_t pid, const char *filename,
+		      char *buf, size_t bufsiz)
+{
+  enum mnsh_fs_code access = linux_mntns_access_fs (pid);
+  struct linux_mnsh *helper;
+  int ret, error;
+  ssize_t size;
+
+  if (access == MNSH_FS_ERROR)
+    return -1;
+
+  if (access == MNSH_FS_DIRECT)
+    return readlink (filename, buf, bufsiz);
+
+  gdb_assert (access == MNSH_FS_HELPER);
+
+  helper = linux_mntns_get_helper ();
+
+  size = mnsh_send_readlink (helper, filename);
+  if (size < 0)
+    return -1;
+
+  size = mnsh_recv_intstr (helper, &ret, &error, buf, bufsiz);
+
+  if (size < 0)
+    {
+      ret = -1;
+      errno = error;
+    }
+  else
+    gdb_assert (size == ret);
+
+  return ret;
+}
diff --git a/gdb/nat/linux-namespaces.h b/gdb/nat/linux-namespaces.h
new file mode 100644
index 0000000..f9adc07
--- /dev/null
+++ b/gdb/nat/linux-namespaces.h
@@ -0,0 +1,76 @@ 
+/* Linux namespaces(7) support.
+
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef LINUX_NAMESPACES_H
+#define LINUX_NAMESPACES_H
+
+/* Set to nonzero to enable debugging of Linux namespaces code.  */
+
+extern int debug_linux_namespaces;
+
+/* Enumeration of Linux namespace types.  */
+
+enum linux_ns_type
+  {
+    /* IPC namespace: System V IPC, POSIX message queues.  */
+    LINUX_NS_IPC,
+
+    /* Mount namespace: mount points.  */
+    LINUX_NS_MNT,
+
+    /* Network namespace: network devices, stacks, ports, etc.  */
+    LINUX_NS_NET,
+
+    /* PID namespace: process IDs.  */
+    LINUX_NS_PID,
+
+    /* User namespace: user and group IDs.  */
+    LINUX_NS_USER,
+
+    /* UTS namespace: hostname and NIS domain name.  */
+    LINUX_NS_UTS,
+
+    /* Number of Linux namespaces.  */
+    NUM_LINUX_NS_TYPES
+  };
+
+/* Return nonzero if process PID has the same TYPE namespace as the
+   calling process, or if the kernel does not support TYPE namespaces
+   (in which case there is only one TYPE namespace).  Return zero if
+   the kernel supports TYPE namespaces and the two processes have
+   different TYPE namespaces.  */
+
+extern int linux_ns_same (pid_t pid, enum linux_ns_type type);
+
+/* Like gdb_open_cloexec, but in the mount namespace of process
+   PID.  */
+
+extern int linux_mntns_open_cloexec (pid_t pid, const char *filename,
+				     int flags, mode_t mode);
+
+/* Like unlink(2), but in the mount namespace of process PID.  */
+
+extern int linux_mntns_unlink (pid_t pid, const char *filename);
+
+/* Like readlink(2), but in the mount namespace of process PID.  */
+
+extern ssize_t linux_mntns_readlink (pid_t pid, const char *filename,
+				     char *buf, size_t bufsiz);
+
+#endif /* LINUX_NAMESPACES_H */