Skip to content

Commit 1444e14

Browse files
committed
portable opcache via portable system_id
1 parent 28cd376 commit 1444e14

File tree

8 files changed

+208
-8
lines changed

8 files changed

+208
-8
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ config.h.in
9898
/main/php_config.h.in
9999
/main/php_config.h
100100
/Zend/zend_config.h
101+
/Zend/zend_abi_signature.h
101102

102103
# ------------------------------------------------------------------------------
103104
# Manual (man 1 and 8) pages generated from templates for *nix alike systems
@@ -125,6 +126,7 @@ config.h.in
125126
# ------------------------------------------------------------------------------
126127
# Executable binaries and scripts generated during the build process
127128
# ------------------------------------------------------------------------------
129+
/Zend/gen_abi_sig
128130
/ext/phar/phar.phar
129131
/ext/phar/phar.php
130132
/pear/install-pear-nozlib.phar

Zend/Makefile.frag

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,28 @@ vm.gen.intermediate: $(srcdir)/zend_vm_def.h $(srcdir)/zend_vm_execute.skl $(src
4242
$(builddir)/zend_highlight.lo $(builddir)/zend_compile.lo: $(srcdir)/zend_language_parser.h
4343

4444
Zend/zend_execute.lo: $(srcdir)/zend_vm_execute.h $(srcdir)/zend_vm_opcodes.h
45+
46+
# Define the headers that constitute the ABI
47+
ABI_HEADERS = \
48+
$(top_srcdir)/Zend/zend_types.h \
49+
$(top_srcdir)/Zend/zend_API.h \
50+
$(top_srcdir)/Zend/zend_compile.h \
51+
$(top_srcdir)/ext/opcache/zend_persist.h \
52+
$(top_srcdir)/Zend/zend_vm_opcodes.h
53+
54+
# Rule to build our generator program.
55+
# This is a simple, self-contained C file with no special dependencies.
56+
Zend/gen_abi_sig: Zend/gen_abi_sig.c
57+
$(CC) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $< -o $@
58+
59+
# Rule to generate the ABI signature string and embed it into a header file
60+
Zend/zend_abi_signature.h: Zend/gen_abi_sig $(ABI_HEADERS)
61+
@echo "Generating Opcache ABI Signature..."
62+
@echo "/* This file is automatically generated. DO NOT EDIT. */" > $@
63+
@echo "#ifndef ZEND_ABI_SIGNATURE_H" >> $@
64+
@echo "#define ZEND_ABI_SIGNATURE_H" >> $@
65+
@echo "static const char *zend_opcache_abi_signature = \"" `$(CC) -E $(CPPFLAGS) $(INCLUDES) $(ABI_HEADERS) | $(top_builddir)/Zend/gen_abi_sig` "\";" >> $@
66+
@echo "#endif" >> $@
67+
68+
# Ensure the system_id object depends on the generated header
69+
Zend/zend_system_id.lo: Zend/zend_abi_signature.h

Zend/gen_abi_sig.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Zend Engine |
4+
+----------------------------------------------------------------------+
5+
| Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
6+
+----------------------------------------------------------------------+
7+
| This source file is subject to version 2.00 of the Zend license, |
8+
| that is bundled with this package in the file LICENSE, and is |
9+
| available through the world-wide-web at the following url: |
10+
| http://www.zend.com/license/2_00.txt. |
11+
| If you did not receive a copy of the Zend license and are unable to |
12+
| obtain it through the world-wide-web, please send a note to |
13+
| [email protected] so we can mail you a copy immediately. |
14+
+----------------------------------------------------------------------+
15+
| Authors: Samuel Melrose <[email protected]> |
16+
+----------------------------------------------------------------------+
17+
*/
18+
19+
/*
20+
* Opcache ABI Signature Generator
21+
*
22+
* This program is compiled and run during the PHP build process.
23+
* It reads pre-processed C header source from stdin, calculates a
24+
* CRC32 checksum of it, and prints the hex digest to stdout.
25+
*
26+
* A simple CRC32 is used as a lightweight, dependency-free way to
27+
* detect changes in the core data structures.
28+
*/
29+
30+
#include <stdio.h>
31+
#include <stdint.h>
32+
33+
/* A self-contained CRC32 implementation */
34+
static uint32_t crc32_table[256];
35+
36+
static void build_crc32_table(void) {
37+
for (uint32_t i = 0; i < 256; i++) {
38+
uint32_t ch = i;
39+
uint32_t crc = 0;
40+
for (size_t j = 0; j < 8; j++) {
41+
uint32_t b = (ch ^ crc) & 1;
42+
crc >>= 1;
43+
if (b) {
44+
crc = crc ^ 0xEDB88320;
45+
}
46+
ch >>= 1;
47+
}
48+
crc32_table[i] = crc;
49+
}
50+
}
51+
52+
static uint32_t crc32_update(uint32_t crc, const unsigned char *buf, size_t len) {
53+
crc = ~crc;
54+
while (len--) {
55+
crc = (crc >> 8) ^ crc32_table[(crc ^ *buf++) & 0xFF];
56+
}
57+
return ~crc;
58+
}
59+
60+
int main(int argc, char *argv[]) {
61+
uint32_t crc = 0;
62+
unsigned char buf[4096];
63+
size_t n;
64+
65+
build_crc32_table();
66+
67+
while ((n = fread(buf, 1, sizeof(buf), stdin)) > 0) {
68+
crc = crc32_update(crc, buf, n);
69+
}
70+
71+
printf("%08x", crc);
72+
73+
return 0;
74+
}

Zend/zend.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ ZEND_INI_BEGIN()
278278
/* Subtracted from the max allowed stack size, as a buffer, when checking for overflow. 0: auto detect. */
279279
STD_ZEND_INI_ENTRY("zend.reserved_stack_size", "0", ZEND_INI_SYSTEM, OnUpdateReservedStackSize, reserved_stack_size, zend_executor_globals, executor_globals)
280280
#endif
281+
STD_ZEND_INI_BOOLEAN("zend.portable_build", "0", ZEND_INI_SYSTEM, OnUpdateBool, portable_build, zend_executor_globals, executor_globals)
281282

282283
ZEND_INI_END()
283284

Zend/zend_globals.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,9 @@ struct _zend_executor_globals {
249249
/* timeout support */
250250
zend_long timeout_seconds;
251251

252+
/* portable system_id */
253+
bool portable_build;
254+
252255
HashTable *ini_directives;
253256
HashTable *modified_ini_directives;
254257
zend_ini_entry *error_reporting_ini_entry;

Zend/zend_system_id.c

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "php.h"
1919
#include "zend_system_id.h"
2020
#include "zend_extensions.h"
21+
#include "zend_abi_signature.h"
2122
#include "ext/standard/md5.h"
2223
#include "ext/hash/php_hash.h"
2324

@@ -44,14 +45,6 @@ ZEND_API zend_result zend_add_system_entropy(const char *module_name, const char
4445
void zend_startup_system_id(void)
4546
{
4647
PHP_MD5Init(&context);
47-
PHP_MD5Update(&context, PHP_VERSION, sizeof(PHP_VERSION)-1);
48-
PHP_MD5Update(&context, ZEND_EXTENSION_BUILD_ID, sizeof(ZEND_EXTENSION_BUILD_ID)-1);
49-
PHP_MD5Update(&context, ZEND_BIN_ID, sizeof(ZEND_BIN_ID)-1);
50-
if (strstr(PHP_VERSION, "-dev") != 0) {
51-
/* Development versions may be changed from build to build */
52-
PHP_MD5Update(&context, __DATE__, sizeof(__DATE__)-1);
53-
PHP_MD5Update(&context, __TIME__, sizeof(__TIME__)-1);
54-
}
5548
zend_system_id[0] = '\0';
5649
}
5750

@@ -66,6 +59,27 @@ void zend_finalize_system_id(void)
6659
unsigned char digest[16];
6760
uint8_t hooks = 0;
6861

62+
if (EG(portable_build)) {
63+
/* Portable build mode: Use the ABI signature and major/minor version */
64+
int major_version = PHP_MAJOR_VERSION;
65+
int minor_version = PHP_MINOR_VERSION;
66+
67+
PHP_MD5Update(&context, (const unsigned char *)&major_version, sizeof(int));
68+
PHP_MD5Update(&context, (const unsigned char *)&minor_version, sizeof(int));
69+
PHP_MD5Update(&context, (const unsigned char *)zend_opcache_abi_signature, strlen(zend_opcache_abi_signature));
70+
} else {
71+
/* Default strict mode: Use the original full-fat build ID */
72+
PHP_MD5Update(&context, PHP_VERSION, sizeof(PHP_VERSION)-1);
73+
PHP_MD5Update(&context, ZEND_EXTENSION_BUILD_ID, sizeof(ZEND_EXTENSION_BUILD_ID)-1);
74+
if (strstr(PHP_VERSION, "-dev") != 0) {
75+
PHP_MD5Update(&context, __DATE__, sizeof(__DATE__)-1);
76+
PHP_MD5Update(&context, __TIME__, sizeof(__TIME__)-1);
77+
}
78+
}
79+
80+
/* These are always critical for compatibility */
81+
PHP_MD5Update(&context, ZEND_BIN_ID, sizeof(ZEND_BIN_ID)-1);
82+
6983
if (zend_ast_process) {
7084
hooks |= ZEND_HOOK_AST_PROCESS;
7185
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
--TEST--
2+
Zend Opcache: Check zend.portable_build system_id for file cache
3+
--SKIPIF--
4+
<?php
5+
// Ensure the cache directory exists BEFORE OPcache needs it
6+
$cacheDir = __DIR__ . '/portable_build_cache';
7+
if (!is_dir($cacheDir)) {
8+
@mkdir($cacheDir, 0777, true);
9+
}
10+
// Check if mkdir failed potentially due to permissions
11+
if (!is_dir($cacheDir) || !is_writable($cacheDir)) {
12+
die('skip Could not create or write to cache directory: ' . $cacheDir);
13+
}
14+
?>
15+
--INI--
16+
opcache.enable=1
17+
opcache.enable_cli=1
18+
opcache.jit=disable
19+
opcache.file_cache="{PWD}/portable_build_cache"
20+
--EXTENSIONS--
21+
opcache
22+
--FILE--
23+
<?php
24+
$cache_dir = __DIR__ . '/portable_build_cache';
25+
$test_file = __FILE__;
26+
27+
$ini_settings_portable = [
28+
'zend_extension' => 'opcache',
29+
'opcache.enable' => 1,
30+
'opcache.enable_cli' => 1,
31+
'opcache.jit' => 'disable',
32+
'opcache.file_cache' => $cache_dir,
33+
'opcache.file_update_protection' => 0,
34+
'zend.portable_build' => 1,
35+
];
36+
37+
$ini_str_portable = '';
38+
foreach ($ini_settings_portable as $key => $value) {
39+
$ini_str_portable .= "-d " . escapeshellarg("$key=$value") . " ";
40+
}
41+
42+
$php_executable = getenv('TEST_PHP_EXECUTABLE');
43+
$command_portable = "$php_executable $ini_str_portable -r 'opcache_compile_file(\"$test_file\"); echo opcache_get_status()[\"system_id\"];'";
44+
$portable_system_id = trim(shell_exec($command_portable));
45+
46+
if (substr(PHP_OS, 0, 3) === 'WIN') {
47+
$pattern = $cache_dir . DIRECTORY_SEPARATOR . $portable_system_id . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . str_replace(':', '', __FILE__) . '.bin';
48+
} else {
49+
$pattern = $cache_dir . DIRECTORY_SEPARATOR . $portable_system_id . DIRECTORY_SEPARATOR . __FILE__ . '.bin';
50+
}
51+
$cache_files = glob($pattern);
52+
if (count($cache_files) !== 1) {
53+
die('Failed to find the generated cache file in ' . $cache_dir);
54+
}
55+
$cache_file_path = $cache_files[0];
56+
$cache_file_handle = fopen($cache_file_path, 'rb');
57+
fread($cache_file_handle, 8); // skip the first 8 bytes
58+
$header = fread($cache_file_handle, 32); // Read the first 32 bytes (the system_id)
59+
fclose($cache_file_handle);
60+
$stored_system_id = trim($header);
61+
62+
var_dump($portable_system_id === $stored_system_id);
63+
64+
$default_system_id = opcache_get_status()['system_id'];
65+
66+
var_dump($portable_system_id === $default_system_id);
67+
68+
?>
69+
--CLEAN--
70+
<?php
71+
require __DIR__ . '/cleanup_helper.inc';
72+
73+
$cacheDir = __DIR__ . '/portable_build_cache';
74+
75+
removeDirRecursive($cacheDir);
76+
?>
77+
--EXPECT--
78+
bool(true)
79+
bool(false)

ext/opcache/zend_accelerator_module.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include "php.h"
2525
#include "ZendAccelerator.h"
26+
#include "zend_system_id.h"
2627
#include "zend_API.h"
2728
#include "zend_closures.h"
2829
#include "zend_shared_alloc.h"
@@ -651,6 +652,7 @@ ZEND_FUNCTION(opcache_get_status)
651652

652653
if (ZCG(accel_directives).file_cache) {
653654
add_assoc_string(return_value, "file_cache", ZCG(accel_directives).file_cache);
655+
add_assoc_stringl(return_value, "system_id", zend_system_id, 32);
654656
}
655657
if (file_cache_only) {
656658
add_assoc_bool(return_value, "file_cache_only", 1);

0 commit comments

Comments
 (0)