Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SAPI module folder to DLL search path #16718

Open
hoangngx opened this issue Nov 7, 2024 · 6 comments · May be fixed by #16958
Open

Add SAPI module folder to DLL search path #16718

hoangngx opened this issue Nov 7, 2024 · 6 comments · May be fixed by #16958

Comments

@hoangngx
Copy link

hoangngx commented Nov 7, 2024

Description

I added "extension=amqp" in php.ini file, as well as the php_amqp.dll file in "C:\xampp\php\ext", and 2 additional files
rabbitmq.4.dll and rabbitmq.4.pdb in "C:\xampp\php", but the following error appears:

PHP Warning: PHP Startup: Unable to load dynamic library 'amqp' (tried: C:\xampp\php\ext\amqp (The specified module could not be found), C:\xampp\php\ext\php_amqp.dll (The specified module could not be found)) in Unknown on line 0

PHP Version

PHP 8.2.12

Operating System

Windows 10

@cmb69
Copy link
Member

cmb69 commented Nov 7, 2024

PHP extension DLLs (php_amqp.dll) are looked up in the extension_dir; their dependencies (rabbitmq.4.dll) are looked up in the PATH. Use phpinfo() to find out which folders are in the PATH and put rabbitmq.4.dll in one of these folders (C:\xampp\bin might be a good option).

@salem-zitoun
Copy link

J'ai le meme problème

@cmb69
Copy link
Member

cmb69 commented Nov 9, 2024

Well, that is a pretty common problem. For many SAPIs (CLI, CGI), it works fine if you put dependencies of extensions in the folder where php.exe/php-cgi.exe resides. That does usually not work when PHP is run as Webserver module. That might also be relevant for PIE (cc @asgrim).

We could tell the system to also look in the folder where the Webserver module resides.

quick and dirty patch for apache2handler
 sapi/apache2handler/sapi_apache2.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/sapi/apache2handler/sapi_apache2.c b/sapi/apache2handler/sapi_apache2.c
index 1d85a92ebf..25f94a64c2 100644
--- a/sapi/apache2handler/sapi_apache2.c
+++ b/sapi/apache2handler/sapi_apache2.c
@@ -383,6 +383,13 @@ extern zend_module_entry php_apache_module;
 
 static int php_apache2_startup(sapi_module_struct *sapi_module)
 {
+	HMODULE mod;
+	wchar_t filename[1024];
+	GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)php_apache2_startup, &mod);
+	GetModuleFileNameW(mod, filename, 1024);
+	wchar_t *slash = wcsrchr(filename, L'\\');
+	slash[0] = L'\0';
+	SetDllDirectoryW(filename);
 	return php_module_startup(sapi_module, &php_apache_module);
 }

We need to thoroughly consider BC and security implications, though.

PS: should also pass GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT to GetModuleHandleEx(). And for reference: https://learn.microsoft.com/en-us/windows/win32/api/Winbase/nf-winbase-setdlldirectorya

@cmb69 cmb69 changed the title PHP Startup: Unable to load dynamic library 'amqp' although added php_amqp.dll in "C:\xampp\php\ext\" Lookup dependency DLLs in the folder where the SAPI module resides Nov 9, 2024
@asgrim
Copy link
Contributor

asgrim commented Nov 11, 2024

Well, that is a pretty common problem. For many SAPIs (CLI, CGI), it works fine if you put dependencies of extensions in the folder where php.exe/php-cgi.exe resides. That does usually not work when PHP is run as Webserver module. That might also be relevant for PIE (cc @asgrim).

thanks for the ping, I believe PIE does this correctly already:

The pre-built ZIP should contain at minimum a DLL named in the same way as the ZIP itself, for example php_{extension-name}-{tag}-{php-maj/min}-{ts|nts}-{compiler}-{arch}.dll. The .dll will be moved into the PHP extensions path, and renamed, e.g. to C:\path\to\php\ext\php_{extension-name}.dll. The ZIP file may include additional resources, such as:

  • php_{extension-name}.pdb - this will be moved alongside the C:\path\to\php\ext\php_{extension-name}.dll
  • *.dll - any other .dll would be moved alongside C:\path\to\php\php.exe
  • Any other file, which would be moved into C:\path\to\php\extras\{extension-name}\.

@cmb69
Copy link
Member

cmb69 commented Nov 11, 2024

Thanks @asgrim; the relevant item:

*.dll - any other .dll would be moved alongside C:\path\to\php\php.exe

This is exactly how it should be done (as good as it gets), but that will (usually) not work with Webserver modules (like apache2handler). Amongst other locations, Windows searches for DLLs where the .exe which needs them is placed. So to make that work for Apache, you either put httpd.exe and php.exe in the same folder (total mess), or you put the folder of php.exe in the PATH (only necessary if you plan to call php.exe via exec() and friends). The third option is to put these DLLs somewhere in the PATH; many just put stuff into C:\Windows\System32, but I consider that bad practice.

Anyhow, I'll submit a PR for this issue soon, and shall also actually try out PIE. :)

@cmb69
Copy link
Member

cmb69 commented Nov 14, 2024

See also https://news-web.php.net/php.internals.win/1237.

PS: that message mentions AddDllDirectory() what might be a more suitable alternative to SetDllDirectory. Should also have a look at SetDefaultDllDirectories().

cmb69 added a commit to cmb69/php-src that referenced this issue Nov 27, 2024
On Windows, it is customary to put dependency DLLs in the main PHP
folder, i.e. right besides php.exe.  If the SAPI is implemented as
.exe, dependency lookup works fine, since DLLs are always searched in
the folder of the .exe.  However, for Webserver modules, this usually
does not work, since the Webserver's executable likely resides in a
different folder.  While there are obviously solutions to this, the
Windows error message if a module can't be loaded are confusing, so
users and even popular AMP distributions often choose to put the DLLs
in the folder of the Webserver (e.g. right besides httpd.exe).  That
can easily cause more confusion later, when users update to a newer PHP
version, which requires more recent dependencies.

Thus, to simplify setup and to bring the behavior more in line with CLI
and CGI SAPIs, we add the SAPI module folder to the DLL search path.
We keep that simple, and do not cater to long paths, and silently don't
change the DLL search order in case of unexpected failures.

Since we are using `SetDllDirectory()` for all SAPIs, that effectively
also prevents DLLs being looked up in the current folder, what might
break some scripts, but can also avoid confusion regarding ZTS builds,
where the current directory of the process is not necessarily what is
reported by `getcwd()`.  It is also a small security measure, even
stronger than safe DLL search mode[1].

Note that this certainly does not solve all problems related to DLL
lookup, e.g. that the application folder is still searched first, and
that already loaded DLLs will not be searched and loaded again.  But it
appears to be a sensible improvement, and hopefully prevents others to
put DLLs in folders where they don't belong, in the future.

[1] <https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order>
cmb69 added a commit to cmb69/php-src that referenced this issue Nov 27, 2024
On Windows, it is customary to put dependency DLLs in the main PHP
folder, i.e. right besides php.exe.  If the SAPI is implemented as
.exe, dependency lookup works fine, since DLLs are always searched in
the folder of the .exe.  However, for Webserver modules, this usually
does not work, since the Webserver's executable likely resides in a
different folder.  While there are obviously solutions to this, the
Windows error messages if a module can't be loaded are confusing, so
users and even popular AMP distributions often choose to put the DLLs
in the folder of the Webserver (e.g. right besides httpd.exe).  That
can easily cause more confusion later, when users update to a newer PHP
version, which requires more recent dependencies.

Thus, to simplify setup and to bring the behavior more in line with CLI
and CGI SAPIs, we add the SAPI module folder to the DLL search path.
We keep that simple, and do not cater to long paths, and silently don't
change the DLL search order in case of unexpected failures.

Since we are using `SetDllDirectory()` for all SAPIs, that effectively
also prevents DLLs being looked up in the current folder, what might
break some scripts, but can also avoid confusion regarding ZTS builds,
where the current directory of the process is not necessarily what is
reported by `getcwd()`.  It is also a small security measure, even
stronger than safe DLL search mode[1].

Note that this certainly does not solve all problems related to DLL
lookup, e.g. that the application folder is still searched first, and
that already loaded DLLs will not be searched and loaded again.  But it
appears to be a sensible improvement, and hopefully prevents others to
put DLLs in folders where they don't belong, in the future.

[1] <https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order>
@cmb69 cmb69 changed the title Lookup dependency DLLs in the folder where the SAPI module resides Add SAPI module folder to DLL search path Nov 27, 2024
@cmb69 cmb69 linked a pull request Nov 27, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants