[RFC PATCH v5 4/9] Add basic Linux kernel support

Philipp Rudo prudo@linux.vnet.ibm.com
Mon Mar 12 15:31:00 GMT 2018


Implement the basic infrastructure and functionality to allow Linux kernel
debugging with GDB.  This contains handling of kernel symbols and data
structures as well as a simple target_ops to hook into GDB.  For the code
to work architectures must provide an implementation for the virtual
methods in linux_kernel_ops.

For simplicity this patch only supports static targets, i.e. core dumps.
Support for live debugging will be provided in a separate patch.

gdb/ChangeLog:

	* gdbarch.sh (get_new_lk_ops): New hook.
	* gdbarch.h: Regenerated.
	* gdbarch.c: Regenerated.
	* defs.h (gdb_osabi): Add GDB_OSABI_LINUX_KERNEL.
	* osabi.c (gdb_osabi_names): Add Linux kernel entry.
	* lk-low.h: New file.
	* lk-low.c: New file.
	* lk-list.h: New file.
	* lk-bitmap.h: New file.
	* Makefile.in (ALLDEPFILES): Add lk-low.c.
	(HFILES_NO_SRCDIR): Add lk-low.h.
	(ALL_TARGET_OBS): Add lk-low.o.
	* configure.tgt (lk_tobjs): New variable with object files for Linux
	kernel support.
	(s390*-*-linux*): Add lk_tobjs.
---
 gdb/Makefile.in   |   3 +
 gdb/configure.tgt |   7 +-
 gdb/defs.h        |   1 +
 gdb/gdbarch.c     |  32 ++
 gdb/gdbarch.h     |   9 +
 gdb/gdbarch.sh    |   4 +
 gdb/lk-bitmap.h   | 226 ++++++++++++++
 gdb/lk-list.h     | 201 +++++++++++++
 gdb/lk-low.c      | 864 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/lk-low.h      | 335 +++++++++++++++++++++
 gdb/osabi.c       |   1 +
 11 files changed, 1682 insertions(+), 1 deletion(-)
 create mode 100644 gdb/lk-bitmap.h
 create mode 100644 gdb/lk-list.h
 create mode 100644 gdb/lk-low.c
 create mode 100644 gdb/lk-low.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 690653ac04..056333e2cd 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -715,6 +715,7 @@ ALL_TARGET_OBS = \
 	iq2000-tdep.o \
 	linux-record.o \
 	linux-tdep.o \
+	lk-low.o \
 	lm32-tdep.o \
 	m32c-tdep.o \
 	m32r-linux-tdep.o \
@@ -1276,6 +1277,7 @@ HFILES_NO_SRCDIR = \
 	linux-nat.h \
 	linux-record.h \
 	linux-tdep.h \
+	lk-low.h \
 	location.h \
 	m2-lang.h \
 	m32r-tdep.h \
@@ -2256,6 +2258,7 @@ ALLDEPFILES = \
 	linux-fork.c \
 	linux-record.c \
 	linux-tdep.c \
+	lk-low.c \
 	lm32-tdep.c \
 	m32r-linux-nat.c \
 	m32r-linux-tdep.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index ba90411782..be68ac50fc 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -40,6 +40,10 @@ esac
 i386_tobjs="i386-tdep.o arch/i386.o i387-tdep.o"
 amd64_tobjs="amd64-tdep.o arch/amd64.o"
 
+# List of objectfiles for Linux kernel support.  To be included into *-linux*
+# targets wich support Linux kernel debugging.
+lk_tobjs="lk-low.o"
+
 # Here are three sections to get a list of target specific object
 # files according to target triplet $TARG.
 
@@ -516,7 +520,8 @@ powerpc*-*-*)
 s390*-*-linux*)
 	# Target: S390 running Linux
 	gdb_target_obs="s390-linux-tdep.o s390-tdep.o solib-svr4.o \
-			linux-tdep.o linux-record.o symfile-mem.o"
+			linux-tdep.o linux-record.o symfile-mem.o \
+			${lk_tobjs}"
 	build_gdbserver=yes
 	;;
 
diff --git a/gdb/defs.h b/gdb/defs.h
index 91988758a3..692a7b8407 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -490,6 +490,7 @@ enum gdb_osabi
   GDB_OSABI_HURD,
   GDB_OSABI_SOLARIS,
   GDB_OSABI_LINUX,
+  GDB_OSABI_LINUX_KERNEL,
   GDB_OSABI_FREEBSD,
   GDB_OSABI_NETBSD,
   GDB_OSABI_OPENBSD,
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index b8703e5a55..fd37c51f6b 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -352,6 +352,7 @@ struct gdbarch
   gdbarch_addressable_memory_unit_size_ftype *addressable_memory_unit_size;
   char ** disassembler_options;
   const disasm_options_t * valid_disassembler_options;
+  gdbarch_get_new_lk_ops_ftype *get_new_lk_ops;
 };
 
 /* Create a new ``struct gdbarch'' based on information provided by
@@ -713,6 +714,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of addressable_memory_unit_size, invalid_p == 0 */
   /* Skip verify of disassembler_options, invalid_p == 0 */
   /* Skip verify of valid_disassembler_options, invalid_p == 0 */
+  /* Skip verify of get_new_lk_ops, has predicate.  */
   if (!log.empty ())
     internal_error (__FILE__, __LINE__,
                     _("verify_gdbarch: the following are invalid ...%s"),
@@ -1058,6 +1060,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: get_longjmp_target = <%s>\n",
                       host_address_to_string (gdbarch->get_longjmp_target));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_get_new_lk_ops_p() = %d\n",
+                      gdbarch_get_new_lk_ops_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: get_new_lk_ops = <%s>\n",
+                      host_address_to_string (gdbarch->get_new_lk_ops));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: gdbarch_get_siginfo_type_p() = %d\n",
                       gdbarch_get_siginfo_type_p (gdbarch));
   fprintf_unfiltered (file,
@@ -5077,6 +5085,30 @@ set_gdbarch_valid_disassembler_options (struct gdbarch *gdbarch,
   gdbarch->valid_disassembler_options = valid_disassembler_options;
 }
 
+int
+gdbarch_get_new_lk_ops_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->get_new_lk_ops != NULL;
+}
+
+linux_kernel_ops *
+gdbarch_get_new_lk_ops (struct gdbarch *gdbarch, struct target_ops *target)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->get_new_lk_ops != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_get_new_lk_ops called\n");
+  return gdbarch->get_new_lk_ops (gdbarch, target);
+}
+
+void
+set_gdbarch_get_new_lk_ops (struct gdbarch *gdbarch,
+                            gdbarch_get_new_lk_ops_ftype get_new_lk_ops)
+{
+  gdbarch->get_new_lk_ops = get_new_lk_ops;
+}
+
 
 /* Keep a registry of per-architecture data-pointers required by GDB
    modules.  */
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 5cb131de1d..d1f54c08c9 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -65,6 +65,7 @@ struct mem_range;
 struct syscalls_info;
 struct thread_info;
 struct ui_out;
+class linux_kernel_ops;
 
 #include "regcache.h"
 
@@ -1554,6 +1555,14 @@ extern void set_gdbarch_disassembler_options (struct gdbarch *gdbarch, char ** d
 extern const disasm_options_t * gdbarch_valid_disassembler_options (struct gdbarch *gdbarch);
 extern void set_gdbarch_valid_disassembler_options (struct gdbarch *gdbarch, const disasm_options_t * valid_disassembler_options);
 
+/* Return a new instance of a class inherited from linux_kernel_ops */
+
+extern int gdbarch_get_new_lk_ops_p (struct gdbarch *gdbarch);
+
+typedef linux_kernel_ops * (gdbarch_get_new_lk_ops_ftype) (struct gdbarch *gdbarch, struct target_ops *target);
+extern linux_kernel_ops * gdbarch_get_new_lk_ops (struct gdbarch *gdbarch, struct target_ops *target);
+extern void set_gdbarch_get_new_lk_ops (struct gdbarch *gdbarch, gdbarch_get_new_lk_ops_ftype *get_new_lk_ops);
+
 /* Definition for an unknown syscall, used basically in error-cases.  */
 #define UNKNOWN_SYSCALL (-1)
 
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index 33dfa6b349..80167f2dc2 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -1160,6 +1160,9 @@ m;int;addressable_memory_unit_size;void;;;default_addressable_memory_unit_size;;
 v;char **;disassembler_options;;;0;0;;0;pstring_ptr (gdbarch->disassembler_options)
 v;const disasm_options_t *;valid_disassembler_options;;;0;0;;0;host_address_to_string (gdbarch->valid_disassembler_options)
 
+# Return a new instance of a class inherited from linux_kernel_ops
+M;linux_kernel_ops *;get_new_lk_ops;struct target_ops *target;target
+
 EOF
 }
 
@@ -1285,6 +1288,7 @@ struct mem_range;
 struct syscalls_info;
 struct thread_info;
 struct ui_out;
+class linux_kernel_ops;
 
 #include "regcache.h"
 
diff --git a/gdb/lk-bitmap.h b/gdb/lk-bitmap.h
new file mode 100644
index 0000000000..1247e7f9fb
--- /dev/null
+++ b/gdb/lk-bitmap.h
@@ -0,0 +1,226 @@
+/* Iterator for bitmaps from the Linux kernel.
+
+   Copyright (C) 2017 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 __LK_BITMAP_H__
+#define __LK_BITMAP_H__
+
+#include "defs.h"
+
+#include "lk-low.h"
+
+/* Short hand access to frequently used bitmap.  */
+#define lk_cpu_online_mask lk_bitmap ("cpu_online_mask", "cpumask->bits")
+
+/* Container class to handle bitmaps declared with DECLARE_BITMAP from
+   <linux>/include/linux/types.h.  */
+
+class lk_bitmap
+{
+public:
+
+  template<class T>
+  class base_iterator
+  : public std::iterator<std::bidirectional_iterator_tag, T>
+  {
+  public:
+    base_iterator (const base_iterator<T> &it) = default;
+    base_iterator (std::vector<unsigned long>::const_iterator start,
+		   size_t bit, size_t size)
+      : m_start (start), m_bit (bit), m_size (size)
+    { next (); }
+
+    base_iterator<T> &operator++ ()
+    { m_bit++; return next (); }
+
+    base_iterator<T> operator++ (int)
+    { base_iterator<T> retval = *this; ++(*this); return retval; }
+
+    base_iterator<T> &operator-- ()
+    { m_bit--; return prev (); }
+
+    base_iterator<T> operator-- (int)
+    { base_iterator<T> retval = *this; --(*this); return retval; }
+
+    bool operator== (base_iterator<T> other) const
+    { return (m_start == other.m_start && m_bit == other.m_bit
+	      && m_size == other.m_size); }
+
+    bool operator!= (base_iterator<T> other) const
+    { return !(*this == other); }
+
+    T operator* () const
+    { return m_bit; }
+
+  private:
+    /* Start of the vector containing the bitmap.  */
+    std::vector<unsigned long>::const_iterator m_start;
+
+    /* Last set bit returned.  */
+    size_t m_bit;
+
+    /* Size of the bitmap in bit.  */
+    size_t m_size;
+
+    /* Get next set bit.  */
+    base_iterator<T> &next ();
+
+    /* Get previous set bit.  */
+    base_iterator<T> &prev ();
+  }; /* class base_iterator  */
+
+  /* Constructor for bitmaps defined as variable NAME.  */
+  inline lk_bitmap (const std::string &name);
+
+  /* Constructor for bitmaps defined as field in variable NAME.  */
+  inline lk_bitmap (const std::string &name, const std::string &alias);
+
+  typedef base_iterator<size_t> iterator;
+  typedef base_iterator<const size_t> const_iterator;
+
+  iterator begin () { return iterator (m_bitmap.cbegin (), 0, size ()); }
+  iterator end () { return iterator (m_bitmap.cbegin (), size (), size ()); }
+
+  const_iterator cbegin () const
+  { return const_iterator (m_bitmap.cbegin (), 0, size ()); }
+  const_iterator cend () const
+  { return const_iterator (m_bitmap.cbegin (), size (), size ()); }
+
+  const_iterator begin () const
+  { return this->cbegin (); }
+  const_iterator end () const
+  { return this->cend (); }
+
+  /* Returns size of bitmap in bits.  */
+  inline size_t size () const;
+
+  /* Returns Hamming weight, i.e. number of set bits, of bitmap.  */
+  inline size_t hweight () const;
+
+private:
+  /* Read content of bitmap NAME.  */
+  inline void read (const std::string &name);
+
+  /* Returns number of unsigned longs needed to store N bytes.  */
+  inline size_t byte_to_ulong (size_t n) const;
+
+  /* Storage for content of bitmap.  */
+  std::vector<unsigned long> m_bitmap;
+}; /* class bitmap  */
+
+/* see declaration.  */
+
+template<class T>
+lk_bitmap::base_iterator<T> &
+lk_bitmap::base_iterator<T>::next ()
+{
+  size_t ulong_bits = lk_builtin_type_size (unsigned_long) * LK_BITS_PER_BYTE;
+  auto ulong = m_start + m_bit / ulong_bits;
+  while (m_bit < m_size)
+  {
+    if (*ulong & (1 << m_bit))
+	return *this;
+
+    m_bit++;
+    if ((m_bit % ulong_bits) == 0)
+      ulong++;
+  }
+  return *this;
+}
+
+/* see declaration.  */
+
+template<class T>
+lk_bitmap::base_iterator<T> &
+lk_bitmap::base_iterator<T>::prev ()
+{
+  size_t ulong_bits = lk_builtin_type_size (unsigned_long) * LK_BITS_PER_BYTE;
+  auto ulong = m_start + m_bit / ulong_bits;
+  while (m_bit > m_size)
+  {
+    if (*ulong & (1 << m_bit))
+	return *this;
+
+    m_bit--;
+    if ((m_bit % ulong_bits) == 0)
+      ulong--;
+  }
+  return *this;
+}
+
+/* see declaration.  */
+
+lk_bitmap::lk_bitmap (const std::string &name)
+{
+  symbol *sym = lookup_symbol (name.c_str (), NULL, VAR_DOMAIN, NULL).symbol;
+  size_t size = TYPE_LENGTH (check_typedef (SYMBOL_TYPE (sym)));
+
+  m_bitmap.resize (byte_to_ulong (size));
+  read (name);
+}
+
+/* see declaration.  */
+
+lk_bitmap::lk_bitmap (const std::string &name, const std::string &alias)
+{
+  field *field = lk_field (alias);
+  m_bitmap.resize (byte_to_ulong (FIELD_SIZE (field)));
+  read (name);
+}
+
+/* see declaration.  */
+
+void
+lk_bitmap::read (const std::string &name)
+{
+  size_t ulong_size = lk_builtin_type_size (unsigned_long);
+  CORE_ADDR addr = lk_address (name);
+
+  for (size_t i = 0; i < m_bitmap.size (); i++)
+    m_bitmap[i] = lk_read_ulong (addr + i * ulong_size);
+}
+
+/* see declaration.  */
+size_t
+lk_bitmap::byte_to_ulong (size_t n) const
+{
+  size_t ulong_size = lk_builtin_type_size (unsigned_long);
+  return (n + ulong_size - 1) / ulong_size;
+}
+
+/* see declaration.  */
+
+size_t
+lk_bitmap::size () const
+{
+  size_t ulong_size = lk_builtin_type_size (unsigned_long);
+  return (m_bitmap.size () * ulong_size * LK_BITS_PER_BYTE);
+}
+
+/* see declaration.  */
+
+size_t
+lk_bitmap::hweight () const
+{
+  size_t ret = 0;
+  for (auto bit : *this)
+    ret++;
+  return ret;
+}
+
+#endif /* __LK_BITMAP_H__ */
diff --git a/gdb/lk-list.h b/gdb/lk-list.h
new file mode 100644
index 0000000000..512a47ba08
--- /dev/null
+++ b/gdb/lk-list.h
@@ -0,0 +1,201 @@
+/* Iterators for internal data structures of the Linux kernel.
+
+   Copyright (C) 2016 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 __LK_LIST_H__
+#define __LK_LIST_H__
+
+#include "defs.h"
+
+#include "inferior.h"
+#include "lk-low.h"
+
+
+/* Container class to handle doubly linked list using struct list_head from
+   <linux>/include/linux/types.h .  */
+
+class lk_list
+{
+  template<class T>
+  class base_iterator
+  : public std::iterator<std::bidirectional_iterator_tag, T>
+  {
+  public:
+    base_iterator (const base_iterator<T> &it) = default;
+    base_iterator (CORE_ADDR start, CORE_ADDR offset, bool embedded)
+      : m_current (start), m_start (start), m_offset (offset)
+    {
+      if (!embedded)
+	next ();
+    }
+
+    base_iterator<T> &operator++ ()
+    { return next (); }
+
+    base_iterator<T> operator++ (int)
+    { base_iterator<T> retval = *this; ++(*this); return retval; }
+
+    base_iterator<T> &operator-- ()
+    { return next (false); }
+
+    base_iterator<T> operator-- (int)
+    { base_iterator<T> retval = *this; --(*this); return retval; }
+
+    bool operator== (base_iterator<T> &other) const
+    { return (m_start == other.m_start && m_current == other.m_current
+	      && !m_first); }
+
+    bool operator!= (base_iterator<T> &other) const
+    { return !(*this == other); }
+
+    /* Return container of the list_head.  */
+    T operator* () const
+    { return m_current - m_offset; }
+
+  private:
+    /* The list_head we are currently at.  */
+    CORE_ADDR m_current;
+
+    /* First element of the list.  */
+    CORE_ADDR m_start;
+
+    /* Offset of the list_head in the containing struct.  */
+    CORE_ADDR m_offset;
+
+    /* For doubly linked lists start == end.  Use m_first to track if we
+       just started.  */
+    bool m_first = true;
+
+    /* Go to the next (forward) or prev (!forward) element.  */
+    base_iterator<T> &next (bool forward = true);
+
+    /* We must always assume that the data we handle is corrupted.  Use
+       curr->next->prev == curr (or ->prev->next if goining back).  */
+    bool is_valid_next (CORE_ADDR next, bool forward) const;
+  }; /* class base_iterator  */
+
+public:
+  /* Constructor for lists starting at address START.  */
+  inline lk_list (CORE_ADDR start, const std::string &alias,
+		  bool embedded = true);
+
+  /* Constructor for lists starting at variable NAME.  */
+  inline lk_list (const std::string &name, const std::string &alias)
+    : lk_list (lk_address (name), alias, is_embedded (name))
+  {}
+
+  typedef base_iterator<CORE_ADDR> iterator;
+  typedef base_iterator<const CORE_ADDR> const_iterator;
+
+  /* Never advance to next element for end () --> embedded = true.  */
+  iterator begin () { return iterator (m_start, m_offset, m_embedded); }
+  iterator end () { return iterator (m_start, m_offset, true); }
+
+  const_iterator cbegin () const
+  { return const_iterator (m_start, m_offset, m_embedded); }
+  const_iterator cend () const
+  { return const_iterator (m_start, m_offset, true); }
+
+  const_iterator begin () const
+  { return this->cbegin (); }
+  const_iterator end () const
+  { return this->cend (); }
+
+private:
+  /* First element of the list.  */
+  CORE_ADDR m_start;
+
+  /* Offset of the list_head in the containing struct.  */
+  CORE_ADDR m_offset;
+
+  /* Is the first list_head embedded in the containing struct, i.e. do we
+     have to consider m_start as a full element of the list or just an entry
+     point?  */
+  bool m_embedded;
+
+  /* Check whether variable name is embeded, i.e. is not a list_head.  */
+  inline bool is_embedded (const std::string &name) const;
+}; /* class lk_list */
+
+/* see declaration.  */
+
+lk_list::lk_list (CORE_ADDR start, const std::string &alias, bool embedded)
+  : m_offset (lk_offset (alias)), m_embedded (embedded)
+{
+  m_start = start;
+  if (m_embedded)
+    m_start += m_offset;
+}
+
+/* see declaration.  */
+
+bool
+lk_list::is_embedded (const std::string &name) const
+{
+  symbol *sym = lookup_symbol (name.c_str (), NULL, VAR_DOMAIN, NULL).symbol;
+  type *type = SYMBOL_TYPE (sym);
+
+  return !(TYPE_CODE (type) == TYPE_CODE_STRUCT
+	   && streq ("list_head", TYPE_TAG_NAME (type)));
+}
+
+/* see declaration.  */
+
+template<class T>
+bool
+lk_list::base_iterator<T>::is_valid_next (CORE_ADDR next, bool forward) const
+{
+  if (forward)
+    next += lk_offset ("list_head->prev");
+  else
+    next += lk_offset ("list_head->next");
+
+  return m_current == lk_read_addr (next);
+}
+
+/* see declaration.  */
+
+template<class T>
+lk_list::base_iterator<T> &
+lk_list::base_iterator<T>::next (bool forward)
+{
+  CORE_ADDR next;
+
+  if (m_current == m_start && !m_first)
+    return *this;
+
+  m_first = false;
+
+  if (forward)
+    next = lk_read_addr (m_current + lk_offset ("list_head->next"));
+  else
+    next = lk_read_addr (m_current + lk_offset ("list_head->prev"));
+
+  if (!is_valid_next (next, forward))
+    {
+      error (_("Memory corruption detected while iterating list_head at "
+	       "0x%s: list_head->%s != list_head."),
+	     phex (m_current, lk_builtin_type_size (unsigned_long)),
+	     forward ? "next->prev" : "prev->next");
+    }
+
+  m_current = next;
+
+  return *this;
+}
+#endif /* __LK_LIST_H__ */
diff --git a/gdb/lk-low.c b/gdb/lk-low.c
new file mode 100644
index 0000000000..193619e6f5
--- /dev/null
+++ b/gdb/lk-low.c
@@ -0,0 +1,864 @@
+/* Basic Linux kernel support, architecture independent.
+
+   Copyright (C) 2016 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 "defs.h"
+
+#include "block.h"
+#include "exceptions.h"
+#include "frame.h"
+#include "gdbarch.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "gdbtypes.h"
+#include "inferior.h"
+#include "lk-bitmap.h"
+#include "lk-list.h"
+#include "lk-low.h"
+#include "objfiles.h"
+#include "observer.h"
+#include "solib.h"
+#include "target.h"
+#include "value.h"
+
+#include <algorithm>
+
+target_ops *lk_target_ops = NULL;
+linux_kernel_ops *lk_ops = NULL;
+
+/* Helper function for declare_address.  Returns address of variable NAME on
+   success or -1 on failure.  */
+
+static CORE_ADDR
+lk_find_address (const std::string &name)
+{
+  bound_minimal_symbol bmsym = lookup_minimal_symbol (name.c_str (), NULL,
+						      NULL);
+  if (bmsym.minsym == NULL)
+    return -1;
+
+  return BMSYMBOL_VALUE_ADDRESS (bmsym);
+}
+
+/* See lk-low.h.  */
+
+bool
+linux_kernel_ops::try_declare_address (const std::string &alias,
+				       const std::string &name)
+{
+  if (has_address (alias))
+    return true;
+
+  CORE_ADDR addr = lk_find_address (name);
+  if (addr == -1)
+    return false;
+
+  m_symbols[alias].addr = addr;
+  return true;
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::declare_address (const std::string &alias,
+				   const std::string &name,
+				   const lk_kconfig config)
+{
+  if (!try_declare_address (alias, name))
+    {
+      m_kconfig |= config;
+      warning (_("Missing address: %s"), alias.c_str ());
+    }
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::declare_address (const std::string &alias,
+				   const std::initializer_list<const std::string> names,
+				   const lk_kconfig config)
+{
+  for (auto &name: names)
+    if (try_declare_address (alias, name))
+      break;
+
+  if (!has_address (alias))
+    {
+      m_kconfig |= config;
+      warning (_("Missing address: %s"), alias.c_str ());
+    }
+}
+
+/* Helper function for try_declare_type.  Returns type on success or NULL on
+   failure  */
+
+static struct type *
+lk_find_type (const std::string &name)
+{
+  const struct block *global;
+  const struct symbol *sym;
+
+  global = block_global_block(get_selected_block (0));
+  sym = lookup_symbol (name.c_str (), global, STRUCT_DOMAIN, NULL).symbol;
+  if (sym != NULL)
+    return SYMBOL_TYPE (sym);
+
+  /*  Chek for "typedef struct { ... } name;"-like definitions.  */
+  sym = lookup_symbol (name.c_str (), global, VAR_DOMAIN, NULL).symbol;
+  if (sym == NULL)
+    return NULL;
+
+  struct type *type = check_typedef (SYMBOL_TYPE (sym));
+  if (TYPE_CODE (type) != TYPE_CODE_STRUCT)
+    return NULL;
+
+  return type;
+}
+
+/* See lk-low.h.  */
+
+bool
+linux_kernel_ops::try_declare_type (const std::string &alias,
+				    const std::string &name)
+{
+  if (has_type (alias))
+    return true;
+
+  struct type *type = lk_find_type (name);
+
+  if (type == NULL)
+    return false;
+
+  m_symbols[unique_type_alias (alias)].type = type;
+
+  /* Also add an entry with the name actually used to m_symbol.  Needed to
+     support chained field lookup.  */
+  if (alias != name)
+    m_symbols[unique_type_alias (name)].type = type;
+
+  return true;
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::declare_type (const std::string &alias,
+				const std::string &name,
+				const lk_kconfig config)
+{
+  if (!try_declare_type (alias, name))
+    {
+      m_kconfig |= config;
+      warning (_("Missing type: %s"), unique_type_alias (alias).c_str ());
+    }
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::declare_type (const std::string &alias,
+				const std::initializer_list<const std::string> names,
+				const lk_kconfig config)
+{
+  for (auto &name: names)
+    if (try_declare_type (alias, name))
+      break;
+
+  if (!has_type (alias))
+    {
+      m_kconfig |= config;
+      warning (_("Missing type: %s"), unique_type_alias (alias).c_str ());
+    }
+}
+
+/* Helper function for try_declare_field.  Returns lk_symbol with field
+   belonging to TYPE on success or empty on failure.  */
+
+static lk_symbol
+lk_find_field (const std::string &f_name, const struct type *type)
+{
+  struct field *field = TYPE_FIELDS (type);
+  struct field *last = field + TYPE_NFIELDS (type);
+
+  while (field != last)
+    {
+      if (streq (field->name, f_name.c_str ()))
+	return lk_symbol (field, FIELD_BYTEPOS (field));
+
+      /* Check if field is defined in anonymous struct within TYPE.  */
+      if (streq (field->name, ""))
+	{
+	  lk_symbol sym = lk_find_field (f_name, FIELD_TYPE (*field));
+	  if (sym.field != NULL)
+	    return lk_symbol (sym.field, FIELD_BYTEPOS (field) + sym.offset);
+	}
+      field++;
+    }
+  return lk_symbol ();
+}
+
+/* Helper class to parse C-like field names (type->field1->field2->...) and
+   generate aliases used in lk_ops->m_symbols.  */
+
+class lk_field_parser
+{
+public:
+  lk_field_parser (const std::string &alias)
+    : m_alias (alias)
+  {
+    /* The alias must begin with s_name->f_name of the first field.  */
+    m_end = m_alias.find (delim);
+    gdb_assert (m_end != std::string::npos);
+    m_end = m_alias.find (delim, m_end + delim.size ());
+  }
+
+  /* Return the struct, i.e. type name of the current field.  */
+  std::string s_name () const
+    {
+      if (m_last_type == NULL)
+	return m_alias.substr (0, m_alias.find (delim));
+
+      if (TYPE_CODE (m_last_type) == TYPE_CODE_TYPEDEF)
+	return TYPE_NAME (m_last_type);
+      else
+	return TYPE_TAG_NAME (m_last_type);
+    }
+
+  /* Return the field name of the current field.  */
+  std::string f_name () const
+    {
+      size_t start;
+
+      if (m_last_type == NULL)
+	start = m_alias.find (delim) + delim.size ();
+      else
+	start = m_start;
+
+      return m_alias.substr (start, m_end - start);
+    }
+
+  /* Return the full name of the current field.  */
+  std::string name () const
+  { return s_name () + delim + f_name (); }
+
+  /* Advance to the next field.  */
+  lk_field_parser *next ()
+    {
+      gdb_assert (!empty ());
+
+      m_last_type = FIELD_TYPE (*lk_field (name ()));
+      m_start = m_end + delim.size ();
+      m_end = m_alias.find (delim, m_start);
+
+      return this;
+    }
+
+  /* True when all fiels have been parsed.  */
+  bool empty () const
+  { return m_end == std::string::npos; }
+
+  /* Return the depth, i.e. number of fields, in m_alias.  */
+  unsigned int depth () const
+  {
+    size_t pos = m_alias.find (delim);
+    unsigned int ret = 0;
+
+    while (pos != std::string::npos)
+      {
+	ret ++;
+	pos = m_alias.find (delim, pos + delim.size ());
+      }
+
+    return ret;
+  }
+
+private:
+  /* Alias originally passed to parser.  */
+  std::string m_alias;
+
+  /* First index of current field in m_alias.  */
+  size_t m_start = 0;
+
+  /* Last index of current field in m_alias.  */
+  size_t m_end = 0;
+
+  /* Type of the last field found.  Needed to get s_name of embedded
+     fields.  */
+  struct type *m_last_type = NULL;
+
+  /* Delemiter used to separate fields.  */
+  const std::string delim = "->";
+};
+
+/* See lk-low.h.  */
+
+bool
+linux_kernel_ops::try_declare_field (const std::string &orig_alias,
+				     const std::string &orig_name)
+{
+  if (has_field (orig_alias))
+    return true;
+
+  lk_field_parser alias (orig_alias);
+  lk_field_parser name (orig_name);
+
+  /* Only allow declaration of one field at a time.  */
+  gdb_assert (alias.depth () == 1);
+  gdb_assert (name.depth () == 1);
+
+  if (!try_declare_type (alias.s_name (), name.s_name ()))
+    return false;
+
+  lk_symbol field = lk_find_field (name.f_name (), type (alias.s_name ()));
+  if (field.field == NULL)
+    return false;
+
+  m_symbols[alias.name ()] = field;
+  return true;
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::declare_field (const std::string &alias,
+				 const std::string &name,
+				 const lk_kconfig config)
+{
+  if (!try_declare_field (alias, name))
+    {
+      m_kconfig |= config;
+      warning (_("Missing field: %s"), alias.c_str ());
+    }
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::declare_field (const std::string &alias,
+				 const std::initializer_list<const std::string> names,
+				 const lk_kconfig config)
+{
+  for (auto &name: names)
+    if (try_declare_field (alias, name))
+      break;
+
+  if (!has_field (alias))
+    {
+      m_kconfig |= config;
+      warning (_("Missing field: %s"), alias.c_str ());
+    }
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::read_symbols ()
+{
+  if (!m_symbols.empty ())
+    m_symbols.clear ();
+
+  declare_field ("task_struct->tasks", LK_CONFIG_ALWAYS);
+  declare_field ("task_struct->pid", LK_CONFIG_ALWAYS);
+  declare_field ("task_struct->tgid", LK_CONFIG_ALWAYS);
+  declare_field ("task_struct->thread_group", LK_CONFIG_ALWAYS);
+  declare_field ("task_struct->comm", LK_CONFIG_ALWAYS);
+  declare_field ("task_struct->thread", LK_CONFIG_ALWAYS);
+
+  declare_field ("list_head->next", LK_CONFIG_ALWAYS);
+  declare_field ("list_head->prev", LK_CONFIG_ALWAYS);
+
+  declare_field ("rq->curr", LK_CONFIG_ALWAYS);
+
+  declare_field ("cpumask->bits", LK_CONFIG_ALWAYS);
+
+  declare_address ("init_task", LK_CONFIG_ALWAYS);
+  declare_address ("runqueues", LK_CONFIG_ALWAYS);
+  declare_address ("__per_cpu_offset", LK_CONFIG_ALWAYS);
+
+  declare_address ("cpu_online_mask", {"__cpu_online_mask", /* linux 4.5+ */
+				       "cpu_online_bits"},  /* linux -4.4 */
+		   LK_CONFIG_ALWAYS);
+
+  arch_read_symbols ();
+
+  if (!ifdef (LK_CONFIG_ALWAYS))
+    error (_("Could not find all symbols needed.  Aborting."));
+}
+
+/* See lk-low.h.  */
+
+CORE_ADDR
+linux_kernel_ops::offset (const std::string &orig_alias) const
+{
+  lk_field_parser alias (orig_alias);
+  CORE_ADDR ret = m_symbols.at (alias.name ()).offset;
+
+  while (!alias.empty ())
+    ret += m_symbols.at (alias.next ()->name ()).offset;
+
+  return ret;
+}
+
+/* Map cpu number CPU to the original PTID from target beneath.  */
+
+static ptid_t
+lk_cpu_to_old_ptid (const unsigned int cpu)
+{
+  struct lk_ptid_map *ptid_map;
+
+  for (ptid_map = lk_ops->old_ptid; ptid_map;
+       ptid_map = ptid_map->next)
+    {
+      if (ptid_map->cpu == cpu)
+	return ptid_map->old_ptid;
+    }
+
+  error (_("Could not map CPU %d to original PTID.  Aborting."), cpu);
+}
+
+/* Helper functions to read and return basic types at a given ADDRess.  */
+
+/* Read and return the integer value at address ADDR.  */
+
+int
+lk_read_int (CORE_ADDR addr)
+{
+  size_t int_size = lk_builtin_type_size (int);
+  enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
+  return read_memory_integer (addr, int_size, endian);
+}
+
+/* Read and return the unsigned integer value at address ADDR.  */
+
+unsigned int
+lk_read_uint (CORE_ADDR addr)
+{
+  size_t uint_size = lk_builtin_type_size (unsigned_int);
+  enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
+  return read_memory_unsigned_integer (addr, uint_size, endian);
+}
+
+/* Read and return the long integer value at address ADDR.  */
+
+LONGEST
+lk_read_long (CORE_ADDR addr)
+{
+  size_t long_size = lk_builtin_type_size (long);
+  enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
+  return read_memory_integer (addr, long_size, endian);
+}
+
+/* Read and return the unsigned long integer value at address ADDR.  */
+
+ULONGEST
+lk_read_ulong (CORE_ADDR addr)
+{
+  size_t ulong_size = lk_builtin_type_size (unsigned_long);
+  enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
+  return read_memory_unsigned_integer (addr, ulong_size, endian);
+}
+
+/* Read and return the address value at address ADDR.  */
+
+CORE_ADDR
+lk_read_addr (CORE_ADDR addr)
+{
+  return (CORE_ADDR) lk_read_ulong (addr);
+}
+
+/* See lk-low.h.  */
+
+CORE_ADDR
+linux_kernel_ops::percpu_offset (unsigned int cpu)
+{
+  size_t ulong_size = lk_builtin_type_size (unsigned_long);
+  CORE_ADDR percpu_elt = address ("__per_cpu_offset") + (ulong_size * cpu);
+  return lk_read_addr (percpu_elt);
+}
+
+/* See lk-low.h.  */
+
+unsigned int
+linux_kernel_ops::beneath_thread_to_cpu (thread_info *ti)
+{
+  for (unsigned int cpu : lk_cpu_online_mask)
+    {
+      CORE_ADDR rq = address ("runqueues") + percpu_offset (cpu);
+      CORE_ADDR curr = lk_read_addr (rq + offset ("rq->curr"));
+      int pid = lk_read_int (curr + offset ("task_struct->pid"));
+
+      if (pid == ti->ptid.lwp ())
+	return cpu;
+    }
+
+  error (_("Could not map thread with pid %d, lwp %lu to a cpu."),
+	 ti->ptid.pid (), ti->ptid.lwp ());
+}
+
+/* Test if a given task TASK is running.  See comment in lk-low.h for
+   details.  */
+
+unsigned int
+lk_task_running (CORE_ADDR task)
+{
+  for (unsigned int cpu : lk_cpu_online_mask)
+    {
+      CORE_ADDR rq = lk_address ("runqueues") + lk_ops->percpu_offset (cpu);
+      CORE_ADDR curr = lk_read_addr (rq + lk_offset ("rq->curr"));
+
+      if (curr == task)
+	return cpu;
+    }
+
+  return LK_CPU_INVAL;
+}
+
+/* Update running tasks with information from struct rq->curr. */
+
+static void
+lk_update_running_tasks ()
+{
+  for (unsigned int cpu : lk_cpu_online_mask)
+    {
+      CORE_ADDR rq = lk_address ("runqueues") + lk_ops->percpu_offset (cpu);
+      CORE_ADDR curr = lk_read_addr (rq + lk_offset ("rq->curr"));
+      int pid = lk_read_int (curr + lk_offset ("task_struct->pid"));
+      int inf_pid = current_inferior ()->pid;
+
+      ptid_t new_ptid (inf_pid, pid, curr);
+      ptid_t old_ptid = lk_cpu_to_old_ptid (cpu); /* FIXME not suitable for
+						     running targets? */
+
+      thread_info *tp = find_thread_ptid (old_ptid);
+      if (tp && tp->state != THREAD_EXITED)
+	thread_change_ptid (old_ptid, new_ptid);
+    }
+}
+
+/* Update sleeping tasks by walking the task_structs starting from
+   init_task.  */
+
+static void
+lk_update_sleeping_tasks ()
+{
+  int inf_pid = current_inferior ()->pid;
+
+  for (CORE_ADDR task : lk_list ("init_task", "task_struct->tasks"))
+    {
+      for (CORE_ADDR thread : lk_list (task, "task_struct->thread_group"))
+	{
+	  int pid = lk_read_int (thread + lk_offset ("task_struct->pid"));
+	  ptid_t ptid (inf_pid, pid, thread);
+
+	  thread_info *tp = find_thread_ptid (ptid);
+	  if (tp == NULL || tp->state == THREAD_EXITED)
+	    add_thread (ptid);
+	}
+    }
+}
+
+/* Function for targets to_update_thread_list hook.  */
+
+static void
+lk_update_thread_list (struct target_ops *target)
+{
+  prune_threads ();
+  lk_update_running_tasks ();
+  lk_update_sleeping_tasks ();
+}
+
+/* Function for targets to_fetch_registers hook.  */
+
+static void
+lk_fetch_registers (struct target_ops *target,
+		    struct regcache *regcache, int regnum)
+{
+  CORE_ADDR task = (CORE_ADDR) regcache->ptid ().tid ();
+
+  /* Are we called during init?  */
+  if (task == 0)
+    return target->beneath->to_fetch_registers (target, regcache, regnum);
+
+  unsigned int cpu = lk_task_running (task);
+
+  /* Let the target beneath fetch registers of running tasks.  */
+  if (cpu != LK_CPU_INVAL)
+    {
+      scoped_restore_regcache_ptid restore_regcache (regcache);
+      regcache->set_ptid (lk_cpu_to_old_ptid (cpu));
+
+      lk_ops->beneath ()->to_fetch_registers (target, regcache, regnum);
+    }
+  else
+    {
+      lk_ops->get_registers (task, target, regcache, regnum);
+
+      /* Mark all registers not found as unavailable.  */
+      for (int i = 0; i < gdbarch_num_regs (regcache->arch ()); i++)
+	{
+	  if (regcache->get_register_status (i) != REG_VALID)
+	    regcache->invalidate (i);
+	}
+    }
+}
+
+/* Function for targets to_pid_to_str hook.  Marks running tasks with an
+   asterisk "*".  */
+
+static const char *
+lk_pid_to_str (struct target_ops *target, ptid_t ptid)
+{
+  CORE_ADDR task = (CORE_ADDR) ptid.tid ();
+  static std::string str;
+  const char *fmt;
+
+  if (lk_task_running (task) != LK_CPU_INVAL)
+    fmt = "PID: %5li*, 0x%s";
+  else
+    fmt = "PID: %6li, 0x%s";
+
+  str = string_printf (fmt, ptid.lwp (),
+		       phex (task, lk_builtin_type_size (unsigned_long)));
+
+  return str.c_str ();
+}
+
+/* Function for targets to_thread_name hook.  */
+
+static const char *
+lk_thread_name (struct target_ops *target, struct thread_info *ti)
+{
+  static std::string str (LK_TASK_COMM_LEN, '\0');
+
+  size_t size = std::min ((unsigned int) LK_TASK_COMM_LEN,
+			  LK_ARRAY_LEN(lk_field ("task_struct->comm")));
+
+  CORE_ADDR task = (CORE_ADDR) ti->ptid.tid ();
+  CORE_ADDR comm = task + lk_offset ("task_struct->comm");
+  read_memory (comm, (gdb_byte *) str.data (), size);
+
+  str = string_printf ("%-16s", str.c_str ());
+
+  return str.c_str ();
+}
+
+
+/* Functions to initialize and free target_ops and its private data.  As well
+   as functions for targets to_open/close/detach hooks.  */
+
+/* Check if OBFFILE is a Linux kernel.  */
+
+static bool
+lk_is_linux_kernel (struct objfile *objfile)
+{
+  int ok = 0;
+
+  if (objfile == NULL || !(objfile->flags & OBJF_MAINLINE))
+    return false;
+
+  ok += lookup_minimal_symbol ("linux_banner", NULL, objfile).minsym != NULL;
+  ok += lookup_minimal_symbol ("_stext", NULL, objfile).minsym != NULL;
+  ok += lookup_minimal_symbol ("_etext", NULL, objfile).minsym != NULL;
+
+  return (ok > 2);
+}
+
+/* Frees the cpu to old ptid map.  */
+
+static void
+lk_free_ptid_map ()
+{
+  while (lk_ops->old_ptid)
+    {
+      struct lk_ptid_map *tmp;
+
+      tmp = lk_ops->old_ptid;
+      lk_ops->old_ptid = tmp->next;
+      XDELETE (tmp);
+    }
+}
+
+/* Initialize the cpu to old ptid map.  */
+
+static void
+lk_init_ptid_map ()
+{
+  struct thread_info *ti;
+
+  if (lk_ops->old_ptid != NULL)
+    lk_free_ptid_map ();
+
+  ALL_THREADS (ti)
+    {
+      struct lk_ptid_map *ptid_map = XCNEW (struct lk_ptid_map);
+
+      ptid_map->cpu = lk_ops->beneath_thread_to_cpu (ti);
+      ptid_map->old_ptid = ti->ptid;
+
+      ptid_map->next = lk_ops->old_ptid;
+      lk_ops->old_ptid = ptid_map;
+    }
+}
+
+/* Initializes all private data and pushes the linux kernel target, if not
+   already done.  */
+
+static void
+lk_try_push_target ()
+{
+  struct gdbarch *gdbarch;
+
+  gdbarch = current_inferior ()->gdbarch;
+  if (!(gdbarch && gdbarch_get_new_lk_ops_p (gdbarch)))
+    error (_("Linux kernel debugging not supported on %s."),
+	   gdbarch_bfd_arch_info (gdbarch)->printable_name);
+
+  lk_ops = gdbarch_get_new_lk_ops (gdbarch, lk_target_ops);
+  lk_ops->read_symbols ();
+
+  lk_init_ptid_map ();
+  lk_update_thread_list (lk_ops->target ());
+
+  if (!target_is_pushed (lk_target_ops))
+    push_target (lk_ops->target ());
+}
+
+/* Function for targets to_open hook.  */
+
+static void
+lk_open (const char *args, int from_tty)
+{
+  struct objfile *objfile;
+
+  if (target_is_pushed (lk_target_ops))
+    {
+      printf_unfiltered (_("Linux kernel target already pushed.  Aborting\n"));
+      return;
+    }
+
+  for (objfile = current_program_space->objfiles; objfile;
+       objfile = objfile->next)
+    {
+      if (lk_is_linux_kernel (objfile)
+	  && inferior_ptid.pid () != 0)
+	{
+	  lk_try_push_target ();
+	  return;
+	}
+    }
+  printf_unfiltered (_("Could not find a valid Linux kernel object file.  "
+		       "Aborting.\n"));
+}
+
+/* Function for targets to_close hook.  Deletes all private data.  */
+
+static void
+lk_close (struct target_ops *ops)
+{
+  lk_free_ptid_map ();
+
+  delete (lk_ops);
+}
+
+/* Function for targets to_detach hook.  */
+
+static void
+lk_detach (struct target_ops *t, inferior *inf, int from_tty)
+{
+  struct target_ops *beneath = lk_ops->beneath ();
+
+  unpush_target (lk_ops->target ());
+  reinit_frame_cache ();
+  if (from_tty)
+    printf_filtered (_("Linux kernel target detached.\n"));
+
+  beneath->to_detach (beneath, inf, from_tty);
+}
+
+/* Function for new objfile observer.  */
+
+static void
+lk_observer_new_objfile (struct objfile *objfile)
+{
+  if (lk_is_linux_kernel (objfile) && inferior_ptid.pid () != 0)
+    lk_try_push_target ();
+}
+
+/* Function for inferior created observer.  */
+
+static void
+lk_observer_inferior_created (struct target_ops *ops, int from_tty)
+{
+  struct objfile *objfile;
+
+  if (inferior_ptid.pid () == 0)
+    return;
+
+  for (objfile = current_inferior ()->pspace->objfiles; objfile;
+       objfile = objfile->next)
+    {
+      if (lk_is_linux_kernel (objfile))
+	{
+	  lk_try_push_target ();
+	  return;
+	}
+    }
+}
+
+/* Initialize linux kernel target.  */
+
+static void
+init_lk_target_ops (void)
+{
+  struct target_ops *t;
+
+  if (lk_target_ops != NULL)
+    return;
+
+  t = XCNEW (struct target_ops);
+  t->to_shortname = "linux-kernel";
+  t->to_longname = "linux kernel support";
+  t->to_doc = "Adds support to debug the Linux kernel";
+
+  t->to_open = lk_open;
+  t->to_close = lk_close;
+  t->to_detach = lk_detach;
+  t->to_fetch_registers = lk_fetch_registers;
+  t->to_update_thread_list = lk_update_thread_list;
+  t->to_pid_to_str = lk_pid_to_str;
+  t->to_thread_name = lk_thread_name;
+
+  t->to_stratum = thread_stratum;
+  t->to_magic = OPS_MAGIC;
+
+  lk_target_ops = t;
+
+  add_target (t);
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes.  */
+extern initialize_file_ftype _initialize_linux_kernel;
+
+void
+_initialize_linux_kernel (void)
+{
+  init_lk_target_ops ();
+
+  observer_attach_new_objfile (lk_observer_new_objfile);
+  observer_attach_inferior_created (lk_observer_inferior_created);
+}
diff --git a/gdb/lk-low.h b/gdb/lk-low.h
new file mode 100644
index 0000000000..39c0d88f43
--- /dev/null
+++ b/gdb/lk-low.h
@@ -0,0 +1,335 @@
+/* Basic Linux kernel support, architecture independent.
+
+   Copyright (C) 2016 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 __LK_LOW_H__
+#define __LK_LOW_H__
+
+#include <unordered_map>
+
+#include "gdbtypes.h"
+#include "target.h"
+
+/* Copied constants defined in Linux kernel.  */
+#define LK_TASK_COMM_LEN 16
+#define LK_BITS_PER_BYTE 8
+
+/* Definitions used in linux kernel target.  */
+#define LK_CPU_INVAL -1U
+
+/* Helper functions to read and return a value at a given ADDRess.  */
+extern int lk_read_int (CORE_ADDR addr);
+extern unsigned int lk_read_uint (CORE_ADDR addr);
+extern LONGEST lk_read_long (CORE_ADDR addr);
+extern ULONGEST lk_read_ulong (CORE_ADDR addr);
+extern CORE_ADDR lk_read_addr (CORE_ADDR addr);
+
+/* Enum to track the config options used to build the kernel.  Whenever
+   a symbol is declared (in linux_kernel_ops::{arch_}read_symbols) which
+   only exists if the kernel was built with a certain config option an entry
+   has to be added here.  */
+enum lk_kconfig_values
+{
+  LK_CONFIG_ALWAYS	= 1 << 0,
+  LK_CONFIG_SMT		= 1 << 1,
+  LK_CONFIG_MODULES	= 1 << 2,
+};
+DEF_ENUM_FLAGS_TYPE (enum lk_kconfig_values, lk_kconfig);
+
+/* We use the following convention for PTIDs:
+
+   ptid->pid = inferiors PID
+   ptid->lwp = PID from task_stuct
+   ptid->tid = address of task_struct
+
+   The task_structs address as TID has two reasons.  First, we need it quite
+   often and there is no other reasonable way to pass it down.  Second, it
+   helps us to distinguish swapper tasks as they all have PID = 0.
+
+   Furthermore we cannot rely on the target beneath to use the same PID as the
+   task_struct. Thus we need a mapping between our PTID and the PTID of the
+   target beneath. Otherwise it is impossible to pass jobs, e.g. fetching
+   registers of running tasks, to the target beneath.  */
+
+/* Private data struct to map between our and the target beneath PTID.  */
+
+struct lk_ptid_map
+{
+  struct lk_ptid_map *next;
+  unsigned int cpu;
+  ptid_t old_ptid;
+};
+
+/* Cache for the value of a symbol.  Used in linux_kernel_ops->m_symbols.  */
+
+union lk_symbol
+{
+  CORE_ADDR addr;
+  struct type *type;
+  struct
+  {
+    struct field *field;
+    CORE_ADDR offset;
+  };
+
+  lk_symbol () {field = NULL; offset = 0;}
+  lk_symbol (struct field *f, CORE_ADDR o) {field = f, offset = o;}
+};
+
+class linux_kernel_ops
+{
+public:
+  linux_kernel_ops (struct target_ops *ops)
+    : m_ops (ops)
+  {}
+
+  virtual ~linux_kernel_ops () = default;
+
+  /* Read registers from the target and supply their content to regcache.  */
+  virtual void get_registers (CORE_ADDR task, struct target_ops *target,
+			      struct regcache *regcache, int regnum) = 0;
+
+  /* Return the per_cpu_offset of cpu CPU.  Default uses __per_cpu_offset
+     array to determine the offset.  */
+  virtual CORE_ADDR percpu_offset (unsigned int cpu);
+
+  /* Maps thread of target beneath to a cpu id.  Default assumes
+     rq->curr->pid == beneath_ptid.lwp.  */
+  virtual unsigned int beneath_thread_to_cpu (thread_info *ti);
+
+  /* Return the linux-kernel target.  */
+  struct target_ops *target () const
+  { return m_ops; }
+
+  /* Return the target beneath the linux-kernel target.  */
+  struct target_ops *beneath () const
+  { return m_ops->beneath; }
+
+  /* Return a previously declared address with key ALIAS.
+     Throws internal_error if requested symbol was not declared first.  */
+  CORE_ADDR address (const std::string &alias) const
+  {
+    gdb_assert (has_address (alias));
+    return m_symbols.at (alias).addr;
+  }
+
+  /* Same like address but for types.  */
+  struct type *type (const std::string &alias) const
+  {
+    gdb_assert (has_type (alias));
+    return m_symbols.at (unique_type_alias(alias)).type;
+  }
+
+  /* Same like address but for fields.  */
+  struct field *field (const std::string &alias) const
+  {
+    gdb_assert (has_field (alias));
+    return m_symbols.at (alias).field;
+  }
+
+  /* Checks whether address ALIAS exists in m_symbols.  */
+  bool has_address (const std::string &alias) const
+  { return has_symbol (alias); }
+
+  /* Same like has_address but for types.  */
+  bool has_type (const std::string &alias) const
+  { return has_symbol (unique_type_alias (alias)); }
+
+  /* Same like has_address but for fields.  */
+  bool has_field (const std::string &alias) const
+  { return has_symbol (alias); }
+
+  /* Return offset of field ALIAS (in byte).  */
+  CORE_ADDR offset (const std::string &alias) const;
+
+  /* Check whether the kernel was build using this config option.  */
+  bool ifdef (lk_kconfig config) const
+  { return !(m_kconfig & config); }
+
+  /* Linked list to map between cpu number and original ptid from target
+     beneath.  */
+  struct lk_ptid_map *old_ptid = NULL;
+
+  /* Declare and initialize all symbols needed.  Must be called _after_
+     symbol tables were initialized.  */
+  void read_symbols ();
+
+protected:
+  /* Virtual method to allow architectures to declare their own symbols.
+     Called by read_symbols.  */
+  virtual void arch_read_symbols ()
+  {}
+
+  /* Helper function to declare_address.  Returns true when symbol NAME
+     using key ALIAS was successfully declared, false otherwise.  Try not to
+     use this function directly but use declare_address instead.  */
+  bool try_declare_address (const std::string &alias,
+			    const std::string &names);
+
+  /* Same like try_declare_address but for types.  */
+  bool try_declare_type (const std::string &alias, const std::string &name);
+
+  /* Same like try_declare_address but for fields.  */
+  bool try_declare_field (const std::string &alias, const std::string &name);
+
+  /* Same like try_declare_field but with NAME = ALIAS.  */
+  bool try_declare_field (const std::string &name)
+  { return try_declare_field (name, name); }
+
+  /* Declare symbol NAME using key ALIAS.  If no symbol NAME could be found
+     mark CONFIG as missing.  */
+  void declare_address (const std::string &alias, const std::string &name,
+			const lk_kconfig config);
+
+  /* Same like above but with NAME = ALIAS.  */
+  void declare_address (const std::string &name, const lk_kconfig config)
+  { declare_address (name, name, config); }
+
+  /* Same like above but only mark CONFIG as missing if none of the symbols
+     in NAMES could be found.  */
+  void declare_address (const std::string &alias,
+			const std::initializer_list<const std::string> names,
+			const lk_kconfig config);
+
+  /* See declare_address.  */
+  void declare_type (const std::string &alias, const std::string &name,
+		     const lk_kconfig config);
+
+  /* See declare_address.  */
+  void declare_type (const std::string &name, const lk_kconfig config)
+  { declare_type (name, name, config); }
+
+  /* See declare_address.  */
+  void declare_type (const std::string &alias,
+		     const std::initializer_list<const std::string> names,
+		     const lk_kconfig config);
+
+  /* See declare_address.  */
+  void declare_field (const std::string &alias, const std::string &name,
+		      const lk_kconfig kconfig);
+
+  /* See declare_address.  */
+  void declare_field (const std::string &name, const lk_kconfig kconfig)
+  { declare_field (name, name, kconfig); }
+
+  /* See declare_address.  */
+  void declare_field (const std::string &alias,
+		      const std::initializer_list <const std::string> names,
+		      const lk_kconfig config);
+
+private:
+  /* The target_ops we are working with.  */
+  struct target_ops *m_ops;
+
+  /* The configuration used to build the kernel.  To make the implementation
+     easier m_kconfig is inverse, i.e. it tracks the _missing_ config options
+     not the set ones.  */
+  lk_kconfig m_kconfig = 0;
+
+  /* Collection of all declared symbols (addresses, fields etc.).  */
+  std::unordered_map<std::string, union lk_symbol> m_symbols;
+
+  /* Returns unique alias for struct ALIAS.  */
+  const std::string unique_type_alias (const std::string &alias) const
+  {
+    std::string prefix ("struct ");
+    if (startswith (alias.c_str (), prefix.c_str ()))
+	return alias;
+    return prefix + alias;
+  }
+
+  /* Check if m_symbols contains ALIAS.  */
+  bool has_symbol (const std::string &alias) const
+  { return m_symbols.count (alias) != 0; }
+};
+
+extern target_ops *lk_target_ops;
+extern linux_kernel_ops *lk_ops;
+
+/* Short hand access to frequently used lk_ops methods.  */
+
+static inline CORE_ADDR
+lk_address (const std::string &alias)
+{
+  return lk_ops->address (alias);
+}
+
+static inline struct type *
+lk_type (const std::string &alias)
+{
+  return lk_ops->type (alias);
+}
+
+static inline struct field *
+lk_field (const std::string &alias)
+{
+  return lk_ops->field (alias);
+}
+
+static inline CORE_ADDR
+lk_offset (const std::string &alias)
+{
+  return lk_ops->offset (alias);
+}
+
+static inline bool
+lk_ifdef (lk_kconfig config)
+{
+  return lk_ops->ifdef (config);
+}
+
+/* Align VAL to BASE. BASE must be a power of 2.  */
+#define LK_ALIGN(val, base) LK_ALIGN_MASK ((val), (typeof(val))(base) - 1)
+
+/* Same as LK_ALIGN, but aligns down.  */
+#define LK_ALIGN_DOWN(val, base) ((val) & ~((typeof(val))(base) - 1))
+
+/* Helper for LK_ALIGN.  */
+#define LK_ALIGN_MASK(val, mask) (((val) + (mask)) & ~(mask))
+
+/* Short hand access to current gdbarchs builtin types and their
+   size (in byte).  For TYPE replace spaces " " by underscore "_", e.g.
+   "unsigned int" => "unsigned_int".  */
+#define lk_builtin_type(type)					\
+  (builtin_type (current_inferior ()->gdbarch)->builtin_##type)
+#define lk_builtin_type_size(type)		\
+  (lk_builtin_type (type)->length)
+
+/* If field FIELD is an array returns its length (in #elements).  */
+#define LK_ARRAY_LEN(field)			\
+  (FIELD_SIZE (field) / FIELD_TARGET_SIZE (field))
+
+/* Additional access macros to fields in the style of gdbtypes.h */
+/* Returns the size of field FIELD (in bytes). If FIELD is an array returns
+   the size of the whole array.  */
+#define FIELD_SIZE(field)			\
+  TYPE_LENGTH (check_typedef (FIELD_TYPE ((*field))))
+
+/* Returns the size of the target type of field FIELD (in bytes).  If FIELD is
+   an array returns the size of its elements.  */
+#define FIELD_TARGET_SIZE(field)		\
+  TYPE_LENGTH (check_typedef (TYPE_TARGET_TYPE (FIELD_TYPE ((*field)))))
+
+#define FIELD_BYTEPOS(field)			\
+  (FIELD_BITPOS (*field) / LK_BITS_PER_BYTE)
+
+/* Tests if a given task TASK is running. Returns either the cpu-id
+   if running or LK_CPU_INVAL if not.  */
+extern unsigned int lk_task_running (CORE_ADDR task);
+
+#endif /* __LK_LOW_H__ */
diff --git a/gdb/osabi.c b/gdb/osabi.c
index fd44deb9fa..14ddd414f3 100644
--- a/gdb/osabi.c
+++ b/gdb/osabi.c
@@ -64,6 +64,7 @@ static const struct osabi_names gdb_osabi_names[] =
   { "GNU/Hurd", NULL },
   { "Solaris", NULL },
   { "GNU/Linux", "linux(-gnu[^-]*)?" },
+  { "Linux kernel", NULL },
   { "FreeBSD", NULL },
   { "NetBSD", NULL },
   { "OpenBSD", NULL },
-- 
2.13.5



More information about the Gdb-patches mailing list