propagate from branch 'com.redhat.elfutils.roland.pending' (head e0c7abd450c9e49093cfae30af8a22782a74a403)
            to branch 'com.redhat.elfutils' (head 2c784d50eee72e33972c333138a3a28df304da63)
diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c
index 4ea391c..2aaa25a 100644
--- a/libdwfl/linux-kernel-modules.c
+++ b/libdwfl/linux-kernel-modules.c
@@ -63,8 +63,11 @@
 #include <fts.h>
 
 
+#define KERNEL_MODNAME	"kernel"
+
 #define MODULEDIRFMT	"/lib/modules/%s"
 
+#define KSYMSFILE	"/proc/kallsyms"
 #define MODULELIST	"/proc/modules"
 #define	SECADDRDIRFMT	"/sys/module/%s/sections/"
 #define MODULE_SECT_NAME_LEN 32	/* Minimum any linux/module.h has had.  */
@@ -116,29 +119,45 @@
 }
 
 static int
+find_kernel_elf (Dwfl *dwfl, const char *release, char **fname)
+{
+  if ((release[0] == '/'
+       ? asprintf (fname, "%s/vmlinux", release)
+       : asprintf (fname, "/boot/vmlinux-%s", release)) < 0)
+    return -1;
+
+  int fd = try_kernel_name (dwfl, fname);
+  if (fd < 0 && release[0] != '/')
+    {
+      free (*fname);
+      if (asprintf (fname, MODULEDIRFMT "/vmlinux", release) < 0)
+	return -1;
+      fd = try_kernel_name (dwfl, fname);
+    }
+
+  return fd;
+}
+
+static int
 report_kernel (Dwfl *dwfl, const char *release,
 	       int (*predicate) (const char *module, const char *file))
 {
   if (dwfl == NULL)
     return -1;
 
-  char *fname;
-  if ((release[0] == '/'
-       ? asprintf (&fname, "%s/vmlinux", release)
-       : asprintf (&fname, "/boot/vmlinux-%s", release)) < 0)
-    return -1;
-  int fd = try_kernel_name (dwfl, &fname);
-  if (fd < 0 && release[0] != '/')
+  if (release == NULL)
     {
-      free (fname);
-      if (asprintf (&fname, MODULEDIRFMT "/vmlinux", release) < 0)
-	return -1;
-      fd = try_kernel_name (dwfl, &fname);
+      release = kernel_release ();
+      if (release == NULL)
+	return errno;
     }
 
+  char *fname;
+  int fd = find_kernel_elf (dwfl, release, &fname);
+
   int result = 0;
   if (fd < 0)
-    result = ((predicate != NULL && !(*predicate) ("kernel", NULL))
+    result = ((predicate != NULL && !(*predicate) (KERNEL_MODNAME, NULL))
 	      ? 0 : errno ?: ENOENT);
   else
     {
@@ -147,14 +166,15 @@
       if (predicate != NULL)
 	{
 	  /* Let the predicate decide whether to use this one.  */
-	  int want = (*predicate) ("kernel", fname);
+	  int want = (*predicate) (KERNEL_MODNAME, fname);
 	  if (want < 0)
 	    result = errno;
 	  report = want > 0;
 	}
 
       if (report
-	  && INTUSE(dwfl_report_elf) (dwfl, "kernel", fname, fd, 0) == NULL)
+	  && INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME,
+				      fname, fd, 0) == NULL)
 	{
 	  close (fd);
 	  result = -1;
@@ -177,13 +197,6 @@
 				  int (*predicate) (const char *module,
 						    const char *file))
 {
-  if (release == NULL)
-    {
-      release = kernel_release ();
-      if (release == NULL)
-	return errno;
-    }
-
   /* First report the kernel.  */
   int result = report_kernel (dwfl, release, predicate);
   if (result == 0)
@@ -279,15 +292,87 @@
 INTDEF (dwfl_linux_kernel_report_offline)
 
 
-/* Find the ELF file for the running kernel and dwfl_report_elf it.  */
+/* Grovel around to guess the bounds of the runtime kernel image.  */
+static int
+intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end)
+{
+  FILE *f = fopen (KSYMSFILE, "r");
+  if (f == NULL)
+    return errno;
+
+  (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
+
+  char *line = NULL;
+  size_t linesz = 0;
+  size_t n = getline (&line, &linesz, f);
+  Dwarf_Addr first;
+  char *p = NULL;
+  int result = 0;
+  if (n > 0 && (first = strtoull (line, &p, 16)) > 0 && p > line)
+    {
+      Dwarf_Addr last = 0;
+      while ((n = getline (&line, &linesz, f)) > 1 && line[n - 2] != ']')
+	{
+	  p = NULL;
+	  last = strtoull (line, &p, 16);
+	  if (p == NULL || p == line || last == 0)
+	    {
+	      result = -1;
+	      break;
+	    }
+	}
+      if ((n == 0 && feof_unlocked (f)) || (n > 1 && line[n - 2] == ']'))
+	{
+	  Dwarf_Addr round_kernel = sysconf (_SC_PAGE_SIZE);
+	  first &= -(Dwarf_Addr) round_kernel;
+	  last += round_kernel - 1;
+	  last &= -(Dwarf_Addr) round_kernel;
+	  *start = first;
+	  *end = last;
+	  result = 0;
+	}
+    }
+  free (line);
+
+  if (result == -1)
+    result = ferror_unlocked (f) ? errno : ENOEXEC;
+
+  fclose (f);
+
+  return result;
+}
+
 int
 dwfl_linux_kernel_report_kernel (Dwfl *dwfl)
 {
-  const char *release = kernel_release ();
-  if (release == NULL)
-    return errno;
+  Dwarf_Addr start;
+  Dwarf_Addr end;
+  inline int report (void)
+    {
+      return INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME,
+					 start, end) == NULL ? -1 : 0;
+    }
 
-  return report_kernel (dwfl, release, NULL);
+  /* This is a bit of a kludge.  If we already reported the kernel,
+     don't bother figuring it out again--it never changes.  */
+  for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next)
+    if (!strcmp (m->name, KERNEL_MODNAME))
+      {
+	start = m->low_addr;
+	end = m->high_addr;
+	return report ();
+      }
+
+  /* Try to figure out the bounds of the kernel image without
+     looking for any vmlinux file.  */
+  int result = intuit_kernel_bounds (&start, &end);
+  if (result == 0)
+    return report ();
+  if (result != ENOENT)
+    return result;
+
+  /* Find the ELF file for the running kernel and dwfl_report_elf it.  */
+  return report_kernel (dwfl, NULL, NULL);
 }
 INTDEF (dwfl_linux_kernel_report_kernel)
 
@@ -306,6 +391,9 @@
   if (release == NULL)
     return errno;
 
+  if (!strcmp (module_name, KERNEL_MODNAME))
+    return find_kernel_elf (mod->dwfl, release, file_name);
+
   /* Do "find /lib/modules/`uname -r`/kernel -name MODULE_NAME.ko".  */
 
   char *modulesdir[] = { NULL, NULL };
@@ -416,7 +504,7 @@
  Dwarf_Addr *addr)
 {
   char *sysfile;
-  if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname))
+  if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname) < 0)
     return ENOMEM;
 
   FILE *f = fopen (sysfile, "r");