[3/5] Add support for 'info proc files' on FreeBSD core dumps.

Message ID 20180908003659.37482-4-jhb@FreeBSD.org
State New, archived
Headers

Commit Message

John Baldwin Sept. 8, 2018, 12:36 a.m. UTC
  Walk the list of struct kinfo_file objects in the
NT_FREEBSD_PROCSTAT_FILES core dump note outputting a description of
each open file descriptor.  For sockets, the local and remote socket
addresses are displayed in place of the file name field.  For UNIX
local domain sockets, only a single address is displayed since most
UNIX sockets only have one valid address and printing both pathnames
could be quite long.  The output format was somewhat inspired by the
output of the "procstat -f" command on FreeBSD, but with a few less
details and some fields were condensed.

gdb/ChangeLog:

	* fbsd-tdep.c (KF_FLAGS, KF_OFFSET, KF_VNODE_TYPE, KF_SOCK_DOMAIN)
	(KF_SOCK_TYPE, KF_SOCK_PROTOCOL, KF_SA_LOCAL, KF_SA_PEER)
	(KINFO_FILE_TYPE_SOCKET, KINFO_FILE_TYPE_PIPE)
	(KINFO_FILE_TYPE_FIFO, KINFO_FILE_TYPE_KQUEUE)
	(KINFO_FILE_TYPE_CRYPTO, KINFO_FILE_TYPE_MQUEUE)
	(KINFO_FILE_TYPE_SHM, KINFO_FILE_TYPE_SEM, KINFO_FILE_TYPE_PTS)
	(KINFO_FILE_TYPE_PROCDESC, KINFO_FILE_FD_TYPE_ROOT)
	(KINFO_FILE_FD_TYPE_JAIL, KINFO_FILE_FD_TYPE_TRACE)
	(KINFO_FILE_FD_TYPE_CTTY, KINFO_FILE_FLAG_READ)
	(KINFO_FILE_FLAG_WRITE, KINFO_FILE_FLAG_APPEND)
	(KINFO_FILE_FLAG_ASYNC, KINFO_FILE_FLAG_FSYNC)
	(KINFO_FILE_FLAG_NONBLOCK, KINFO_FILE_FLAG_DIRECT)
	(KINFO_FILE_FLAG_HASLOCK, KINFO_FILE_FLAG_EXEC)
	(KINFO_FILE_VTYPE_VREG, KINFO_FILE_VTYPE_VDIR)
	(KINFO_FILE_VTYPE_VCHR, KINFO_FILE_VTYPE_VLNK)
	(KINFO_FILE_VTYPE_VSOCK, KINFO_FILE_VTYPE_VFIFO, FBSD_AF_UNIX)
	(FBSD_AF_INET, FBSD_AF_INET6, FBSD_SOCK_STREAM, FBSD_SOCK_DGRAM)
	(FBSD_SOCK_SEQPACKET, FBSD_IPPROTO_ICMP, FBSD_IPPROTO_TCP)
	(FBSD_IPPROTO_UDP, FBSD_IPPROTO_SCTP): New defines.
	(struct fbsd_sockaddr_in, struct fbsd_sockaddr_in6)
	(struct fbsd_sockaddr_un): New types.
	(fbsd_file_fd, fbsd_file_type, fbsd_file_flags, fbsd_ipproto)
	(fbsd_print_sockaddr_in, fbsd_print_sockaddr_in6)
	(fbsd_print_socket, fbsd_core_info_proc_files): New functions.
	(fbsd_core_info_proc): List open file descriptors for IP_FILES and
	IP_ALL.
	* fbsd-tdep.h (fbsd_file_fd, fbsd_file_type, fbsd_file_flags)
	(fbsd_print_socket): New.
---
 gdb/ChangeLog   |  31 ++++
 gdb/fbsd-tdep.c | 439 ++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/fbsd-tdep.h |  31 ++++
 3 files changed, 501 insertions(+)
  

Comments

Simon Marchi Sept. 8, 2018, 10:54 p.m. UTC | #1
On 2018-09-08 01:36 AM, John Baldwin wrote:
> Walk the list of struct kinfo_file objects in the
> NT_FREEBSD_PROCSTAT_FILES core dump note outputting a description of
> each open file descriptor.  For sockets, the local and remote socket
> addresses are displayed in place of the file name field.  For UNIX
> local domain sockets, only a single address is displayed since most
> UNIX sockets only have one valid address and printing both pathnames
> could be quite long.  The output format was somewhat inspired by the
> output of the "procstat -f" command on FreeBSD, but with a few less
> details and some fields were condensed.

Just some nits, LGTM otherwise.
> diff --git a/gdb/fbsd-tdep.c b/gdb/fbsd-tdep.c
> index 9e6d7276c4..a8b5b2f146 100644
> --- a/gdb/fbsd-tdep.c
> +++ b/gdb/fbsd-tdep.c
> @@ -90,18 +90,115 @@
>  #define	KF_STRUCTSIZE		0x0
>  #define	KF_TYPE			0x4
>  #define	KF_FD			0x8
> +#define	KF_FLAGS		0x10
> +#define	KF_OFFSET		0x18

In sys/user.h, it says:

 /* XXX Hidden alignment padding here on amd64 */

Does that mean the field offsets can be different for other arches?

> +/* Constats for socket types.  These match SOCK_* constants in

"Constats"

> +/* Helper function to print out an IPv6 socket address.  The address
> +   is formatted similar to inet_ntop.  */
> +
> +static void
> +fbsd_print_sockaddr_in6 (void *sockaddr)
> +{
> +  struct fbsd_sockaddr_in6 *sin6 =
> +    reinterpret_cast<struct fbsd_sockaddr_in6 *>(sockaddr);
> +  uint16_t words[ARRAY_SIZE(sin6->sin6_addr) / 2];
> +
> +  /* Populate the array of 16-bit words from network-order bytes.  */
> +  for (int i = 0; i < ARRAY_SIZE(words); i++)
> +    words[i] = (sin6->sin6_addr[i * 2] << 8) | sin6->sin6_addr[i * 2 + 1];
> +
> +  /* Find the longest run of zero words.  */
> +  int best, bestlen, current, len;
> +
> +  best = -1;
> +  bestlen = 0;
> +  current = -1;
> +  len = 0;
> +  for (int i = 0; i < ARRAY_SIZE(words); i++)
> +    {
> +      if (words[i] == 0)
> +	{
> +	  if (current >= 0)
> +	    len++;
> +	  else
> +	    {
> +	      current = i;
> +	      len = 1;
> +	    }
> +	}
> +      else
> +	{
> +	  if (current >= 0 && len > bestlen)
> +	    {
> +	      best = current;
> +	      bestlen = len;
> +	    }
> +	  current = -1;
> +	  len = 0;
> +	}
> +    }
> +  if (current >= 0 && len > bestlen)
> +    {
> +      best = current;
> +      bestlen = len;
> +    }
> +  if (bestlen < 2)
> +    best = -1;
> +
> +  for (int i = 0; i < ARRAY_SIZE(words); i++)
> +    {
> +      if (best >= 0 && i >= best && i < best + bestlen)
> +	{
> +	  if (i == best || i == ARRAY_SIZE(words) - 1)
> +	    printf_filtered (":");
> +	}
> +      else
> +	{
> +	  if (i != 0)
> +	    printf_filtered (":");
> +	  printf_filtered ("%x", words[i]);
> +	}
> +    }
> +  printf_filtered (".%u", (sin6->sin6_port[0] << 8) | sin6->sin6_port[1]);
> +}

Hmm, I suppose we'll want to re-use this ipv6 address printing code for the Linux...
We can take care of moving it to a common file then.

> +/* Implement "info proc files" for a corefile.  */
> +
> +static void
> +fbsd_core_info_proc_files (struct gdbarch *gdbarch)
> +{
> +  asection *section;
> +  unsigned char *descdata, *descend;
> +  size_t note_size;

Don't hesitate to declare them variables at the point you use the the first time :).
> +
> +  section = bfd_get_section_by_name (core_bfd, ".note.freebsdcore.files");
> +  if (section == NULL)
> +    {
> +      warning (_("unable to find open files in core file"));
> +      return;
> +    }
> +
> +  note_size = bfd_get_section_size (section);
> +  if (note_size < 4)
> +    error (_("malformed core note - too short for header"));
> +
> +  gdb::def_vector<unsigned char> contents (note_size);
> +  if (!bfd_get_section_contents (core_bfd, section, contents.data (),
> +				 0, note_size))
> +    error (_("could not get core note contents"));
> +
> +  descdata = contents.data ();
> +  descend = descdata + note_size;
> +
> +  /* Skip over the structure size.  */
> +  descdata += 4;
> +
> +  printf_filtered (_("Open files:\n\n"));
> +  printf_filtered ("  %6s %6s %10s %9s %s\n",
> +		   "FD", "Type", "Offset", "Flags  ", "Name");
> +
> +  while (descdata + KF_PATH < descend)
> +    {
> +      LONGEST fd, flags, offset, type, vnode_type;
> +      ULONGEST structsize;
> +
> +      structsize = bfd_get_32 (core_bfd, descdata + KF_STRUCTSIZE);
> +      if (structsize < KF_PATH)
> +	error (_("malformed core note - vmmap entry too small"));

Copy pasta?  Actually, there seems to be the same mistake in fbsd_core_vnode_path.

Simon
  
John Baldwin Sept. 10, 2018, 7:37 p.m. UTC | #2
On 9/8/18 3:54 PM, Simon Marchi wrote:
> On 2018-09-08 01:36 AM, John Baldwin wrote:
>> Walk the list of struct kinfo_file objects in the
>> NT_FREEBSD_PROCSTAT_FILES core dump note outputting a description of
>> each open file descriptor.  For sockets, the local and remote socket
>> addresses are displayed in place of the file name field.  For UNIX
>> local domain sockets, only a single address is displayed since most
>> UNIX sockets only have one valid address and printing both pathnames
>> could be quite long.  The output format was somewhat inspired by the
>> output of the "procstat -f" command on FreeBSD, but with a few less
>> details and some fields were condensed.
> 
> Just some nits, LGTM otherwise.
>> diff --git a/gdb/fbsd-tdep.c b/gdb/fbsd-tdep.c
>> index 9e6d7276c4..a8b5b2f146 100644
>> --- a/gdb/fbsd-tdep.c
>> +++ b/gdb/fbsd-tdep.c
>> @@ -90,18 +90,115 @@
>>  #define	KF_STRUCTSIZE		0x0
>>  #define	KF_TYPE			0x4
>>  #define	KF_FD			0x8
>> +#define	KF_FLAGS		0x10
>> +#define	KF_OFFSET		0x18
> 
> In sys/user.h, it says:
> 
>  /* XXX Hidden alignment padding here on amd64 */
> 
> Does that mean the field offsets can be different for other arches?

That comment is in the structure 'struct kinfo_ofile' which is for
an older format of this structure used in FreeBSD 7.0 kernels.  7.1
and later kernels use the 'struct kinfo_file' structure which has a
fixed layout (and includes an explicit kf_pad0 padding field).  FreeBSD
didn't add the NT_PROCSTAT_FILES note to process core dumps until FreeBSD
10.0 (also merged to 9.2), so process cores have only ever included the
newer format with a fixed layout.

If it is helpful I could add a comment.  I think I redid the same
archaeology when adding the initial 'info proc' core dump bits for
FreeBSD. :)

>> +/* Constats for socket types.  These match SOCK_* constants in
> 
> "Constats"

Oops, fixed.

>> +/* Helper function to print out an IPv6 socket address.  The address
>> +   is formatted similar to inet_ntop.  */
>> +
>> +static void
>> +fbsd_print_sockaddr_in6 (void *sockaddr)
>> +{
>> +  ...
>> +}
> 
> Hmm, I suppose we'll want to re-use this ipv6 address printing code for the Linux...
> We can take care of moving it to a common file then.

So I did hate to duplicate a lot of what is normally common code and even
POSIX (inet_ntoa() and inet_ntop()).  We currently don't use those routines
anywhere else in GDB, and I wasn't sure if we could depend on their always
existing.  gnulib doesn't seem to have existing fallbacks for those.  If
we could somehow require them or provide standard fallbacks then I'd be
happier using those instead.

>> +/* Implement "info proc files" for a corefile.  */
>> +
>> +static void
>> +fbsd_core_info_proc_files (struct gdbarch *gdbarch)
>> +{
>> +  asection *section;
>> +  unsigned char *descdata, *descend;
>> +  size_t note_size;
> 
> Don't hesitate to declare them variables at the point you use the the first time :).

Old C habits die hard (even with C99).  I've fixed for V2 though. :)

>> +  while (descdata + KF_PATH < descend)
>> +    {
>> +      LONGEST fd, flags, offset, type, vnode_type;
>> +      ULONGEST structsize;
>> +
>> +      structsize = bfd_get_32 (core_bfd, descdata + KF_STRUCTSIZE);
>> +      if (structsize < KF_PATH)
>> +	error (_("malformed core note - vmmap entry too small"));
> 
> Copy pasta?  Actually, there seems to be the same mistake in fbsd_core_vnode_path.

Yes, and I should fix the original mistake in the first patch of the series.
  
Tom Tromey Sept. 13, 2018, 3:07 p.m. UTC | #3
>>>>> "John" == John Baldwin <jhb@FreeBSD.org> writes:

John> That comment is in the structure 'struct kinfo_ofile' which is for
John> an older format of this structure used in FreeBSD 7.0 kernels.  7.1
John> and later kernels use the 'struct kinfo_file' structure which has a
John> fixed layout (and includes an explicit kf_pad0 padding field).  FreeBSD
John> didn't add the NT_PROCSTAT_FILES note to process core dumps until FreeBSD
John> 10.0 (also merged to 9.2), so process cores have only ever included the
John> newer format with a fixed layout.

John> If it is helpful I could add a comment.  I think I redid the same
John> archaeology when adding the initial 'info proc' core dump bits for
John> FreeBSD. :)

I think a comment sounds helpful for future developers.

Tom
  
John Baldwin Sept. 13, 2018, 6:41 p.m. UTC | #4
On 9/13/18 8:07 AM, Tom Tromey wrote:
>>>>>> "John" == John Baldwin <jhb@FreeBSD.org> writes:
> 
> John> That comment is in the structure 'struct kinfo_ofile' which is for
> John> an older format of this structure used in FreeBSD 7.0 kernels.  7.1
> John> and later kernels use the 'struct kinfo_file' structure which has a
> John> fixed layout (and includes an explicit kf_pad0 padding field).  FreeBSD
> John> didn't add the NT_PROCSTAT_FILES note to process core dumps until FreeBSD
> John> 10.0 (also merged to 9.2), so process cores have only ever included the
> John> newer format with a fixed layout.
> 
> John> If it is helpful I could add a comment.  I think I redid the same
> John> archaeology when adding the initial 'info proc' core dump bits for
> John> FreeBSD. :)
> 
> I think a comment sounds helpful for future developers.

I added one in the V2 series.
  

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 2e5cd0a687..e3d9f40d02 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,34 @@ 
+2018-09-07  John Baldwin  <jhb@FreeBSD.org>
+
+	* fbsd-tdep.c (KF_FLAGS, KF_OFFSET, KF_VNODE_TYPE, KF_SOCK_DOMAIN)
+	(KF_SOCK_TYPE, KF_SOCK_PROTOCOL, KF_SA_LOCAL, KF_SA_PEER)
+	(KINFO_FILE_TYPE_SOCKET, KINFO_FILE_TYPE_PIPE)
+	(KINFO_FILE_TYPE_FIFO, KINFO_FILE_TYPE_KQUEUE)
+	(KINFO_FILE_TYPE_CRYPTO, KINFO_FILE_TYPE_MQUEUE)
+	(KINFO_FILE_TYPE_SHM, KINFO_FILE_TYPE_SEM, KINFO_FILE_TYPE_PTS)
+	(KINFO_FILE_TYPE_PROCDESC, KINFO_FILE_FD_TYPE_ROOT)
+	(KINFO_FILE_FD_TYPE_JAIL, KINFO_FILE_FD_TYPE_TRACE)
+	(KINFO_FILE_FD_TYPE_CTTY, KINFO_FILE_FLAG_READ)
+	(KINFO_FILE_FLAG_WRITE, KINFO_FILE_FLAG_APPEND)
+	(KINFO_FILE_FLAG_ASYNC, KINFO_FILE_FLAG_FSYNC)
+	(KINFO_FILE_FLAG_NONBLOCK, KINFO_FILE_FLAG_DIRECT)
+	(KINFO_FILE_FLAG_HASLOCK, KINFO_FILE_FLAG_EXEC)
+	(KINFO_FILE_VTYPE_VREG, KINFO_FILE_VTYPE_VDIR)
+	(KINFO_FILE_VTYPE_VCHR, KINFO_FILE_VTYPE_VLNK)
+	(KINFO_FILE_VTYPE_VSOCK, KINFO_FILE_VTYPE_VFIFO, FBSD_AF_UNIX)
+	(FBSD_AF_INET, FBSD_AF_INET6, FBSD_SOCK_STREAM, FBSD_SOCK_DGRAM)
+	(FBSD_SOCK_SEQPACKET, FBSD_IPPROTO_ICMP, FBSD_IPPROTO_TCP)
+	(FBSD_IPPROTO_UDP, FBSD_IPPROTO_SCTP): New defines.
+	(struct fbsd_sockaddr_in, struct fbsd_sockaddr_in6)
+	(struct fbsd_sockaddr_un): New types.
+	(fbsd_file_fd, fbsd_file_type, fbsd_file_flags, fbsd_ipproto)
+	(fbsd_print_sockaddr_in, fbsd_print_sockaddr_in6)
+	(fbsd_print_socket, fbsd_core_info_proc_files): New functions.
+	(fbsd_core_info_proc): List open file descriptors for IP_FILES and
+	IP_ALL.
+	* fbsd-tdep.h (fbsd_file_fd, fbsd_file_type, fbsd_file_flags)
+	(fbsd_print_socket): New.
+
 2018-09-07  John Baldwin  <jhb@FreeBSD.org>
 
 	* defs.h (enum info_proc_what) [IP_FILES]: New value.
diff --git a/gdb/fbsd-tdep.c b/gdb/fbsd-tdep.c
index 9e6d7276c4..a8b5b2f146 100644
--- a/gdb/fbsd-tdep.c
+++ b/gdb/fbsd-tdep.c
@@ -90,18 +90,115 @@ 
 #define	KF_STRUCTSIZE		0x0
 #define	KF_TYPE			0x4
 #define	KF_FD			0x8
+#define	KF_FLAGS		0x10
+#define	KF_OFFSET		0x18
+#define	KF_VNODE_TYPE		0x20
+#define	KF_SOCK_DOMAIN		0x24
+#define	KF_SOCK_TYPE		0x28
+#define	KF_SOCK_PROTOCOL	0x2c
+#define	KF_SA_LOCAL		0x30
+#define	KF_SA_PEER		0xb0
 #define	KF_PATH			0x170
 
 /* Constants for the 'kf_type' field in struct kinfo_file.  These
    match the KF_TYPE_* constants in <sys/user.h>.  */
 
 #define	KINFO_FILE_TYPE_VNODE	1
+#define	KINFO_FILE_TYPE_SOCKET	2
+#define	KINFO_FILE_TYPE_PIPE	3
+#define	KINFO_FILE_TYPE_FIFO	4
+#define	KINFO_FILE_TYPE_KQUEUE	5
+#define	KINFO_FILE_TYPE_CRYPTO	6
+#define	KINFO_FILE_TYPE_MQUEUE	7
+#define	KINFO_FILE_TYPE_SHM	8
+#define	KINFO_FILE_TYPE_SEM	9
+#define	KINFO_FILE_TYPE_PTS	10
+#define	KINFO_FILE_TYPE_PROCDESC 11
 
 /* Special values for the 'kf_fd' field in struct kinfo_file.  These
    match the KF_FD_TYPE_* constants in <sys/user.h>.  */
 
 #define	KINFO_FILE_FD_TYPE_CWD	-1
+#define	KINFO_FILE_FD_TYPE_ROOT	-2
+#define	KINFO_FILE_FD_TYPE_JAIL	-3
+#define	KINFO_FILE_FD_TYPE_TRACE -4
 #define	KINFO_FILE_FD_TYPE_TEXT	-5
+#define	KINFO_FILE_FD_TYPE_CTTY	-6
+
+/* Flags in the 'kf_flags' field in struct kinfo_file.  These match
+   the KF_FLAG_* constants in <sys/user.h>.  */
+
+#define	KINFO_FILE_FLAG_READ		0x00000001
+#define	KINFO_FILE_FLAG_WRITE		0x00000002
+#define	KINFO_FILE_FLAG_APPEND		0x00000004
+#define	KINFO_FILE_FLAG_ASYNC		0x00000008
+#define	KINFO_FILE_FLAG_FSYNC		0x00000010
+#define	KINFO_FILE_FLAG_NONBLOCK	0x00000020
+#define	KINFO_FILE_FLAG_DIRECT		0x00000040
+#define	KINFO_FILE_FLAG_HASLOCK		0x00000080
+#define	KINFO_FILE_FLAG_EXEC		0x00004000
+
+/* Constants for the 'kf_vnode_type' field in struct kinfo_file.
+   These match the KF_VTYPE_* constants in <sys/user.h>.  */
+
+#define	KINFO_FILE_VTYPE_VREG	1
+#define	KINFO_FILE_VTYPE_VDIR	2
+#define	KINFO_FILE_VTYPE_VCHR	4
+#define	KINFO_FILE_VTYPE_VLNK	5
+#define	KINFO_FILE_VTYPE_VSOCK	6
+#define	KINFO_FILE_VTYPE_VFIFO	7
+
+/* Constants for socket address families.  These match AF_* constants
+   in <sys/socket.h>.  */
+
+#define	FBSD_AF_UNIX		1
+#define	FBSD_AF_INET		2
+#define	FBSD_AF_INET6		28
+
+/* Constats for socket types.  These match SOCK_* constants in
+   <sys/socket.h>.  */
+
+#define	FBSD_SOCK_STREAM	1
+#define	FBSD_SOCK_DGRAM		2
+#define	FBSD_SOCK_SEQPACKET	5
+
+/* Constants for IP protocols.  These match IPPROTO_* constants in
+   <netinet/in.h>.  */
+
+#define	FBSD_IPPROTO_ICMP	1
+#define	FBSD_IPPROTO_TCP	6
+#define	FBSD_IPPROTO_UDP	17
+#define	FBSD_IPPROTO_SCTP	132
+
+/* Socket address structures.  These have the same layout on all
+   FreeBSD architectures.  In addition, multibyte fields such as IP
+   addresses are always stored in network byte order.  */
+
+struct fbsd_sockaddr_in
+{
+  uint8_t sin_len;
+  uint8_t sin_family;
+  uint8_t sin_port[2];
+  uint8_t sin_addr[4];
+  char sin_zero[8];
+};
+
+struct fbsd_sockaddr_in6
+{
+  uint8_t sin6_len;
+  uint8_t sin6_family;
+  uint8_t sin6_port[2];
+  uint32_t sin6_flowinfo;
+  uint8_t sin6_addr[16];
+  uint32_t sin6_scope_id;
+};
+
+struct fbsd_sockaddr_un
+{
+  uint8_t sun_len;
+  uint8_t sun_family;
+  char sun_path[104];
+};
 
 /* Number of 32-bit words in a signal set.  This matches _SIG_WORDS in
    <sys/_sigset.h> and is the same value on all architectures.  */
@@ -645,6 +742,341 @@  fbsd_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
   return note_data;
 }
 
+/* Helper function to generate the file descriptor description for a
+   single open file in 'info proc files'.  */
+
+const char *
+fbsd_file_fd (int kf_fd)
+{
+  switch (kf_fd)
+    {
+    case KINFO_FILE_FD_TYPE_CWD:
+      return "cwd";
+    case KINFO_FILE_FD_TYPE_ROOT:
+      return "root";
+    case KINFO_FILE_FD_TYPE_JAIL:
+      return "jail";
+    case KINFO_FILE_FD_TYPE_TRACE:
+      return "trace";
+    case KINFO_FILE_FD_TYPE_TEXT:
+      return "text";
+    case KINFO_FILE_FD_TYPE_CTTY:
+      return "ctty";
+    default:
+      return int_string (kf_fd, 10, 1, 0, 0);
+    }
+}
+
+/* Helper function to generate the file type for a single open file in
+   'info proc files'.  */
+
+const char *
+fbsd_file_type (int kf_type, int kf_vnode_type)
+{
+  switch (kf_type)
+    {
+    case KINFO_FILE_TYPE_VNODE:
+      switch (kf_vnode_type)
+	{
+	case KINFO_FILE_VTYPE_VREG:
+	  return "file";
+	case KINFO_FILE_VTYPE_VDIR:
+	  return "dir";
+	case KINFO_FILE_VTYPE_VCHR:
+	  return "chr";
+	case KINFO_FILE_VTYPE_VLNK:
+	  return "link";
+	case KINFO_FILE_VTYPE_VSOCK:
+	  return "socket";
+	case KINFO_FILE_VTYPE_VFIFO:
+	  return "fifo";
+	default:
+	  {
+	    char *str = get_print_cell ();
+
+	    xsnprintf (str, PRINT_CELL_SIZE, "vn:%d", kf_vnode_type);
+	    return str;
+	  }
+	}
+    case KINFO_FILE_TYPE_SOCKET:
+      return "socket";
+    case KINFO_FILE_TYPE_PIPE:
+      return "pipe";
+    case KINFO_FILE_TYPE_FIFO:
+      return "fifo";
+    case KINFO_FILE_TYPE_KQUEUE:
+      return "kqueue";
+    case KINFO_FILE_TYPE_CRYPTO:
+      return "crypto";
+    case KINFO_FILE_TYPE_MQUEUE:
+      return "mqueue";
+    case KINFO_FILE_TYPE_SHM:
+      return "shm";
+    case KINFO_FILE_TYPE_SEM:
+      return "sem";
+    case KINFO_FILE_TYPE_PTS:
+      return "pts";
+    case KINFO_FILE_TYPE_PROCDESC:
+      return "proc";
+    default:
+      return int_string (kf_type, 10, 1, 0, 0);
+    }
+}
+
+/* Helper function to generate the file flags for a single open file in
+   'info proc files'.  */
+
+const char *
+fbsd_file_flags (int kf_flags)
+{
+  static char file_flags[10];
+
+  file_flags[0] = (kf_flags & KINFO_FILE_FLAG_READ) ? 'r' : '-';
+  file_flags[1] = (kf_flags & KINFO_FILE_FLAG_WRITE) ? 'w' : '-';
+  file_flags[2] = (kf_flags & KINFO_FILE_FLAG_EXEC) ? 'x' : '-';
+  file_flags[3] = (kf_flags & KINFO_FILE_FLAG_APPEND) ? 'a' : '-';
+  file_flags[4] = (kf_flags & KINFO_FILE_FLAG_ASYNC) ? 's' : '-';
+  file_flags[5] = (kf_flags & KINFO_FILE_FLAG_FSYNC) ? 'f' : '-';
+  file_flags[6] = (kf_flags & KINFO_FILE_FLAG_NONBLOCK) ? 'n' : '-';
+  file_flags[7] = (kf_flags & KINFO_FILE_FLAG_DIRECT) ? 'd' : '-';
+  file_flags[8] = (kf_flags & KINFO_FILE_FLAG_HASLOCK) ? 'l' : '-';
+  file_flags[9] = '\0';
+
+  return file_flags;
+}
+
+/* Helper function to generate the name of an IP protocol.  */
+
+static const char *
+fbsd_ipproto (int protocol)
+{
+  switch (protocol)
+    {
+    case FBSD_IPPROTO_ICMP:
+      return "icmp";
+    case FBSD_IPPROTO_TCP:
+      return "tcp";
+    case FBSD_IPPROTO_UDP:
+      return "udp";
+    case FBSD_IPPROTO_SCTP:
+      return "sctp";
+    default:
+      {
+	char *str = get_print_cell ();
+
+	xsnprintf (str, PRINT_CELL_SIZE, "ip<%d>", protocol);
+	return str;
+      }
+    }
+}
+
+/* Helper function to print out an IPv4 socket address.  The address
+   is formatted similar to inet_ntoa.  */
+
+static void
+fbsd_print_sockaddr_in (void *sockaddr)
+{
+  struct fbsd_sockaddr_in *sin =
+    reinterpret_cast<struct fbsd_sockaddr_in *>(sockaddr);
+
+  printf_filtered ("%u.%u.%u.%u:%u", sin->sin_addr[0], sin->sin_addr[1],
+		   sin->sin_addr[2], sin->sin_addr[3],
+		   (sin->sin_port[0] << 8) | sin->sin_port[1]);
+}
+
+/* Helper function to print out an IPv6 socket address.  The address
+   is formatted similar to inet_ntop.  */
+
+static void
+fbsd_print_sockaddr_in6 (void *sockaddr)
+{
+  struct fbsd_sockaddr_in6 *sin6 =
+    reinterpret_cast<struct fbsd_sockaddr_in6 *>(sockaddr);
+  uint16_t words[ARRAY_SIZE(sin6->sin6_addr) / 2];
+
+  /* Populate the array of 16-bit words from network-order bytes.  */
+  for (int i = 0; i < ARRAY_SIZE(words); i++)
+    words[i] = (sin6->sin6_addr[i * 2] << 8) | sin6->sin6_addr[i * 2 + 1];
+
+  /* Find the longest run of zero words.  */
+  int best, bestlen, current, len;
+
+  best = -1;
+  bestlen = 0;
+  current = -1;
+  len = 0;
+  for (int i = 0; i < ARRAY_SIZE(words); i++)
+    {
+      if (words[i] == 0)
+	{
+	  if (current >= 0)
+	    len++;
+	  else
+	    {
+	      current = i;
+	      len = 1;
+	    }
+	}
+      else
+	{
+	  if (current >= 0 && len > bestlen)
+	    {
+	      best = current;
+	      bestlen = len;
+	    }
+	  current = -1;
+	  len = 0;
+	}
+    }
+  if (current >= 0 && len > bestlen)
+    {
+      best = current;
+      bestlen = len;
+    }
+  if (bestlen < 2)
+    best = -1;
+
+  for (int i = 0; i < ARRAY_SIZE(words); i++)
+    {
+      if (best >= 0 && i >= best && i < best + bestlen)
+	{
+	  if (i == best || i == ARRAY_SIZE(words) - 1)
+	    printf_filtered (":");
+	}
+      else
+	{
+	  if (i != 0)
+	    printf_filtered (":");
+	  printf_filtered ("%x", words[i]);
+	}
+    }
+  printf_filtered (".%u", (sin6->sin6_port[0] << 8) | sin6->sin6_port[1]);
+}
+
+/* Helper function to print out a description of a socket for 'info
+   proc files'.  */
+
+void
+fbsd_print_socket (int kf_sock_domain, int kf_sock_type, int kf_sock_protocol,
+		   void *kf_sa_local, void *kf_sa_peer)
+{
+  switch (kf_sock_domain)
+    {
+    case FBSD_AF_UNIX:
+      {
+	switch (kf_sock_type)
+	  {
+	  case FBSD_SOCK_STREAM:
+	    printf_filtered ("unix stream:");
+	    break;
+	  case FBSD_SOCK_DGRAM:
+	    printf_filtered ("unix dgram:");
+	    break;
+	  case FBSD_SOCK_SEQPACKET:
+	    printf_filtered ("unix seqpacket:");
+	    break;
+	  default:
+	    printf_filtered ("unix <%d>:", kf_sock_type);
+	    break;
+	  }
+
+	/* For local sockets, print out the first non-nul path rather
+	   than both paths.  */
+	struct fbsd_sockaddr_un *sun =
+	  reinterpret_cast<struct fbsd_sockaddr_un *>(kf_sa_local);
+	if (sun->sun_path[0] == 0)
+	  sun = reinterpret_cast<struct fbsd_sockaddr_un *>(kf_sa_peer);
+	printf_filtered ("%s", sun->sun_path);
+	break;
+      }
+    case FBSD_AF_INET:
+      printf_filtered ("%s4 ", fbsd_ipproto (kf_sock_protocol));
+      fbsd_print_sockaddr_in (kf_sa_local);
+      printf_filtered (" -> ");
+      fbsd_print_sockaddr_in (kf_sa_peer);
+      break;
+    case FBSD_AF_INET6:
+      printf_filtered ("%s6 ", fbsd_ipproto (kf_sock_protocol));
+      fbsd_print_sockaddr_in6 (kf_sa_local);
+      printf_filtered (" -> ");
+      fbsd_print_sockaddr_in6 (kf_sa_peer);
+      break;
+    }
+}
+
+/* Implement "info proc files" for a corefile.  */
+
+static void
+fbsd_core_info_proc_files (struct gdbarch *gdbarch)
+{
+  asection *section;
+  unsigned char *descdata, *descend;
+  size_t note_size;
+
+  section = bfd_get_section_by_name (core_bfd, ".note.freebsdcore.files");
+  if (section == NULL)
+    {
+      warning (_("unable to find open files in core file"));
+      return;
+    }
+
+  note_size = bfd_get_section_size (section);
+  if (note_size < 4)
+    error (_("malformed core note - too short for header"));
+
+  gdb::def_vector<unsigned char> contents (note_size);
+  if (!bfd_get_section_contents (core_bfd, section, contents.data (),
+				 0, note_size))
+    error (_("could not get core note contents"));
+
+  descdata = contents.data ();
+  descend = descdata + note_size;
+
+  /* Skip over the structure size.  */
+  descdata += 4;
+
+  printf_filtered (_("Open files:\n\n"));
+  printf_filtered ("  %6s %6s %10s %9s %s\n",
+		   "FD", "Type", "Offset", "Flags  ", "Name");
+
+  while (descdata + KF_PATH < descend)
+    {
+      LONGEST fd, flags, offset, type, vnode_type;
+      ULONGEST structsize;
+
+      structsize = bfd_get_32 (core_bfd, descdata + KF_STRUCTSIZE);
+      if (structsize < KF_PATH)
+	error (_("malformed core note - vmmap entry too small"));
+
+      type = bfd_get_signed_32 (core_bfd, descdata + KF_TYPE);
+      fd = bfd_get_signed_32 (core_bfd, descdata + KF_FD);
+      flags = bfd_get_signed_32 (core_bfd, descdata + KF_FLAGS);
+      offset = bfd_get_signed_64 (core_bfd, descdata + KF_OFFSET);
+      vnode_type = bfd_get_signed_32 (core_bfd, descdata + KF_VNODE_TYPE);
+      printf_filtered ("  %6s %6s %10s %8s ",
+		       fbsd_file_fd (fd),
+		       fbsd_file_type (type, vnode_type),
+		       offset > -1 ? hex_string (offset) : "-",
+		       fbsd_file_flags (flags));
+      if (type == KINFO_FILE_TYPE_SOCKET)
+	{
+	  LONGEST sock_domain, sock_type, sock_protocol;
+
+	  sock_domain = bfd_get_signed_32 (core_bfd, descdata + KF_SOCK_DOMAIN);
+	  sock_type = bfd_get_signed_32 (core_bfd, descdata + KF_SOCK_TYPE);
+	  sock_protocol = bfd_get_signed_32 (core_bfd,
+					     descdata + KF_SOCK_PROTOCOL);
+	  fbsd_print_socket (sock_domain, sock_type, sock_protocol,
+			     descdata + KF_SA_LOCAL, descdata + KF_SA_PEER);
+	}
+      else
+	printf_filtered ("%s", descdata + KF_PATH);
+      printf_filtered ("\n");
+
+      descdata += structsize;
+    }
+}
+
 /* Helper function to generate mappings flags for a single VM map
    entry in 'info proc mappings'.  */
 
@@ -995,6 +1427,7 @@  fbsd_core_info_proc (struct gdbarch *gdbarch, const char *args,
   bool do_cmdline = false;
   bool do_cwd = false;
   bool do_exe = false;
+  bool do_files = false;
   bool do_mappings = false;
   bool do_status = false;
   int pid;
@@ -1022,10 +1455,14 @@  fbsd_core_info_proc (struct gdbarch *gdbarch, const char *args,
     case IP_CWD:
       do_cwd = true;
       break;
+    case IP_FILES:
+      do_files = true;
+      break;
     case IP_ALL:
       do_cmdline = true;
       do_cwd = true;
       do_exe = true;
+      do_files = true;
       do_mappings = true;
       do_status = true;
       break;
@@ -1065,6 +1502,8 @@  fbsd_core_info_proc (struct gdbarch *gdbarch, const char *args,
       else
 	warning (_("unable to read executable path name"));
     }
+  if (do_files)
+    fbsd_core_info_proc_files (gdbarch);
   if (do_mappings)
     fbsd_core_info_proc_mappings (gdbarch);
   if (do_status)
diff --git a/gdb/fbsd-tdep.h b/gdb/fbsd-tdep.h
index 0b293e5a25..0e0ba57be8 100644
--- a/gdb/fbsd-tdep.h
+++ b/gdb/fbsd-tdep.h
@@ -22,6 +22,37 @@ 
 
 extern void fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch);
 
+/* Helper function to generate the file descriptor description for a
+   single open file in 'info proc files'.  The KF_FD parameter should
+   contain the value of the 'kf_fd' field from a 'struct
+   kinfo_file'.  */
+
+extern const char *fbsd_file_fd (int kf_fd);
+
+/* Helper function to generate the file type for a single open file in
+   'info proc files'.  The KF_TYPE and KF_VNODE_TYPE parameters should
+   contain the values of the corresponding fields in a 'struct
+   kinfo_file'.  */
+
+extern const char *fbsd_file_type (int kf_type, int kf_vnode_type);
+
+/* Helper function to generate the file flags for a single open file
+   in 'info proc files'.  The KF_FLAGS parameter should contain the
+   value of the 'kf_flags' field from a 'struct kinfo_file'.  */
+
+extern const char *fbsd_file_flags (int kf_flags);
+
+/* Helper function to print out a description of a socket for 'info
+   proc files'.  The KF_SOCK_DOMAIN, KF_SOCK_TYPE, and
+   KF_SOCK_PROTOCOL, parameters should contain the value of the
+   corresponding fields in a 'struct kinfo_file'.  The KF_SA_LOCAL and
+   KF_SA_PEER should contain pointers to the corresponding fields in a
+   'struct kinfo_file'.  */
+
+extern void fbsd_print_socket (int kf_sock_domain, int kf_sock_type,
+			       int kf_sock_protocol, void *kf_sa_local,
+			       void *kf_sa_peer);
+
 /* Helper function to generate mappings flags for a single VM map
    entry in 'info proc mappings'.  The KVE_FLAGS and KVE_PROTECTION
    parameters should contain the values of the corresponding fields in