From 9eb320258a1f494727bd2d21d068012880fe2a96 Mon Sep 17 00:00:00 2001 From: Luther Monson Date: Wed, 27 May 2026 23:11:29 -0700 Subject: [PATCH] Add --enable-embed=static support on Windows PHP's Unix build already documents and accepts --enable-embed=static (see sapi/embed/config.m4) for building phpembed as a static library with no DLL at runtime. The Windows config.w32 ignored the static value, leaving phpembed.lib as a thin wrapper that still depended on php.dll at runtime. When --enable-embed=static is passed on Windows: * win32/build/confutils.js generates a Makefile rule for phpembed .lib that links in PHP core (PHP_GLOBAL_OBJS), statically built extensions (STATIC_EXT_OBJS + STATIC_EXT_LIBS), the embed SAPI objects, and ASM_OBJS in place of the import lib (BUILD_DIR/PHPLIB). The resulting phpembed.lib is a self-contained static library with no runtime DLL dependency. * sapi/embed/config.w32 adds /D PHP_EXPORTS /D LIBZEND_EXPORTS /D SAPI_EXPORTS /D TSRM_EXPORTS to CFLAGS_EMBED so php_embed.c references PHP/Zend/SAPI/TSRM symbols directly instead of through __declspec(dllimport) thunks (which would produce LNK2019 with no DLL to import from), and defines PHP_EMBED_STATIC as a gate. * sapi/embed/php_embed.c skips its ZEND_TSRMLS_CACHE_DEFINE() when PHP_EMBED_STATIC is set. In static mode zend.c is in the same link unit and already defines _tsrm_ls_cache; the duplicate from php_embed.c produced LNK4006 and a corrupt binary. --- NEWS | 8 ++++++++ sapi/embed/config.w32 | 10 ++++++++++ sapi/embed/php_embed.c | 2 +- win32/build/confutils.js | 14 +++++++++++--- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index ae3cae760320..4a79fcc1d584 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,14 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.5.8 +- Embed: + . Added --enable-embed=static support on Windows: phpembed.lib is now + produced as a self-contained static library (no php.dll dependency) + by linking PHP core, statically built extensions, and the embed SAPI + into a single .lib, with the corresponding source-side adjustments + (PHP/Zend/SAPI/TSRM export defines and TSRMLS cache deduplication). + (Luther Monson) + - GD: . Fixed bug GH-22121 (Double free in gdImageSetStyle() after overflow-triggered early return). (iliaal) diff --git a/sapi/embed/config.w32 b/sapi/embed/config.w32 index 394982126cad..7502a8fc3aef 100644 --- a/sapi/embed/config.w32 +++ b/sapi/embed/config.w32 @@ -6,5 +6,15 @@ var PHP_EMBED_PGO = false; if (PHP_EMBED != "no") { SAPI('embed', 'php_embed.c', 'php' + PHP_VERSION + 'embed.lib', '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); + if (PHP_EMBED == "static") { + // Static embed mode: phpembed.lib is intended to contain PHP + // core, extensions and the embed SAPI in a single static library + // (no php.dll at runtime). php_embed.c must reach PHP/Zend/ + // SAPI/TSRM symbols directly rather than through dllimport thunks. + ADD_FLAG('CFLAGS_EMBED', + '/D PHP_EMBED_STATIC' + + ' /D PHP_EXPORTS /D LIBZEND_EXPORTS' + + ' /D SAPI_EXPORTS /D TSRM_EXPORTS'); + } PHP_INSTALL_HEADERS("sapi/embed", "php_embed.h"); } diff --git a/sapi/embed/php_embed.c b/sapi/embed/php_embed.c index 06cd1fb0763b..f7e3686cd73c 100644 --- a/sapi/embed/php_embed.c +++ b/sapi/embed/php_embed.c @@ -30,7 +30,7 @@ static const char HARDCODED_INI[] = "max_execution_time=0\n" "max_input_time=-1\n\0"; -#if defined(PHP_WIN32) && defined(ZTS) +#if defined(PHP_WIN32) && defined(ZTS) && !defined(PHP_EMBED_STATIC) ZEND_TSRMLS_CACHE_DEFINE() #endif diff --git a/win32/build/confutils.js b/win32/build/confutils.js index cea1368b4de3..3d5dff4e36fe 100644 --- a/win32/build/confutils.js +++ b/win32/build/confutils.js @@ -1197,6 +1197,14 @@ function SAPI(sapiname, file_list, makefiletarget, cflags, obj_dir, duplicate_so var ld; var manifest; + // In --enable-embed=static, phpembed.lib must contain PHP core, all + // statically built extensions, and the embed SAPI itself - no runtime + // dependency on php.dll. Substitute the PHP object groups for the + // import lib in both the dependency line and the link command. + var is_static_embed = (sapiname == "embed" && PHP_EMBED == "static"); + var dep_phplib_deps = is_static_embed ? "$(PHP_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS)" : "$(BUILD_DIR)\\$(PHPLIB)"; + var link_phplib_args = is_static_embed ? "$(PHP_GLOBAL_OBJS_RESP) $(STATIC_EXT_OBJS_RESP) $(ASM_OBJS) $(STATIC_EXT_LIBS)" : "$(BUILD_DIR)\\$(PHPLIB)"; + if (typeof(obj_dir) == "undefined") { sapiname_for_printing = configure_module_dirname; } else { @@ -1228,7 +1236,7 @@ function SAPI(sapiname, file_list, makefiletarget, cflags, obj_dir, duplicate_so if (MODE_PHPIZE) { MFO.WriteLine("$(BUILD_DIR)\\" + makefiletarget + ": $(DEPS_" + SAPI + ") $(" + SAPI + "_GLOBAL_OBJS) $(PHPLIB) $(BUILD_DIR)\\" + resname + " $(BUILD_DIR)\\" + manifest_name); } else { - MFO.WriteLine("$(BUILD_DIR)\\" + makefiletarget + ": $(DEPS_" + SAPI + ") $(" + SAPI + "_GLOBAL_OBJS) $(BUILD_DIR)\\$(PHPLIB) $(BUILD_DIR)\\" + resname + " $(BUILD_DIR)\\" + manifest_name); + MFO.WriteLine("$(BUILD_DIR)\\" + makefiletarget + ": $(DEPS_" + SAPI + ") $(" + SAPI + "_GLOBAL_OBJS) " + dep_phplib_deps + " $(BUILD_DIR)\\" + resname + " $(BUILD_DIR)\\" + manifest_name); } var is_lib = makefiletarget.match(new RegExp("\\.lib$")); @@ -1276,10 +1284,10 @@ function SAPI(sapiname, file_list, makefiletarget, cflags, obj_dir, duplicate_so } } else { if (ld) { - MFO.WriteLine("\t" + ld + " /nologo /out:$(BUILD_DIR)\\" + makefiletarget + " " + ldflags + " $(" + SAPI + "_GLOBAL_OBJS_RESP) $(BUILD_DIR)\\$(PHPLIB) $(ARFLAGS_" + SAPI + ") $(LIBS_" + SAPI + ") $(BUILD_DIR)\\" + resname); + MFO.WriteLine("\t" + ld + " /nologo /out:$(BUILD_DIR)\\" + makefiletarget + " " + ldflags + " $(" + SAPI + "_GLOBAL_OBJS_RESP) " + link_phplib_args + " $(ARFLAGS_" + SAPI + ") $(LIBS_" + SAPI + ") $(BUILD_DIR)\\" + resname); } else { ld = CMD_MOD1 + '"$(LINK)"'; - MFO.WriteLine("\t" + ld + " /nologo " + " $(" + SAPI + "_GLOBAL_OBJS_RESP) $(BUILD_DIR)\\$(PHPLIB) $(LIBS_" + SAPI + ") $(BUILD_DIR)\\" + resname + " /out:$(BUILD_DIR)\\" + makefiletarget + " " + ldflags + " $(LDFLAGS_" + SAPI + ")"); + MFO.WriteLine("\t" + ld + " /nologo " + " $(" + SAPI + "_GLOBAL_OBJS_RESP) " + link_phplib_args + " $(LIBS_" + SAPI + ") $(BUILD_DIR)\\" + resname + " /out:$(BUILD_DIR)\\" + makefiletarget + " " + ldflags + " $(LDFLAGS_" + SAPI + ")"); } }