diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 056333e2cd..afa8039a95 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -716,6 +716,7 @@ ALL_TARGET_OBS = \
 	linux-record.o \
 	linux-tdep.o \
 	lk-low.o \
+	lk-modules.o \
 	lm32-tdep.o \
 	m32c-tdep.o \
 	m32r-linux-tdep.o \
@@ -1278,6 +1279,7 @@ HFILES_NO_SRCDIR = \
 	linux-record.h \
 	linux-tdep.h \
 	lk-low.h \
+	lk-modules.h \
 	location.h \
 	m2-lang.h \
 	m32r-tdep.h \
@@ -2259,6 +2261,7 @@ ALLDEPFILES = \
 	linux-record.c \
 	linux-tdep.c \
 	lk-low.c \
+	lk-modules.c \
 	lm32-tdep.c \
 	m32r-linux-nat.c \
 	m32r-linux-tdep.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index be68ac50fc..39b2144e00 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -42,7 +42,7 @@ 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"
+lk_tobjs="lk-low.o lk-modules.o"
 
 # Here are three sections to get a list of target specific object
 # files according to target triplet $TARG.
diff --git a/gdb/lk-low.c b/gdb/lk-low.c
index 193619e6f5..54d3b6272d 100644
--- a/gdb/lk-low.c
+++ b/gdb/lk-low.c
@@ -30,6 +30,7 @@
 #include "lk-bitmap.h"
 #include "lk-list.h"
 #include "lk-low.h"
+#include "lk-modules.h"
 #include "objfiles.h"
 #include "observer.h"
 #include "solib.h"
@@ -386,9 +387,68 @@ linux_kernel_ops::read_symbols ()
 
   declare_field ("cpumask->bits", LK_CONFIG_ALWAYS);
 
+  declare_field ("mm_struct->pgd", LK_CONFIG_ALWAYS);
+
+  declare_field ("pgd_t->pgd", LK_CONFIG_ALWAYS);
+
+  declare_field ("module->state", LK_CONFIG_MODULES);
+  declare_field ("module->list", LK_CONFIG_MODULES);
+  declare_field ("module->name", LK_CONFIG_MODULES);
+  declare_field ("module->source_list", LK_CONFIG_MODULES);
+  declare_field ("module->arch", LK_CONFIG_MODULES);
+  declare_field ("module->percpu", LK_CONFIG_SMT);
+  declare_field ("module->percpu_size", LK_CONFIG_SMT);
+
+  /* Module offset moved to new struct module_layout with linux 4.5.
+     It must be checked in code which of these fields exist.  */
+  if (try_declare_field ("module_layout->base")) /* linux 4.5+ */
+    {
+      declare_field ("module->init_layout", LK_CONFIG_MODULES);
+      declare_field ("module->core_layout", LK_CONFIG_MODULES);
+
+      declare_field ("module_layout->size", LK_CONFIG_MODULES);
+      declare_field ("module_layout->text_size", LK_CONFIG_MODULES);
+      declare_field ("module_layout->ro_size", LK_CONFIG_MODULES);
+
+      /* Only try to declare -> existence needs to be checked in code.  */
+      try_declare_field ("module_layout->ro_after_init_size"); /* linux 4.8+ */
+    }
+  else if (try_declare_field ("module->module_core")) /* linux -4.4 */
+    {
+      declare_field ("module->module_init", LK_CONFIG_MODULES);
+
+      declare_field ("module->init_size", LK_CONFIG_MODULES);
+      declare_field ("module->core_size", LK_CONFIG_MODULES);
+
+      declare_field ("module->init_text_size", LK_CONFIG_MODULES);
+      declare_field ("module->core_text_size", LK_CONFIG_MODULES);
+
+      declare_field ("module->init_ro_size", LK_CONFIG_MODULES);
+      declare_field ("module->core_ro_size", LK_CONFIG_MODULES);
+    }
+  else
+    {
+      m_kconfig |= LK_CONFIG_MODULES;
+      warning (_("No symbols for the module layout found."));
+    }
+
+  declare_field ("module_use->source_list", LK_CONFIG_MODULES);
+  declare_field ("module_use->source", LK_CONFIG_MODULES);
+
+  declare_field ("uts_namespace->name", LK_CONFIG_ALWAYS);
+
+  declare_type ("utsname",
+		{"new_utsname", "old_utsname", "oldold_utsname"},
+		LK_CONFIG_ALWAYS);
+  declare_field ("utsname->version", LK_CONFIG_ALWAYS);
+  declare_field ("utsname->release", 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 ("init_mm", LK_CONFIG_ALWAYS);
+  declare_address ("modules", LK_CONFIG_MODULES);
+  declare_address ("init_uts_ns", LK_CONFIG_ALWAYS);
 
   declare_address ("cpu_online_mask", {"__cpu_online_mask", /* linux 4.5+ */
 				       "cpu_online_bits"},  /* linux -4.4 */
@@ -657,6 +717,25 @@ lk_thread_name (struct target_ops *target, struct thread_info *ti)
   return str.c_str ();
 }
 
+/* Function for targets to_xfer_partial hook.  */
+
+enum target_xfer_status
+lk_xfer_partial (struct target_ops *ops, enum target_object object,
+		 const char *annex, gdb_byte *readbuf,
+		 const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
+		 ULONGEST *xfered_len)
+{
+  enum target_xfer_status ret_val;
+  scoped_restore restore_target = make_scoped_restore (&current_target.beneath,
+						       ops->beneath);
+  if (lk_ops->is_kvaddr (offset))
+    offset = lk_ops->kvtop (offset);
+
+  ret_val =  ops->beneath->to_xfer_partial (ops->beneath, object, annex,
+					    readbuf, writebuf, offset, len,
+					    xfered_len);
+  return ret_val;
+}
 
 /* Functions to initialize and free target_ops and its private data.  As well
    as functions for targets to_open/close/detach hooks.  */
@@ -736,6 +815,12 @@ lk_try_push_target ()
 
   if (!target_is_pushed (lk_target_ops))
     push_target (lk_ops->target ());
+
+  if (lk_ifdef (LK_CONFIG_MODULES))
+    lk_init_modules (gdbarch);
+  else
+    warning (_("Could not find all symbols for module support.  "
+	       "Module support turned off."));
 }
 
 /* Function for targets to_open hook.  */
@@ -842,6 +927,7 @@ init_lk_target_ops (void)
   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_xfer_partial = lk_xfer_partial;
 
   t->to_stratum = thread_stratum;
   t->to_magic = OPS_MAGIC;
diff --git a/gdb/lk-low.h b/gdb/lk-low.h
index 39c0d88f43..31ae4d7966 100644
--- a/gdb/lk-low.h
+++ b/gdb/lk-low.h
@@ -23,11 +23,14 @@
 #include <unordered_map>
 
 #include "gdbtypes.h"
+#include "lk-modules.h"
 #include "target.h"
 
 /* Copied constants defined in Linux kernel.  */
 #define LK_TASK_COMM_LEN 16
 #define LK_BITS_PER_BYTE 8
+#define LK_MODULE_NAME_LEN 56
+#define LK_UTS_NAME_LEN 64
 
 /* Definitions used in linux kernel target.  */
 #define LK_CPU_INVAL -1U
@@ -104,6 +107,30 @@ public:
   virtual void get_registers (CORE_ADDR task, struct target_ops *target,
 			      struct regcache *regcache, int regnum) = 0;
 
+  /* Check if address ADDR is a kernel virtual address.
+     NOTE: This hook is called in the context of target beneath.  */
+  virtual bool is_kvaddr (CORE_ADDR addr) = 0;
+
+  /* Translate virtual adress ADDR to a pysical address using page table
+     located at PGD.
+     NOTE: This hook is called in the context of target beneath.  */
+  virtual CORE_ADDR vtop (CORE_ADDR addr, CORE_ADDR pgd) = 0;
+
+  /* Translate a kernel virtual address ADDR to a physical address.
+     NOTE: This hook is called in the context of target beneath.  */
+  virtual CORE_ADDR kvtop (CORE_ADDR addr)
+  {
+    CORE_ADDR pgd = lk_read_addr (address ("init_mm")
+				  + offset ("mm_struct->pgd"));
+    return vtop (addr, pgd);
+  }
+
+  /* Add offset between a modules base and the start of its first section.
+     Equivalent to
+     <linux>/include/linux/moduleloader.h:module_frob_arch_sections  */
+  virtual void adjust_module_layout (CORE_ADDR mod, lk_module *module)
+  {}
+
   /* 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);
diff --git a/gdb/lk-modules.c b/gdb/lk-modules.c
new file mode 100644
index 0000000000..18e31aadda
--- /dev/null
+++ b/gdb/lk-modules.c
@@ -0,0 +1,501 @@
+/* Handle Linux kernel modules as shared libraries.
+
+   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 "common/filestuff.h"
+#include "filenames.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "gdb_regex.h"
+#include "lk-list.h"
+#include "lk-low.h"
+#include "lk-modules.h"
+#include "objfiles.h"
+#include "observer.h"
+#include "readline/readline.h"
+#include "solib.h"
+#include "solist.h"
+#include "utils.h"
+
+#include <unordered_map>
+
+static struct target_so_ops *lk_modules_so_ops = NULL;
+
+
+/* See comment in lk-modules.h.  */
+
+void
+lk_module_section_type::clear ()
+{
+  base = 0;
+  offset = 0;
+  size = 0;
+}
+
+/* Constructor for module_layout located at address LAYOUT.
+   Linux 4.5+ only! */
+
+lk_module_layout::lk_module_layout (CORE_ADDR layout)
+{
+  base = lk_read_addr (layout + lk_offset ("module_layout->base"));
+  size = lk_read_uint (layout + lk_offset ("module_layout->size"));
+
+  text.base = base;
+  text.size = lk_read_uint (layout
+			    + lk_offset ("module_layout->text_size"));
+
+  ro_data.base = base + text.size;
+  ro_data.size = lk_read_uint (layout
+			       + lk_offset ("module_layout->ro_size"));
+
+  if (lk_ops->has_field ("module_layout->ro_after_init_size")) /* linux 4.8+ */
+    {
+      ro_after_init_data.base = base + ro_data.size;
+      ro_after_init_data.size
+	= lk_read_uint (layout
+			+ lk_offset ("module_layout->ro_after_init_size"));
+
+      rw_data.base = base + ro_after_init_data.size;
+    }
+  else /* linux 4.5 - 4.8 */
+    {
+      rw_data.base = base + ro_data.size;
+    }
+
+  rw_data.size = size;
+}
+
+/* See comment in lk-modules.h.  */
+
+void
+lk_module_layout::clear ()
+{
+  base = 0;
+  size = 0;
+
+  text.clear ();
+  ro_data.clear ();
+  rw_data.clear ();
+}
+
+/* Init MODULE from struct module located at address MOD.
+   Linux -4.4 only!  */
+
+static void
+lk_module_init_module_from_struct_module (CORE_ADDR mod, lk_module *module)
+{
+  module->core = new lk_module_layout;
+  module->init = new lk_module_layout;
+
+  /* Short-hand access.  */
+  lk_module_layout *core = module->core;
+  lk_module_layout *init = module->init;
+
+  /* Core layout.  */
+  core->base = lk_read_addr (mod + lk_offset ("module->module_core"));
+  core->size = lk_read_uint (mod + lk_offset ("module->core_size"));
+
+  core->text.base = core->base;
+  core->text.size = lk_read_uint (mod
+				  + lk_offset ("module->core_text_size"));
+
+  core->ro_data.base = core->base + core->text.size;
+  core->ro_data.size = lk_read_uint (mod
+				     + lk_offset ("module->core_ro_size"));
+
+  core->rw_data.base = core->base + core->ro_data.size;
+  core->rw_data.size = core->size;
+
+  /* Init layout.  */
+  init->base = lk_read_addr (mod + lk_offset ("module->module_init"));
+  init->size = lk_read_uint (mod + lk_offset ("module->init_size"));
+
+  init->text.base = init->base;
+  init->text.size = lk_read_uint (mod
+				  + lk_offset ("module->init_text_size"));
+
+  init->ro_data.base = init->base + init->text.size;
+  init->ro_data.size = lk_read_uint (mod
+				     + lk_offset ("module->init_ro_size"));
+
+  init->rw_data.base = init->base + init->ro_data.size;
+  init->rw_data.size = init->size;
+}
+
+/* Constructor for kernel module from struct module located at MOD.  */
+
+lk_module::lk_module (CORE_ADDR mod)
+{
+  state = (lk_module_state) lk_read_int (mod + lk_offset ("module->state"));
+
+  if (lk_ops->has_field ("module->module_core")) /* linux -4.4 */
+    {
+      lk_module_init_module_from_struct_module (mod, this);
+    }
+  else /* linux 4.5+ */
+    {
+      core = new lk_module_layout (mod + lk_offset ("module->core_layout"));
+      init = new lk_module_layout (mod + lk_offset ("module->init_layout"));
+    }
+
+  if (lk_ifdef (LK_CONFIG_SMT))
+    {
+      percpu.base = lk_read_addr (mod + lk_offset ("module->percpu"));
+      percpu.size = lk_read_uint (mod + lk_offset ("module->percpu_size"));
+    }
+
+  /* Architectures can adjust the sections in the kernel (via
+     module_frob_arch_sections in <linux>/kernel/module.c:layout_and_allocate).
+     Give them a chance to do the same in GDB.  */
+  lk_ops->adjust_module_layout (mod, this);
+}
+
+lk_module::~lk_module ()
+{
+  delete core;
+  delete init;
+}
+
+/* See comment in lk-modules.h.  */
+
+void
+lk_module::clear ()
+{
+  state = LK_MODULE_STATE_INVALID;
+
+  core->clear ();
+  init->clear ();
+
+  percpu.clear ();
+}
+
+/* Check if debug info for module NAME is loaded.  */
+
+bool
+lk_modules_debug_info_loaded (const std::string &name)
+{
+  for (so_list *so = master_so_list (); so != NULL; so = so->next)
+    if (name == so->so_original_name)
+      return (so->symbols_loaded && objfile_has_symbols (so->objfile));
+
+  return false;
+}
+
+/* Replace tags, like '$release', with corresponding data in
+   solib_search_path.
+
+   Known tags:
+   $release	Linux kernel release, same as 'uname -r'
+
+   Returns the expanded path.  */
+
+static std::string
+lk_modules_expand_search_path ()
+{
+  char release[LK_UTS_NAME_LEN + 1];
+  CORE_ADDR utsname;
+
+  utsname = lk_address ("init_uts_ns") + lk_offset ("uts_namespace->name");
+  read_memory_string (utsname + lk_offset ("utsname->release"),
+		      release, LK_UTS_NAME_LEN);
+  release[LK_UTS_NAME_LEN] = '\0';
+
+  std::string search_path = get_solib_search_path ();
+  substitute_path_component (search_path, "$release", release);
+
+  return search_path;
+}
+
+/* With kernel modules there is the problem that the kernel only stores
+   the modules name but not the path from wich it was loaded from.
+   Thus we need to map the name to a path GDB can read from.  We use file
+   modules.order to do so.  It is created by kbuild containing the order in
+   which the modules appear in the Makefile and is also used by modprobe.
+   The drawback of this method is that it needs the modules.order file and
+   all relative paths, starting from <solib-search-path>, must be exactly the
+   same as decribed in it.  */
+
+/* Open file <solib-search-path>/modules.order and return its file
+   pointer.  */
+
+static gdb_file_up
+lk_modules_open_mod_order ()
+{
+  std::string filename = concat_path (lk_modules_expand_search_path (),
+				      "modules.order");
+  gdb_file_up mod_order = gdb_fopen_cloexec (filename.c_str (), "r");
+
+  if (!mod_order)
+    {
+      error (_("\
+Can not find file module.order at %s \
+to load module symbol files.\n\
+Please check if solib-search-path is set correctly."),
+	     filename.c_str ());
+    }
+
+  return mod_order;
+}
+
+/* Build map between module name and path to binary file by reading file
+   modules.order.  Returns unordered_map with module name as key and its
+   path as value.  */
+
+static std::unordered_map<std::string, std::string>
+lk_modules_build_path_map ()
+{
+  std::unordered_map<std::string, std::string> umap;
+  char line[SO_NAME_MAX_PATH_SIZE + 1];
+
+  gdb_file_up mod_order = lk_modules_open_mod_order ();
+
+  line[SO_NAME_MAX_PATH_SIZE] = '\0';
+  std::string search_path = lk_modules_expand_search_path ();
+  while (fgets (line, SO_NAME_MAX_PATH_SIZE, mod_order.get ()))
+    {
+      /* Remove trailing newline.  */
+      line[strlen (line) - 1] = '\0';
+
+      std::string name = lbasename (line);
+
+      /* 3 = strlen (".ko").  */
+      if (!endswith (name.c_str (), ".ko")
+	  || name.length () >= LK_MODULE_NAME_LEN + 3)
+	continue;
+
+      name = name.substr (0, name.length () - 3);
+
+      /* Kernel modules are named after the files they are stored in with
+	 all minus '-' replaced by underscore '_'.  Do the same to enable
+	 mapping.  */
+      for (size_t p = name.find('-'); p != std::string::npos;
+	   p = name.find ('-', p + 1))
+	name[p] = '_';
+
+      umap[name] = concat_path(search_path, line);
+    }
+
+  return umap;
+}
+
+/* Implement current_sos target_so_ops method.  */
+
+static struct so_list *
+lk_modules_current_sos (void)
+{
+  struct so_list *list = NULL;
+  std::unordered_map<std::string, std::string> umap;
+
+  umap = lk_modules_build_path_map ();
+  for (CORE_ADDR mod : lk_list ("modules", "module->list"))
+    {
+      char name[LK_MODULE_NAME_LEN];
+      CORE_ADDR name_addr;
+
+      name_addr = mod + lk_offset ("module->name");
+      read_memory_string (name_addr, name, LK_MODULE_NAME_LEN);
+
+      if (!umap.count (name))
+	continue;
+
+      struct so_list *newso = XCNEW (struct so_list);
+
+      newso->next = list;
+      list = newso;
+      newso->lm_info = new lk_module (mod);
+      strncpy (newso->so_original_name, name, SO_NAME_MAX_PATH_SIZE);
+      strncpy (newso->so_name, umap[name].c_str (), SO_NAME_MAX_PATH_SIZE);
+      newso->pspace = current_program_space;
+    }
+
+  return list;
+}
+
+/* Relocate target_section GDB_SEC using section type LK_SEC.  Helper
+   function for lk_modules_relocate_section_addresses.  */
+
+static void
+lk_modules_relocate_sec (struct target_section *gdb_sec,
+			 lk_module_section_type &lk_sec)
+{
+  unsigned int alignment = 1 << gdb_sec->the_bfd_section->alignment_power;
+  CORE_ADDR base = lk_sec.base + lk_sec.offset;
+
+  /* Adjust offset to section alignment.  */
+  if (base % alignment != 0)
+    base += alignment - (base % alignment);
+
+  gdb_sec->addr += base;
+  gdb_sec->endaddr += base;
+  lk_sec.offset += gdb_sec->endaddr - gdb_sec->addr;
+}
+
+/* Function for relocate_section_addresses hook.  */
+
+static void
+lk_modules_relocate_section_addresses (struct so_list *so,
+				       struct target_section *sec)
+{
+  lk_module *module = (lk_module *) so->lm_info;
+  unsigned int flags = sec->the_bfd_section->flags;
+  const char *name = sec->the_bfd_section->name;
+
+  /* Remove unmapped sections.  */
+  if (streq (name, ".modinfo") || streq (name, "__versions")
+      || (startswith (name, ".init")
+	  && module->state != LK_MODULE_STATE_COMING))
+    {
+      // FIXME Not mapping a section != unmapping a section
+      //
+      // In particular this causes GDB to map the "unmapped" section at the
+      // same address as the last mapped section (e.g. init.text starts at
+      // the same address like .text).  Thus printing symbols definied in the
+      // unmapped section returnes random values instead of <opimized out>.
+      return;
+    }
+
+  /* module->percpu.base points to data of cpu0.  To access the data of
+     a cpu other than cpu0 the corresponding percpu offset needs to added
+     by hand.  */
+  if (lk_ifdef (LK_CONFIG_MODULES) && endswith (name, ".percpu"))
+    {
+      lk_modules_relocate_sec (sec, module->percpu);
+      return;
+    }
+
+  lk_module_layout *layout;
+  if (startswith (name, ".init"))
+    layout = module->init;
+  else
+    layout = module->core;
+
+  if (flags & SEC_CODE)
+    lk_modules_relocate_sec (sec, layout->text);
+  else if (endswith (name, ".ro_after_init"))
+    lk_modules_relocate_sec (sec, layout->ro_after_init_data);
+  else if (flags & SEC_READONLY)
+    lk_modules_relocate_sec (sec, layout->ro_data);
+  else if (flags & SEC_ALLOC)
+    lk_modules_relocate_sec (sec, layout->rw_data);
+
+  /* Set address range displayed with 'info shared' to match core_layout.  */
+  if (so->addr_low == so->addr_high)
+    {
+      so->addr_low = module->core->base;
+      so->addr_high = module->core->base + module->core->size;
+    }
+}
+
+/* Implement free_so target_so_ops method.  */
+
+static void
+lk_modules_free_so (struct so_list *so)
+{
+  delete so->lm_info;
+}
+
+/* Implement clear_so target_so_ops method.  */
+
+static void
+lk_modules_clear_so (struct so_list *so)
+{
+  lk_module *module = (lk_module *) so->lm_info;
+  gdb_assert (module != NULL);
+
+  module->clear ();
+}
+
+/* Implement clear_solib target_so_ops method.  */
+
+static void
+lk_modules_clear_solib ()
+{
+  /* Nothing to do.  */
+}
+
+/* Implement clear_create_inferior_hook target_so_ops method.  */
+
+static void
+lk_modules_create_inferior_hook (int from_tty)
+{
+  /* Nothing to do.  */
+}
+
+/* Implement in_dynsym_resolve_code target_so_ops method.  */
+
+static int
+lk_modules_in_dynsym_resolve_code (CORE_ADDR pc)
+{
+  return 0;
+}
+
+/* Implement same target_so_ops method.  Assume the module to be different
+   if its state changed.  Otherwise there might be problems with pruning the
+   .init sections if the module was first seen during init.  */
+
+static int
+lk_modules_same (struct so_list *gdb, struct so_list *inf)
+{
+  lk_module_state gdb_state = ((lk_module *) gdb->lm_info)->state;
+  lk_module_state inf_state = ((lk_module *) inf->lm_info)->state;
+
+  return streq (gdb->so_name, inf->so_name) && gdb_state == inf_state;
+}
+
+/* See lk-modules.h.  */
+
+void
+lk_init_modules (struct gdbarch *gdbarch)
+{
+  set_solib_ops (gdbarch, lk_modules_so_ops);
+}
+
+/* Initialize linux modules solib target.  */
+
+static void
+init_lk_modules_so_ops (void)
+{
+  struct target_so_ops *t;
+
+  if (lk_modules_so_ops != NULL)
+    return;
+
+  t = XCNEW (struct target_so_ops);
+  t->relocate_section_addresses = lk_modules_relocate_section_addresses;
+  t->free_so = lk_modules_free_so;
+  t->clear_so = lk_modules_clear_so;
+  t->clear_solib = lk_modules_clear_solib;
+  t->solib_create_inferior_hook = lk_modules_create_inferior_hook;
+  t->current_sos = lk_modules_current_sos;
+  t->bfd_open = solib_bfd_open;
+  t->in_dynsym_resolve_code = lk_modules_in_dynsym_resolve_code;
+  t->same = lk_modules_same;
+
+  lk_modules_so_ops = t;
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes.  */
+extern initialize_file_ftype _initialize_lk_modules;
+
+void
+_initialize_lk_modules (void)
+{
+  init_lk_modules_so_ops ();
+}
diff --git a/gdb/lk-modules.h b/gdb/lk-modules.h
new file mode 100644
index 0000000000..44d86a2a95
--- /dev/null
+++ b/gdb/lk-modules.h
@@ -0,0 +1,148 @@
+/* Handle kernel modules as shared libraries.
+
+   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_MODULES_H__
+#define __LK_MODULES_H__
+
+#include "solist.h"
+
+/* Copied from <linux>/include/linux/module.h.  */
+
+enum
+lk_module_state
+{
+  LK_MODULE_STATE_INVALID = -1, /* Invalid state, GDB only.  */
+  LK_MODULE_STATE_LIVE,		/* Normal state. */
+  LK_MODULE_STATE_COMING,	/* Full formed, running module_init. */
+  LK_MODULE_STATE_GOING,	/* Going away. */
+  LK_MODULE_STATE_UNFORMED,	/* Still setting it up. */
+};
+
+/* The kernel modules .ko files are only partially linked, i.e. they contain
+   section headers but no program headers.  When the module is loaded the
+   sections are relocated by the kernel (<linux>/kernel/module.c:load_module
+   & layout_and_allocate & layout_sections).  Here the kernel destinguishes
+   between different section types
+
+   * text (SHF_EXECINSTR | SHF_ALLOC)
+   * read-only data (SHF_ALLOC & !SHF_WRITE)
+   * read-only-after-init data (SHF_ALLOC | SHF_RO_AFTER_INIT)
+   * read-write data (SHF_WRITE | SHF_ALLOC)
+
+   combined in module layouts.
+
+   There are two module layouts, init and core.  The init module layout
+   contains all sections where the section name starts with ".init".  The
+   content of the init module layout gets unlinked once the module is
+   loaded.  The core module layout contains the "normal" text and data
+   needed during module runtime.
+
+   Furthermore there can be a
+
+   * data..percpu (<linux>/kernel/module.c:percpu_modalloc)
+
+   section which gets relocated to the other percpu data. */
+
+struct lk_module_section_type
+{
+  /* Lowest address of this section type.  */
+  CORE_ADDR base = 0;
+
+  /* Size of this section type.  Calculated by GDB during relocation.  */
+  CORE_ADDR offset = 0;
+
+  /* Accumulated size of all section types "below".  Read from kernel
+     (except for rw_data).  */
+  unsigned int size = 0;
+
+  /* Reset this module_section to default (all zero).  Needed for
+     target_so_ops->clear_so hook.  */
+  void clear ();
+};
+
+/* Copied from <linux>/include/linux/module.h: struct module_layout.  */
+
+struct lk_module_layout
+{
+  lk_module_layout () = default;
+  lk_module_layout (CORE_ADDR layout);
+
+  /* Reset this module_layout to default (all zero).  Needed for
+     target_so_ops->clear_so hook.  */
+  void clear ();
+
+  /* Lowest address of this module layout.  */
+  CORE_ADDR base = 0;
+
+  /* Total size.  */
+  unsigned int size = 0;
+
+  /* Executable text.
+     text.size = text.  */
+  lk_module_section_type text;
+
+  /* Read-only data.
+     ro_data.size = text + ro_data.  */
+  lk_module_section_type ro_data;
+
+  /* Read-only-after-init data.
+     ro_after_init_data.size = text + ro_data + ro_after_init_data.  */
+  lk_module_section_type ro_after_init_data; /* linux 4.8+ */
+
+  /* Writable data.
+     rw_data.size = layout.size.  */
+  lk_module_section_type rw_data;
+};
+
+/* Link map info to include in an allocated so_list entry.  */
+
+struct lk_module : public lm_info_base
+{
+  lk_module () = delete;
+  lk_module (CORE_ADDR mod);
+
+  ~lk_module ();
+
+  /* Reset this module to default (all zero).  Needed for
+     target_so_ops->clear_so hook.  */
+  void clear ();
+
+  /* This modules state as seen from the kernel.  */
+  lk_module_state state = LK_MODULE_STATE_INVALID;
+
+  /* "core" layout, i.e. text + data after init.  */
+  lk_module_layout *core;
+
+  /* "init" layout, i.e. text + data only needed during init
+     (LK_MODULE_STATE_COMING).  It is unmapped after initialization.  */
+  lk_module_layout *init;
+
+  /* Per CPU data.  Mapped together with "ordinary" per cpu data.  */
+  lk_module_section_type percpu;
+};
+
+
+/* Check if debug info for module NAME are loaded.  Needed by lsmod command.  */
+
+extern bool lk_modules_debug_info_loaded (const std::string &name);
+
+/* Initialize module support for lk target.  */
+extern void lk_init_modules (struct gdbarch *gdbarch);
+
+#endif /*  __LK_MODULES_H__  */
diff --git a/gdb/solib.c b/gdb/solib.c
index 1c78845938..c411cc6124 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -105,6 +105,14 @@ show_solib_search_path (struct ui_file *file, int from_tty,
 		    value);
 }
 
+/* see solib.h.  */
+
+const char *
+get_solib_search_path ()
+{
+  return solib_search_path ? solib_search_path : "";
+}
+
 /* Same as HAVE_DOS_BASED_FILE_SYSTEM, but useable as an rvalue.  */
 #if (HAVE_DOS_BASED_FILE_SYSTEM)
 #  define DOS_BASED_FILE_SYSTEM 1
diff --git a/gdb/solib.h b/gdb/solib.h
index f6f02ad77a..8d58cc0675 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -31,6 +31,11 @@ struct program_space;
 /* List of known shared objects */
 #define so_list_head current_program_space->so_list
 
+/* Returns the solib_search_path.  The returned string is malloc'ed and must be
+   freed by the caller.  */
+
+extern const char *get_solib_search_path ();
+
 /* Called when we free all symtabs, to free the shared library information
    as well.  */
 
