Conversation
…truct probe structs/x86_64/php_structs_86.h: sapi_globals_struct_86.global_request_time was declared at offset 440; real PHP 8.6.0beta2 has it at 432 (verified via gdb/offsetof). Before: `-r` reported `# ts = 0.000000` on 8.6. After: a correct wall-clock timestamp. phpspy_trace.c/phpspy_trace_tpl.c/structs/structs.h: the CV frame-slot constant used by --peek-var was hardcoded as the literal 5. The real value is ZEND_CALL_FRAME_SLOT = ceil(sizeof(zend_execute_data)/sizeof(zval)), which is 6 on PHP 7.0 (it still carries execute_data.called_scope, removed in 7.1) and 5 on every later version -- verified against all 12 supported versions' real headers. Before: --peek-var silently found nothing on 7.0. After: tests/test_varpeek.sh passes on 7.0; every other version unaffected. struct_dump.sh/struct_dump.gdb: bring the dump tooling current -- add the missing php-8.4.25/php-8.5.10 tags (it stopped at 8.3/master), add an optional single-version argument so a CI matrix cell can dump just one version, and add sizeof(zend_execute_data)/sizeof(zval) cross-check lines for the frame-slot fix above. .github/workflows/dump_structs.yml: new, workflow_dispatch-only job that builds php-src from source for every supported version on both x86_64 and aarch64 and uploads the raw struct_dump.gdb output as artifacts -- this is how real (not placeholder/guessed) aarch64 offsets get produced; see CLAUDE.md and the phpspy CLAUDE.md for the struct-mirror background. Verified: full test suite passes unchanged across all 13 locally installed PHP versions (70-86, excluding asan) before and after this change; the only failures present (pdo_args_packed_array on 72/73/74/80/81/82) are a pre-existing, unrelated HASH_FLAG_PACKED bug, confirmed identical via git stash before committing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8kSnhaw7WUaR61G6cBp1w
Leave version bumps to the maintainer's judgment on when a new version is warranted. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8kSnhaw7WUaR61G6cBp1w
structs/aarch64/php_structs_{70,71,72,73,74,80,81,82}.h were byte-identical
copies of their x86_64 counterparts with a "these structs are wrong for
aarch64" comment -- never actually measured on arm. 83/84/85 carried one
real but unverified guess (a 16-byte shrink in sapi_globals_struct, since
zend_stat_t/struct stat is 128 bytes on aarch64/glibc vs 144 on x86_64/glibc);
86 carried a guess that was wrong given the x86_64 fix in the prior commit.
Derived via the new dump_structs.yml workflow (workflow_dispatch): it builds
php-src from source for every supported version on both ubuntu-24.04 and
ubuntu-24.04-arm and runs the (now current) struct_dump.gdb against each
resulting binary, uploading the raw offsets as build artifacts. Run at
https://github.com/rlerdorf/phpspy/actions/runs/34219729971 (all 24 cells
succeeded).
Before touching any header, the x86_64 leg of that same run was diffed
field-for-field against this session's own independently-gathered x86_64
ground truth (gdb/offsetof against the locally installed PHP 7.0-8.6) --
byte-identical across all 12 versions, confirming the build+dump pipeline
itself is trustworthy. The aarch64 leg then showed a single, consistent
difference from x86_64 for every version: sapi_globals_struct's
global_request_time shifts by exactly -16 bytes, matching the
zend_stat_t size delta above, and nothing else in the struct surface
phpspy reads differs by architecture. That means:
- 83 and 85's existing guessed delta was correct -- confirmed against real
hardware, no change needed.
- 70-74/80-82 needed the same delta, applied for the first time.
- 86 needed the new delta following the corrected x86_64 offset from the
prior commit (432 - 16 = 416, not the previously-guessed 424).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V8kSnhaw7WUaR61G6cBp1w
This was referenced Sep 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three bugs in the struct mirrors, fixed
1. PHP 8.6 x86_64 offset bug.
sapi_globals_struct_86.global_request_timewasdeclared at offset 440; real PHP 8.6.0beta2 has it at 432. Effect:
-rreported# ts = 0.000000on 8.6 instead of a real timestamp. Verified viagdb/offsetofagainst an installed PHP 8.6 build, and independently reproduced by a fresh
from-source build in CI (see below).
2. PHP 7.0 frame-slot bug. The CV frame-slot constant used by
--peek-var(
phpspy_trace.c,ZEND_CALL_VAR_NUM) was hardcoded as the literal5. The realvalue is
ZEND_CALL_FRAME_SLOT = ceil(sizeof(zend_execute_data)/sizeof(zval)),which is 6 on PHP 7.0 (it still carries
execute_data.called_scope, removed in7.1) and 5 on every later version. Verified against real headers for all 12
supported versions. Before:
--peek-varsilently found nothing on 7.0. After:tests/test_varpeek.shpasses on 7.0; every other version is unaffected (thenew
phpspy_frame_slotresolves to the same5they already used). Works inboth build modes -- under
USE_ZENDit's justZEND_CALL_FRAME_SLOTdirectly.3. aarch64 structs were mostly untested placeholders.
structs/aarch64/php_structs_{70,71,72,73,74,80,81,82}.hwere byte-identicalcopies of their x86_64 counterparts with a "these structs are wrong for
aarch64" comment -- never actually measured on arm hardware. 83/84/85 carried
a real but unverified guess (a 16-byte shrink in
sapi_globals_struct, sincezend_stat_t/struct statis 128 bytes on aarch64/glibc vs 144 onx86_64/glibc); 86 carried a guess that was wrong given the offset fixed in
(1). See below for how this was fixed with real data.
How the aarch64 data was derived
Added
.github/workflows/dump_structs.yml, aworkflow_dispatch-only job(too slow for routine push/PR CI -- it's a full
php-srcsource build percell) that builds every supported PHP version from source on both
ubuntu-24.04andubuntu-24.04-arm, using the (also updated in this PR)struct_dump.sh/struct_dump.gdb, and uploads the raw offset dumps asartifacts.
struct_dump.sh's version list had stopped at 8.3/master; this PRadds the
php-8.4.25/php-8.5.10tags it was missing and an optionalsingle-version argument so a matrix cell can dump just one version.
I ran that workflow once against a fork
(run, all 24
cells succeeded). Before touching any header, the x86_64 leg of that run was
diffed field-for-field against independently-gathered x86_64 ground truth
(
gdb/offsetofagainst locally installed PHP 7.0-8.6 builds) -- byte-identicalacross all 12 versions, which is what gives confidence the build+dump pipeline
itself is trustworthy. The aarch64 leg then showed exactly one consistent
difference from x86_64 for every version --
sapi_globals_struct.global_request_timeshifts by exactly -16 bytes (matching the
zend_stat_tsize delta above) -- andnothing else in the struct surface phpspy reads differs by architecture. So:
real data, no change needed.
the placeholder copies).
432 - 16 = 416, not the previously-guessed 424.
Verification
Full test suite run against all locally available PHP versions (7.0-8.6,
NTS, both debug and release builds) before and after this change. No new
failures; the only pre-existing failure (
pdo_args_packed_arrayon a fewversions) is an unrelated
HASH_FLAG_PACKEDissue in packed-array handling,confirmed identical with and without this patch.
tests/test_varpeek.shspecifically flips from failing to passing on PHP 7.0, which is the
regression test for bug (2).
No
PHPSPY_VERSIONbump included -- leaving that to your judgment on when anew version is warranted.
🤖 Generated with Claude Code