From 0949f49326e9511feb76bab3a271d25ac7bd84d3 Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 13 Oct 2026 20:02:49 +0200 Subject: [PATCH 1/4] fix GH-24262: default apache build to global-dynamic TLS, use --with-tsrm-tls-model=initial-exec to explicitly override it --- TSRM/TSRM.c | 5 ++++- TSRM/TSRM.h | 9 +++++++-- UPGRADING.INTERNALS | 4 ++++ configure.ac | 35 +++++++++++++++++++++++++++++++++++ sapi/apache2handler/config.m4 | 6 ++++++ 5 files changed, 56 insertions(+), 3 deletions(-) diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index a9f6ed5c59bb..476e8d0a18e8 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -855,7 +855,10 @@ TSRM_API void *tsrm_get_ls_cache(void) /* Returns offset of tsrm_ls_cache slot from Thread Control Block address */ TSRM_API size_t tsrm_get_ls_cache_tcb_offset(void) {/*{{{*/ -#if defined(__APPLE__) && defined(__x86_64__) +#if defined(TSRM_TLS_MODEL_GLOBAL_DYNAMIC) + /* No constant TCB offset under global-dynamic, can't use fast path */ + return 0; +#elif defined(__APPLE__) && defined(__x86_64__) // TODO: Implement support for fast JIT ZTS code ??? return 0; #elif defined(__x86_64__) && defined(__GNUC__) && !defined(__FreeBSD__) && \ diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h index 6a72dd3c02f3..72d0ba9897d9 100644 --- a/TSRM/TSRM.h +++ b/TSRM/TSRM.h @@ -167,8 +167,13 @@ TSRM_API bool tsrm_is_managed_thread(void); # define TSRM_TLS_MODEL_ATTR # define TSRM_TLS_MODEL_DEFAULT #elif defined(__PIC__) && !defined(__PIE__) -# define TSRM_TLS_MODEL_ATTR __attribute__((tls_model("initial-exec"))) -# define TSRM_TLS_MODEL_INITIAL_EXEC +# if defined(TSRM_TLS_MODEL_USE_GLOBAL_DYNAMIC) +# define TSRM_TLS_MODEL_ATTR __attribute__((tls_model("global-dynamic"))) +# define TSRM_TLS_MODEL_GLOBAL_DYNAMIC +# else +# define TSRM_TLS_MODEL_ATTR __attribute__((tls_model("initial-exec"))) +# define TSRM_TLS_MODEL_INITIAL_EXEC +# endif #else # define TSRM_TLS_MODEL_ATTR __attribute__((tls_model("local-exec"))) # define TSRM_TLS_MODEL_LOCAL_EXEC diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 255d4c2ca893..2892002a95f9 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -267,6 +267,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES HAVE_STRUCT_STAT_ST_BLOCKS). . Added a new configure option --disable-apache2-conf to prevent apxs from editing httpd.conf during installation. + . Added --with-tsrm-tls-model (initial-exec, global-dynamic, auto) to pick the + TLS model; auto uses global-dynamic for dlopen()ed SAPIs (the apache module). + For forcing initial-exec there needs a larger static TLS surplus: set + GLIBC_TUNABLES=glibc.rtld.optional_static_tls=8192 for the host process. - Windows build system changes: . Function SETUP_OPENSSL() doesn't accept 6th argument anymore and doesn't diff --git a/configure.ac b/configure.ac index b1ff51bcc2d1..1de69237ed19 100644 --- a/configure.ac +++ b/configure.ac @@ -284,6 +284,9 @@ SAPI_SHARED=libs/$SAPI_LIBNAME_SHARED SAPI_STATIC=libs/$SAPI_LIBNAME_STATIC SAPI_LIBTOOL=libphp.la +dnl Set by SAPIs whose host program dlopen()s libphp without linking against it. +php_tsrm_dlopened_sapi=no + PHP_CONFIGURE_PART([Configuring SAPI modules]) esyscmd(./build/config-stubs sapi) @@ -887,6 +890,38 @@ AS_VAR_IF([PHP_THREAD_SAFETY], [yes], [ AC_MSG_RESULT([yes]) ]) +PHP_ARG_WITH([tsrm-tls-model],, + [AS_HELP_STRING([[--with-tsrm-tls-model=MODEL]], + [TLS model for the ZTS globals cache: initial-exec, global-dynamic, or + auto for global-dynamic on dlopen()ed SAPIs and initial-exec elsewhere + [auto]])], + [auto], + [no]) + +AS_VAR_IF([PHP_THREAD_SAFETY], [yes], [ + AS_CASE([$PHP_TSRM_TLS_MODEL], + [auto], [ + AS_VAR_IF([php_tsrm_dlopened_sapi], [yes], + [php_tsrm_tls_model=global-dynamic], + [php_tsrm_tls_model=initial-exec]) + ], + [initial-exec|global-dynamic], [php_tsrm_tls_model=$PHP_TSRM_TLS_MODEL], + [AC_MSG_ERROR(m4_text_wrap([ + Unknown TLS model '$PHP_TSRM_TLS_MODEL' given to --with-tsrm-tls-model. + Valid values are: auto, initial-exec, global-dynamic. + ]))]) + + AC_MSG_CHECKING([which TLS model to use for the ZTS globals cache]) + AC_MSG_RESULT([$php_tsrm_tls_model]) + + dnl In php_config.h, not CFLAGS, so phpize builds use the same model. + AS_VAR_IF([php_tsrm_tls_model], [global-dynamic], [ + AC_DEFINE([TSRM_TLS_MODEL_USE_GLOBAL_DYNAMIC], [1], + [Define to 1 to use the global-dynamic TLS model for the ZTS globals + cache.]) + ]) +]) + PHP_ARG_ENABLE([rtld-now], [whether to dlopen extensions with RTLD_NOW instead of RTLD_LAZY], [AS_HELP_STRING([--enable-rtld-now], diff --git a/sapi/apache2handler/config.m4 b/sapi/apache2handler/config.m4 index 3001a4d61d9a..e6c4490076e6 100644 --- a/sapi/apache2handler/config.m4 +++ b/sapi/apache2handler/config.m4 @@ -94,6 +94,12 @@ if test "$PHP_APXS2" != "no"; then LIBPHP_CFLAGS="-shared" PHP_SUBST([LIBPHP_CFLAGS]) + dnl httpd dlopen's libphp.so without linking against it, so we must + dnl use global-dynamic by default (_tsrm_ls_cache overflows the default + dnl static TLS surplus). Force --with-tsrm-tls-model=initial-exec to + dnl override this behaviour, see UPGRADING.INTERNALS + php_tsrm_dlopened_sapi=yes + php_sapi_apache2handler_type=shared AS_CASE([$host_alias], [*aix*], [ From 3cb631a8e862c8fe326c53a717d7a5d272e97a9c Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 15 Oct 2026 14:54:30 +0200 Subject: [PATCH 2/4] advise to use -ftls-model=... instead, no extra configure option --- TSRM/TSRM.c | 4 ++-- TSRM/TSRM.h | 6 +++--- UPGRADING.INTERNALS | 8 ++++---- configure.ac | 33 +++------------------------------ sapi/apache2handler/config.m4 | 5 ++--- 5 files changed, 14 insertions(+), 42 deletions(-) diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index 476e8d0a18e8..39b1db9b712d 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -855,8 +855,8 @@ TSRM_API void *tsrm_get_ls_cache(void) /* Returns offset of tsrm_ls_cache slot from Thread Control Block address */ TSRM_API size_t tsrm_get_ls_cache_tcb_offset(void) {/*{{{*/ -#if defined(TSRM_TLS_MODEL_GLOBAL_DYNAMIC) - /* No constant TCB offset under global-dynamic, can't use fast path */ +#if defined(TSRM_TLS_MODEL_USE_DEFAULT) && defined(__PIC__) && !defined(__PIE__) + /* The compiler may use dynamic TLS. Do not force a static TLS relocation. */ return 0; #elif defined(__APPLE__) && defined(__x86_64__) // TODO: Implement support for fast JIT ZTS code ??? diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h index 72d0ba9897d9..5fe5fb1cf354 100644 --- a/TSRM/TSRM.h +++ b/TSRM/TSRM.h @@ -167,9 +167,9 @@ TSRM_API bool tsrm_is_managed_thread(void); # define TSRM_TLS_MODEL_ATTR # define TSRM_TLS_MODEL_DEFAULT #elif defined(__PIC__) && !defined(__PIE__) -# if defined(TSRM_TLS_MODEL_USE_GLOBAL_DYNAMIC) -# define TSRM_TLS_MODEL_ATTR __attribute__((tls_model("global-dynamic"))) -# define TSRM_TLS_MODEL_GLOBAL_DYNAMIC +# if defined(TSRM_TLS_MODEL_USE_DEFAULT) +# define TSRM_TLS_MODEL_ATTR +# define TSRM_TLS_MODEL_DEFAULT # else # define TSRM_TLS_MODEL_ATTR __attribute__((tls_model("initial-exec"))) # define TSRM_TLS_MODEL_INITIAL_EXEC diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 2892002a95f9..0b1e29379413 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -267,10 +267,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES HAVE_STRUCT_STAT_ST_BLOCKS). . Added a new configure option --disable-apache2-conf to prevent apxs from editing httpd.conf during installation. - . Added --with-tsrm-tls-model (initial-exec, global-dynamic, auto) to pick the - TLS model; auto uses global-dynamic for dlopen()ed SAPIs (the apache module). - For forcing initial-exec there needs a larger static TLS surplus: set - GLIBC_TUNABLES=glibc.rtld.optional_static_tls=8192 for the host process. + . Apache ZTS builds now use the compiler's default TLS model. To opt into + initial-exec, add -ftls-model=initial-exec to CFLAGS (and CXXFLAGS for C++ + code). This may require increasing the host processes static TLS surplus, + e.g. by setting GLIBC_TUNABLES=glibc.rtld.optional_static_tls=8192. - Windows build system changes: . Function SETUP_OPENSSL() doesn't accept 6th argument anymore and doesn't diff --git a/configure.ac b/configure.ac index 1de69237ed19..8ce5c4451319 100644 --- a/configure.ac +++ b/configure.ac @@ -888,37 +888,10 @@ AS_VAR_IF([PHP_THREAD_SAFETY], [yes], [ AC_MSG_CHECKING([for POSIX threads]) AC_MSG_RESULT([yes]) -]) - -PHP_ARG_WITH([tsrm-tls-model],, - [AS_HELP_STRING([[--with-tsrm-tls-model=MODEL]], - [TLS model for the ZTS globals cache: initial-exec, global-dynamic, or - auto for global-dynamic on dlopen()ed SAPIs and initial-exec elsewhere - [auto]])], - [auto], - [no]) - -AS_VAR_IF([PHP_THREAD_SAFETY], [yes], [ - AS_CASE([$PHP_TSRM_TLS_MODEL], - [auto], [ - AS_VAR_IF([php_tsrm_dlopened_sapi], [yes], - [php_tsrm_tls_model=global-dynamic], - [php_tsrm_tls_model=initial-exec]) - ], - [initial-exec|global-dynamic], [php_tsrm_tls_model=$PHP_TSRM_TLS_MODEL], - [AC_MSG_ERROR(m4_text_wrap([ - Unknown TLS model '$PHP_TSRM_TLS_MODEL' given to --with-tsrm-tls-model. - Valid values are: auto, initial-exec, global-dynamic. - ]))]) - - AC_MSG_CHECKING([which TLS model to use for the ZTS globals cache]) - AC_MSG_RESULT([$php_tsrm_tls_model]) - dnl In php_config.h, not CFLAGS, so phpize builds use the same model. - AS_VAR_IF([php_tsrm_tls_model], [global-dynamic], [ - AC_DEFINE([TSRM_TLS_MODEL_USE_GLOBAL_DYNAMIC], [1], - [Define to 1 to use the global-dynamic TLS model for the ZTS globals - cache.]) + AS_VAR_IF([php_tsrm_dlopened_sapi], [yes], [ + AC_DEFINE([TSRM_TLS_MODEL_USE_DEFAULT], [1], + [Define to 1 to use the compiler's default TLS model.]) ]) ]) diff --git a/sapi/apache2handler/config.m4 b/sapi/apache2handler/config.m4 index e6c4490076e6..b000acbef198 100644 --- a/sapi/apache2handler/config.m4 +++ b/sapi/apache2handler/config.m4 @@ -95,9 +95,8 @@ if test "$PHP_APXS2" != "no"; then PHP_SUBST([LIBPHP_CFLAGS]) dnl httpd dlopen's libphp.so without linking against it, so we must - dnl use global-dynamic by default (_tsrm_ls_cache overflows the default - dnl static TLS surplus). Force --with-tsrm-tls-model=initial-exec to - dnl override this behaviour, see UPGRADING.INTERNALS + dnl use global-dynamic by default. Use -ftls-model=initial-exec to + dnl override the compiler choice, see UPGRADING.INTERNALS php_tsrm_dlopened_sapi=yes php_sapi_apache2handler_type=shared From f7d3a739859963506289ebad5320806bb4e515e5 Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 15 Oct 2026 20:42:48 +0200 Subject: [PATCH 3/4] fast path for initial-exec --- configure.ac | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 8ce5c4451319..354bb3692977 100644 --- a/configure.ac +++ b/configure.ac @@ -890,8 +890,17 @@ AS_VAR_IF([PHP_THREAD_SAFETY], [yes], [ AC_MSG_RESULT([yes]) AS_VAR_IF([php_tsrm_dlopened_sapi], [yes], [ - AC_DEFINE([TSRM_TLS_MODEL_USE_DEFAULT], [1], - [Define to 1 to use the compiler's default TLS model.]) + php_tsrm_tls_model= + for flag in $CFLAGS; do + AS_CASE([$flag], [-ftls-model=*], [php_tsrm_tls_model=$flag]) + done + + dnl Define TSRM_TLS_MODEL_USE_DEFAULT to avoid forcing static TLS in dlopened SAPIs. + dnl Omit it for explicit initial-exec to enable the TCB offset fast path. + AS_IF([test "$php_tsrm_tls_model" != "-ftls-model=initial-exec"], [ + AC_DEFINE([TSRM_TLS_MODEL_USE_DEFAULT], [1], + [Define to 1 to use the compiler's default TLS model.]) + ]) ]) ]) From 8f6a4bc8aefbabb18cc1b4f54ddbb3510c63575e Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 15 Oct 2026 21:45:45 +0200 Subject: [PATCH 4/4] add extra_cflags to scan --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 354bb3692977..a1d5664b5058 100644 --- a/configure.ac +++ b/configure.ac @@ -891,7 +891,7 @@ AS_VAR_IF([PHP_THREAD_SAFETY], [yes], [ AS_VAR_IF([php_tsrm_dlopened_sapi], [yes], [ php_tsrm_tls_model= - for flag in $CFLAGS; do + for flag in $CFLAGS $EXTRA_CFLAGS; do AS_CASE([$flag], [-ftls-model=*], [php_tsrm_tls_model=$flag]) done