summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPHO <pho@cielonegro.org>2023-01-22 00:55:16 +0900
committerMarge Bot <ben+marge-bot@smart-cactus.org>2023-02-14 11:29:09 -0500
commit4771602447c877a4ec6e159e016668569e4a5366 (patch)
treeaa5df3ef535ca607f724c3f0d6408439f76504c2
parentaa3a262d1537b30c21a8b887385cc538440e7a44 (diff)
downloadhaskell-4771602447c877a4ec6e159e016668569e4a5366.tar.gz
RTS linker: Improve compatibility with NetBSD
1. Hint address to NetBSD mmap(2) has a different semantics from that of Linux. When a hint address is provided, mmap(2) searches for a free region at or below the hint but *never* above it. This means we can't reliably search for free regions incrementally on the userland, especially when ASLR is enabled. Let the kernel do it for us if we don't care where the mapped address is going to be. 2. NetBSD not only hates to map pages as rwx, but also disallows to switch pages from rw- to r-x unless the intention is declared when pages are initially requested. This means we need a new MemoryAccess mode for pages that are going to be changed to r-x.
-rw-r--r--rts/linker/MMap.c48
-rw-r--r--rts/linker/MMap.h2
2 files changed, 39 insertions, 11 deletions
diff --git a/rts/linker/MMap.c b/rts/linker/MMap.c
index 2d6c2aa148..30abad106e 100644
--- a/rts/linker/MMap.c
+++ b/rts/linker/MMap.c
@@ -46,6 +46,8 @@ static const char *memoryAccessDescription(MemoryAccess mode)
case MEM_NO_ACCESS: return "no-access";
case MEM_READ_ONLY: return "read-only";
case MEM_READ_WRITE: return "read-write";
+ case MEM_READ_WRITE_THEN_READ_EXECUTE:
+ return "read-write-then-read-execute";
case MEM_READ_EXECUTE: return "read-execute";
case MEM_READ_WRITE_EXECUTE:
return "read-write-execute";
@@ -63,13 +65,6 @@ struct MemoryRegion {
*/
};
-#define LOW_ADDR 0x01000000
-static struct MemoryRegion allMemory = {
- .start = (void *) LOW_ADDR,
- .end = (void *) -1,
- .last = (void *) LOW_ADDR
-};
-
#if defined(mingw32_HOST_OS)
/* A wrapper for VirtualQuery() providing useful debug output */
@@ -196,6 +191,8 @@ memoryAccessToProt(MemoryAccess access)
case MEM_NO_ACCESS: return PAGE_NOACCESS;
case MEM_READ_ONLY: return PAGE_READONLY;
case MEM_READ_WRITE: return PAGE_READWRITE;
+ case MEM_READ_WRITE_THEN_READ_EXECUTE:
+ return PAGE_READWRITE;
case MEM_READ_EXECUTE: return PAGE_EXECUTE_READ;
case MEM_READ_WRITE_EXECUTE:
return PAGE_EXECUTE_READWRITE;
@@ -258,6 +255,17 @@ memoryAccessToProt(MemoryAccess access)
case MEM_NO_ACCESS: return 0;
case MEM_READ_ONLY: return PROT_READ;
case MEM_READ_WRITE: return PROT_READ | PROT_WRITE;
+ case MEM_READ_WRITE_THEN_READ_EXECUTE:
+# if defined(netbsd_HOST_OS)
+ /* PROT_MPROTECT(PROT_EXEC) means that the pages are going to be
+ * marked as executable in the future. On NetBSD requesting
+ * additional permissions with mprotect(2) only succeeds when
+ * permissions were initially requested in this manner.
+ */
+ return PROT_READ | PROT_WRITE | PROT_MPROTECT(PROT_EXEC);
+# else
+ return PROT_READ | PROT_WRITE;
+# endif
case MEM_READ_EXECUTE: return PROT_READ | PROT_EXEC;
case MEM_READ_WRITE_EXECUTE:
return PROT_READ | PROT_WRITE | PROT_EXEC;
@@ -302,6 +310,18 @@ nearImage(void) {
}
static void *
+mmapAnywhere (
+ size_t bytes,
+ MemoryAccess access,
+ uint32_t flags,
+ int fd,
+ int offset)
+{
+ int prot = memoryAccessToProt(access);
+ return doMmap(NULL, bytes, prot, flags, fd, offset);
+}
+
+static void *
mmapInRegion (
struct MemoryRegion *region,
size_t bytes,
@@ -358,17 +378,23 @@ mmapForLinker (size_t bytes, MemoryAccess access, uint32_t flags, int fd, int of
IF_DEBUG(linker_verbose, debugBelch("mmapForLinker: start\n"));
if (RtsFlags.MiscFlags.linkerAlwaysPic) {
/* make no attempt at mapping low memory if we are assuming PIC */
- region = &allMemory;
+ region = NULL;
} else {
region = nearImage();
}
/* Use MAP_32BIT if appropriate */
- if (region->end <= (void *) 0xffffffff) {
+ if (region && region->end <= (void *) 0xffffffff) {
flags |= TRY_MAP_32BIT;
}
- void *result = mmapInRegion(region, bytes, access, flags, fd, offset);
+ void *result;
+ if (region) {
+ result = mmapInRegion(region, bytes, access, flags, fd, offset);
+ }
+ else {
+ result = mmapAnywhere(bytes, access, flags, fd, offset);
+ }
IF_DEBUG(linker_verbose,
debugBelch("mmapForLinker: mapped %zd bytes starting at %p\n",
bytes, result));
@@ -383,7 +409,7 @@ mmapForLinker (size_t bytes, MemoryAccess access, uint32_t flags, int fd, int of
void *
mmapAnonForLinker (size_t bytes)
{
- return mmapForLinker (bytes, MEM_READ_WRITE, MAP_ANONYMOUS, -1, 0);
+ return mmapForLinker (bytes, MEM_READ_WRITE_THEN_READ_EXECUTE, MAP_ANONYMOUS, -1, 0);
}
void munmapForLinker (void *addr, size_t bytes, const char *caller)
diff --git a/rts/linker/MMap.h b/rts/linker/MMap.h
index 9eebc3c4b2..683ec1f6ea 100644
--- a/rts/linker/MMap.h
+++ b/rts/linker/MMap.h
@@ -54,6 +54,8 @@ typedef enum {
MEM_NO_ACCESS,
MEM_READ_ONLY,
MEM_READ_WRITE,
+ // Initially map pages as rw- and then switch to r-x later.
+ MEM_READ_WRITE_THEN_READ_EXECUTE,
MEM_READ_EXECUTE,
MEM_READ_WRITE_EXECUTE,
} MemoryAccess;