diff --git a/ext/opcache/shared_alloc_mmap.c b/ext/opcache/shared_alloc_mmap.c index 8cfb4407f3faa..add07cd1694b0 100644 --- a/ext/opcache/shared_alloc_mmap.c +++ b/ext/opcache/shared_alloc_mmap.c @@ -39,17 +39,21 @@ # define MAP_ANONYMOUS MAP_ANON #endif #if defined(MAP_ALIGNED_SUPER) +# include +# include +# include # define MAP_HUGETLB MAP_ALIGNED_SUPER #endif -#if defined(__linux__) && (defined(__x86_64__) || defined (__aarch64__)) +#if (defined(__linux__) || defined(__FreeBSD__)) && (defined(__x86_64__) || defined (__aarch64__)) static void *find_prefered_mmap_base(size_t requested_size) { - FILE *f; size_t huge_page_size = 2 * 1024 * 1024; uintptr_t last_free_addr = 0; uintptr_t last_candidate = (uintptr_t)MAP_FAILED; uintptr_t start, end, text_start = 0; +#if defined(__linux__) + FILE *f; char buffer[MAXPATHLEN]; f = fopen("/proc/self/maps", "r"); @@ -89,6 +93,59 @@ static void *find_prefered_mmap_base(size_t requested_size) } fclose(f); +#elif defined(__FreeBSD__) + size_t s = 0; + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; + if (sysctl(mib, 4, NULL, &s, NULL, 0) == 0) { + s = s * 4 / 3; + void *addr = mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); + if (addr != MAP_FAILED) { + if (sysctl(mib, 4, addr, &s, NULL, 0) == 0) { + start = (uintptr_t)addr; + end = start + s; + while (start < end) { + struct kinfo_vmentry *entry = (struct kinfo_vmentry *)start; + size_t sz = entry->kve_structsize; + if (sz == 0) { + break; + } + uintptr_t e_start = entry->kve_start; + uintptr_t e_end = entry->kve_end; + if ((uintptr_t)execute_ex >= e_start) { + /* the current segment lays before PHP .text segment or PHP .text segment itself */ + if (last_free_addr + requested_size <= e_start) { + last_candidate = last_free_addr; + } + if ((uintptr_t)execute_ex < e_end) { + /* the current segment is PHP .text segment itself */ + if (last_candidate != (uintptr_t)MAP_FAILED) { + if (e_end - last_candidate < UINT32_MAX) { + /* we have found a big enough hole before the text segment */ + break; + } + last_candidate = (uintptr_t)MAP_FAILED; + } + text_start = e_start; + } + } else { + /* the current segment lays after PHP .text segment */ + if (last_free_addr + requested_size - text_start > UINT32_MAX) { + /* the current segment and the following segments lay too far from PHP .text segment */ + break; + } + if (last_free_addr + requested_size <= e_start) { + last_candidate = last_free_addr; + break; + } + } + last_free_addr = ZEND_MM_ALIGNED_SIZE_EX(e_end, huge_page_size); + start += sz; + } + } + munmap(addr, s); + } + } +#endif return (void*)last_candidate; } @@ -109,7 +166,7 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_ #ifdef PROT_MAX flags |= PROT_MAX(PROT_READ | PROT_WRITE | PROT_EXEC); #endif -#if defined(__linux__) && (defined(__x86_64__) || defined (__aarch64__)) +#if (defined(__linux__) || defined(__FreeBSD__)) && (defined(__x86_64__) || defined (__aarch64__)) void *hint = find_prefered_mmap_base(requested_size); if (hint != MAP_FAILED) { # ifdef MAP_HUGETLB