summaryrefslogtreecommitdiff
path: root/ext/opcache/shared_alloc_win32.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/opcache/shared_alloc_win32.c')
-rw-r--r--ext/opcache/shared_alloc_win32.c344
1 files changed, 344 insertions, 0 deletions
diff --git a/ext/opcache/shared_alloc_win32.c b/ext/opcache/shared_alloc_win32.c
new file mode 100644
index 0000000000..37431fb18a
--- /dev/null
+++ b/ext/opcache/shared_alloc_win32.c
@@ -0,0 +1,344 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@zend.com> |
+ | Zeev Suraski <zeev@zend.com> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "ZendAccelerator.h"
+#include "zend_shared_alloc.h"
+#include "zend_accelerator_util_funcs.h"
+#include <winbase.h>
+#include <process.h>
+#include <LMCONS.H>
+
+#define ACCEL_FILEMAP_NAME "ZendOPcache.SharedMemoryArea"
+#define ACCEL_MUTEX_NAME "ZendOPcache.SharedMemoryMutex"
+#define ACCEL_FILEMAP_BASE_DEFAULT 0x01000000
+#define ACCEL_FILEMAP_BASE "ZendOPcache.MemoryBase"
+#define ACCEL_EVENT_SOURCE "Zend OPcache"
+
+static HANDLE memfile = NULL, memory_mutex = NULL;
+static void *mapping_base;
+
+#define MAX_MAP_RETRIES 25
+
+static void zend_win_error_message(int type, char *msg, int err)
+{
+ LPVOID lpMsgBuf;
+ HANDLE h;
+ char *ev_msgs[2];
+
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+
+ h = RegisterEventSource(NULL, TEXT(ACCEL_EVENT_SOURCE));
+ ev_msgs[0] = msg;
+ ev_msgs[1] = lpMsgBuf;
+ ReportEvent(h, // event log handle
+ EVENTLOG_ERROR_TYPE, // event type
+ 0, // category zero
+ err, // event identifier
+ NULL, // no user security identifier
+ 2, // one substitution string
+ 0, // no data
+ ev_msgs, // pointer to string array
+ NULL); // pointer to data
+ DeregisterEventSource(h);
+
+ LocalFree( lpMsgBuf );
+
+ zend_accel_error(type, msg);
+}
+
+static char *create_name_with_username(char *name)
+{
+ static char newname[MAXPATHLEN + UNLEN + 4];
+ char uname[UNLEN + 1];
+ DWORD unsize = UNLEN;
+
+ GetUserName(uname, &unsize);
+ snprintf(newname, sizeof(newname) - 1, "%s@%s", name, uname);
+ return newname;
+}
+
+static char *get_mmap_base_file(void)
+{
+ static char windir[MAXPATHLEN+UNLEN + 3 + sizeof("\\\\@")];
+ char uname[UNLEN + 1];
+ DWORD unsize = UNLEN;
+ int l;
+
+ GetTempPath(MAXPATHLEN, windir);
+ GetUserName(uname, &unsize);
+ l = strlen(windir);
+ snprintf(windir + l, sizeof(windir) - l - 1, "\\%s@%s", ACCEL_FILEMAP_BASE, uname);
+ return windir;
+}
+
+void zend_shared_alloc_create_lock(void)
+{
+ memory_mutex = CreateMutex(NULL, FALSE, create_name_with_username(ACCEL_MUTEX_NAME));
+ if (!memory_mutex) {
+ zend_accel_error(ACCEL_LOG_FATAL, "Cannot create mutex");
+ return;
+ }
+ ReleaseMutex(memory_mutex);
+}
+
+void zend_shared_alloc_lock_win32(void)
+{
+ DWORD waitRes = WaitForSingleObject(memory_mutex, INFINITE);
+
+ if (waitRes == WAIT_FAILED) {
+ zend_accel_error(ACCEL_LOG_ERROR, "Cannot lock mutex");
+ }
+}
+
+void zend_shared_alloc_unlock_win32(void)
+{
+ ReleaseMutex(memory_mutex);
+}
+
+static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
+{
+ int err;
+ void *wanted_mapping_base;
+ char *mmap_base_file = get_mmap_base_file();
+ FILE *fp = fopen(mmap_base_file, "r");
+ MEMORY_BASIC_INFORMATION info;
+
+ err = GetLastError();
+ if (!fp) {
+ zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err);
+ zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open base address file", err);
+ *error_in="fopen";
+ return ALLOC_FAILURE;
+ }
+ if (!fscanf(fp, "%p", &wanted_mapping_base)) {
+ err = GetLastError();
+ zend_win_error_message(ACCEL_LOG_FATAL, "Unable to read base address", err);
+ *error_in="read mapping base";
+ fclose(fp);
+ return ALLOC_FAILURE;
+ }
+ fclose(fp);
+
+ /* Check if the requested address space is free */
+ if (VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 ||
+ info.State != MEM_FREE ||
+ info.RegionSize < requested_size) {
+ err = ERROR_INVALID_ADDRESS;
+ zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to base address", err);
+ return ALLOC_FAILURE;
+ }
+
+ mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, wanted_mapping_base);
+ err = GetLastError();
+
+ if (mapping_base == NULL) {
+ if (err == ERROR_INVALID_ADDRESS) {
+ zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to base address", err);
+ return ALLOC_FAILURE;
+ }
+ return ALLOC_FAIL_MAPPING;
+ }
+ smm_shared_globals = (zend_smm_shared_globals *) mapping_base;
+
+ return SUCCESSFULLY_REATTACHED;
+}
+
+static int create_segments(size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in)
+{
+ int err, ret;
+ zend_shared_segment *shared_segment;
+ int map_retries = 0;
+ void *default_mapping_base_set[] = { 0, 0 };
+ /* TODO:
+ improve fixed addresses on x64. It still makes no sense to do it as Windows addresses are virtual per se and can or should be randomized anyway
+ through Address Space Layout Radomization (ASLR). We can still let the OS do its job and be sure that each process gets the same address if
+ desired. Not done yet, @zend refused but did not remember the exact reason, pls add info here if one of you know why :)
+ */
+#if defined(_WIN64)
+ void *vista_mapping_base_set[] = { (void *) 0x0000100000000000, (void *) 0x0000200000000000, (void *) 0x0000300000000000, (void *) 0x0000700000000000, 0 };
+#else
+ void *vista_mapping_base_set[] = { (void *) 0x20000000, (void *) 0x21000000, (void *) 0x30000000, (void *) 0x31000000, (void *) 0x50000000, 0 };
+#endif
+ void **wanted_mapping_base = default_mapping_base_set;
+ TSRMLS_FETCH();
+
+ zend_shared_alloc_lock_win32();
+ /* Mapping retries: When Apache2 restarts, the parent process startup routine
+ can be called before the child process is killed. In this case, the map will fail
+ and we have to sleep some time (until the child releases the mapping object) and retry.*/
+ do {
+ memfile = OpenFileMapping(FILE_MAP_WRITE, 0, create_name_with_username(ACCEL_FILEMAP_NAME));
+ err = GetLastError();
+ if (memfile == NULL) {
+ break;
+ }
+
+ ret = zend_shared_alloc_reattach(requested_size, error_in);
+ err = GetLastError();
+ if (ret == ALLOC_FAIL_MAPPING) {
+ /* Mapping failed, wait for mapping object to get freed and retry */
+ CloseHandle(memfile);
+ memfile = NULL;
+ Sleep(1000 * (map_retries + 1));
+ } else {
+ zend_shared_alloc_unlock_win32();
+ return ret;
+ }
+ } while (++map_retries < MAX_MAP_RETRIES);
+
+ if (map_retries == MAX_MAP_RETRIES) {
+ zend_shared_alloc_unlock_win32();
+ zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open file mapping", err);
+ *error_in = "OpenFileMapping";
+ return ALLOC_FAILURE;
+ }
+
+ /* creating segment here */
+ *shared_segments_count = 1;
+ *shared_segments_p = (zend_shared_segment **) calloc(1, sizeof(zend_shared_segment)+sizeof(void *));
+ if (!*shared_segments_p) {
+ zend_shared_alloc_unlock_win32();
+ zend_win_error_message(ACCEL_LOG_FATAL, "calloc() failed", GetLastError());
+ *error_in = "calloc";
+ return ALLOC_FAILURE;
+ }
+ shared_segment = (zend_shared_segment *)((char *)(*shared_segments_p) + sizeof(void *));
+ (*shared_segments_p)[0] = shared_segment;
+
+ memfile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, requested_size,
+ create_name_with_username(ACCEL_FILEMAP_NAME));
+ err = GetLastError();
+ if (memfile == NULL) {
+ zend_shared_alloc_unlock_win32();
+ zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create file mapping", err);
+ *error_in = "CreateFileMapping";
+ return ALLOC_FAILURE;
+ }
+
+ /* Starting from windows Vista, heap randomization occurs which might cause our mapping base to
+ be taken (fail to map). So under Vista, we try to map into a hard coded predefined addresses
+ in high memory. */
+ if (!ZCG(accel_directives).mmap_base || !*ZCG(accel_directives).mmap_base) {
+ do {
+ OSVERSIONINFOEX osvi;
+ SYSTEM_INFO si;
+
+ ZeroMemory(&si, sizeof(SYSTEM_INFO));
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ if (! GetVersionEx ((OSVERSIONINFO *) &osvi)) {
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (!GetVersionEx((OSVERSIONINFO *)&osvi)) {
+ break;
+ }
+ }
+
+ GetSystemInfo(&si);
+
+ /* Are we running Vista ? */
+ if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion == 6) {
+ wanted_mapping_base = vista_mapping_base_set;
+ }
+ } while (0);
+ } else {
+ char *s = ZCG(accel_directives).mmap_base;
+
+ /* skip leading 0x, %p assumes hexdeciaml format anyway */
+ if (*s == '0' && *(s + 1) == 'x') {
+ s += 2;
+ }
+ if (sscanf(s, "%p", &default_mapping_base_set[0]) != 1) {
+ zend_shared_alloc_unlock_win32();
+ zend_win_error_message(ACCEL_LOG_FATAL, "Bad mapping address specified in opcache.mmap_base", err);
+ return ALLOC_FAILURE;
+ }
+ }
+
+ do {
+ shared_segment->p = mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, *wanted_mapping_base);
+ if (*wanted_mapping_base == NULL) { /* Auto address (NULL) is the last option on the array */
+ break;
+ }
+ wanted_mapping_base++;
+ } while (!mapping_base);
+
+ err = GetLastError();
+ if (mapping_base == NULL) {
+ zend_shared_alloc_unlock_win32();
+ zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create view for file mapping", err);
+ *error_in = "MapViewOfFile";
+ return ALLOC_FAILURE;
+ } else {
+ char *mmap_base_file = get_mmap_base_file();
+ FILE *fp = fopen(mmap_base_file, "w");
+ err = GetLastError();
+ if (!fp) {
+ zend_shared_alloc_unlock_win32();
+ zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err);
+ zend_win_error_message(ACCEL_LOG_FATAL, "Unable to write base address", err);
+ return ALLOC_FAILURE;
+ }
+ fprintf(fp, "%p\n", mapping_base);
+ fclose(fp);
+ }
+
+ shared_segment->pos = 0;
+ shared_segment->size = requested_size;
+
+ zend_shared_alloc_unlock_win32();
+
+ return ALLOC_SUCCESS;
+}
+
+static int detach_segment(zend_shared_segment *shared_segment)
+{
+ zend_shared_alloc_lock_win32();
+ if (mapping_base) {
+ UnmapViewOfFile(mapping_base);
+ }
+ CloseHandle(memfile);
+ zend_shared_alloc_unlock_win32();
+ CloseHandle(memory_mutex);
+ return 0;
+}
+
+static size_t segment_type_size(void)
+{
+ return sizeof(zend_shared_segment);
+}
+
+zend_shared_memory_handlers zend_alloc_win32_handlers = {
+ create_segments,
+ detach_segment,
+ segment_type_size
+};