@@ -20,62 +20,177 @@
#include "config.h"
#include "system.h"
#include "coretypes.h"
+#include "options.h"
#include "diagnostic-core.h"
#include "config/host-darwin.h"
-
-/* Yes, this is really supposed to work. */
-/* This allows for a pagesize of 16384, which we have on Darwin20, but should
- continue to work OK for pagesize 4096 which we have on earlier versions.
- The size is 1 (binary) Gb. */
-static char pch_address_space[65536*16384] __attribute__((aligned (16384)));
-
-/* Return the address of the PCH address space, if the PCH will fit in it. */
+#include <errno.h>
+
+/* For Darwin (macOS only) platforms, without ASLR (PIE) enabled on the
+ binaries, the following VM addresses are expected to be available.
+ NOTE, that for aarch64, ASLR is always enabled - but the VM address
+ mentioned below is available (at least on Darwin20).
+
+ The spaces should all have 512Mb available c.f. PCH files for large
+ C++ or Objective-C in the range of 150Mb for 64b hosts.
+
+ We also try to steer clear of places already used for sanitizers. */
+
+#define PAGE_SZ 4096
+#if defined(__x86_64) && defined(__LP64__)
+# define TRY_EMPTY_VM_SPACE 0x180000000000ULL
+# define SAFE_ALLOC_SIZE 0x20000000
+#elif defined(__x86_64)
+# define TRY_EMPTY_VM_SPACE 0x00006fe00000ULL
+# define SAFE_ALLOC_SIZE 0x20000000
+#elif defined(__i386)
+# define TRY_EMPTY_VM_SPACE 0x00006fe00000ULL
+# define SAFE_ALLOC_SIZE 0x20000000
+#elif defined(__POWERPC__) && defined(__LP64__)
+# define TRY_EMPTY_VM_SPACE 0x180000000000ULL
+# define SAFE_ALLOC_SIZE 0x20000000
+#elif defined(__POWERPC__)
+# define TRY_EMPTY_VM_SPACE 0x00006fe00000ULL
+# define SAFE_ALLOC_SIZE 0x20000000
+#elif defined(__aarch64__)
+# undef PAGE_SZ
+# define PAGE_SZ 16384
+# define TRY_EMPTY_VM_SPACE 0x180000000000ULL
+# define SAFE_ALLOC_SIZE 0x20000000
+#else
+# error "unknown Darwin target"
+#endif
+
+/* Try to map a known position in the VM. The current PCH implementation
+ can adjust values at write-time, but not at read-time thus we need to
+ pick up the same position when reading as we got at write-time. */
void *
-darwin_gt_pch_get_address (size_t sz, int fd ATTRIBUTE_UNUSED)
+darwin_gt_pch_get_address (size_t sz, int fd)
{
- if (sz <= sizeof (pch_address_space))
- return pch_address_space;
- else
- return NULL;
+ if (sz > SAFE_ALLOC_SIZE)
+ {
+ error ("PCH memory request exceeds the available space");
+ return NULL;
+ }
+
+ /* Now try with the constraint that we really want this address... */
+ void *addr = mmap ((void *)TRY_EMPTY_VM_SPACE, sz, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_FIXED, fd, 0);
+
+ if (addr != (void *) MAP_FAILED)
+ munmap (addr, sz);
+
+ /* This ought to be the only alternative to failure, but there are comments
+ that suggest some versions of mmap can be buggy and return a different
+ value. */
+ if (addr == (void *) TRY_EMPTY_VM_SPACE)
+ return addr;
+
+ warning (OPT_Winvalid_pch, "PCH memory [fixed at %p] is not available %m",
+ (void *) TRY_EMPTY_VM_SPACE);
+
+ /* OK try to find a space without the constraint. */
+ addr = mmap ((void *) TRY_EMPTY_VM_SPACE, sz, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd, 0);
+
+ /* If we failed this time, that means there is *no* large enough free
+ space. */
+ if (addr == (void *) MAP_FAILED)
+ {
+ error ("no memory is available for PCH : %m");
+ return NULL;
+ }
+
+ /* Unmap the area before returning. */
+ munmap (addr, sz);
+
+ /* If we got the exact area we requested, then that's great. */
+ if (TRY_EMPTY_VM_SPACE && addr == (void *) TRY_EMPTY_VM_SPACE)
+ return addr;
+
+ warning (OPT_Winvalid_pch, "PCH memory at %p is not available",
+ (void *) TRY_EMPTY_VM_SPACE);
+
+ /* Otherwise, we need to try again but put some buffer space first. */
+ size_t buffer_size = 32 * 1024 * 1024;
+ void *buffer = mmap (0, buffer_size, PROT_NONE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ addr = mmap ((void *)TRY_EMPTY_VM_SPACE, sz, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd, 0);
+ if (buffer != (void *) MAP_FAILED)
+ munmap (buffer, buffer_size);
+
+ if (addr == (void *) MAP_FAILED)
+ {
+ error ("PCH memory not available %m");
+ return NULL;
+ }
+
+ warning (OPT_Winvalid_pch, "PCH memory at %p used instead", addr);
+ munmap (addr, sz);
+ return addr;
}
-/* Check ADDR and SZ for validity, and deallocate (using munmap) that part of
- pch_address_space beyond SZ. */
+/* Try to mmap the PCH file at ADDR for SZ bytes at OFF offset in the file.
+ If we succeed return 1, if we cannot mmap the desired address, then we
+ fail with -1. */
int
darwin_gt_pch_use_address (void *addr, size_t sz, int fd, size_t off)
{
- const size_t pagesize = getpagesize();
- void *mmap_result;
- int ret;
+ void *mapped_addr;
+
+ /* We're called with size == 0 if we're not planning to load a PCH
+ file at all. This allows the hook to free any static space that
+ we might have allocated at link time. */
+ if (sz == 0)
+ return -1;
- gcc_assert ((size_t)pch_address_space % pagesize == 0
- && sizeof (pch_address_space) % pagesize == 0);
-
- ret = (addr == pch_address_space && sz <= sizeof (pch_address_space));
- if (! ret)
- sz = 0;
+ gcc_checking_assert (!(off % PAGE_SZ));
+ if (addr != (void *) TRY_EMPTY_VM_SPACE)
+ warning (OPT_Winvalid_pch, "PCH at %p does not use the default position",
+ addr);
- /* Round the size to a whole page size. Normally this is a no-op. */
- sz = (sz + pagesize - 1) / pagesize * pagesize;
+ /* Try to map the file with MAP_PRIVATE and FIXED. */
+ mapped_addr = mmap (addr, sz, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_FIXED, fd, (off_t) off);
- if (munmap (pch_address_space + sz, sizeof (pch_address_space) - sz) != 0)
- fatal_error (input_location,
- "could not unmap %<pch_address_space%>: %m");
+ /* Hopefully, we succeed. */
+ if (mapped_addr == addr)
+ return 1;
- if (ret)
+ warning (OPT_Winvalid_pch, "PCH private mmap of written position (%p)"
+ " failed [errno %d] %m", addr, errno);
+
+ if (mapped_addr != (void *) MAP_FAILED)
+ munmap (mapped_addr, sz);
+
+ /* Try to make an anonymous private mmap at the desired location. */
+ mapped_addr = mmap (addr, sz, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, (off_t)0);
+
+ if (mapped_addr != addr)
{
- mmap_result = mmap (addr, sz,
- PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED,
- fd, off);
+ warning (OPT_Winvalid_pch, "PCH anon mmap at written position (%p)"
+ " failed [errno %d] %m", addr, errno);
+ if (mapped_addr != (void *) MAP_FAILED)
+ munmap (mapped_addr, sz);
+ return -1;
+ }
+
+ if (lseek (fd, off, SEEK_SET) == (off_t) -1)
+ return -1;
- /* The file might not be mmap-able. */
- ret = mmap_result != (void *) MAP_FAILED;
+ while (sz)
+ {
+ ssize_t nbytes;
- /* Sanity check for broken MAP_FIXED. */
- gcc_assert (!ret || mmap_result == addr);
+ nbytes = read (fd, addr, MIN (sz, (size_t) -1 >> 1));
+ if (nbytes <= 0)
+ return -1;
+ addr = (char *) addr + nbytes;
+ sz -= nbytes;
}
- return ret;
+ return 1;
}