Support NetBSD with mprotect. Signed-off-by: Thomas Klausner <wiz@NetBSD.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
diff --git a/src/closures.c b/src/closures.c
index abbba7c..6afcd39 100644
--- a/src/closures.c
+++ b/src/closures.c
@@ -34,6 +34,82 @@
#include <ffi.h>
#include <ffi_common.h>
+#ifdef __NetBSD__
+#include <sys/param.h>
+#endif
+
+#if __NetBSD_Version__ - 0 >= 799007200
+/* NetBSD with PROT_MPROTECT */
+#include <sys/mman.h>
+
+#include <stddef.h>
+#include <unistd.h>
+
+static const size_t overhead =
+ (sizeof(max_align_t) > sizeof(void *) + sizeof(size_t)) ?
+ sizeof(max_align_t)
+ : sizeof(void *) + sizeof(size_t);
+
+#define ADD_TO_POINTER(p, d) ((void *)((uintptr_t)(p) + (d)))
+
+void *
+ffi_closure_alloc (size_t size, void **code)
+{
+ static size_t page_size;
+ size_t rounded_size;
+ void *codeseg, *dataseg;
+ int prot;
+
+ /* Expect that PAX mprotect is active and a separate code mapping is necessary. */
+ if (!code)
+ return NULL;
+
+ /* Obtain system page size. */
+ if (!page_size)
+ page_size = sysconf(_SC_PAGESIZE);
+
+ /* Round allocation size up to the next page, keeping in mind the size field and pointer to code map. */
+ rounded_size = (size + overhead + page_size - 1) & ~(page_size - 1);
+
+ /* Primary mapping is RW, but request permission to switch to PROT_EXEC later. */
+ prot = PROT_READ | PROT_WRITE | PROT_MPROTECT(PROT_EXEC);
+ dataseg = mmap(NULL, rounded_size, prot, MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (dataseg == MAP_FAILED)
+ return NULL;
+
+ /* Create secondary mapping and switch it to RX. */
+ codeseg = mremap(dataseg, rounded_size, NULL, rounded_size, MAP_REMAPDUP);
+ if (codeseg == MAP_FAILED) {
+ munmap(dataseg, rounded_size);
+ return NULL;
+ }
+ if (mprotect(codeseg, rounded_size, PROT_READ | PROT_EXEC) == -1) {
+ munmap(codeseg, rounded_size);
+ munmap(dataseg, rounded_size);
+ return NULL;
+ }
+
+ /* Remember allocation size and location of the secondary mapping for ffi_closure_free. */
+ memcpy(dataseg, &rounded_size, sizeof(rounded_size));
+ memcpy(ADD_TO_POINTER(dataseg, sizeof(size_t)), &codeseg, sizeof(void *));
+ *code = ADD_TO_POINTER(codeseg, overhead);
+ return ADD_TO_POINTER(dataseg, overhead);
+}
+
+void
+ffi_closure_free (void *ptr)
+{
+ void *codeseg, *dataseg;
+ size_t rounded_size;
+
+ dataseg = ADD_TO_POINTER(ptr, -overhead);
+ memcpy(&rounded_size, dataseg, sizeof(rounded_size));
+ memcpy(&codeseg, ADD_TO_POINTER(dataseg, sizeof(size_t)), sizeof(void *));
+ munmap(dataseg, rounded_size);
+ munmap(codeseg, rounded_size);
+}
+#else /* !NetBSD with PROT_MPROTECT */
+
#if !FFI_MMAP_EXEC_WRIT && !FFI_EXEC_TRAMPOLINE_TABLE
# if __linux__ && !defined(__ANDROID__)
/* This macro indicates it may be forbidden to map anonymous memory
@@ -856,3 +932,5 @@ ffi_closure_free (void *ptr)
# endif /* ! FFI_MMAP_EXEC_WRIT */
#endif /* FFI_CLOSURES */
+
+#endif /* NetBSD with PROT_MPROTECT */