maint: update copyright
[gnulib.git] / lib / vma-iter.c
index 46c0212..7673388 100644 (file)
@@ -1,5 +1,5 @@
 /* Iteration over virtual memory areas.
-   Copyright (C) 2011 Free Software Foundation, Inc.
+   Copyright (C) 2011-2014 Free Software Foundation, Inc.
    Written by Bruno Haible <bruno@clisp.org>, 2011.
 
    This program is free software: you can redistribute it and/or modify
@@ -32,7 +32,7 @@
 # include <sys/procfs.h> /* PIOC*, prmap_t */
 #endif
 
-#if defined __APPLE__ && defined __MACH__ /* MacOS X */
+#if defined __APPLE__ && defined __MACH__ /* Mac OS X */
 # include <mach/mach.h>
 #endif
 
 # include <OS.h>
 #endif
 
+#if HAVE_MQUERY /* OpenBSD */
+# include <sys/types.h>
+# include <sys/mman.h> /* mquery */
+#endif
+
 
 /* Support for reading text files in the /proc file system.  */
 
@@ -354,7 +359,7 @@ vma_iterate (vma_iterate_callback_fn callback, void *data)
   close (fd);
   return;
 
-#elif defined __APPLE__ && defined __MACH__ /* MacOS X */
+#elif defined __APPLE__ && defined __MACH__ /* Mac OS X */
 
   task_t task = mach_task_self ();
   vm_address_t address;
@@ -365,10 +370,10 @@ vma_iterate (vma_iterate_callback_fn callback, void *data)
       int more;
       mach_port_t object_name;
       unsigned int flags;
-      /* In MacOS X 10.5, the types vm_address_t, vm_offset_t, vm_size_t have
+      /* In Mac OS X 10.5, the types vm_address_t, vm_offset_t, vm_size_t have
          32 bits in 32-bit processes and 64 bits in 64-bit processes. Whereas
          mach_vm_address_t and mach_vm_size_t are always 64 bits large.
-         MacOS X 10.5 has three vm_region like methods:
+         Mac OS X 10.5 has three vm_region like methods:
            - vm_region. It has arguments that depend on whether the current
              process is 32-bit or 64-bit. When linking dynamically, this
              function exists only in 32-bit processes. Therefore we use it only
@@ -490,6 +495,99 @@ vma_iterate (vma_iterate_callback_fn callback, void *data)
         break;
     }
 
+#elif HAVE_MQUERY /* OpenBSD */
+
+  uintptr_t pagesize;
+  uintptr_t address;
+  int /*bool*/ address_known_mapped;
+
+  pagesize = getpagesize ();
+  /* Avoid calling mquery with a NULL first argument, because this argument
+     value has a specific meaning.  We know the NULL page is unmapped.  */
+  address = pagesize;
+  address_known_mapped = 0;
+  for (;;)
+    {
+      /* Test whether the page at address is mapped.  */
+      if (address_known_mapped
+          || mquery ((void *) address, pagesize, 0, MAP_FIXED, -1, 0)
+             == (void *) -1)
+        {
+          /* The page at address is mapped.
+             This is the start of an interval.  */
+          uintptr_t start = address;
+          uintptr_t end;
+
+          /* Find the end of the interval.  */
+          end = (uintptr_t) mquery ((void *) address, pagesize, 0, 0, -1, 0);
+          if (end == (uintptr_t) (void *) -1)
+            end = 0; /* wrap around */
+          address = end;
+
+          /* It's too complicated to find out about the flags.  Just pass 0.  */
+          if (callback (data, start, end, 0))
+            break;
+
+          if (address < pagesize) /* wrap around? */
+            break;
+        }
+      /* Here we know that the page at address is unmapped.  */
+      {
+        uintptr_t query_size = pagesize;
+
+        address += pagesize;
+
+        /* Query larger and larger blocks, to get through the unmapped address
+           range with few mquery() calls.  */
+        for (;;)
+          {
+            if (2 * query_size > query_size)
+              query_size = 2 * query_size;
+            if (address + query_size - 1 < query_size) /* wrap around? */
+              {
+                address_known_mapped = 0;
+                break;
+              }
+            if (mquery ((void *) address, query_size, 0, MAP_FIXED, -1, 0)
+                == (void *) -1)
+              {
+                /* Not all the interval [address .. address + query_size - 1]
+                   is unmapped.  */
+                address_known_mapped = (query_size == pagesize);
+                break;
+              }
+            /* The interval [address .. address + query_size - 1] is
+               unmapped.  */
+            address += query_size;
+          }
+        /* Reduce the query size again, to determine the precise size of the
+           unmapped interval that starts at address.  */
+        while (query_size > pagesize)
+          {
+            query_size = query_size / 2;
+            if (address + query_size - 1 >= query_size)
+              {
+                if (mquery ((void *) address, query_size, 0, MAP_FIXED, -1, 0)
+                    != (void *) -1)
+                  {
+                    /* The interval [address .. address + query_size - 1] is
+                       unmapped.  */
+                    address += query_size;
+                    address_known_mapped = 0;
+                  }
+                else
+                  address_known_mapped = (query_size == pagesize);
+              }
+          }
+        /* Here again query_size = pagesize, and
+           either address + pagesize - 1 < pagesize, or
+           mquery ((void *) address, pagesize, 0, MAP_FIXED, -1, 0) fails.
+           So, the unmapped area ends at address.  */
+      }
+      if (address + pagesize - 1 < pagesize) /* wrap around? */
+        break;
+    }
+
 #endif
 }