Skip to content

Commit 1a1a83f

Browse files
committed
Fix GH-18136: tracing JIT floating point register clobbering on Windows and ARM64
On win64, xmm6-xmm15 are preserved registers, but the prologues and epilogues of JITted code don't handle these. The issue occurs when calling into the JIT code again via an internal handler (like call_user_func). Therefore, we want to save/restore xmm registers upon entering/leaving execute_ex. Since MSVC x64 does not support inline assembly, we create an assembly wrapper around the real execute_ex function. The alternative is to always save/restore these xmm registers into the fixed call frame, but this causes unnecessary overhead. The same issue occurs for ARM64 platforms for floating point register 8 to 15. However, there we can use inline asm to fix this. Closes GH-18352.
1 parent affffe1 commit 1a1a83f

File tree

6 files changed

+108
-1
lines changed

6 files changed

+108
-1
lines changed

NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ PHP NEWS
3838
- Opcache:
3939
. Fixed bug GH-18294 (assertion failure zend_jit_ir.c). (nielsdos)
4040
. Fixed bug GH-18289 (Fix segfault in JIT). (Florian Engelhardt)
41+
. Fixed bug GH-18136 (tracing JIT floating point register clobbering on
42+
Windows and ARM64). (nielsdos)
4143

4244
- OpenSSL:
4345
. Fix memory leak in openssl_sign() when passing invalid algorithm.

Zend/asm/save_xmm_x86_64_ms_masm.asm

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
.code
2+
3+
; ZEND_API void execute_ex(zend_execute_data *ex)
4+
PUBLIC execute_ex
5+
6+
EXTERN execute_ex_real:PROC
7+
8+
; Assembly wrapper around the real execute_ex function, so that we can
9+
; save the preserved registers when re-entering the VM from JIT code.
10+
; See GH-18136.
11+
execute_ex PROC EXPORT FRAME
12+
; 10 floating points numbers
13+
; 32 bytes shadow space
14+
; 8 bytes to align after the return address
15+
sub rsp, 8*10 + 32 + 8
16+
.allocstack 8*10 + 32 + 8
17+
.endprolog
18+
movsd qword ptr [rsp + 32 + 8*0], xmm6
19+
movsd qword ptr [rsp + 32 + 8*1], xmm7
20+
movsd qword ptr [rsp + 32 + 8*2], xmm8
21+
movsd qword ptr [rsp + 32 + 8*3], xmm9
22+
movsd qword ptr [rsp + 32 + 8*4], xmm10
23+
movsd qword ptr [rsp + 32 + 8*5], xmm11
24+
movsd qword ptr [rsp + 32 + 8*6], xmm12
25+
movsd qword ptr [rsp + 32 + 8*7], xmm13
26+
movsd qword ptr [rsp + 32 + 8*8], xmm14
27+
movsd qword ptr [rsp + 32 + 8*9], xmm15
28+
call execute_ex_real
29+
movsd xmm6, qword ptr [rsp + 32 + 8*0]
30+
movsd xmm7, qword ptr [rsp + 32 + 8*1]
31+
movsd xmm8, qword ptr [rsp + 32 + 8*2]
32+
movsd xmm9, qword ptr [rsp + 32 + 8*3]
33+
movsd xmm10, qword ptr [rsp + 32 + 8*4]
34+
movsd xmm11, qword ptr [rsp + 32 + 8*5]
35+
movsd xmm12, qword ptr [rsp + 32 + 8*6]
36+
movsd xmm13, qword ptr [rsp + 32 + 8*7]
37+
movsd xmm14, qword ptr [rsp + 32 + 8*8]
38+
movsd xmm15, qword ptr [rsp + 32 + 8*9]
39+
add rsp, 8*10 + 32 + 8
40+
ret
41+
execute_ex ENDP
42+
43+
END

Zend/zend_vm_execute.h

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Zend/zend_vm_execute.skl

+9
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,19 @@
55
# pragma GCC optimize("no-gcse")
66
# pragma GCC optimize("no-ivopts")
77
#endif
8+
#ifdef _WIN64
9+
/* See save_xmm_x86_64_ms_masm.asm */
10+
void {%EXECUTOR_NAME%}_ex_real(zend_execute_data *ex)
11+
#else
812
ZEND_API void {%EXECUTOR_NAME%}_ex(zend_execute_data *ex)
13+
#endif
914
{
1015
DCL_OPLINE
1116

17+
#if defined(__GNUC__) && defined(__aarch64__)
18+
__asm__ __volatile__ (""::: "v8","v9","v10","v11","v12","v13","v14","v15");
19+
#endif
20+
1221
{%HELPER_VARS%}
1322

1423
{%INTERNAL_LABELS%}

ext/opcache/tests/jit/gh18136.phpt

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
GH-18136 (tracing JIT floating point register clobbering on Windows and ARM64)
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.jit=tracing
7+
opcache.jit_buffer_size=64M
8+
opcache.jit_hot_func=4
9+
opcache.jit_hot_loop=4
10+
--FILE--
11+
<?php
12+
namespace Foo;
13+
14+
function diff($point1, $point2)
15+
{
16+
$a = deg2rad($point1); // Prefixing these with \ also makes the issue go away
17+
$b = deg2rad($point2);
18+
return $a - $b;
19+
}
20+
21+
function getRawDistance()
22+
{
23+
$distance = 0;
24+
for ($p = 0; $p < 200; $p++) {
25+
// Needs to be a namespaced call_user_func call to reproduce the issue (i.e. adding \ at front makes the issue go away)
26+
$distance += call_user_func('Foo\diff', 0, $p);
27+
}
28+
29+
return $distance;
30+
}
31+
32+
var_dump(getRawDistance());
33+
?>
34+
--EXPECT--
35+
float(-347.3205211468715)

win32/build/config.w32

+10-1
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,23 @@ if (TARGET_ARCH == 'arm64') {
267267
DEFINE('FIBER_ASM_FLAGS', '/DBOOST_CONTEXT_EXPORT=EXPORT /nologo /c /Fo');
268268
}
269269

270-
ADD_FLAG('ASM_OBJS', '$(BUILD_DIR)\\Zend\\jump_' + FIBER_ASM_ABI + '.obj $(BUILD_DIR)\\Zend\\make_' + FIBER_ASM_ABI + '.obj');
270+
var all_asm_objs = '$(BUILD_DIR)\\Zend\\jump_' + FIBER_ASM_ABI + '.obj $(BUILD_DIR)\\Zend\\make_' + FIBER_ASM_ABI + '.obj';
271+
if (TARGET_ARCH == 'x64') {
272+
all_asm_objs += ' $(BUILD_DIR)\\Zend\\save_xmm_x86_64_ms_masm.obj';
273+
}
274+
ADD_FLAG('ASM_OBJS', all_asm_objs);
271275

272276
MFO.WriteLine('$(BUILD_DIR)\\Zend\\jump_' + FIBER_ASM_ABI + '.obj: Zend\\asm\\jump_' + FIBER_ASM_ABI + '.asm');
273277
MFO.WriteLine('\t$(PHP_ASSEMBLER) $(FIBER_ASM_FLAGS) $(BUILD_DIR)\\Zend\\jump_$(FIBER_ASM_ABI).obj Zend\\asm\\jump_$(FIBER_ASM_ABI).asm');
274278

275279
MFO.WriteLine('$(BUILD_DIR)\\Zend\\make_' + FIBER_ASM_ABI + '.obj: Zend\\asm\\make_' + FIBER_ASM_ABI + '.asm');
276280
MFO.WriteLine('\t$(PHP_ASSEMBLER) $(FIBER_ASM_FLAGS) $(BUILD_DIR)\\Zend\\make_$(FIBER_ASM_ABI).obj Zend\\asm\\make_$(FIBER_ASM_ABI).asm');
277281

282+
if (TARGET_ARCH == 'x64') {
283+
MFO.WriteLine('$(BUILD_DIR)\\Zend\\save_xmm_x86_64_ms_masm.obj: Zend\\asm\\save_xmm_x86_64_ms_masm.asm');
284+
MFO.WriteLine('\t$(PHP_ASSEMBLER) $(FIBER_ASM_FLAGS) $(BUILD_DIR)\\Zend\\save_xmm_x86_64_ms_masm.obj Zend\\asm\\save_xmm_x86_64_ms_masm.asm');
285+
}
286+
278287
ADD_FLAG("CFLAGS_BD_ZEND", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
279288
if (VS_TOOLSET && VCVERS >= 1914) {
280289
ADD_FLAG("CFLAGS_BD_ZEND", "/d2FuncCache1");

0 commit comments

Comments
 (0)