diff --git a/.circleci/config.yml b/.circleci/config.yml index 0ce68b36d9013..f5dddad093158 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -90,7 +90,6 @@ jobs: --prefix=/usr \ --enable-phpdbg \ --enable-fpm \ - --enable-opcache \ --with-pdo-mysql=mysqlnd \ --with-mysqli=mysqlnd \ --with-pgsql \ @@ -168,7 +167,6 @@ jobs: no_output_timeout: 30m command: | sapi/cli/php run-tests.php \ - -d zend_extension=opcache.so \ -d opcache.enable_cli=1 \ -d opcache.jit_buffer_size=64M \ -d opcache.jit=tracing \ diff --git a/.github/actions/freebsd/action.yml b/.github/actions/freebsd/action.yml index d375a51a6d173..525280f5f800c 100644 --- a/.github/actions/freebsd/action.yml +++ b/.github/actions/freebsd/action.yml @@ -101,4 +101,3 @@ runs: --show-diff \ --show-slow 1000 \ --set-timeout 120 \ - -d zend_extension=opcache.so diff --git a/.github/scripts/windows/test_task.bat b/.github/scripts/windows/test_task.bat index 43e7763e70294..d97a5dad922b8 100644 --- a/.github/scripts/windows/test_task.bat +++ b/.github/scripts/windows/test_task.bat @@ -128,7 +128,6 @@ mkdir %PHP_BUILD_DIR%\test_file_cache rem generate php.ini echo extension_dir=%PHP_BUILD_DIR% > %PHP_BUILD_DIR%\php.ini echo opcache.file_cache=%PHP_BUILD_DIR%\test_file_cache >> %PHP_BUILD_DIR%\php.ini -if "%OPCACHE%" equ "1" echo zend_extension=php_opcache.dll >> %PHP_BUILD_DIR%\php.ini rem work-around for some spawned PHP processes requiring OpenSSL and sockets echo extension=php_openssl.dll >> %PHP_BUILD_DIR%\php.ini echo extension=php_sockets.dll >> %PHP_BUILD_DIR%\php.ini diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 1b1532af7f799..27ea1cedaafaa 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -126,7 +126,6 @@ jobs: jitType: tracing runTestsParameters: >- --asan -x - -d zend_extension=opcache.so -d opcache.enable_cli=1 - name: Notify Slack if: failure() @@ -240,14 +239,12 @@ jobs: jitType: tracing runTestsParameters: >- ${{ matrix.run_tests_parameters }} - -d zend_extension=opcache.so -d opcache.enable_cli=1 - name: Test OpCache uses: ./.github/actions/test-linux with: runTestsParameters: >- ${{ matrix.run_tests_parameters }} - -d zend_extension=opcache.so -d opcache.enable_cli=1 - name: Test Function JIT # ASAN frequently timeouts. Each test run takes ~90 minutes, we can @@ -258,7 +255,6 @@ jobs: jitType: function runTestsParameters: >- ${{ matrix.run_tests_parameters }} - -d zend_extension=opcache.so -d opcache.enable_cli=1 - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files @@ -332,14 +328,12 @@ jobs: jitType: tracing runTestsParameters: >- ${{ matrix.run_tests_parameters }} - -d zend_extension=opcache.so -d opcache.enable_cli=1 - name: Test OpCache uses: ./.github/actions/test-linux with: runTestsParameters: >- ${{ matrix.run_tests_parameters }} - -d zend_extension=opcache.so -d opcache.enable_cli=1 - name: Test Function JIT uses: ./.github/actions/test-linux @@ -347,7 +341,6 @@ jobs: jitType: function runTestsParameters: >- ${{ matrix.run_tests_parameters }} - -d zend_extension=opcache.so -d opcache.enable_cli=1 - name: Notify Slack if: failure() @@ -392,13 +385,11 @@ jobs: with: jitType: tracing runTestsParameters: >- - -d zend_extension=opcache.so -d opcache.enable_cli=1 - name: Test OpCache uses: ./.github/actions/test-macos with: runTestsParameters: >- - -d zend_extension=opcache.so -d opcache.enable_cli=1 - name: Test Function JIT if: matrix.os != '14' || !matrix.zts @@ -406,7 +397,6 @@ jobs: with: jitType: function runTestsParameters: >- - -d zend_extension=opcache.so -d opcache.enable_cli=1 - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files @@ -468,7 +458,6 @@ jobs: with: jitType: tracing runTestsParameters: >- - -d zend_extension=opcache.so -d opcache.enable_cli=1 - uses: codecov/codecov-action@v4 if: ${{ !cancelled() }} @@ -522,7 +511,6 @@ jobs: - name: Enable Opcache run: | echo memory_limit=-1 >> /etc/php.d/opcache.ini - echo zend_extension=opcache.so > /etc/php.d/opcache.ini echo opcache.enable_cli=1 >> /etc/php.d/opcache.ini echo opcache.enable=1 >> /etc/php.d/opcache.ini echo opcache.protect_memory=1 >> /etc/php.d/opcache.ini @@ -713,21 +701,18 @@ jobs: uses: ./.github/actions/test-linux with: runTestsParameters: >- - -d zend_extension=opcache.so -d opcache.enable_cli=1 --file-cache-prime - name: Test File Cache (prime shm, use shm) uses: ./.github/actions/test-linux with: runTestsParameters: >- - -d zend_extension=opcache.so -d opcache.enable_cli=1 --file-cache-use - name: Test File Cache (prime shm, use file) uses: ./.github/actions/test-linux with: runTestsParameters: >- - -d zend_extension=opcache.so -d opcache.enable_cli=1 --file-cache-use -d opcache.file_cache_only=1 @@ -735,7 +720,6 @@ jobs: uses: ./.github/actions/test-linux with: runTestsParameters: >- - -d zend_extension=opcache.so -d opcache.enable_cli=1 --file-cache-prime -d opcache.file_cache_only=1 @@ -743,7 +727,6 @@ jobs: uses: ./.github/actions/test-linux with: runTestsParameters: >- - -d zend_extension=opcache.so -d opcache.enable_cli=1 --file-cache-use -d opcache.file_cache_only=1 @@ -837,7 +820,6 @@ jobs: with: runTestsParameters: >- --msan - -d zend_extension=opcache.so -d opcache.enable_cli=1 - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 2f82179b90ec6..38be0086fc8df 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -130,7 +130,6 @@ jobs: with: jitType: tracing runTestsParameters: >- - -d zend_extension=opcache.so -d opcache.enable_cli=1 ${{ matrix.asan && '--asan -x' || '' }} - name: Verify generated files are up to date @@ -190,7 +189,6 @@ jobs: with: jitType: tracing runTestsParameters: >- - -d zend_extension=opcache.so -d opcache.enable_cli=1 MACOS_DEBUG_NTS: if: github.repository == 'php/php-src' || github.event_name == 'pull_request' @@ -229,7 +227,6 @@ jobs: with: jitType: tracing runTestsParameters: >- - -d zend_extension=opcache.so -d opcache.enable_cli=1 - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files @@ -299,7 +296,6 @@ jobs: ./configure \ --disable-debug \ --enable-mbstring \ - --enable-opcache \ --enable-option-checking=fatal \ --enable-sockets \ --enable-werror \ @@ -319,7 +315,6 @@ jobs: sudo mkdir -p /etc/php.d sudo chmod 777 /etc/php.d echo mysqli.default_socket=/var/run/mysqld/mysqld.sock > /etc/php.d/mysqli.ini - echo zend_extension=opcache.so >> /etc/php.d/opcache.ini echo opcache.enable=1 >> /etc/php.d/opcache.ini echo opcache.enable_cli=1 >> /etc/php.d/opcache.ini - name: Setup diff --git a/docs/release-process.md b/docs/release-process.md index 5d603a22f078e..4351942900fc5 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -209,7 +209,7 @@ slightly different steps. We'll call attention where the steps differ. # With ZTS make distclean || \ ./buildconf --force \ - && ./configure --enable-zts --disable-all --enable-debug --enable-opcache --enable-opcache-jit \ + && ./configure --enable-zts --disable-all --enable-debug --enable-opcache-jit \ && make -j$(nproc) \ && make test TEST_PHP_ARGS="-q -j$(nproc)" \ || ./sapi/cli/php -v @@ -217,7 +217,7 @@ slightly different steps. We'll call attention where the steps differ. # Without ZTS make distclean || \ ./buildconf --force \ - && ./configure --disable-all --enable-debug --enable-opcache --enable-opcache-jit \ + && ./configure --disable-all --enable-debug --enable-opcache-jit \ && make -j$(nproc) \ && make test TEST_PHP_ARGS="-q -j$(nproc)" \ || ./sapi/cli/php -v @@ -528,7 +528,7 @@ slightly different steps. We'll call attention where the steps differ. # With ZTS make distclean || \ ./buildconf --force \ - && ./configure --enable-zts --disable-all --enable-debug --enable-opcache --enable-opcache-jit \ + && ./configure --enable-zts --disable-all --enable-debug --enable-opcache-jit \ && make -j$(nproc) \ && make test TEST_PHP_ARGS="-q -j$(nproc)" \ || ./sapi/cli/php -v @@ -536,7 +536,7 @@ slightly different steps. We'll call attention where the steps differ. # Without ZTS make distclean || \ ./buildconf --force \ - && ./configure --disable-all --enable-debug --enable-opcache --enable-opcache-jit \ + && ./configure --disable-all --enable-debug --enable-opcache-jit \ && make -j$(nproc) \ && make test TEST_PHP_ARGS="-q -j$(nproc)" \ || ./sapi/cli/php -v diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index a2b2d0fde8b42..ce0c1a441bb6f 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -25,6 +25,7 @@ #include "zend_extensions.h" #include "zend_compile.h" #include "ZendAccelerator.h" +#include "zend_modules.h" #include "zend_persist.h" #include "zend_shared_alloc.h" #include "zend_accelerator_module.h" @@ -100,7 +101,13 @@ typedef int gid_t; #include "zend_simd.h" +#ifdef COMPILE_DL_OPCACHE ZEND_EXTENSION(); +ZEND_EXT_API zend_extension zend_extension_entry; +#define opcache_extension_entry zend_extension_entry +#else +zend_extension opcache_extension_entry; +#endif #ifndef ZTS zend_accel_globals accel_globals; @@ -118,6 +125,7 @@ zend_accel_shared_globals *accel_shared_globals = NULL; #ifdef ZEND_WIN32 char accel_uname_id[32]; #endif +bool accel_starting = false; bool accel_startup_ok = false; static const char *zps_failure_reason = NULL; const char *zps_api_failure_reason = NULL; @@ -3154,8 +3162,21 @@ static void accel_move_code_to_huge_pages(void) # endif /* defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE) */ #endif /* HAVE_HUGE_CODE_PAGES */ +void start_accel_extension(void) +{ + if (accel_starting) { + return; + } + + accel_starting = true; + + zend_register_extension(&opcache_extension_entry, NULL); +} + static int accel_startup(zend_extension *extension) { + accel_starting = true; + #ifdef ZTS accel_globals_id = ts_allocate_id(&accel_globals_id, sizeof(zend_accel_globals), (ts_allocate_ctor) accel_globals_ctor, (ts_allocate_dtor) accel_globals_dtor); #else @@ -3172,12 +3193,15 @@ static int accel_startup(zend_extension *extension) # endif #endif - if (start_accel_module() == FAILURE) { + zend_module_entry *module = start_accel_module(); + if (module == NULL) { accel_startup_ok = false; zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": module registration failed!"); return FAILURE; } + accel_register_ini_entries(module); + #ifdef ZEND_WIN32 if (UNEXPECTED(accel_gen_uname_id() == FAILURE)) { zps_startup_failure("Unable to get user name", NULL, accelerator_remove_cb); @@ -3461,6 +3485,8 @@ void accel_shutdown(void) zend_ini_entry *ini_entry; bool _file_cache_only = false; + accel_starting = false; + #ifdef HAVE_JIT zend_jit_shutdown(); #endif @@ -5037,7 +5063,11 @@ static void accel_activate(void) { } } +#ifdef COMPILE_DL_OPCACHE ZEND_EXT_API zend_extension zend_extension_entry = { +#else +zend_extension opcache_extension_entry = { +#endif ACCELERATOR_PRODUCT_NAME, /* name */ PHP_VERSION, /* version */ "Zend Technologies", /* author */ diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index 1bac9af9b8b7a..ff4ced45416ca 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -314,6 +314,7 @@ extern const char *zps_api_failure_reason; BEGIN_EXTERN_C() +void start_accel_extension(void); void accel_shutdown(void); ZEND_RINIT_FUNCTION(zend_accelerator); zend_result accel_post_deactivate(void); @@ -336,6 +337,10 @@ zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str); uint32_t zend_accel_get_class_name_map_ptr(zend_string *type_name); +#ifndef COMPILE_DL_OPCACHE +extern zend_extension opcache_extension_entry; +#endif + END_EXTERN_C() /* memory write protection */ diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4 index 8f6d5ab711b28..25303a65d1ba4 100644 --- a/ext/opcache/config.m4 +++ b/ext/opcache/config.m4 @@ -1,9 +1,3 @@ -PHP_ARG_ENABLE([opcache], - [whether to enable Zend OPcache support], - [AS_HELP_STRING([--disable-opcache], - [Disable Zend OPcache support])], - [yes]) - PHP_ARG_ENABLE([huge-code-pages], [whether to enable copying PHP CODE pages into HUGE PAGES], [AS_HELP_STRING([--disable-huge-code-pages], @@ -25,89 +19,85 @@ PHP_ARG_WITH([capstone], [no], [no]) -if test "$PHP_OPCACHE" != "no"; then - dnl Always build as shared extension. - ext_shared=yes +AS_VAR_IF([PHP_HUGE_CODE_PAGES], [yes], + [AC_DEFINE([HAVE_HUGE_CODE_PAGES], [1], + [Define to 1 to enable copying PHP CODE pages into HUGE PAGES.])]) - AS_VAR_IF([PHP_HUGE_CODE_PAGES], [yes], - [AC_DEFINE([HAVE_HUGE_CODE_PAGES], [1], - [Define to 1 to enable copying PHP CODE pages into HUGE PAGES.])]) +AS_VAR_IF([PHP_OPCACHE_JIT], [yes], [ + AS_CASE([$host_cpu], + [[i[34567]86*|x86*|aarch64|amd64]], [], + [ + AC_MSG_WARN([JIT not supported by host architecture]) + PHP_OPCACHE_JIT=no + ]) - AS_VAR_IF([PHP_OPCACHE_JIT], [yes], [ - AS_CASE([$host_cpu], - [[i[34567]86*|x86*|aarch64|amd64]], [], - [ - AC_MSG_WARN([JIT not supported by host architecture]) - PHP_OPCACHE_JIT=no - ]) + if test "$host_vendor" = "apple" && test "$host_cpu" = "aarch64" && test "$PHP_THREAD_SAFETY" = "yes"; then + AC_MSG_WARN([JIT not supported on Apple Silicon with ZTS]) + PHP_OPCACHE_JIT=no + fi +]) + +AS_VAR_IF([PHP_OPCACHE_JIT], [yes], [ + AC_DEFINE([HAVE_JIT], [1], [Define to 1 to enable JIT.]) + ZEND_JIT_SRC=m4_normalize([" + jit/ir/ir_cfg.c + jit/ir/ir_check.c + jit/ir/ir_dump.c + jit/ir/ir_emit.c + jit/ir/ir_gcm.c + jit/ir/ir_gdb.c + jit/ir/ir_patch.c + jit/ir/ir_perf.c + jit/ir/ir_ra.c + jit/ir/ir_save.c + jit/ir/ir_sccp.c + jit/ir/ir_strtab.c + jit/ir/ir.c + jit/zend_jit_vm_helpers.c + jit/zend_jit.c + "]) + + dnl Find out which ABI we are using. + AS_CASE([$host_alias], + [x86_64-*-darwin*], [ + IR_TARGET=IR_TARGET_X64 + DASM_FLAGS="-D X64APPLE=1 -D X64=1" + DASM_ARCH="x86" + ], + [*x86_64*|amd64-*-freebsd*], [ + IR_TARGET=IR_TARGET_X64 + DASM_FLAGS="-D X64=1" + DASM_ARCH="x86" + ], + [[i[34567]86*|x86*]], [ + IR_TARGET=IR_TARGET_X86 + DASM_ARCH="x86" + ], + [aarch64*], [ + IR_TARGET=IR_TARGET_AARCH64 + DASM_ARCH="aarch64" + ]) - if test "$host_vendor" = "apple" && test "$host_cpu" = "aarch64" && test "$PHP_THREAD_SAFETY" = "yes"; then - AC_MSG_WARN([JIT not supported on Apple Silicon with ZTS]) - PHP_OPCACHE_JIT=no - fi - ]) + AS_VAR_IF([PHP_CAPSTONE], [yes], + [PKG_CHECK_MODULES([CAPSTONE], [capstone >= 3.0.0], [ + AC_DEFINE([HAVE_CAPSTONE], [1], [Define to 1 if Capstone is available.]) + PHP_EVAL_LIBLINE([$CAPSTONE_LIBS], [OPCACHE_SHARED_LIBADD]) + PHP_EVAL_INCLINE([$CAPSTONE_CFLAGS]) + ZEND_JIT_SRC="$ZEND_JIT_SRC jit/ir/ir_disasm.c" + ])]) - AS_VAR_IF([PHP_OPCACHE_JIT], [yes], [ - AC_DEFINE([HAVE_JIT], [1], [Define to 1 to enable JIT.]) - ZEND_JIT_SRC=m4_normalize([" - jit/ir/ir_cfg.c - jit/ir/ir_check.c - jit/ir/ir_dump.c - jit/ir/ir_emit.c - jit/ir/ir_gcm.c - jit/ir/ir_gdb.c - jit/ir/ir_patch.c - jit/ir/ir_perf.c - jit/ir/ir_ra.c - jit/ir/ir_save.c - jit/ir/ir_sccp.c - jit/ir/ir_strtab.c - jit/ir/ir.c - jit/zend_jit_vm_helpers.c - jit/zend_jit.c - "]) - - dnl Find out which ABI we are using. - AS_CASE([$host_alias], - [x86_64-*-darwin*], [ - IR_TARGET=IR_TARGET_X64 - DASM_FLAGS="-D X64APPLE=1 -D X64=1" - DASM_ARCH="x86" - ], - [*x86_64*|amd64-*-freebsd*], [ - IR_TARGET=IR_TARGET_X64 - DASM_FLAGS="-D X64=1" - DASM_ARCH="x86" - ], - [[i[34567]86*|x86*]], [ - IR_TARGET=IR_TARGET_X86 - DASM_ARCH="x86" - ], - [aarch64*], [ - IR_TARGET=IR_TARGET_AARCH64 - DASM_ARCH="aarch64" - ]) - - AS_VAR_IF([PHP_CAPSTONE], [yes], - [PKG_CHECK_MODULES([CAPSTONE], [capstone >= 3.0.0], [ - AC_DEFINE([HAVE_CAPSTONE], [1], [Define to 1 if Capstone is available.]) - PHP_EVAL_LIBLINE([$CAPSTONE_LIBS], [OPCACHE_SHARED_LIBADD]) - PHP_EVAL_INCLINE([$CAPSTONE_CFLAGS]) - ZEND_JIT_SRC="$ZEND_JIT_SRC jit/ir/ir_disasm.c" - ])]) - - PHP_SUBST([IR_TARGET]) - PHP_SUBST([DASM_FLAGS]) - PHP_SUBST([DASM_ARCH]) - - JIT_CFLAGS="-I@ext_builddir@/jit/ir -D$IR_TARGET -DIR_PHP" - AS_VAR_IF([ZEND_DEBUG], [yes], [JIT_CFLAGS="$JIT_CFLAGS -DIR_DEBUG"]) - ]) + PHP_SUBST([IR_TARGET]) + PHP_SUBST([DASM_FLAGS]) + PHP_SUBST([DASM_ARCH]) - AC_CHECK_FUNCS([mprotect shm_create_largepage]) + JIT_CFLAGS="-I@ext_builddir@/jit/ir -D$IR_TARGET -DIR_PHP" + AS_VAR_IF([ZEND_DEBUG], [yes], [JIT_CFLAGS="$JIT_CFLAGS -DIR_DEBUG"]) +]) - AC_CACHE_CHECK([for sysvipc shared memory support], [php_cv_shm_ipc], - [AC_RUN_IFELSE([AC_LANG_SOURCE([ +AC_CHECK_FUNCS([mprotect shm_create_largepage]) + +AC_CACHE_CHECK([for sysvipc shared memory support], [php_cv_shm_ipc], + [AC_RUN_IFELSE([AC_LANG_SOURCE([ #include #include #include @@ -309,56 +299,55 @@ int main(void) { } return 0; }]])], - [php_cv_shm_mmap_posix=yes], - [php_cv_shm_mmap_posix=no], - [php_cv_shm_mmap_posix=no]) - ]) + [php_cv_shm_mmap_posix=yes], + [php_cv_shm_mmap_posix=no], + [php_cv_shm_mmap_posix=no]) ]) - LIBS=$LIBS_save +]) +LIBS=$LIBS_save + +AS_VAR_IF([php_cv_shm_mmap_posix], [yes], [ + AC_DEFINE([HAVE_SHM_MMAP_POSIX], [1], + [Define to 1 if you have the POSIX mmap() SHM support.]) + AS_CASE([$ac_cv_search_shm_open], ["none required"|no], [], + [PHP_EVAL_LIBLINE([$ac_cv_search_shm_open], [OPCACHE_SHARED_LIBADD])]) +]) + +PHP_NEW_EXTENSION([opcache], m4_normalize([ + shared_alloc_mmap.c + shared_alloc_posix.c + shared_alloc_shm.c + zend_accelerator_blacklist.c + zend_accelerator_debug.c + zend_accelerator_hash.c + zend_accelerator_module.c + zend_accelerator_util_funcs.c + zend_file_cache.c + zend_persist_calc.c + zend_persist.c + zend_shared_alloc.c + ZendAccelerator.c + $ZEND_JIT_SRC + ]), + [$ext_shared],, + [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 $JIT_CFLAGS],, + [yes]) - AS_VAR_IF([php_cv_shm_mmap_posix], [yes], [ - AC_DEFINE([HAVE_SHM_MMAP_POSIX], [1], - [Define to 1 if you have the POSIX mmap() SHM support.]) - AS_CASE([$ac_cv_search_shm_open], ["none required"|no], [], - [PHP_EVAL_LIBLINE([$ac_cv_search_shm_open], [OPCACHE_SHARED_LIBADD])]) - ]) +PHP_ADD_EXTENSION_DEP(opcache, date) +PHP_ADD_EXTENSION_DEP(opcache, pcre) - PHP_NEW_EXTENSION([opcache], m4_normalize([ - shared_alloc_mmap.c - shared_alloc_posix.c - shared_alloc_shm.c - zend_accelerator_blacklist.c - zend_accelerator_debug.c - zend_accelerator_hash.c - zend_accelerator_module.c - zend_accelerator_util_funcs.c - zend_file_cache.c - zend_persist_calc.c - zend_persist.c - zend_shared_alloc.c - ZendAccelerator.c - $ZEND_JIT_SRC - ]), - [$ext_shared],, - [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 $JIT_CFLAGS],, - [yes]) - - PHP_ADD_EXTENSION_DEP(opcache, date) - PHP_ADD_EXTENSION_DEP(opcache, pcre) - - if test "$php_cv_shm_ipc" != "yes" && test "$php_cv_shm_mmap_posix" != "yes" && test "$php_cv_shm_mmap_anon" != "yes"; then - AC_MSG_FAILURE(m4_text_wrap([ - No supported shared memory caching support was found when configuring - opcache. - ])) - fi +if test "$php_cv_shm_ipc" != "yes" && test "$php_cv_shm_mmap_posix" != "yes" && test "$php_cv_shm_mmap_anon" != "yes"; then + AC_MSG_FAILURE(m4_text_wrap([ + No supported shared memory caching support was found when configuring + opcache. + ])) +fi - AS_VAR_IF([PHP_OPCACHE_JIT], [yes], [ - PHP_ADD_BUILD_DIR([ - $ext_builddir/jit - $ext_builddir/jit/ir - ]) - PHP_ADD_MAKEFILE_FRAGMENT([$ext_srcdir/jit/Makefile.frag]) +AS_VAR_IF([PHP_OPCACHE_JIT], [yes], [ + PHP_ADD_BUILD_DIR([ + $ext_builddir/jit + $ext_builddir/jit/ir ]) - PHP_SUBST([OPCACHE_SHARED_LIBADD]) -fi + PHP_ADD_MAKEFILE_FRAGMENT([$ext_srcdir/jit/Makefile.frag]) +]) +PHP_SUBST([OPCACHE_SHARED_LIBADD]) diff --git a/ext/opcache/config.w32 b/ext/opcache/config.w32 index fa89ca1f18a39..dc677305ce4c7 100644 --- a/ext/opcache/config.w32 +++ b/ext/opcache/config.w32 @@ -15,7 +15,7 @@ if (PHP_OPCACHE != "no") { zend_persist_calc.c \ zend_file_cache.c \ zend_shared_alloc.c \ - shared_alloc_win32.c", true, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); + shared_alloc_win32.c", PHP_OPCACHE_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); ADD_EXTENSION_DEP('opcache', 'date'); ADD_EXTENSION_DEP('opcache', 'hash'); diff --git a/ext/opcache/jit/Dockerfile.arm64.example b/ext/opcache/jit/Dockerfile.arm64.example index e7b6a03b2db89..6665f4ab81b75 100644 --- a/ext/opcache/jit/Dockerfile.arm64.example +++ b/ext/opcache/jit/Dockerfile.arm64.example @@ -12,4 +12,4 @@ ADD . /php-src/ WORKDIR /php-src RUN ./buildconf # Compile a minimal debug build. --enable-debug adds runtime assertions and is slower than regular builds. -RUN ./configure --enable-debug --disable-all --enable-opcache && make clean && make -j$(nproc) +RUN ./configure --enable-debug --disable-all && make clean && make -j$(nproc) diff --git a/ext/opcache/jit/README.md b/ext/opcache/jit/README.md index 6ec58378acc7e..c87c625e845bb 100644 --- a/ext/opcache/jit/README.md +++ b/ext/opcache/jit/README.md @@ -76,7 +76,7 @@ export LDFLAGS=-L/usr/lib/i386-linux-gnu export CFLAGS='-m32' export CXXFLAGS='-m32' export PKG_CONFIG=/usr/bin/i686-linux-gnu-pkg-config -./configure --disable-all --enable-opcache --build=i686-pc-linux-gnu +./configure --disable-all --build=i686-pc-linux-gnu make -j$(nproc) ``` diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 1fdb1b9b3af91..91942cf7e2f6a 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -334,6 +334,8 @@ static int zend_jit_assign_to_variable(zend_jit_ctx *jit, zend_jit_addr ref_addr, bool check_exception); +static ir_ref jit_CONST_FUNC(zend_jit_ctx *jit, uintptr_t addr, uint16_t flags); + typedef struct _zend_jit_stub { const char *name; int (*stub)(zend_jit_ctx *jit); @@ -463,6 +465,10 @@ static const char* zend_reg_name(int8_t reg) /* IR helpers */ #ifdef ZTS +static void * ZEND_FASTCALL zend_jit_get_tsrm_ls_cache(void) { + return _tsrm_ls_cache; +} + static ir_ref jit_TLS(zend_jit_ctx *jit) { ZEND_ASSERT(jit->ctx.control); @@ -482,9 +488,13 @@ static ir_ref jit_TLS(zend_jit_ctx *jit) ref = insn->op1; } } - jit->tls = ir_TLS( - tsrm_ls_cache_tcb_offset ? tsrm_ls_cache_tcb_offset : tsrm_tls_index, - tsrm_ls_cache_tcb_offset ? IR_NULL : tsrm_tls_offset); + if (tsrm_ls_cache_tcb_offset == 0 && tsrm_tls_index == -1 && tsrm_tls_offset == -1) { + jit->tls = ir_CALL(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_get_tsrm_ls_cache)); + } else { + jit->tls = ir_TLS( + tsrm_ls_cache_tcb_offset ? tsrm_ls_cache_tcb_offset : tsrm_tls_index, + tsrm_ls_cache_tcb_offset ? IR_NULL : tsrm_tls_offset); + } return jit->tls; } #endif @@ -3113,6 +3123,7 @@ static void zend_jit_setup_disasm(void) REGISTER_HELPER(zend_fcall_interrupt); #ifndef ZTS + REGISTER_DATA(EG(current_execute_data)); REGISTER_DATA(EG(exception)); REGISTER_DATA(EG(opline_before_exception)); @@ -3127,6 +3138,8 @@ static void zend_jit_setup_disasm(void) REGISTER_DATA(EG(symbol_table)); REGISTER_DATA(CG(map_ptr_base)); +#else /* ZTS */ + REGISTER_HELPER(zend_jit_get_tsrm_ls_cache); #endif #endif } @@ -3293,6 +3306,535 @@ static void zend_jit_setup_unwinder(void) } #endif +#if defined(ZTS) +# if defined(IR_TARGET_AARCH64) + +/* https://developer.arm.com/documentation/ddi0602/2025-03/Base-Instructions/ADRP--Form-PC-relative-address-to-4KB-page- */ +# define AARCH64_ADRP_IMM_MASK 0x60ffffe0 /* bits 30-29, 23-5 */ +# define AARCH64_LDR_UNSIGNED_IMM_MASK 0x003ffc00 /* bits 21-10 */ +# define AARCH64_ADD_IMM_MASK 0x003ffc00 /* bits 21-10 */ +# define AARCH64_MOVZ_IMM_MASK 0x001fffe0 /* bits 20-5 */ +# define AARCH64_MOVZ_HW_MASK 0x00600000 /* bits 22-21 */ +# define AARCH64_MOVK_IMM_MASK 0x001fffe0 /* bits 20-5 */ +# define AARCH64_MOVK_HW_MASK 0x00600000 /* bits 22-21 */ +# define AARCH64_NOP 0xd503201f + +# ifdef __MUSL__ + +# define DTV_OFFSET -8 +# define DTV_INDEX_GAP 0 + +typedef struct _dtv_pointer_t { + uintptr_t val; +} dtv_pointer_t; + +typedef struct TLSDescriptor { + size_t index; + size_t offset; +} TLSDescriptor; + +# elif defined(__FreeBSD__) + +# define DTV_OFFSET 0 +/* Index is offset by 1 on FreeBSD (https://github.com/freebsd/freebsd-src/blob/22ca6db50f4e6bd75a141f57cf953d8de6531a06/lib/libc/gen/tls.c#L88) */ +# define DTV_INDEX_GAP 1 + +typedef struct _dtv_pointer_t { + uintptr_t val; +} dtv_pointer_t; + +/* https://github.com/freebsd/freebsd-src/blob/c52ca7dd09066648b1cc40f758289404d68ab886/libexec/rtld-elf/aarch64/reloc.c#L180-L184 */ +typedef struct TLSDescriptor { + void* thunk; + int index; + size_t offset; +} TLSDescriptor; + +# else /* Glibc */ + +# define DTV_OFFSET 0 +# define DTV_INDEX_GAP 0 + +typedef struct _dtv_pointer_t { + uintptr_t val; + uintptr_t _; +} dtv_pointer_t; + +typedef struct TLSDescriptor { + size_t index; + size_t offset; +} TLSDescriptor; + +# endif + +ZEND_ATTRIBUTE_UNUSED static zend_result zend_jit_resolve_tsrm_ls_cache_offsets(void) { + void *addr; + uint32_t *insn; + void *thread_pointer; + + __asm__ __volatile__( + /* Load thread pointer address */ + "mrs %0, tpidr_el0\n" + /* Load next instruction address */ + "adr %1, .+4\n\t" + /* General Dynamic code sequence as expected by linkers */ + "adrp x0, :tlsdesc:_tsrm_ls_cache\n" + "ldr x1, [x0, #:tlsdesc_lo12:_tsrm_ls_cache]\n" + "add x0, x0, :tlsdesc_lo12:_tsrm_ls_cache\n" + ".tlsdesccall _tsrm_ls_cache\n" + "blr x1\n" + "mrs x8, tpidr_el0\n" + "add %2, x8, x0\n" + : "=r" (thread_pointer), "=r" (insn), "=r" (addr) + : + : "x0", "x1", "x8"); + + ZEND_ASSERT(addr == &_tsrm_ls_cache); + + /* Check if the general dynamic code was relaxed by the linker */ + + // adrp x0, #any + if ((insn[0] & ~AARCH64_ADRP_IMM_MASK) != 0x90000000) { + zend_accel_error(ACCEL_LOG_DEBUG, "adrp insn does not match: 0x%08x\n", insn[0]); + goto code_changed; + } + + // ldr x1, [x0, #any] + if ((insn[1] & ~AARCH64_LDR_UNSIGNED_IMM_MASK) != 0xf9400001) { + zend_accel_error(ACCEL_LOG_DEBUG, "ldr insn does not match: 0x%08x\n", insn[1]); + goto code_changed; + } + + // add x0, x0, any + if ((insn[2] & ~AARCH64_ADD_IMM_MASK) != 0x91000000) { + zend_accel_error(ACCEL_LOG_DEBUG, "add insn does not match: 0x%08x\n", insn[2]); + goto code_changed; + } + + /* Code is intact, we can extract immediate values */ + + uint64_t adrp_imm = (uint64_t)( ((insn[0] & 0x00ffffe0) >> 3) | ((insn[0] & 0x60000000) >> 29) ) << 12; + uint64_t add_imm = (uint64_t)(insn[2] & AARCH64_ADD_IMM_MASK) >> 10; + uint64_t pc = (uint64_t)insn; + uintptr_t **where = (uintptr_t**)((pc & ~(4096-1)) + adrp_imm + add_imm); + + /* See https://github.com/ARM-software/abi-aa/blob/2a70c42d62e9c3eb5887fa50b71257f20daca6f9/aaelf64/aaelf64.rst + * section "Relocations for thread-local storage". + * The first entry holds a pointer to the variable's TLS descriptor resolver + * function and the second entry holds a platform-specific offset or + * pointer. */ + TLSDescriptor *tlsdesc = (TLSDescriptor*)(where[1]); + + if ((uintptr_t)&_tsrm_ls_cache - (uintptr_t)thread_pointer == (uintptr_t)tlsdesc) { + zend_accel_error(ACCEL_LOG_DEBUG, "static tls at offset %p from thread pointer (inferred from tlsdesc)\n", tlsdesc); + tsrm_ls_cache_tcb_offset = (uintptr_t)tlsdesc; + return SUCCESS; + } + + /* We've got the TLS descriptor. Double check: */ + +# if ZEND_DEBUG + dtv_pointer_t *dtv = *(dtv_pointer_t**)((uintptr_t)thread_pointer + DTV_OFFSET); + addr = (void*) (dtv[tlsdesc->index + DTV_INDEX_GAP].val + tlsdesc->offset); + + ZEND_ASSERT(addr == &_tsrm_ls_cache); +# endif + + zend_accel_error(ACCEL_LOG_DEBUG, "dynamic tls module idx %zu offset %zu (inferred from code)\n", (size_t)tlsdesc->index, tlsdesc->offset); + + tsrm_tls_index = (tlsdesc->index + DTV_INDEX_GAP) * sizeof(dtv_pointer_t); + tsrm_tls_offset = tlsdesc->offset; + + return SUCCESS; + +code_changed: + + /* Code was changed by the linker. Check if we recognize the updated code */ + + // movz x0, #0, lsl #16 + if ((insn[0] & ~AARCH64_MOVZ_IMM_MASK) != 0xd2a00000) { + zend_accel_error(ACCEL_LOG_DEBUG, "movz insn does not match: 0x%08x\n", insn[0]); + return FAILURE; + } + + // movk x0, #0x10 + if ((insn[1] & ~AARCH64_MOVK_IMM_MASK) != 0xf2800000) { + zend_accel_error(ACCEL_LOG_DEBUG, "movk insn does not match: 0x%08x\n", insn[1]); + return FAILURE; + } + + // nop + for (int i = 0; i < 2; i++) { + if (insn[2+i] != 0xd503201f) { + zend_accel_error(ACCEL_LOG_DEBUG, "nop(%d) insn does not match: 0x%08x\n", i, insn[2+i]); + return FAILURE; + } + } + + /* Extract immediate values */ + + uint64_t movz_imm = (insn[0] & AARCH64_MOVZ_IMM_MASK) >> 5; + uint64_t movz_shift = (((insn[0] & AARCH64_MOVZ_HW_MASK) >> 21) << 4); + uint64_t mozk_imm = (insn[1] & AARCH64_MOVK_IMM_MASK) >> 5; + uint64_t offset = (movz_imm << movz_shift) | mozk_imm; + + if ((uintptr_t)&_tsrm_ls_cache - (uintptr_t)thread_pointer == offset) { + zend_accel_error(ACCEL_LOG_DEBUG, "static tls at offset %" PRIx64 " from thread pointer (inferred from code)\n", offset); + tsrm_ls_cache_tcb_offset = offset; + return SUCCESS; + } + + return FAILURE; +} + +# elif defined(__GNUC__) && defined(__i386__) + +# ifdef __MUSL__ + +# define DTV_OFFSET 4 +# define DTV_INDEX_GAP 0 + +typedef struct _dtv_pointer_t { + uintptr_t val; +} dtv_pointer_t; + +typedef struct TLSDescriptor { + size_t index; + size_t offset; +} TLSDescriptor; + +# elif defined(__FreeBSD__) + +# define DTV_OFFSET 4 +# define DTV_INDEX_GAP 1 + +typedef struct _dtv_pointer_t { + uintptr_t val; +} dtv_pointer_t; + +/* https://github.com/freebsd/freebsd-src/blob/6b94546a7ea2dc593f5765bd5465a8b7bb80c325/libexec/rtld-elf/i386/rtld_machdep.h#L65 */ +typedef struct TLSDescriptor { + unsigned long index; + unsigned long offset; +} TLSDescriptor; + +# else + +# define DTV_OFFSET 4 +# define DTV_INDEX_GAP 0 + +typedef struct _dtv_pointer_t { + uintptr_t val; + uintptr_t _; +} dtv_pointer_t; + +typedef struct TLSDescriptor { + size_t index; + size_t offset; +} TLSDescriptor; + +# endif + +ZEND_ATTRIBUTE_UNUSED static zend_result zend_jit_resolve_tsrm_ls_cache_offsets(void) { +# if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__MUSL__) + size_t ret; + + asm ("leal _tsrm_ls_cache@ntpoff,%0\n" + : "=a" (ret)); + tsrm_ls_cache_tcb_offset = ret; + + return SUCCESS; + +# else + + void *t_addr; + unsigned char *code; + void *thread_pointer; + + __asm__ __volatile__( + /* Load next instruction address */ + "call 1f\n" + ".subsection 1\n" + "1:\n" + "movl (%%esp), %%ebx\n" + "movl %%ebx, %%esi\n" + "ret\n" + ".previous\n" + /* General Dynamic code sequence as expected by linkers */ + "addl $_GLOBAL_OFFSET_TABLE_, %%ebx\n" + "leal _tsrm_ls_cache@TLSGD(,%%ebx,1), %%eax\n" + "call ___tls_get_addr@PLT\n" + /* Load thread pointer address */ + "movl %%gs:0, %%ebx\n" + : "=a" (t_addr), "=S" (code), "=b" (thread_pointer) + ); + + ZEND_ASSERT(t_addr == &_tsrm_ls_cache); + + /* Check if the general dynamic code was relaxed by the linker */ + + // addl any,%ebx + if (code[0] != 0x81 || code[1] != 0xc3) { + uint64_t bytes; + memcpy(&bytes, &code[0], 8); + zend_accel_error(ACCEL_LOG_DEBUG, "addl insn does not match: 0x%16" PRIx64 "\n", bytes); + goto code_changed; + } + + // leal any(,%ebx,1),%eax + if (code[6] != 0x8d || code[7] != 0x04 || code[8] != 0x1d) { + uint64_t bytes; + memcpy(&bytes, &code[6], 8); + zend_accel_error(ACCEL_LOG_DEBUG, "leal insn does not match: 0x%16" PRIx64 "\n", bytes); + goto code_changed; + } + + // call any + if (code[13] != 0xe8) { + uint64_t bytes; + memcpy(&bytes, &code[13], 8); + zend_accel_error(ACCEL_LOG_DEBUG, "call insn does not match: 0x%16" PRIx64 "\n", bytes); + goto code_changed; + } + + /* Code is intact, we can extract immediate values */ + + uint32_t addl_imm = ((uint32_t)code[5] << 24) + | ((uint32_t)code[4] << 16) + | ((uint32_t)code[3] << 8) + | ((uint32_t)code[2]); + uint32_t leal_imm = ((uint32_t)code[12] << 24) + | ((uint32_t)code[11] << 16) + | ((uint32_t)code[10] << 8) + | ((uint32_t)code[9]); + + TLSDescriptor *tlsdesc = (TLSDescriptor*)(leal_imm + addl_imm + (uintptr_t)code); + + /* We've got the TLS descriptor. Double check: */ + +# if ZEND_DEBUG + dtv_pointer_t *dtv = *(dtv_pointer_t**)((uintptr_t)thread_pointer + DTV_OFFSET); + void *addr = (void*) (dtv[tlsdesc->index + DTV_INDEX_GAP].val + tlsdesc->offset); + + ZEND_ASSERT((void*)addr == &_tsrm_ls_cache); +# endif + + zend_accel_error(ACCEL_LOG_DEBUG, "dynamic tls module idx %zu offset %zu (inferred from code)\n", (size_t)tlsdesc->index, (size_t)tlsdesc->offset); + + tsrm_tls_index = tlsdesc->index * 8; + tsrm_tls_offset = tlsdesc->offset; + + return SUCCESS; + +code_changed: + + /* Code was changed by the linker. Check if we recognize the updated code */ + + /* + * 81 c3 98 2d 00 00 addl $0x2d98,%ebx + * 65 a1 00 00 00 00 movl %gs:0x0,%eax + * 81 e8 04 00 00 00 subl $0x4,%eax + */ + + // movl %gs:0x0,%eax + if (memcmp(&code[6], "\x65\xa1\x00\x00\x00\x00", 6) != 0) { + uint64_t bytes; + memcpy(&bytes, &code[6], 8); + zend_accel_error(ACCEL_LOG_DEBUG, "movl insn does not match: 0x%16" PRIx64 "\n", bytes); + return FAILURE; + } + + // subl $any,%eax + if (code[12] != 0x81 || code[13] != 0xe8) { + uint64_t bytes; + memcpy(&bytes, &code[6], 8); + zend_accel_error(ACCEL_LOG_DEBUG, "subl insn does not match: 0x%16" PRIx64 "\n", bytes); + return FAILURE; + } + + /* Extract immediate values */ + + uint32_t offset = -(((uint32_t)code[17] << 24) + | ((uint32_t)code[16] << 16) + | ((uint32_t)code[15] << 8) + | ((uint32_t)code[14])); + + if ((uintptr_t)&_tsrm_ls_cache - (uintptr_t)thread_pointer == offset) { + zend_accel_error(ACCEL_LOG_DEBUG, "static tls at offset %" PRIx32 " from thread pointer (inferred from code)\n", offset); + tsrm_ls_cache_tcb_offset = offset; + return SUCCESS; + } + + zend_accel_error(ACCEL_LOG_DEBUG, "static tls offset does not match: 0x%08" PRIx32 " (expected 0x%08" PRIx32 ")\n", offset, (uint32_t)((uintptr_t)&_tsrm_ls_cache - (uintptr_t)thread_pointer)); + + return FAILURE; + +# endif +} +# elif defined(__GNUC__) && defined(__x86_64__) +# ifdef __MUSL__ + +# define DTV_OFFSET 8 +# define DTV_INDEX_GAP 0 + +typedef struct _dtv_pointer_t { + uintptr_t val; +} dtv_pointer_t; + +typedef struct TLSDescriptor { + size_t index; + size_t offset; +} TLSDescriptor; + +# elif defined(__FreeBSD__) + +# define DTV_OFFSET 4 +# define DTV_INDEX_GAP 1 + +typedef struct _dtv_pointer_t { + uintptr_t val; +} dtv_pointer_t; + +/* https://github.com/freebsd/freebsd-src/blob/6b94546a7ea2dc593f5765bd5465a8b7bb80c325/libexec/rtld-elf/amd64/rtld_machdep.h#L65 */ +typedef struct TLSDescriptor { + unsigned long index; + unsigned long offset; +} TLSDescriptor; + +# else + +# define DTV_OFFSET 8 +# define DTV_INDEX_GAP 0 + +typedef struct _dtv_pointer_t { + uintptr_t val; + uintptr_t _; +} dtv_pointer_t; + +typedef struct TLSDescriptor { + size_t index; + size_t offset; +} TLSDescriptor; + +# endif + +ZEND_ATTRIBUTE_UNUSED static zend_result zend_jit_resolve_tsrm_ls_cache_offsets(void) { +# if defined(__has_attribute) && __has_attribute(tls_model) && !defined(__FreeBSD__) && \ + !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__MUSL__) + size_t ret; + + asm ("movq _tsrm_ls_cache@gottpoff(%%rip),%0" + : "=r" (ret)); + tsrm_ls_cache_tcb_offset = ret; + + return SUCCESS; +# else + + void *addr; + unsigned char *code; + void *thread_pointer; + + __asm__ __volatile__( + /* Load next instruction address */ + "leaq (%%rip), %%rbx\n" + /* General Dynamic code sequence as expected by linkers */ + ".byte 0x66\n" + "leaq _tsrm_ls_cache@tlsgd(%%rip), %%rdi\n" + ".word 0x6666\n" + "rex64\n" + "call __tls_get_addr\n" + /* Load thread pointer address */ + "movq %%fs:0, %%rsi\n" + : "=a" (addr), "=b" (code), "=S" (thread_pointer) + ); + + ZEND_ASSERT(addr == &_tsrm_ls_cache); + + /* Check if the general dynamic code was relaxed by the linker */ + + // data16 leaq any(%rip),%rdi + if (code[0] != 0x66 || code[1] != 0x48 || code[2] != 0x8d || code[3] != 0x3d) { + uint64_t bytes; + memcpy(&bytes, &code[0], 8); + zend_accel_error(ACCEL_LOG_DEBUG, "leaq insn does not match: 0x%016" PRIx64 "\n", bytes); + goto code_changed; + } + + // data16 data16 rex.W call any + if (code[8] != 0x66 || code[9] != 0x66 || code[10] != 0x48 || code[11] != 0xe8) { + uint64_t bytes; + memcpy(&bytes, &code[8], 8); + zend_accel_error(ACCEL_LOG_DEBUG, "call insn does not match: 0x%016" PRIx64 "\n", bytes); + goto code_changed; + } + + /* Code is intact, we can extract immediate values */ + + uintptr_t leaq_imm = (uintptr_t)(int32_t)((uint32_t)code[7] << 24) + | ((uint32_t)code[6] << 16) + | ((uint32_t)code[5] << 8) + | ((uint32_t)code[4]); + + TLSDescriptor *tlsdesc = (TLSDescriptor*)(leaq_imm + (uintptr_t)code + 8 /* leaq */); + + /* We've got the TLS descriptor. Double check: */ + +# if ZEND_DEBUG + dtv_pointer_t *dtv = *(dtv_pointer_t**)((uintptr_t)thread_pointer + DTV_OFFSET); + addr = (void*) (dtv[tlsdesc->index + DTV_INDEX_GAP].val + tlsdesc->offset); + + ZEND_ASSERT((void*)addr == &_tsrm_ls_cache); +# endif + + zend_accel_error(ACCEL_LOG_DEBUG, "dynamic tls module idx %zu offset %zu (infered from code)\n", (size_t)tlsdesc->index, (size_t)tlsdesc->offset); + + return SUCCESS; + +code_changed: + + /* Code was changed by the linker. Check if we recognize the updated code */ + + /* + * 64 48 8b 04 25 00 00 00 00 movq %fs:0x0,%rax + * 48 8d 80 f8 ff ff ff leaq -0x8(%rax),%rax + */ + + // movq %fs:0x0,%rax + if (memcmp(&code[0], "\x64\x48\x8b\x04\x25\x00\x00\x00\x00", 9) != 0) { + uint64_t bytes; + memcpy(&bytes, &code[0], 8); + zend_accel_error(ACCEL_LOG_DEBUG, "movq insn does not match: 0x%016" PRIx64 "\n", bytes); + return FAILURE; + } + + // leaq any(%rax),$rax + if (code[9] != 0x48 || code[10] != 0x8d || code[11] != 0x80) { + uint64_t bytes; + memcpy(&bytes, &code[10], 8); + zend_accel_error(ACCEL_LOG_DEBUG, "leaq insn does not match: 0x%016" PRIx64 "\n", bytes); + return FAILURE; + } + + /* Extract immediate values */ + + uintptr_t offset = (uintptr_t)(int32_t)(((uint32_t)code[15] << 24) + | ((uint32_t)code[14] << 16) + | ((uint32_t)code[13] << 8) + | ((uint32_t)code[12])); + + if ((uintptr_t)&_tsrm_ls_cache - (uintptr_t)thread_pointer == offset) { + tsrm_ls_cache_tcb_offset = offset; + zend_accel_error(ACCEL_LOG_DEBUG, "static tls at offset 0x%" PRIxPTR " from thread pointer (inferred from code)\n", offset); + return SUCCESS; + } + + zend_accel_error(ACCEL_LOG_DEBUG, "static tls offset does not match: 0x%016" PRIxPTR " (expected 0x%016" PRIxPTR ")\n", offset, ((uintptr_t)&_tsrm_ls_cache - (uintptr_t)thread_pointer)); + + return FAILURE; +# endif +} +# endif +#endif /* ZTS */ static void zend_jit_setup(bool reattached) { @@ -3313,40 +3855,16 @@ static void zend_jit_setup(bool reattached) # endif #endif #ifdef ZTS + #if defined(IR_TARGET_AARCH64) tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset(); -# ifdef __FreeBSD__ +# if defined(__FreeBSD__) || defined(__MUSL__) if (tsrm_ls_cache_tcb_offset == 0) { - TLSDescriptor **where; - - __asm__( - "adrp %0, :tlsdesc:_tsrm_ls_cache\n" - "add %0, %0, :tlsdesc_lo12:_tsrm_ls_cache\n" - : "=r" (where)); - /* See https://github.com/ARM-software/abi-aa/blob/2a70c42d62e9c3eb5887fa50b71257f20daca6f9/aaelf64/aaelf64.rst - * section "Relocations for thread-local storage". - * The first entry holds a pointer to the variable's TLS descriptor resolver function and the second entry holds - * a platform-specific offset or pointer. */ - TLSDescriptor *tlsdesc = where[1]; - - tsrm_tls_offset = tlsdesc->offset; - /* Index is offset by 1 on FreeBSD (https://github.com/freebsd/freebsd-src/blob/22ca6db50f4e6bd75a141f57cf953d8de6531a06/lib/libc/gen/tls.c#L88) */ - tsrm_tls_index = (tlsdesc->index + 1) * 8; - } -# elif defined(__MUSL__) - if (tsrm_ls_cache_tcb_offset == 0) { - size_t **where; - - __asm__( - "adrp %0, :tlsdesc:_tsrm_ls_cache\n" - "add %0, %0, :tlsdesc_lo12:_tsrm_ls_cache\n" - : "=r" (where)); - /* See https://github.com/ARM-software/abi-aa/blob/2a70c42d62e9c3eb5887fa50b71257f20daca6f9/aaelf64/aaelf64.rst */ - size_t *tlsdesc = where[1]; - - tsrm_tls_offset = tlsdesc[1]; - tsrm_tls_index = tlsdesc[0] * 8; + if (zend_jit_resolve_tsrm_ls_cache_offsets() == FAILURE) { + tsrm_tls_offset = -1; + tsrm_tls_index = -1; + } } # else ZEND_ASSERT(tsrm_ls_cache_tcb_offset != 0); @@ -3407,69 +3925,13 @@ static void zend_jit_setup(bool reattached) tsrm_tls_offset = ti[2]; tsrm_tls_index = ti[1] * 8; } -# elif defined(__GNUC__) && defined(__x86_64__) +# elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset(); if (tsrm_ls_cache_tcb_offset == 0) { -#if defined(__has_attribute) && __has_attribute(tls_model) && !defined(__FreeBSD__) && \ - !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__MUSL__) - size_t ret; - - asm ("movq _tsrm_ls_cache@gottpoff(%%rip),%0" - : "=r" (ret)); - tsrm_ls_cache_tcb_offset = ret; -#elif defined(__MUSL__) - size_t *ti; - - __asm__( - "leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n" - : "=a" (ti)); - tsrm_tls_offset = ti[1]; - tsrm_tls_index = ti[0] * 8; -#elif defined(__FreeBSD__) - size_t *ti; - - __asm__( - "leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n" - : "=a" (ti)); - tsrm_tls_offset = ti[1]; - /* Index is offset by 1 on FreeBSD (https://github.com/freebsd/freebsd-src/blob/bf56e8b9c8639ac4447d223b83cdc128107cc3cd/libexec/rtld-elf/rtld.c#L5260) */ - tsrm_tls_index = (ti[0] + 1) * 8; -#else - size_t *ti; - - __asm__( - "leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n" - : "=a" (ti)); - tsrm_tls_offset = ti[1]; - tsrm_tls_index = ti[0] * 16; -#endif - } -# elif defined(__GNUC__) && defined(__i386__) - tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset(); - if (tsrm_ls_cache_tcb_offset == 0) { -#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__MUSL__) - size_t ret; - - asm ("leal _tsrm_ls_cache@ntpoff,%0\n" - : "=a" (ret)); - tsrm_ls_cache_tcb_offset = ret; -#else - size_t *ti, _ebx, _ecx, _edx; - - __asm__( - "call 1f\n" - ".subsection 1\n" - "1:\tmovl (%%esp), %%ebx\n\t" - "ret\n" - ".previous\n\t" - "addl $_GLOBAL_OFFSET_TABLE_, %%ebx\n\t" - "leal _tsrm_ls_cache@tlsldm(%%ebx), %0\n\t" - "call ___tls_get_addr@plt\n\t" - "leal _tsrm_ls_cache@tlsldm(%%ebx), %0\n" - : "=a" (ti), "=&b" (_ebx), "=&c" (_ecx), "=&d" (_edx)); - tsrm_tls_offset = ti[1]; - tsrm_tls_index = ti[0] * 8; -#endif + if (zend_jit_resolve_tsrm_ls_cache_offsets() == FAILURE) { + tsrm_tls_index = -1; + tsrm_tls_offset = -1; + } } # endif #endif diff --git a/ext/opcache/tests/gh18417.phpt b/ext/opcache/tests/gh18417.phpt index e7b9790b9e661..f4f430827b3a4 100644 --- a/ext/opcache/tests/gh18417.phpt +++ b/ext/opcache/tests/gh18417.phpt @@ -23,7 +23,6 @@ $proc = proc_open([ PHP_BINARY, "-n", "-d", "extension_dir=$extension_dir", - "-d", "zend_extension=opcache", "-d", "opcache.memory_consumption=$new_memory_consumption", "-d", "opcache.enable=1", "-d", "opcache.enable_cli=1", diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 203a41d93b40a..576c601ee48bb 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -25,6 +25,8 @@ #include "ZendAccelerator.h" #include "zend_API.h" #include "zend_closures.h" +#include "zend_extensions.h" +#include "zend_modules.h" #include "zend_shared_alloc.h" #include "zend_accelerator_blacklist.h" #include "php_ini.h" @@ -55,6 +57,9 @@ )) #define TOKENTOSTR(X) #X +static zend_module_entry *accel_module_loaded = NULL; +zend_module_entry opcache_module_entry; + static zif_handler orig_file_exists = NULL; static zif_handler orig_is_file = NULL; static zif_handler orig_is_readable = NULL; @@ -405,13 +410,19 @@ static ZEND_NAMED_FUNCTION(accel_is_readable) static ZEND_MINIT_FUNCTION(zend_accelerator) { - (void)type; /* keep the compiler happy */ + accel_module_loaded = zend_hash_str_find_ptr_lc(&module_registry, + opcache_module_entry.name, strlen(opcache_module_entry.name)); - REGISTER_INI_ENTRIES(); + start_accel_extension(); return SUCCESS; } +void accel_register_ini_entries(zend_module_entry *module) +{ + zend_register_ini_entries_ex(ini_entries, module->module_number, module->type); +} + void zend_accel_override_file_functions(void) { zend_function *old_function; @@ -442,6 +453,8 @@ static ZEND_MSHUTDOWN_FUNCTION(zend_accelerator) UNREGISTER_INI_ENTRIES(); accel_shutdown(); + accel_module_loaded = false; + return SUCCESS; } @@ -554,7 +567,7 @@ void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS) DISPLAY_INI_ENTRIES(); } -static zend_module_entry accel_module_entry = { +zend_module_entry opcache_module_entry = { STANDARD_MODULE_HEADER, ACCELERATOR_PRODUCT_NAME, ext_functions, @@ -569,9 +582,22 @@ static zend_module_entry accel_module_entry = { STANDARD_MODULE_PROPERTIES_EX }; -int start_accel_module(void) +zend_module_entry *start_accel_module(void) { - return zend_startup_module(&accel_module_entry); + if (accel_module_loaded) { + return accel_module_loaded; + } + + accel_module_loaded = zend_register_internal_module(&opcache_module_entry); + if (!accel_module_loaded) { + return NULL; + } + + if (zend_startup_module_ex(accel_module_loaded) == FAILURE) { + return NULL; + } + + return accel_module_loaded; } /* {{{ Get the scripts which are accelerated by ZendAccelerator */ diff --git a/ext/opcache/zend_accelerator_module.h b/ext/opcache/zend_accelerator_module.h index 656336eeba762..ce0069299f5eb 100644 --- a/ext/opcache/zend_accelerator_module.h +++ b/ext/opcache/zend_accelerator_module.h @@ -22,7 +22,14 @@ #ifndef ZEND_ACCELERATOR_MODULE_H #define ZEND_ACCELERATOR_MODULE_H -int start_accel_module(void); +#include "Zend/zend_modules.h" + +#define phpext_opcache_ptr &opcache_module_entry +extern zend_module_entry opcache_module_entry; + +zend_module_entry *start_accel_module(void); + +void accel_register_ini_entries(zend_module_entry *module); void zend_accel_override_file_functions(void); diff --git a/php.ini-development b/php.ini-development index 162fb3f25c19c..8c58a86eee4b6 100644 --- a/php.ini-development +++ b/php.ini-development @@ -959,8 +959,6 @@ default_socket_timeout = 60 ;extension=xsl ;extension=zip -;zend_extension=opcache - ;;;;;;;;;;;;;;;;;;; ; Module Settings ; ;;;;;;;;;;;;;;;;;;; diff --git a/php.ini-production b/php.ini-production index 042d246943d81..4798a6a563938 100644 --- a/php.ini-production +++ b/php.ini-production @@ -961,8 +961,6 @@ default_socket_timeout = 60 ;extension=xsl ;extension=zip -;zend_extension=opcache - ;;;;;;;;;;;;;;;;;;; ; Module Settings ; ;;;;;;;;;;;;;;;;;;; diff --git a/sapi/cgi/tests/001.phpt b/sapi/cgi/tests/001.phpt index c4b539aa83bc6..0527993e8c164 100644 --- a/sapi/cgi/tests/001.phpt +++ b/sapi/cgi/tests/001.phpt @@ -17,6 +17,5 @@ echo "Done\n"; --EXPECTF-- string(%d) "PHP %s (cgi%s (built: %s Copyright (c) The PHP Group -%AZend Engine v%s, Copyright (c) Zend Technologies -" +%AZend Engine v%s, Copyright (c) Zend Technologies%A" Done diff --git a/sapi/cli/tests/001.phpt b/sapi/cli/tests/001.phpt index e13ab8def300f..fc1ae71fa134a 100644 --- a/sapi/cli/tests/001.phpt +++ b/sapi/cli/tests/001.phpt @@ -14,6 +14,5 @@ echo "Done\n"; --EXPECTF-- string(%d) "PHP %s (cli) (built: %s)%s Copyright (c) The PHP Group -%AZend Engine v%s, Copyright (c) Zend Technologies -" +%AZend Engine v%s, Copyright (c) Zend Technologies%A" Done diff --git a/sapi/cli/tests/bug80092.phpt b/sapi/cli/tests/bug80092.phpt index 350b46b3f57a6..27c02f8b3e1d7 100644 --- a/sapi/cli/tests/bug80092.phpt +++ b/sapi/cli/tests/bug80092.phpt @@ -17,7 +17,6 @@ if (!file_exists($extDir . '/opcache.so')) { $cmd = [ PHP_BINARY, '-dextension_dir=' . ini_get('extension_dir'), - '-dzend_extension=opcache.so', '-dopcache.enable=1', '-dopcache.enable_cli=1', '-dopcache.preload=' . __DIR__ . '/preload.inc', diff --git a/sapi/fpm/tests/main-version.phpt b/sapi/fpm/tests/main-version.phpt index 5ae83562d070b..d03372846ede0 100644 --- a/sapi/fpm/tests/main-version.phpt +++ b/sapi/fpm/tests/main-version.phpt @@ -16,6 +16,5 @@ echo "Done\n"; --EXPECTF-- string(%d) "PHP %s (fpm%s (built: %s Copyright (c) The PHP Group -%AZend Engine v%s, Copyright (c) Zend Technologies -" +%AZend Engine v%s, Copyright (c) Zend Technologies%A" Done diff --git a/sapi/fuzzer/README.md b/sapi/fuzzer/README.md index b4bb2bbe4573f..42e1eb7fa3040 100644 --- a/sapi/fuzzer/README.md +++ b/sapi/fuzzer/README.md @@ -31,8 +31,8 @@ When running `make` it creates these binaries in `sapi/fuzzer/`: * `php-fuzz-mbstring`: Fuzzing `mb_convert_encoding()` (requires `--enable-mbstring`) * `php-fuzz-mbregex`: Fuzzing `mb_ereg[i]()` (requires --enable-mbstring) * `php-fuzz-execute`: Fuzzing the executor -* `php-fuzz-function-jit`: Fuzzing the function JIT (requires --enable-opcache) -* `php-fuzz-tracing-jit`: Fuzzing the tracing JIT (requires --enable-opcache) +* `php-fuzz-function-jit`: Fuzzing the function JIT +* `php-fuzz-tracing-jit`: Fuzzing the tracing JIT Some fuzzers have a seed corpus in `sapi/fuzzer/corpus`. You can use it as follows: