Skip to content

[RFC][PoC] Allow opcache to be built statically #18660

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

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

arnaud-lb
Copy link
Member

@arnaud-lb arnaud-lb commented May 26, 2025

This makes it possible to build opcache statically (and always enables it).

JIT/ZTS is supported.

Summary of changes:

  • Changed the build system so building opcache statically is allowed, and made sure to properly initialize opcache when doing so.
  • Changed how we gather information about the _tsrm_ls_cache thread-specific variable in JIT, because it broke when opcache was linked to the executable.

See commits for details.

RFC: https://wiki.php.net/rfc/make_opcache_required

arnaud-lb added 2 commits May 26, 2025 11:48
Opcache is both a Zend extension and a PHP module. When Opcache is built
statically, the engine will attempt to initialize it as a PHP module, but will
not initialize the Zend extension.

Here I make Opcache a two-way hybrid extension [1] so that initializing it as a
PHP module also initializes the Zend extension.

[1] https://www.phpinternalsbook.com/php7/extensions_design/zend_extensions.html#hybrid-extensions
…`_tsrm_ls_cache' at 0x12fc3 in section `.text' failed"

In order to get information about the _tsrm_ls_cache TLS variable, we emit ASM
code with TLS-related relocations [1]. These relocations, once adjusted by the
linker, give us the offset or address of the variable's TLS descriptor.

Specifically, we use relocations meant for use in the "General Dynamic" model
described in [1].

When building Opcache statically in a binary, the linker will attempt to
optimize the General Dynamic code sequence to a more efficient one.
Unfortunately, linkers will break as we don't use the exact code sequence they
are expecting.

Here I use a different approach to get information about the TLS variable:

 * Emit the exact code sequence expected by linker, so that linking works
 * Extract the information we want by inspecting the ASM code. If the linker
   did something we didn't expect, we fallback to a safer (but slower)
   mechanism.

[1] https://www.akkadia.org/drepper/tls.pdf
@arnaud-lb arnaud-lb force-pushed the static-opcache-hybrid branch from 13a234a to ba04798 Compare May 26, 2025 10:21
@staabm
Copy link
Contributor

staabm commented May 26, 2025

I remember low level extension like xdebug, blackfire, taint and opcache had problems in the past when enabled at the same time. I am not sure whether thats still the case with the latest php versions.

Just want to leave this here in case it rings a bell for someone

arnaud-lb and others added 2 commits May 26, 2025 15:05
SAPIs such as phpdbg may run multiple startup-shutdown cycles in the same
process.
@@ -130,7 +130,6 @@ jobs:
with:
jitType: tracing
runTestsParameters: >-
-d zend_extension=opcache.so
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On my Windows the php.exe has size of 128 KB but the php_opcache.dll has size of 463 KB. This is 3.6x binary size increase which might be an issue for embedded devices running php.

@henderkes
Copy link

While I love the idea, is it necessary to enable opcache by default? Seems to me that --enable-opcache could be used to enable it statically and --enable-opcache=shared would build the shared extension.

I don't know of a reason not to always enable opcache, but I wouldn't be surprised if one existed. I (unfortunately) still know hosting providers who categorically disable opcache, although I'm not sure of their reasons and if they're valid.

@Girgias
Copy link
Member

Girgias commented Jun 1, 2025

While I love the idea, is it necessary to enable opcache by default? Seems to me that --enable-opcache could be used to enable it statically and --enable-opcache=shared would build the shared extension.

I don't know of a reason not to always enable opcache, but I wouldn't be surprised if one existed. I (unfortunately) still know hosting providers who categorically disable opcache, although I'm not sure of their reasons and if they're valid.

Having opcache compiled statically doesn't magically enable it.
And those hosting providers are doing it wrong.

@henderkes
Copy link

Having opcache compiled statically doesn't magically enable it. And those hosting providers are doing it wrong.

Let me quote arnaud here:

This makes it possible to build opcache statically (and always enables it).

@Girgias
Copy link
Member

Girgias commented Jun 2, 2025

Having opcache compiled statically doesn't magically enable it. And those hosting providers are doing it wrong.

Let me quote arnaud here:

This makes it possible to build opcache statically (and always enables it).

The extension always being enabled, does not mean that opcache functionality is enabled.
You can have opcache installed with a shared object, and yet not have any opcache functionality enabled (by setting opcache.enable=0).
This is most notable in CLI, as if you don't set opcache.enable_cli=1 then you do not have opcache in your CLI script.
So even if the extension is always enabled, it does not mean that you must actually "use" opcache.

However, there isn't really a benefit in having opcache.enable=0, and things have moved around between opcache and the engine before, be that the optimizer or opcache.fast_shutdown.

Moreover, hosting providers not knowing what they are doing (which wouldn't be the first time) is not really an argument against this.

@henderkes
Copy link

I misunderstood the kind of "enabled" arnaud meant then, thank you for the clarification!

@TimWolla
Copy link
Member

TimWolla commented Jun 2, 2025

and things have moved around between opcache and the engine before

Indeed. And the goal is for this to be easier and more reliable. Allowing OPcache to be loaded dynamically prevents this. See also: https://externals.io/message/127459#127493

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants