In Linux 6.11 and newer, there is a new ioctl on the /proc/self/maps file,
that returns the extents of the virtual memory areas in binary form
(as opposed to the textual form of "cat /proc/self/maps").

It's about 2.2 times faster (in the simple cases that I measured).
Therefore here are two patches that make the 'vma-iter' and 'sigsegv'
modules use this new ioctl() when available.


2025-08-01  Bruno Haible  <[email protected]>

        sigsegv: Use new ioctl available in Linux >= 6.11.
        * lib/stackvma.c: On Linux, include <sys/ioctl.h>, <linux/fs.h>.
        (vma_iterate_procmap_query): New function.
        (vma_iterate): Try vma_iterate_procmap_query first.

2025-08-01  Bruno Haible  <[email protected]>

        vma-iter: Use new ioctl available in Linux >= 6.11.
        * lib/vma-iter.c: On Linux, include <sys/ioctl.h>, <linux/fs.h>.
        (vma_iterate_procmap_query): New function.
        (vma_iterate): Try vma_iterate_procmap_query first.

>From cb10832ae904698143b7da62a35b3fbde785d98a Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Fri, 1 Aug 2025 22:38:44 +0200
Subject: [PATCH 1/2] vma-iter: Use new ioctl available in Linux >= 6.11.

* lib/vma-iter.c: On Linux, include <sys/ioctl.h>, <linux/fs.h>.
(vma_iterate_procmap_query): New function.
(vma_iterate): Try vma_iterate_procmap_query first.
---
 ChangeLog      |  7 ++++
 lib/vma-iter.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 8065c90e03..99c7edad0e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2025-08-01  Bruno Haible  <[email protected]>
+
+	vma-iter: Use new ioctl available in Linux >= 6.11.
+	* lib/vma-iter.c: On Linux, include <sys/ioctl.h>, <linux/fs.h>.
+	(vma_iterate_procmap_query): New function.
+	(vma_iterate): Try vma_iterate_procmap_query first.
+
 2025-07-31  Simon Josefsson  <[email protected]>
 
 	doc: Improvements for gnulib git bundle.
diff --git a/lib/vma-iter.c b/lib/vma-iter.c
index 4f3cd40493..062f0c229c 100644
--- a/lib/vma-iter.c
+++ b/lib/vma-iter.c
@@ -49,6 +49,11 @@
 # include <limits.h> /* PATH_MAX */
 #endif
 
+#if defined __linux__ || defined __ANDROID__
+# include <sys/ioctl.h> /* ioctl */
+# include <linux/fs.h> /* PROCMAP_QUERY, struct procmap_query */
+#endif
+
 #if defined __linux__ || defined __ANDROID__ || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ || defined __minix /* || defined __CYGWIN__ */
 # include <sys/types.h>
 # include <sys/mman.h> /* mmap, munmap */
@@ -872,9 +877,92 @@ vma_iterate_bsd (vma_iterate_callback_fn callback, void *data)
 #endif
 
 
+/* Support for reading the info from the Linux ioctl() PROCMAP_QUERY
+   system call.  */
+
+#if (defined __linux__ || defined __ANDROID__) && defined PROCMAP_QUERY /* Linux >= 6.11 */
+
+static int
+vma_iterate_procmap_query (vma_iterate_callback_fn callback, void *data)
+{
+  /* Documentation: <linux/fs.h>
+     This implementation is more than twice as fast as vma_iterate_proc.
+     It does not return the [vsyscall] memory area at 0xFFFFFFFFFF600000,
+     but this is not a serious drawback, since that memory area is not
+     controlled by userspace anyway.  */
+  int fd = open ("/proc/self/maps", O_RDONLY | O_CLOEXEC);
+  if (fd < 0)
+    return -1;
+
+  unsigned long addr = 0;
+  do
+    {
+      /* Clear all fields, just in case some 'in' fields are added later.  */
+      struct procmap_query pq = {0};
+      pq.size = sizeof (pq);
+      pq.query_flags = PROCMAP_QUERY_COVERING_OR_NEXT_VMA;
+      pq.query_addr = addr;
+      pq.vma_name_size = 0;
+      pq.vma_name_addr = 0;
+
+      int ret = ioctl (fd, PROCMAP_QUERY, &pq);
+      if (ret == -1)
+        {
+          if (addr == 0)
+            {
+              /* Likely errno == ENOTTY.  */
+              close (fd);
+              return -1;
+            }
+          else
+            /* Likely errno == ENOENT.  */
+            break;
+        }
+
+      unsigned long start = pq.vma_start;
+      unsigned long end = pq.vma_end;
+      unsigned int flags = 0;
+      if (pq.vma_flags & PROCMAP_QUERY_VMA_READABLE)
+        flags |= VMA_PROT_READ;
+      if (pq.vma_flags & PROCMAP_QUERY_VMA_WRITABLE)
+        flags |= VMA_PROT_WRITE;
+      if (pq.vma_flags & PROCMAP_QUERY_VMA_EXECUTABLE)
+        flags |= VMA_PROT_EXECUTE;
+      if (callback (data, start, end, flags))
+        break;
+
+      addr = pq.vma_end;
+    }
+  while (addr != 0);
+
+  close (fd);
+  return 0;
+}
+
+#else
+
+static inline int
+vma_iterate_procmap_query (vma_iterate_callback_fn callback, void *data)
+{
+  return -1;
+}
+
+#endif
+
+
 int
 vma_iterate (vma_iterate_callback_fn callback, void *data)
 {
+#if defined __linux__ || defined __ANDROID__
+  /* This implementation is more than twice as fast as vma_iterate_proc,
+     when supported by the kernel.  Therefore try it first.  */
+  {
+    int retval = vma_iterate_procmap_query (callback, data);
+    if (retval == 0)
+      return 0;
+  }
+#endif
+
 #if defined __linux__ || defined __ANDROID__ || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ || defined __minix /* || defined __CYGWIN__ */
 
 # if defined __FreeBSD__
@@ -885,7 +973,7 @@ vma_iterate (vma_iterate_callback_fn callback, void *data)
      So use vma_iterate_proc only as a fallback.  */
   int retval = vma_iterate_bsd (callback, data);
   if (retval == 0)
-      return 0;
+    return 0;
 
   return vma_iterate_proc (callback, data);
 # else
@@ -893,7 +981,7 @@ vma_iterate (vma_iterate_callback_fn callback, void *data)
      as a fallback.  */
   int retval = vma_iterate_proc (callback, data);
   if (retval == 0)
-      return 0;
+    return 0;
 
   return vma_iterate_bsd (callback, data);
 # endif
-- 
2.50.1

>From c184b2cd50771d792dfcf58a6f92392f6e048096 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Fri, 1 Aug 2025 22:44:39 +0200
Subject: [PATCH 2/2] sigsegv: Use new ioctl available in Linux >= 6.11.

* lib/stackvma.c: On Linux, include <sys/ioctl.h>, <linux/fs.h>.
(vma_iterate_procmap_query): New function.
(vma_iterate): Try vma_iterate_procmap_query first.
---
 ChangeLog      |  7 +++++
 lib/stackvma.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 99c7edad0e..60b8d08d37 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2025-08-01  Bruno Haible  <[email protected]>
+
+	sigsegv: Use new ioctl available in Linux >= 6.11.
+	* lib/stackvma.c: On Linux, include <sys/ioctl.h>, <linux/fs.h>.
+	(vma_iterate_procmap_query): New function.
+	(vma_iterate): Try vma_iterate_procmap_query first.
+
 2025-08-01  Bruno Haible  <[email protected]>
 
 	vma-iter: Use new ioctl available in Linux >= 6.11.
diff --git a/lib/stackvma.c b/lib/stackvma.c
index 68ce5cae60..719a129c22 100644
--- a/lib/stackvma.c
+++ b/lib/stackvma.c
@@ -324,6 +324,13 @@ rof_close (struct rofile *rof)
 
 #endif
 
+/* ============================ stackvma-linux.c ============================ */
+
+#if defined __linux__ || defined __ANDROID__
+# include <sys/ioctl.h> /* ioctl */
+# include <linux/fs.h> /* PROCMAP_QUERY, struct procmap_query */
+#endif
+
 /* ========================== stackvma-vma-iter.c ========================== */
 /* Iterate through the virtual memory areas of the current process,
    by reading from the /proc file system.  */
@@ -539,6 +546,66 @@ vma_iterate_bsd (struct callback_locals *locals)
 # endif
 
 
+/* Support for reading the info from the Linux ioctl() PROCMAP_QUERY
+   system call.  */
+
+# if (defined __linux__ || defined __ANDROID__) && defined PROCMAP_QUERY /* Linux >= 6.11 */
+
+static int
+vma_iterate_procmap_query (struct callback_locals *locals)
+{
+  /* Documentation: <linux/fs.h>
+     This implementation is more than twice as fast as vma_iterate_proc.
+     It does not return the [vsyscall] memory area at 0xFFFFFFFFFF600000,
+     but this is not a serious drawback, since that memory area is not
+     controlled by userspace anyway.  */
+  int fd = open ("/proc/self/maps", O_RDONLY | O_CLOEXEC);
+  if (fd < 0)
+    return -1;
+
+  unsigned long addr = 0;
+  do
+    {
+      /* Clear all fields, just in case some 'in' fields are added later.  */
+      struct procmap_query pq = {0};
+      pq.size = sizeof (pq);
+      pq.query_flags = PROCMAP_QUERY_COVERING_OR_NEXT_VMA;
+      pq.query_addr = addr;
+      pq.vma_name_size = 0;
+      pq.vma_name_addr = 0;
+
+      int ret = ioctl (fd, PROCMAP_QUERY, &pq);
+      if (ret == -1)
+        {
+          if (addr == 0)
+            {
+              /* Likely errno == ENOTTY.  */
+              close (fd);
+              return -1;
+            }
+          else
+            /* Likely errno == ENOENT.  */
+            break;
+        }
+
+      if (callback (locals, pq.vma_start, pq.vma_end))
+        break;
+
+      addr = pq.vma_end;
+    }
+  while (addr != 0);
+
+  close (fd);
+  return 0;
+}
+
+# else
+
+#  define vma_iterate_procmap_query(locals) (-1)
+
+# endif
+
+
 /* Iterate over the virtual memory areas of the current process.
    If such iteration is supported, the callback is called once for every
    virtual memory area, in ascending order, with the following arguments:
@@ -553,6 +620,16 @@ vma_iterate_bsd (struct callback_locals *locals)
 static int
 vma_iterate (struct callback_locals *locals)
 {
+# if defined __linux__ || defined __ANDROID__
+  /* This implementation is more than twice as fast as vma_iterate_proc,
+     when supported by the kernel.  Therefore try it first.  */
+  {
+    int retval = vma_iterate_procmap_query (locals);
+    if (retval == 0)
+      return 0;
+  }
+# endif
+
 # if defined __FreeBSD__
   /* On FreeBSD with procfs (but not GNU/kFreeBSD, which uses linprocfs), the
      function vma_iterate_proc does not return the virtual memory areas that
@@ -561,7 +638,7 @@ vma_iterate (struct callback_locals *locals)
      So use vma_iterate_proc only as a fallback.  */
   int retval = vma_iterate_bsd (locals);
   if (retval == 0)
-      return 0;
+    return 0;
 
   return vma_iterate_proc (locals);
 # else
@@ -569,7 +646,7 @@ vma_iterate (struct callback_locals *locals)
      as a fallback.  */
   int retval = vma_iterate_proc (locals);
   if (retval == 0)
-      return 0;
+    return 0;
 
   return vma_iterate_bsd (locals);
 # endif
-- 
2.50.1

Reply via email to