2013-02-13 12:26:47 +00:00
/*
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2013-03-19 06:32:24 +00:00
| Zend OPcache |
2013-02-13 12:26:47 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2016-01-01 17:19:27 +00:00
| Copyright ( c ) 1998 - 2016 The PHP Group |
2013-02-13 12:26:47 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| 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>
2013-03-19 06:32:24 +00:00
# define ACCEL_FILEMAP_NAME "ZendOPcache.SharedMemoryArea"
# define ACCEL_MUTEX_NAME "ZendOPcache.SharedMemoryMutex"
2013-02-13 12:26:47 +00:00
# define ACCEL_FILEMAP_BASE_DEFAULT 0x01000000
2013-03-19 06:32:24 +00:00
# define ACCEL_FILEMAP_BASE "ZendOPcache.MemoryBase"
# define ACCEL_EVENT_SOURCE "Zend OPcache"
2013-02-13 12:26:47 +00:00
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 )
{
2015-12-07 21:52:51 +00:00
static char newname [ MAXPATHLEN + UNLEN + 4 + 1 + 32 ] ;
2013-02-25 07:29:54 +00:00
char uname [ UNLEN + 1 ] ;
2013-02-13 12:26:47 +00:00
DWORD unsize = UNLEN ;
GetUserName ( uname , & unsize ) ;
2015-12-07 21:52:51 +00:00
snprintf ( newname , sizeof ( newname ) - 1 , " %s@%s@%.32s " , name , uname , ZCG ( system_id ) ) ;
2013-02-13 12:26:47 +00:00
return newname ;
}
2013-02-25 12:06:38 +00:00
static char * get_mmap_base_file ( void )
2013-02-13 12:26:47 +00:00
{
2015-12-07 21:52:51 +00:00
static char windir [ MAXPATHLEN + UNLEN + 3 + sizeof ( " \\ \\ @ " ) + 1 + 32 ] ;
2013-02-25 07:29:54 +00:00
char uname [ UNLEN + 1 ] ;
2013-02-13 12:26:47 +00:00
DWORD unsize = UNLEN ;
int l ;
GetTempPath ( MAXPATHLEN , windir ) ;
GetUserName ( uname , & unsize ) ;
l = strlen ( windir ) ;
2015-12-07 21:52:51 +00:00
snprintf ( windir + l , sizeof ( windir ) - l - 1 , " \\ %s@%s@%.32s " , ACCEL_FILEMAP_BASE , uname , ZCG ( system_id ) ) ;
2013-02-13 12:26:47 +00:00
return windir ;
}
void zend_shared_alloc_create_lock ( void )
{
memory_mutex = CreateMutex ( NULL , FALSE , create_name_with_username ( ACCEL_MUTEX_NAME ) ) ;
2013-02-18 07:12:20 +00:00
if ( ! memory_mutex ) {
zend_accel_error ( ACCEL_LOG_FATAL , " Cannot create mutex " ) ;
return ;
}
2013-02-13 12:26:47 +00:00
ReleaseMutex ( memory_mutex ) ;
}
2013-02-25 12:06:38 +00:00
void zend_shared_alloc_lock_win32 ( void )
2013-02-13 12:26:47 +00:00
{
DWORD waitRes = WaitForSingleObject ( memory_mutex , INFINITE ) ;
2013-02-22 06:56:05 +00:00
if ( waitRes = = WAIT_FAILED ) {
2013-02-13 12:26:47 +00:00
zend_accel_error ( ACCEL_LOG_ERROR , " Cannot lock mutex " ) ;
}
}
2013-02-25 12:06:38 +00:00
void zend_shared_alloc_unlock_win32 ( void )
2013-02-13 12:26:47 +00:00
{
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 ( ) ;
2013-02-22 06:56:05 +00:00
if ( ! fp ) {
2013-02-13 12:26:47 +00:00
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 ;
}
2013-02-22 06:56:05 +00:00
if ( ! fscanf ( fp , " %p " , & wanted_mapping_base ) ) {
2013-02-13 12:26:47 +00:00
err = GetLastError ( ) ;
zend_win_error_message ( ACCEL_LOG_FATAL , " Unable to read base address " , err ) ;
* error_in = " read mapping base " ;
2013-02-15 13:46:38 +00:00
fclose ( fp ) ;
2013-02-13 12:26:47 +00:00
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 ) {
2015-10-09 21:07:10 +00:00
# if ENABLE_FILE_CACHE_FALLBACK
if ( ZCG ( accel_directives ) . file_cache & & ZCG ( accel_directives ) . file_cache_fallback ) {
size_t pre_size , wanted_mb_save ;
wanted_mb_save = ( size_t ) wanted_mapping_base ;
err = ERROR_INVALID_ADDRESS ;
zend_win_error_message ( ACCEL_LOG_WARNING , " Base address marks unusable memory region (fall-back to file cache) " , err ) ;
pre_size = ZEND_ALIGNED_SIZE ( sizeof ( zend_smm_shared_globals ) ) + ZEND_ALIGNED_SIZE ( sizeof ( zend_shared_segment ) ) + ZEND_ALIGNED_SIZE ( sizeof ( void * ) ) + ZEND_ALIGNED_SIZE ( sizeof ( int ) ) ;
/* Map only part of SHM to have access opcache shared globals */
mapping_base = MapViewOfFileEx ( memfile , FILE_MAP_ALL_ACCESS , 0 , 0 , pre_size + ZEND_ALIGNED_SIZE ( sizeof ( zend_accel_shared_globals ) ) , NULL ) ;
if ( mapping_base = = NULL ) {
err = GetLastError ( ) ;
zend_win_error_message ( ACCEL_LOG_FATAL , " Unable to reattach to opcache shared globals " , err ) ;
return ALLOC_FAILURE ;
}
accel_shared_globals = ( zend_accel_shared_globals * ) ( ( char * ) ( ( zend_smm_shared_globals * ) mapping_base ) - > app_shared_globals + ( ( char * ) mapping_base - ( char * ) wanted_mb_save ) ) ;
/* Make this process to use file-cache only */
ZCG ( accel_directives ) . file_cache_only = 1 ;
return ALLOC_FALLBACK ;
}
# endif
2013-02-13 12:26:47 +00:00
err = ERROR_INVALID_ADDRESS ;
2016-08-26 16:50:30 +00:00
zend_win_error_message ( ACCEL_LOG_FATAL , " Base address marks unusable memory region. Please setup opcache.file_cache and opcache.file_cache_fallback directives for more convenient Opcache usage " , err ) ;
2013-02-13 12:26:47 +00:00
return ALLOC_FAILURE ;
}
mapping_base = MapViewOfFileEx ( memfile , FILE_MAP_ALL_ACCESS , 0 , 0 , 0 , wanted_mapping_base ) ;
err = GetLastError ( ) ;
2013-02-22 06:56:05 +00:00
if ( mapping_base = = NULL ) {
2013-02-13 12:26:47 +00:00
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 ;
}
2013-03-27 18:16:18 +00:00
smm_shared_globals = ( zend_smm_shared_globals * ) mapping_base ;
2013-02-13 12:26:47 +00:00
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 } ;
2015-01-03 09:22:58 +00:00
/* 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
2013-04-20 16:22:39 +00:00
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
2013-02-13 12:26:47 +00:00
void * * wanted_mapping_base = default_mapping_base_set ;
2013-02-25 12:35:44 +00:00
zend_shared_alloc_lock_win32 ( ) ;
2013-02-13 12:26:47 +00:00
/* 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 ( ) ;
2013-02-25 07:29:54 +00:00
if ( memfile = = NULL ) {
2013-02-13 12:26:47 +00:00
break ;
2013-02-25 07:29:54 +00:00
}
2013-02-13 12:26:47 +00:00
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 */
2015-09-24 15:29:51 +00:00
CloseHandle ( memfile ) ;
2013-02-13 12:26:47 +00:00
memfile = NULL ;
2015-09-24 08:19:26 +00:00
if ( + + map_retries > = MAX_MAP_RETRIES ) {
2015-09-24 07:48:34 +00:00
break ;
}
zend_shared_alloc_unlock_win32 ( ) ;
2013-02-25 07:29:54 +00:00
Sleep ( 1000 * ( map_retries + 1 ) ) ;
2015-09-24 07:48:34 +00:00
zend_shared_alloc_lock_win32 ( ) ;
2013-02-13 12:26:47 +00:00
} else {
2013-02-25 12:35:44 +00:00
zend_shared_alloc_unlock_win32 ( ) ;
2013-02-13 12:26:47 +00:00
return ret ;
}
2015-09-24 07:48:34 +00:00
} while ( 1 ) ;
2013-02-13 12:26:47 +00:00
2013-02-22 06:56:05 +00:00
if ( map_retries = = MAX_MAP_RETRIES ) {
2013-02-25 12:35:44 +00:00
zend_shared_alloc_unlock_win32 ( ) ;
2013-02-13 12:26:47 +00:00
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 * ) ) ;
2013-02-19 14:21:31 +00:00
if ( ! * shared_segments_p ) {
2013-02-25 12:35:44 +00:00
zend_shared_alloc_unlock_win32 ( ) ;
2013-02-20 07:21:15 +00:00
zend_win_error_message ( ACCEL_LOG_FATAL , " calloc() failed " , GetLastError ( ) ) ;
2013-02-19 14:21:31 +00:00
* error_in = " calloc " ;
return ALLOC_FAILURE ;
}
2013-02-13 12:26:47 +00:00
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 ( ) ;
2013-02-22 06:56:05 +00:00
if ( memfile = = NULL ) {
2013-02-25 12:35:44 +00:00
zend_shared_alloc_unlock_win32 ( ) ;
2013-02-13 12:26:47 +00:00
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 ) {
2015-10-03 08:57:32 +00:00
wanted_mapping_base = vista_mapping_base_set ;
2013-02-13 12:26:47 +00:00
} else {
char * s = ZCG ( accel_directives ) . mmap_base ;
/* skip leading 0x, %p assumes hexdeciaml format anyway */
2013-02-25 07:29:54 +00:00
if ( * s = = ' 0 ' & & * ( s + 1 ) = = ' x ' ) {
2013-02-13 12:26:47 +00:00
s + = 2 ;
}
if ( sscanf ( s , " %p " , & default_mapping_base_set [ 0 ] ) ! = 1 ) {
2013-02-25 12:35:44 +00:00
zend_shared_alloc_unlock_win32 ( ) ;
2013-03-15 12:21:07 +00:00
zend_win_error_message ( ACCEL_LOG_FATAL , " Bad mapping address specified in opcache.mmap_base " , err ) ;
2013-02-13 12:26:47 +00:00
return ALLOC_FAILURE ;
}
}
do {
shared_segment - > p = mapping_base = MapViewOfFileEx ( memfile , FILE_MAP_ALL_ACCESS , 0 , 0 , 0 , * wanted_mapping_base ) ;
2013-02-25 07:29:54 +00:00
if ( * wanted_mapping_base = = NULL ) { /* Auto address (NULL) is the last option on the array */
2013-02-13 12:26:47 +00:00
break ;
2013-02-25 07:29:54 +00:00
}
2013-02-13 12:26:47 +00:00
wanted_mapping_base + + ;
} while ( ! mapping_base ) ;
err = GetLastError ( ) ;
2013-02-22 06:56:05 +00:00
if ( mapping_base = = NULL ) {
2013-02-25 12:35:44 +00:00
zend_shared_alloc_unlock_win32 ( ) ;
2013-02-13 12:26:47 +00:00
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 ( ) ;
2013-02-22 06:56:05 +00:00
if ( ! fp ) {
2013-02-25 12:35:44 +00:00
zend_shared_alloc_unlock_win32 ( ) ;
2013-02-13 12:26:47 +00:00
zend_win_error_message ( ACCEL_LOG_WARNING , mmap_base_file , err ) ;
zend_win_error_message ( ACCEL_LOG_FATAL , " Unable to write base address " , err ) ;
2013-02-15 13:46:38 +00:00
return ALLOC_FAILURE ;
2013-02-13 12:26:47 +00:00
}
fprintf ( fp , " %p \n " , mapping_base ) ;
fclose ( fp ) ;
}
shared_segment - > pos = 0 ;
shared_segment - > size = requested_size ;
2013-02-25 12:35:44 +00:00
zend_shared_alloc_unlock_win32 ( ) ;
2013-02-13 12:26:47 +00:00
return ALLOC_SUCCESS ;
}
static int detach_segment ( zend_shared_segment * shared_segment )
{
2013-02-25 12:35:44 +00:00
zend_shared_alloc_lock_win32 ( ) ;
2013-02-22 06:56:05 +00:00
if ( mapping_base ) {
2013-02-13 12:26:47 +00:00
UnmapViewOfFile ( mapping_base ) ;
2015-09-24 12:26:15 +00:00
mapping_base = NULL ;
2013-02-13 12:26:47 +00:00
}
CloseHandle ( memfile ) ;
2015-09-24 12:26:15 +00:00
memfile = NULL ;
2013-02-25 12:35:44 +00:00
zend_shared_alloc_unlock_win32 ( ) ;
2013-02-13 12:26:47 +00:00
CloseHandle ( memory_mutex ) ;
2015-09-24 12:26:15 +00:00
memory_mutex = NULL ;
2013-02-13 12:26:47 +00:00
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
} ;