@@ -2974,10 +2974,22 @@ static void accel_globals_dtor(zend_accel_globals *accel_globals)
29742974# include <sys/user.h>
29752975# define MAP_HUGETLB MAP_ALIGNED_SUPER
29762976# endif
2977+ # if __has_include (< link .h > )
2978+ # include <link.h>
2979+ # endif
2980+ # if __has_include (< elf .h > )
2981+ # include <elf.h>
2982+ # endif
29772983# endif
29782984
2979- # if defined(MAP_HUGETLB ) || defined(MADV_HUGEPAGE )
2980- static zend_result accel_remap_huge_pages (void * start , size_t size , size_t real_size , const char * name , size_t offset )
2985+ # define ZEND_HUGE_PAGE_SIZE (2UL * 1024 * 1024)
2986+
2987+ # if (defined(__linux__ ) || defined(__FreeBSD__ )) && (defined(MAP_HUGETLB ) || defined(MADV_HUGEPAGE )) && defined(HAVE_ATTRIBUTE_ALIGNED ) && defined(HAVE_ATTRIBUTE_SECTION ) && __has_include (< link .h > ) && __has_include (< elf .h > )
2988+ static zend_result
2989+ __attribute__((section (".remap_stub" )))
2990+ __attribute__((aligned (ZEND_HUGE_PAGE_SIZE )))
2991+ zend_never_inline
2992+ accel_remap_huge_pages (void * start , size_t size , size_t real_size )
29812993{
29822994 void * ret = MAP_FAILED ;
29832995 void * mem ;
@@ -3030,94 +3042,113 @@ static zend_result accel_remap_huge_pages(void *start, size_t size, size_t real_
30303042
30313043 // Given the MAP_FIXED flag the address can never diverge
30323044 ZEND_ASSERT (ret == start );
3033- zend_mmap_set_name ( start , size , "zend_huge_code_pages" );
3045+
30343046 memcpy (start , mem , real_size );
30353047 mprotect (start , size , PROT_READ | PROT_EXEC );
3048+ zend_mmap_set_name (start , size , "zend_huge_code_pages" );
30363049
30373050 munmap (mem , size );
30383051
30393052 return SUCCESS ;
30403053}
30413054
3042- static void accel_move_code_to_huge_pages (void )
3043- {
3055+ static int accel_dl_iterate_phdr_callback (struct dl_phdr_info * info , size_t size , void * data ) {
3056+ if (info -> dlpi_name == NULL || strcmp (info -> dlpi_name , "" ) == 0 ) {
3057+ * ((uintptr_t * )data ) = info -> dlpi_addr ;
3058+ return 1 ;
3059+ }
3060+ return 0 ;
3061+ }
3062+
3063+ static zend_result accel_find_program_section (ElfW (Shdr ) * section ) {
3064+
3065+ uintptr_t base_addr ;
3066+ if (dl_iterate_phdr (accel_dl_iterate_phdr_callback , & base_addr ) != 1 ) {
3067+ zend_error (E_WARNING , ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: executable base address not found" );
3068+ return FAILURE ;
3069+ }
3070+
30443071#if defined(__linux__ )
3045- FILE * f ;
3046- long unsigned int huge_page_size = 2 * 1024 * 1024 ;
3047-
3048- f = fopen ("/proc/self/maps" , "r" );
3049- if (f ) {
3050- long unsigned int start , end , offset , inode ;
3051- char perm [5 ], dev [10 ], name [MAXPATHLEN ];
3052- int ret ;
3053- extern char * __progname ;
3054- char buffer [MAXPATHLEN ];
3055-
3056- while (fgets (buffer , MAXPATHLEN , f )) {
3057- ret = sscanf (buffer , "%lx-%lx %4s %lx %9s %lu %s\n" , & start , & end , perm , & offset , dev , & inode , name );
3058- if (ret >= 6 ) {
3059- /* try to find the php text segment and map it into huge pages
3060- Lines without 'name' are going to be skipped */
3061- if (ret > 6 && perm [0 ] == 'r' && perm [1 ] == '-' && perm [2 ] == 'x' && name [0 ] == '/' \
3062- && strstr (name , __progname )) {
3063- long unsigned int seg_start = ZEND_MM_ALIGNED_SIZE_EX (start , huge_page_size );
3064- long unsigned int seg_end = (end & ~(huge_page_size - 1L ));
3065- long unsigned int real_end ;
3066-
3067- ret = fscanf (f , "%lx-" , & start );
3068- if (ret == 1 && start == seg_end + huge_page_size ) {
3069- real_end = end ;
3070- seg_end = start ;
3071- } else {
3072- real_end = seg_end ;
3073- }
3072+ FILE * f = fopen ("/proc/self/exe" , "r" );
3073+ #elif defined(__FreeBSD__ )
3074+ char path [4096 ];
3075+ int mib [4 ];
3076+ size_t len = sizeof (path );
30743077
3075- if (seg_end > seg_start ) {
3076- zend_accel_error (ACCEL_LOG_DEBUG , "remap to huge page %lx-%lx %s \n" , seg_start , seg_end , name );
3077- accel_remap_huge_pages ((void * )seg_start , seg_end - seg_start , real_end - seg_start , name , offset + seg_start - start );
3078- }
3079- break ;
3080- }
3081- }
3082- }
3078+ mib [0 ] = CTL_KERN ;
3079+ mib [1 ] = KERN_PROC ;
3080+ mib [2 ] = KERN_PROC_PATHNAME ;
3081+ mib [3 ] = -1 ; /* Current process */
3082+
3083+ if (sysctl (mib , 4 , path , & len , NULL , 0 ) == -1 ) {
3084+ zend_error (E_WARNING , ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: sysctl(KERN_PROC_PATHNAME) failed: %s (%d)" ,
3085+ strerror (errno ), errno );
3086+ return FAILURE ;
3087+ }
3088+
3089+ FILE * f = fopen (path , "r" );
3090+ #endif
3091+ if (!f ) {
3092+ zend_error (E_WARNING , ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fopen(/proc/self/exe) failed: %s (%d)" ,
3093+ strerror (errno ), errno );
3094+ return FAILURE ;
3095+ }
3096+
3097+ /* Read ELF header */
3098+ ElfW (Ehdr ) ehdr ;
3099+ if (!fread (& ehdr , sizeof (ehdr ), 1 , f )) {
3100+ zend_error (E_WARNING , ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fread() failed: %s (%d)" ,
3101+ strerror (errno ), errno );
30833102 fclose (f );
3103+ return FAILURE ;
30843104 }
3085- #elif defined(__FreeBSD__ )
3086- size_t s = 0 ;
3087- int mib [4 ] = {CTL_KERN , KERN_PROC , KERN_PROC_VMMAP , getpid ()};
3088- long unsigned int huge_page_size = 2 * 1024 * 1024 ;
3089- if (sysctl (mib , 4 , NULL , & s , NULL , 0 ) == 0 ) {
3090- s = s * 4 / 3 ;
3091- void * addr = mmap (NULL , s , PROT_READ | PROT_WRITE , MAP_SHARED | MAP_ANON , -1 , 0 );
3092- if (addr != MAP_FAILED ) {
3093- if (sysctl (mib , 4 , addr , & s , NULL , 0 ) == 0 ) {
3094- uintptr_t start = (uintptr_t )addr ;
3095- uintptr_t end = start + s ;
3096- while (start < end ) {
3097- struct kinfo_vmentry * entry = (struct kinfo_vmentry * )start ;
3098- size_t sz = entry -> kve_structsize ;
3099- if (sz == 0 ) {
3100- break ;
3101- }
3102- int permflags = entry -> kve_protection ;
3103- if ((permflags & KVME_PROT_READ ) && !(permflags & KVME_PROT_WRITE ) &&
3104- (permflags & KVME_PROT_EXEC ) && entry -> kve_path [0 ] != '\0' ) {
3105- long unsigned int seg_start = ZEND_MM_ALIGNED_SIZE_EX (start , huge_page_size );
3106- long unsigned int seg_end = (end & ~(huge_page_size - 1L ));
3107- if (seg_end > seg_start ) {
3108- zend_accel_error (ACCEL_LOG_DEBUG , "remap to huge page %lx-%lx %s \n" , seg_start , seg_end , entry -> kve_path );
3109- accel_remap_huge_pages ((void * )seg_start , seg_end - seg_start , seg_end - seg_start , entry -> kve_path , entry -> kve_offset + seg_start - start );
3110- // First relevant segment found is our binary
3111- break ;
3112- }
3113- }
3114- start += sz ;
3115- }
3116- }
3117- munmap (addr , s );
3105+
3106+ /* Read section headers */
3107+ ElfW (Shdr ) shdrs [ehdr .e_shnum ];
3108+ if (fseek (f , ehdr .e_shoff , SEEK_SET ) != 0 ) {
3109+ zend_error (E_WARNING , ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fseek() failed: %s (%d)" ,
3110+ strerror (errno ), errno );
3111+ fclose (f );
3112+ return FAILURE ;
3113+ }
3114+ if (fread (shdrs , sizeof (shdrs [0 ]), ehdr .e_shnum , f ) != ehdr .e_shnum ) {
3115+ zend_error (E_WARNING , ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fread() failed: %s (%d)" ,
3116+ strerror (errno ), errno );
3117+ fclose (f );
3118+ return FAILURE ;
3119+ }
3120+
3121+ fclose (f );
3122+
3123+ /* Find the program section */
3124+ for (ElfW (Half ) idx = 0 ; idx < ehdr .e_shnum ; idx ++ ) {
3125+ ElfW (Shdr ) * sh = & shdrs [idx ];
3126+ uintptr_t start = (uintptr_t )sh -> sh_addr + base_addr ;
3127+ zend_accel_error (ACCEL_LOG_DEBUG , "considering section %016" PRIxPTR "-%016" PRIxPTR " vs %016" PRIxPTR "\n" , start , start + sh -> sh_size , (uintptr_t )accel_find_program_section );
3128+ if ((uintptr_t )accel_find_program_section >= start && (uintptr_t )accel_find_program_section < start + sh -> sh_size ) {
3129+ * section = * sh ;
3130+ section -> sh_addr = (ElfW (Addr ))start ;
3131+ return SUCCESS ;
31183132 }
31193133 }
3120- #endif
3134+
3135+ return FAILURE ;
3136+ }
3137+
3138+ static void accel_move_code_to_huge_pages (void )
3139+ {
3140+ ElfW (Shdr ) section ;
3141+ if (accel_find_program_section (& section ) == FAILURE ) {
3142+ zend_error (E_WARNING , ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: program section not found" );
3143+ return ;
3144+ }
3145+
3146+ uintptr_t start = ZEND_MM_ALIGNED_SIZE_EX (section .sh_addr , ZEND_HUGE_PAGE_SIZE );
3147+ uintptr_t end = (section .sh_addr + section .sh_size ) & ~(ZEND_HUGE_PAGE_SIZE - 1UL );
3148+ if (end > start ) {
3149+ zend_accel_error (ACCEL_LOG_DEBUG , "remap to huge page %" PRIxPTR "-%" PRIxPTR "\n" , start , end );
3150+ accel_remap_huge_pages ((void * )start , end - start , end - start );
3151+ }
31213152}
31223153# else
31233154static void accel_move_code_to_huge_pages (void )
0 commit comments