feat(platform): add full Windows x86_64 PE32+ cross-compilation support#446
Draft
Guikingone wants to merge 19 commits into
Draft
feat(platform): add full Windows x86_64 PE32+ cross-compilation support#446Guikingone wants to merge 19 commits into
Guikingone wants to merge 19 commits into
Conversation
1a6888e to
4b7fd88
Compare
aa5e6be to
2ac8b78
Compare
Guikingone
added a commit
to Guikingone/elephc
that referenced
this pull request
Jul 6, 2026
…es builtin Reconstruct PR illegalstudio#446's feature set on top of current main. Windows x86_64 PE target: - Platform::Windows with the MSx64 ABI (rcx/rdx/r8/r9, 32-byte shadow, 16-byte alignment) and ~40 Win32 shim wrappers converting SysV->MSx64 (WriteFile/ReadFile, sockets, file/dir ops, VirtualAlloc/Heap, BCryptGenRandom, ...). - Syscall->shim transform wired into the pipeline + runtime cache (Windows-only) so PE binaries contain no raw Linux syscalls; unmapped syscalls route to __rt_unsupported_syscall instead of a silent trap. - Entry wrapper (MSx64->SysV), MinGW-w64 assembler/linker, kernel32/msvcrt/winmm/ ws2_32/bcrypt/shlwapi link set. --target windows-x86_64 (alias x86_64-pc-windows-gnu); .exe / .dll outputs. CI windows-pe-cross-compile job (MinGW + Wine) wired into the gate. Fix the implicit end-of-main exit code on Windows: emit_exit and emit_exit_with_result_reg now load the code into edi and call the __rt_sys_exit (ExitProcess) shim directly, identical to an explicit exit($n), instead of returning through the MinGW CRT. The CRT's exit() reaches the same rdi-consuming shim, so the old return path left rdi holding leftover data and exited with a garbage nonzero code. random_bytes(int $length): string: - Cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a constant length below 1. - Expressed as a single-source registry home file (src/builtins/math/random_bytes.rs) instead of the legacy hand-maintained tables; excluded from the legacy runtime-callable wrapper as an EIR-only builtin. Builtin docs + registry JSON regenerated.
2ac8b78 to
5ec3017
Compare
Guikingone
added a commit
to Guikingone/elephc
that referenced
this pull request
Jul 6, 2026
…es builtin Reconstruct PR illegalstudio#446's feature set on top of current main. Windows x86_64 PE target: - Platform::Windows with the MSx64 ABI (rcx/rdx/r8/r9, 32-byte shadow, 16-byte alignment) and ~40 Win32 shim wrappers converting SysV->MSx64 (WriteFile/ReadFile, sockets, file/dir ops, VirtualAlloc/Heap, BCryptGenRandom, ...). - Syscall->shim transform wired into the pipeline + runtime cache (Windows-only) so PE binaries contain no raw Linux syscalls; unmapped syscalls route to __rt_unsupported_syscall instead of a silent trap. - Entry wrapper (MSx64->SysV), MinGW-w64 assembler/linker, kernel32/msvcrt/winmm/ ws2_32/bcrypt/shlwapi link set. --target windows-x86_64 (alias x86_64-pc-windows-gnu); .exe / .dll outputs. CI windows-pe-cross-compile job (MinGW + Wine) wired into the gate. Fix the implicit end-of-main exit code on Windows: emit_exit and emit_exit_with_result_reg now load the code into edi and call the __rt_sys_exit (ExitProcess) shim directly, identical to an explicit exit($n), instead of returning through the MinGW CRT. The CRT's exit() reaches the same rdi-consuming shim, so the old return path left rdi holding leftover data and exited with a garbage nonzero code. random_bytes(int $length): string: - Cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a constant length below 1. - Expressed as a single-source registry home file (src/builtins/math/random_bytes.rs) instead of the legacy hand-maintained tables; excluded from the legacy runtime-callable wrapper as an EIR-only builtin. Builtin docs + registry JSON regenerated.
5ec3017 to
c8aa4df
Compare
Guikingone
added a commit
to Guikingone/elephc
that referenced
this pull request
Jul 6, 2026
…es builtin Reconstruct PR illegalstudio#446's feature set on top of current main. Windows x86_64 PE target: - Platform::Windows with the MSx64 ABI (rcx/rdx/r8/r9, 32-byte shadow, 16-byte alignment) and ~40 Win32 shim wrappers converting SysV->MSx64 (WriteFile/ReadFile, sockets, file/dir ops, VirtualAlloc/Heap, BCryptGenRandom, ...). - Syscall->shim transform wired into the pipeline + runtime cache (Windows-only) so PE binaries contain no raw Linux syscalls; unmapped syscalls route to __rt_unsupported_syscall instead of a silent trap. - Entry wrapper (MSx64->SysV), MinGW-w64 assembler/linker, kernel32/msvcrt/winmm/ ws2_32/bcrypt/shlwapi link set. --target windows-x86_64 (alias x86_64-pc-windows-gnu); .exe / .dll outputs. CI windows-pe-cross-compile job (MinGW + Wine) wired into the gate. Fix the implicit end-of-main exit code on Windows: emit_exit and emit_exit_with_result_reg now load the code into edi and call the __rt_sys_exit (ExitProcess) shim directly, identical to an explicit exit($n), instead of returning through the MinGW CRT. The CRT's exit() reaches the same rdi-consuming shim, so the old return path left rdi holding leftover data and exited with a garbage nonzero code. random_bytes(int $length): string: - Cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a constant length below 1. - Expressed as a single-source registry home file (src/builtins/math/random_bytes.rs) instead of the legacy hand-maintained tables; excluded from the legacy runtime-callable wrapper as an EIR-only builtin. Builtin docs + registry JSON regenerated.
c8aa4df to
2d7dcca
Compare
Guikingone
added a commit
to Guikingone/elephc
that referenced
this pull request
Jul 6, 2026
…es builtin Reconstruct PR illegalstudio#446's feature set on top of current main. Windows x86_64 PE target: - Platform::Windows with the MSx64 ABI (rcx/rdx/r8/r9, 32-byte shadow, 16-byte alignment) and ~40 Win32 shim wrappers converting SysV->MSx64 (WriteFile/ReadFile, sockets, file/dir ops, VirtualAlloc/Heap, BCryptGenRandom, ...). - Syscall->shim transform wired into the pipeline + runtime cache (Windows-only) so PE binaries contain no raw Linux syscalls; unmapped syscalls route to __rt_unsupported_syscall instead of a silent trap. - Entry wrapper (MSx64->SysV), MinGW-w64 assembler/linker, kernel32/msvcrt/winmm/ ws2_32/bcrypt/shlwapi link set. --target windows-x86_64 (alias x86_64-pc-windows-gnu); .exe / .dll outputs. CI windows-pe-cross-compile job (MinGW + Wine) wired into the gate. Fix the implicit end-of-main exit code on Windows: emit_exit and emit_exit_with_result_reg now load the code into edi and call the __rt_sys_exit (ExitProcess) shim directly, identical to an explicit exit($n), instead of returning through the MinGW CRT. The CRT's exit() reaches the same rdi-consuming shim, so the old return path left rdi holding leftover data and exited with a garbage nonzero code. random_bytes(int $length): string: - Cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a constant length below 1. - Expressed as a single-source registry home file (src/builtins/math/random_bytes.rs) instead of the legacy hand-maintained tables; excluded from the legacy runtime-callable wrapper as an EIR-only builtin. Builtin docs + registry JSON regenerated.
2d7dcca to
85541c3
Compare
Guikingone
added a commit
to Guikingone/elephc
that referenced
this pull request
Jul 6, 2026
…es builtin Reconstruct PR illegalstudio#446's feature set on top of current main. Windows x86_64 PE target: - Platform::Windows with the MSx64 ABI (rcx/rdx/r8/r9, 32-byte shadow, 16-byte alignment) and ~40 Win32 shim wrappers converting SysV->MSx64 (WriteFile/ReadFile, sockets, file/dir ops, VirtualAlloc/Heap, BCryptGenRandom, ...). - Syscall->shim transform wired into the pipeline + runtime cache (Windows-only) so PE binaries contain no raw Linux syscalls; unmapped syscalls route to __rt_unsupported_syscall instead of a silent trap. - Entry wrapper (MSx64->SysV), MinGW-w64 assembler/linker, kernel32/msvcrt/winmm/ ws2_32/bcrypt/shlwapi link set. --target windows-x86_64 (alias x86_64-pc-windows-gnu); .exe / .dll outputs. CI windows-pe-cross-compile job (MinGW + Wine) wired into the gate. Fix the implicit end-of-main exit code on Windows: emit_exit and emit_exit_with_result_reg now load the code into edi and call the __rt_sys_exit (ExitProcess) shim directly, identical to an explicit exit($n), instead of returning through the MinGW CRT. The CRT's exit() reaches the same rdi-consuming shim, so the old return path left rdi holding leftover data and exited with a garbage nonzero code. random_bytes(int $length): string: - Cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a constant length below 1. - Expressed as a single-source registry home file (src/builtins/math/random_bytes.rs) instead of the legacy hand-maintained tables; excluded from the legacy runtime-callable wrapper as an EIR-only builtin. Builtin docs + registry JSON regenerated.
c60d605 to
b8483f1
Compare
Guikingone
added a commit
to Guikingone/elephc
that referenced
this pull request
Jul 6, 2026
…es builtin Reconstruct PR illegalstudio#446's feature set on top of current main. Windows x86_64 PE target: - Platform::Windows with the MSx64 ABI (rcx/rdx/r8/r9, 32-byte shadow, 16-byte alignment) and ~40 Win32 shim wrappers converting SysV->MSx64 (WriteFile/ReadFile, sockets, file/dir ops, VirtualAlloc/Heap, BCryptGenRandom, ...). - Syscall->shim transform wired into the pipeline + runtime cache (Windows-only) so PE binaries contain no raw Linux syscalls; unmapped syscalls route to __rt_unsupported_syscall instead of a silent trap. - Entry wrapper (MSx64->SysV), MinGW-w64 assembler/linker, kernel32/msvcrt/winmm/ ws2_32/bcrypt/shlwapi link set. --target windows-x86_64 (alias x86_64-pc-windows-gnu); .exe / .dll outputs. CI windows-pe-cross-compile job (MinGW + Wine) wired into the gate. Fix the implicit end-of-main exit code on Windows: emit_exit and emit_exit_with_result_reg now load the code into edi and call the __rt_sys_exit (ExitProcess) shim directly, identical to an explicit exit($n), instead of returning through the MinGW CRT. The CRT's exit() reaches the same rdi-consuming shim, so the old return path left rdi holding leftover data and exited with a garbage nonzero code. random_bytes(int $length): string: - Cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a constant length below 1. - Expressed as a single-source registry home file (src/builtins/math/random_bytes.rs) instead of the legacy hand-maintained tables; excluded from the legacy runtime-callable wrapper as an EIR-only builtin. Builtin docs + registry JSON regenerated. Callable-invoker ABI on Windows x86_64: - The descriptor-based runtime callable invoker read its descriptor from a hardcoded rdi, but on Windows the caller passes it in the MSx64 first arg (rcx); read it from int_arg_reg_name(target, 0) instead. - The invoker clone helpers passed arguments to the __rt_array_clone_shallow / __rt_array_to_mixed / __rt_hash_clone_shallow / __rt_hash_to_mixed / __rt_array_new runtime helpers via int_arg_reg_name, which returns the target user-call ABI (rcx/rdx on Windows); those helpers use the System V AMD64 ABI on every x86_64 target and read rdi/rsi. Add runtime_int_arg_reg_name (SysV on all x86_64, platform ABI on AArch64) and route the four helper call sites through it. No-op on Linux and AArch64.
c7c3d92 to
719394b
Compare
Guikingone
added a commit
to Guikingone/elephc
that referenced
this pull request
Jul 6, 2026
…es builtin Reconstruct PR illegalstudio#446's feature set on top of current main. Windows x86_64 PE target: - Platform::Windows with the MSx64 ABI (rcx/rdx/r8/r9, 32-byte shadow, 16-byte alignment) and ~40 Win32 shim wrappers converting SysV->MSx64 (WriteFile/ReadFile, sockets, file/dir ops, VirtualAlloc/Heap, BCryptGenRandom, ...). - Syscall->shim transform wired into the pipeline + runtime cache (Windows-only) so PE binaries contain no raw Linux syscalls; unmapped syscalls route to __rt_unsupported_syscall instead of a silent trap. - Entry wrapper (MSx64->SysV), MinGW-w64 assembler/linker, kernel32/msvcrt/winmm/ ws2_32/bcrypt/shlwapi link set. --target windows-x86_64 (alias x86_64-pc-windows-gnu); .exe / .dll outputs. CI windows-pe-cross-compile job (MinGW + Wine) wired into the gate. Fix the implicit end-of-main exit code on Windows: emit_exit and emit_exit_with_result_reg now load the code into edi and call the __rt_sys_exit (ExitProcess) shim directly, identical to an explicit exit($n), instead of returning through the MinGW CRT. The CRT's exit() reaches the same rdi-consuming shim, so the old return path left rdi holding leftover data and exited with a garbage nonzero code. random_bytes(int $length): string: - Cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a constant length below 1. - Expressed as a single-source registry home file (src/builtins/math/random_bytes.rs) instead of the legacy hand-maintained tables; excluded from the legacy runtime-callable wrapper as an EIR-only builtin. Builtin docs + registry JSON regenerated. Callable-invoker ABI on Windows x86_64: - The descriptor-based runtime callable invoker read its descriptor from a hardcoded rdi, but on Windows the caller passes it in the MSx64 first arg (rcx); read it from int_arg_reg_name(target, 0) instead. - The invoker clone helpers passed arguments to the __rt_array_clone_shallow / __rt_array_to_mixed / __rt_hash_clone_shallow / __rt_hash_to_mixed / __rt_array_new runtime helpers via int_arg_reg_name, which returns the target user-call ABI (rcx/rdx on Windows); those helpers use the System V AMD64 ABI on every x86_64 target and read rdi/rsi. Add runtime_int_arg_reg_name (SysV on all x86_64, platform ABI on AArch64) and route the four helper call sites through it. No-op on Linux and AArch64.
719394b to
0240020
Compare
Guikingone
added a commit
to Guikingone/elephc
that referenced
this pull request
Jul 6, 2026
…es builtin Reconstruct PR illegalstudio#446's feature set on top of current main. Windows x86_64 PE target: - Platform::Windows with the MSx64 ABI (rcx/rdx/r8/r9, 32-byte shadow, 16-byte alignment) and ~40 Win32 shim wrappers converting SysV->MSx64 (WriteFile/ReadFile, sockets, file/dir ops, VirtualAlloc/Heap, BCryptGenRandom, ...). - Syscall->shim transform wired into the pipeline + runtime cache (Windows-only) so PE binaries contain no raw Linux syscalls; unmapped syscalls route to __rt_unsupported_syscall instead of a silent trap. - Entry wrapper (MSx64->SysV), MinGW-w64 assembler/linker, kernel32/msvcrt/winmm/ ws2_32/bcrypt/shlwapi link set. --target windows-x86_64 (alias x86_64-pc-windows-gnu); .exe / .dll outputs. CI windows-pe-cross-compile job (MinGW + Wine) wired into the gate. Fix the implicit end-of-main exit code on Windows: emit_exit and emit_exit_with_result_reg now load the code into edi and call the __rt_sys_exit (ExitProcess) shim directly, identical to an explicit exit($n), instead of returning through the MinGW CRT. The CRT's exit() reaches the same rdi-consuming shim, so the old return path left rdi holding leftover data and exited with a garbage nonzero code. random_bytes(int $length): string: - Cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a constant length below 1. - Expressed as a single-source registry home file (src/builtins/math/random_bytes.rs) instead of the legacy hand-maintained tables; excluded from the legacy runtime-callable wrapper as an EIR-only builtin. Builtin docs + registry JSON regenerated. Callable-invoker ABI on Windows x86_64: - The descriptor-based runtime callable invoker read its descriptor from a hardcoded rdi, but on Windows the caller passes it in the MSx64 first arg (rcx); read it from int_arg_reg_name(target, 0) instead. - The invoker clone helpers passed arguments to the __rt_array_clone_shallow / __rt_array_to_mixed / __rt_hash_clone_shallow / __rt_hash_to_mixed / __rt_array_new runtime helpers via int_arg_reg_name, which returns the target user-call ABI (rcx/rdx on Windows); those helpers use the System V AMD64 ABI on every x86_64 target and read rdi/rsi. Add runtime_int_arg_reg_name (SysV on all x86_64, platform ABI on AArch64) and route the four helper call sites through it. No-op on Linux and AArch64.
0240020 to
c0f1476
Compare
Guikingone
added a commit
to Guikingone/elephc
that referenced
this pull request
Jul 6, 2026
…es builtin Reconstruct PR illegalstudio#446's feature set on top of current main. Windows x86_64 PE target: - Platform::Windows with the MSx64 ABI (rcx/rdx/r8/r9, 32-byte shadow, 16-byte alignment) and ~40 Win32 shim wrappers converting SysV->MSx64 (WriteFile/ReadFile, sockets, file/dir ops, VirtualAlloc/Heap, BCryptGenRandom, ...). - Syscall->shim transform wired into the pipeline + runtime cache (Windows-only) so PE binaries contain no raw Linux syscalls; unmapped syscalls route to __rt_unsupported_syscall instead of a silent trap. - Entry wrapper (MSx64->SysV), MinGW-w64 assembler/linker, kernel32/msvcrt/winmm/ ws2_32/bcrypt/shlwapi link set. --target windows-x86_64 (alias x86_64-pc-windows-gnu); .exe / .dll outputs. CI windows-pe-cross-compile job (MinGW + Wine) wired into the gate. Fix the implicit end-of-main exit code on Windows: emit_exit and emit_exit_with_result_reg now load the code into edi and call the __rt_sys_exit (ExitProcess) shim directly, identical to an explicit exit($n), instead of returning through the MinGW CRT. The CRT's exit() reaches the same rdi-consuming shim, so the old return path left rdi holding leftover data and exited with a garbage nonzero code. random_bytes(int $length): string: - Cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a constant length below 1. - Expressed as a single-source registry home file (src/builtins/math/random_bytes.rs) instead of the legacy hand-maintained tables; excluded from the legacy runtime-callable wrapper as an EIR-only builtin. Builtin docs + registry JSON regenerated. Callable-invoker ABI on Windows x86_64: - The descriptor-based runtime callable invoker read its descriptor from a hardcoded rdi, but on Windows the caller passes it in the MSx64 first arg (rcx); read it from int_arg_reg_name(target, 0) instead. - The invoker clone helpers passed arguments to the __rt_array_clone_shallow / __rt_array_to_mixed / __rt_hash_clone_shallow / __rt_hash_to_mixed / __rt_array_new runtime helpers via int_arg_reg_name, which returns the target user-call ABI (rcx/rdx on Windows); those helpers use the System V AMD64 ABI on every x86_64 target and read rdi/rsi. Add runtime_int_arg_reg_name (SysV on all x86_64, platform ABI on AArch64) and route the four helper call sites through it. No-op on Linux and AArch64.
a1b7fa8 to
b0d4620
Compare
Guikingone
added a commit
to Guikingone/elephc
that referenced
this pull request
Jul 7, 2026
…es builtin Reconstruct PR illegalstudio#446's feature set on top of current main. Windows x86_64 PE target: - Platform::Windows with the MSx64 ABI (rcx/rdx/r8/r9, 32-byte shadow, 16-byte alignment) and ~40 Win32 shim wrappers converting SysV->MSx64 (WriteFile/ReadFile, sockets, file/dir ops, VirtualAlloc/Heap, BCryptGenRandom, ...). - Syscall->shim transform wired into the pipeline + runtime cache (Windows-only) so PE binaries contain no raw Linux syscalls; unmapped syscalls route to __rt_unsupported_syscall instead of a silent trap. - Entry wrapper (MSx64->SysV), MinGW-w64 assembler/linker, kernel32/msvcrt/winmm/ ws2_32/bcrypt/shlwapi link set. --target windows-x86_64 (alias x86_64-pc-windows-gnu); .exe / .dll outputs. CI windows-pe-cross-compile job (MinGW + Wine) wired into the gate. Fix the implicit end-of-main exit code on Windows: emit_exit and emit_exit_with_result_reg now load the code into edi and call the __rt_sys_exit (ExitProcess) shim directly, identical to an explicit exit($n), instead of returning through the MinGW CRT. The CRT's exit() reaches the same rdi-consuming shim, so the old return path left rdi holding leftover data and exited with a garbage nonzero code. random_bytes(int $length): string: - Cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a constant length below 1. - Expressed as a single-source registry home file (src/builtins/math/random_bytes.rs) instead of the legacy hand-maintained tables; excluded from the legacy runtime-callable wrapper as an EIR-only builtin. Builtin docs + registry JSON regenerated. Callable-invoker ABI on Windows x86_64: - The descriptor-based runtime callable invoker read its descriptor from a hardcoded rdi, but on Windows the caller passes it in the MSx64 first arg (rcx); read it from int_arg_reg_name(target, 0) instead. - The invoker clone helpers passed arguments to the __rt_array_clone_shallow / __rt_array_to_mixed / __rt_hash_clone_shallow / __rt_hash_to_mixed / __rt_array_new runtime helpers via int_arg_reg_name, which returns the target user-call ABI (rcx/rdx on Windows); those helpers use the System V AMD64 ABI on every x86_64 target and read rdi/rsi. Add runtime_int_arg_reg_name (SysV on all x86_64, platform ABI on AArch64) and route the four helper call sites through it. No-op on Linux and AArch64. # Conflicts: # CHANGELOG.md # CLAUDE.md # docs/internals/builtins/math/mt_rand.md # docs/internals/builtins/math/rand.md # docs/internals/builtins/math/random_int.md # src/codegen/abi/mod.rs # src/codegen/builtins/arrays/call_user_func_array.rs # src/codegen/builtins/system/exit.rs # src/codegen/callable_dispatch.rs # src/codegen/prescan.rs # src/codegen/runtime_callable_invoker.rs # src/codegen_support/abi/bootstrap.rs # src/codegen_support/platform/windows_transform.rs # src/codegen_support/runtime/arrays/random_bytes.rs # src/codegen_support/runtime/emitters.rs # src/pipeline.rs
d2bb042 to
d31d8bf
Compare
a092225 to
740160c
Compare
Guikingone
added a commit
to Guikingone/elephc
that referenced
this pull request
Jul 10, 2026
…es builtin Reconstruct PR illegalstudio#446's feature set on top of current main. Windows x86_64 PE target: - Platform::Windows with the MSx64 ABI (rcx/rdx/r8/r9, 32-byte shadow, 16-byte alignment) and ~40 Win32 shim wrappers converting SysV->MSx64 (WriteFile/ReadFile, sockets, file/dir ops, VirtualAlloc/Heap, BCryptGenRandom, ...). - Syscall->shim transform wired into the pipeline + runtime cache (Windows-only) so PE binaries contain no raw Linux syscalls; unmapped syscalls route to __rt_unsupported_syscall instead of a silent trap. - Entry wrapper (MSx64->SysV), MinGW-w64 assembler/linker, kernel32/msvcrt/winmm/ ws2_32/bcrypt/shlwapi link set. --target windows-x86_64 (alias x86_64-pc-windows-gnu); .exe / .dll outputs. CI windows-pe-cross-compile job (MinGW + Wine) wired into the gate. Fix the implicit end-of-main exit code on Windows: emit_exit and emit_exit_with_result_reg now load the code into edi and call the __rt_sys_exit (ExitProcess) shim directly, identical to an explicit exit($n), instead of returning through the MinGW CRT. The CRT's exit() reaches the same rdi-consuming shim, so the old return path left rdi holding leftover data and exited with a garbage nonzero code. random_bytes(int $length): string: - Cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a constant length below 1. - Expressed as a single-source registry home file (src/builtins/math/random_bytes.rs) instead of the legacy hand-maintained tables; excluded from the legacy runtime-callable wrapper as an EIR-only builtin. Builtin docs + registry JSON regenerated. Callable-invoker ABI on Windows x86_64: - The descriptor-based runtime callable invoker read its descriptor from a hardcoded rdi, but on Windows the caller passes it in the MSx64 first arg (rcx); read it from int_arg_reg_name(target, 0) instead. - The invoker clone helpers passed arguments to the __rt_array_clone_shallow / __rt_array_to_mixed / __rt_hash_clone_shallow / __rt_hash_to_mixed / __rt_array_new runtime helpers via int_arg_reg_name, which returns the target user-call ABI (rcx/rdx on Windows); those helpers use the System V AMD64 ABI on every x86_64 target and read rdi/rsi. Add runtime_int_arg_reg_name (SysV on all x86_64, platform ABI on AArch64) and route the four helper call sites through it. No-op on Linux and AArch64.
735ae01 to
d1e9c08
Compare
…es builtin Reconstruct PR illegalstudio#446's feature set on top of current main. Windows x86_64 PE target: - Platform::Windows with the MSx64 ABI (rcx/rdx/r8/r9, 32-byte shadow, 16-byte alignment) and ~40 Win32 shim wrappers converting SysV->MSx64 (WriteFile/ReadFile, sockets, file/dir ops, VirtualAlloc/Heap, BCryptGenRandom, ...). - Syscall->shim transform wired into the pipeline + runtime cache (Windows-only) so PE binaries contain no raw Linux syscalls; unmapped syscalls route to __rt_unsupported_syscall instead of a silent trap. - Entry wrapper (MSx64->SysV), MinGW-w64 assembler/linker, kernel32/msvcrt/winmm/ ws2_32/bcrypt/shlwapi link set. --target windows-x86_64 (alias x86_64-pc-windows-gnu); .exe / .dll outputs. CI windows-pe-cross-compile job (MinGW + Wine) wired into the gate. Fix the implicit end-of-main exit code on Windows: emit_exit and emit_exit_with_result_reg now load the code into edi and call the __rt_sys_exit (ExitProcess) shim directly, identical to an explicit exit($n), instead of returning through the MinGW CRT. The CRT's exit() reaches the same rdi-consuming shim, so the old return path left rdi holding leftover data and exited with a garbage nonzero code. random_bytes(int $length): string: - Cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a constant length below 1. - Expressed as a single-source registry home file (src/builtins/math/random_bytes.rs) instead of the legacy hand-maintained tables; excluded from the legacy runtime-callable wrapper as an EIR-only builtin. Builtin docs + registry JSON regenerated. Callable-invoker ABI on Windows x86_64: - The descriptor-based runtime callable invoker read its descriptor from a hardcoded rdi, but on Windows the caller passes it in the MSx64 first arg (rcx); read it from int_arg_reg_name(target, 0) instead. - The invoker clone helpers passed arguments to the __rt_array_clone_shallow / __rt_array_to_mixed / __rt_hash_clone_shallow / __rt_hash_to_mixed / __rt_array_new runtime helpers via int_arg_reg_name, which returns the target user-call ABI (rcx/rdx on Windows); those helpers use the System V AMD64 ABI on every x86_64 target and read rdi/rsi. Add runtime_int_arg_reg_name (SysV on all x86_64, platform ABI on AArch64) and route the four helper call sites through it. No-op on Linux and AArch64.
Run the codegen test suite under ELEPHC_TEST_TARGET=windows-x86_64 via MinGW + Wine in a 16-shard CI matrix, emit a per-shard JUnit report (profile.ci.junit), and compare each shard's failing-test set against a curated allow-list of known-good Windows tests (3137 tests) plus a known -failures list. The gate fails only when a previously-passing (allow-listed) test regresses or a known-failure unexpectedly passes, so the full-suite parity percentage stays informational. Adds scripts/gen_windows_codegen_ allowlist.py to regenerate the lists from a full run, and drops 2 wine-flaky include_paths tests from the allow-list with retry tolerance.
…e path constants Windows x86_64 P0 runtime bundle (stacked on the PE32+ base): * `__rt_winsock_init` (WSAStartup MAKEWORD(2,2)) hooked into the main wrapper before `__elephc_main`, and `__rt_winsock_cleanup` (WSACleanup) into `__rt_sys_exit` before ExitProcess — socket shims previously called msvcrt socket funcs with no Winsock initialization. argc/argv are spilled across the init call (rcx/rdx are volatile on MSx64). * `__rt_sys_access` via GetFileAttributesA (INVALID_FILE_ATTRIBUTES -> -1) plus a C-symbol `access` stub. * `__rt_sys_ftruncate` via SetFilePointerEx + SetEndOfFile (fd spilled across the seek) plus a C-symbol `ftruncate` stub so the existing `call ftruncate` lowering resolves on Windows with no lowering change. * C-symbol `umask` no-op stub (mirrors php-src Windows behavior). * Transform routing for syscall 21 (access), 77 (ftruncate), and 82 (rename) — the `__rt_sys_rename` shim was previously orphaned. Target-aware path constants via the existing `PHP_OS` ConstRef precedent: `PHP_EOL`, `DIRECTORY_SEPARATOR`, `PATH_SEPARATOR` now resolve in `prescan::collect_constants` using new `Platform::php_eol()` / `directory_separator()` / `path_separator()` helpers (Windows: CRLF, "\\", ";"; elsewhere: LF, "/", ":"). macOS/Linux behavior is unchanged. `emit_shim_exit` prologue corrected to `sub rsp, 48` (N % 16 == 0) after `and rsp, -16` so WSACleanup and ExitProcess stay 16-byte aligned; a regression test locks the alignment invariant.
…link search-path Cross-build PCRE2 10.42, zlib 1.3.1, bzip2 1.0.8, and libiconv 1.17 from source for x86_64-w64-mingw32 into an actions/cache'd sysroot on the ubuntu Windows-parity runner, since the apt ELF dev libs cannot link into MinGW PE and no packaged MinGW pcre2/bz2/iconv dev libs exist. Surface the sysroot to both the production link (src/linker.rs) and the test harness (tests/codegen/support/runner.rs) via -L/lib added before the -l args, gated on ELEPHC_MINGW_SYSROOT (set in CI, unset on local non-CI builds so no missing-directory warnings). The external-C libs (-lpcre2-8 -lpcre2-posix -lbz2 -lz) were already pushed unconditionally by runtime_features.rs; they only needed a search path. Add require_windows_builtin_library mirroring the macOS helper, and use it for the convert.iconv.* stream filter so Windows emits -liconv (msvcrt ships no iconv), resolved by the cross-built libiconv. Recovers ~90 Windows codegen tests (regex/preg 67 + phar 15 + iconv 5) pending CI validation.
…dows-gnu Cross-build the elephc-image/pdo/crypto/tz bridge staticlibs for --target x86_64-pc-windows-gnu (PE/COFF) when the test target is windows-x86_64, so MinGW can link them into the .exe. The host (ELF) bridge archives MinGW cannot link into PE were the remaining link-config gap after Tier 1's external-C sysroot. tests/codegen/support/runner.rs: ensure_bridge_staticlibs gains --target x86_64-pc-windows-gnu + CC/AR/RANLIB_x86_64_pc_windows_gnu env (gated on Platform::Windows) so PDO's bundled libsqlite3-sys amalgamation is compiled by x86_64-w64-mingw32-gcc; bridge_staticlib_dir resolves to <target>/x86_64-pc-windows-gnu/debug on Windows so MinGW finds the PE archives. Non-Windows path is byte-identical to the pre-Tier-2 path (cargo_target=None skips the --target/env block; subdir stays 'debug'). Three pure helpers + unit tests cover the gating without env mutation. ci.yml: rustup target add x86_64-pc-windows-gnu in the windows-codegen- parity job; its bridge prebuild uses a new BRIDGE_CRATES_WINDOWS subset (image/pdo/crypto/tz) with --target + CC/AR/RANLIB env. elephc-phar (bzip2-sys system libbz2, ELF-only on the ubuntu host) and elephc-tls (ring asm) are excluded from the Windows prebuild so their C/cc-rs deps cannot block the job; on-demand ensure_bridge_staticlibs still attempts them per-fixture and nextest isolates any failure. macOS/Linux bridge steps keep $BRIDGE_CRATES with no --target. Recovers ~210 Windows codegen tests (image ~164 + pdo 37 + crypto 10) pending CI validation.
Add Windows x86_64 runtime coverage for the P0 time/process primitives: - sleep/usleep C-symbol stubs in emit_shim_c_symbols: delegate to Win32 Sleep (seconds->ms via imul; microseconds->ms via div by 1000), return 0. Resolves the direct 'call sleep'/'call usleep' sites from lower_sleep/ lower_usleep on Windows. - __rt_sys_getrusage syscall shim (route 98): GetProcessTimes with the current-process pseudo-handle (HANDLE)-1, FILETIME->timeval conversion (div by 10_000_000 then 10) into ru_utime/ru_stime; success path zeros all 14 resource fields ru_maxrss..ru_nivcsw (offsets 32..144); non-SELF who zeros the full struct and returns 0; failure returns -1. - WIN32_IMPORTS += Sleep, GetProcessTimes. - 4 win32 unit tests + 2 compile-only codegen tests. poll (syscall 7) deliberately deferred: WSAPOLLFD (16B, 64-bit SOCKET fd) is 2x the Linux pollfd (8B, int fd), so a shim needs a conversion loop -> __rt_poll helper coupled with the stream_select/network surface (Action 2).
…eam_select via ws2_32 select
The pselect6 fd_set conversion loops used `mov rcx, r10d` (64-bit dest, 32-bit source), which GAS rejects with an operand-type mismatch, so the Windows stream_select path failed to assemble under MinGW. Switch to `mov ecx, r10d` (32-bit to 32-bit, zero-extends to rcx); `cl` still holds the shift count b, so semantics are unchanged.
Add the proc_open/proc_close builtins as a cross-target PHP surface (C1a of the proc_open effort). The real fork/pipe/exec runtime lands in C1b (Linux/macOS) and C1c (Windows); this slice ships loud stubs returning -1 so the surface compiles and links on every supported target and boxes as PHP false. - builtin homes: src/builtins/io/proc_open.rs (Union(stream_resource, Bool) check hook) and proc_close.rs (no check hook), registered via pub mod in src/builtins/io/mod.rs. - EIR lowering: lower_proc_open (ABI AArch64 x0=descriptor_spec array ptr, x1=command ptr, x2=command len, x3=pipes array ptr; x86_64 rdi/rsi/rdx/rcx) boxes via box_stream_fd_or_false_result_kind kind 5; lower_proc_close mirrors lower_pclose with __rt_proc_close. - runtime stubs: __rt_proc_open/__rt_proc_close return -1 on every target (AArch64 + x86_64 Linux/Windows), registered in io/mod.rs + emitters.rs. - Mixed resource kind 5 in __rt_mixed_free_deep (both arches) dispatches to __rt_proc_close as the scope-exit destructor. - .comm _proc_pids/_proc_child_fds tables in data/fixed.rs. - runtime_builtin_wrapper_excluded entries in callable_dispatch.rs. - tests: tests/codegen/io/proc.rs (run + compile), windows_pe.rs compile-only, error_tests/io_builtins/streams.rs arity checks; docs in docs/php/builtins/process/.
The MinGW cross-build of PCRE2 10.42 runs `make`, which builds `pcre2posix_test.exe` — a noinst_PROGRAMS entry — and on the new ubuntu-24.04 runner image (20260628.225) this test binary fails to link static-only. pcre2-posix is configured --disable-shared, so the test expects `__imp_pcre2_reg*` dllimport stubs the static archive cannot provide (undefined reference to __imp_pcre2_regcomp/regexec/ regerror/regfree; collect2: ld returned 1; make Error 2; exit 2). This broke all Windows parity shards at the sysroot-build step. PCRE2 10.42 has no configure flag to drop the POSIX wrapper (`--disable-pcre2posix` is unrecognized and silently ignored), and the libpcre2-posix.la library itself links fine — only the test binary breaks. Under our configure flags (no --enable-jit / --enable-fuzz-support / --enable-rebuild-chartables) pcre2posix_test is the only noinst program, so override noinst_PROGRAMS= on both make and make install to skip it. elephc links only -lpcre2-8, so the unused libpcre2-posix.a landing in the sysroot is harmless.
CMake's zlib 1.3.1 installs the static archive as libzlibstatic.a (and, despite -DBUILD_SHARED_LIBS=OFF on the current ubuntu-24.04 image, a shared import lib libzlib.dll.a); neither filename matches the -lz stem the Windows link arm (src/linker.rs) and the test harness (tests/codegen/support/runner.rs) pass the linker, nor the libz.a the MinGW sysroot sanity ls checks. The v1 sysroot cache masked this (it held a usable libz.a from an older build); bumping the cache key to v2 forced a fresh rebuild, exposing the mismatch and failing all 16 Windows Codegen Parity shards at the sanity ls (exit 2). Alias the static archive to the canonical libz.a so -lz resolves to the static lib and the sanity ls finds it, reproducing what the cached sysroot provided. No cache-key bump is needed: actions/cache@v4 only saves on job success, so the broken v2 cache was never persisted and the next run rebuilds with the symlink in place.
…64 runtime
The Windows x86_64 (PE32+) target emits SysV AMD64 assembly from every
runtime emitter, but links against MinGW msvcrt/ws2_32/zlib/pcre2/iconv/
libbz2 (Microsoft x64 ABI) and shuffles MSx64->SysV only at the syscall
boundary. Three latent ABI-mismatch classes silently corrupted args or
misread returns on windows-x86_64 (invisible on the macOS/Linux CI, which
never exercises the MSx64 boundary). This commit eradicates all three.
Class 1 (~100 sites) — a bare `call <libc>` from an x86_64 emitter
resolved to an msvcrt/ws2_32/zlib/pcre2/iconv/bzip2 import entered with
SysV registers. Introduce `Emitter::emit_call_c` (codegen_support/emit.rs):
on windows-x86_64 it emits `call __rt_sys_<sym>` when the symbol is in the
`windows_c_shim_name` registry, `call <sym>` when it is a SysV stub-
delegate, else panics — a build-time exhaustiveness guard so this class
cannot silently regrow. On every other target it delegates to `bl_c`, so
Linux-x86_64/macOS/aarch64 emission is byte-identical. Add ~55 SysV->MSx64
`__rt_sys_*` shims: datetime (time/localtime/gmtime/mktime/putenv->_putenv/
tzset->_tzset/getenv/gettimeofday-via-GetSystemTimeAsFileTime), math/FP
(pow/sin/cos/.../fmod/round — FP args pass through xmm0/xmm1 unchanged, the
shim only provides the 32-byte shadow space the bare call lacked), zlib
(compress2/deflate/inflate/deflateInit2_ 8-arg reading its 2 caller-stack
args before its own frame/...), pcre2 (regcomp/regexec-5arg/regfree) +
libc malloc/free, net/dns via ws2_32 (getaddrinfo/inet_pton/inet_ntop/
gethostbyaddr), misc (strtoll->_strtoi64, atof, dup->_dup with cdqe,
chown/lchown as no-op-success per php-src, setlocale), iconv (real shims,
libiconv linked), bzip2 (BZ2_bzCompress/Init/End/BuffToBuffDecompress-6arg).
Semantic-rewrite gaps (opendir/readdir/closedir/rewinddir/scandir/mkstemp,
absent from msvcrt) get loud-fail stubs returning the consumer's failure
sentinel, with the FindFirstFileA/GetTempFileNameA rewrite deferred. Also
fix three platform values in platform/target.rs that the now-ABI-correct
net shims made load-bearing: AF_INET6 (Windows=23), LC_CTYPE (Windows=2),
ADDRINFOA ai_addr offset (Windows=32, matching the Win64 BSD-style layout),
and the hardcoded Linux.af_inet6() in format_sockaddr's shared x86_64 path.
Class 2 (~107 sites) — `abi::int_arg_reg_name` returns the MSx64 arg
sequence (rcx/rdx/r8/r9) on windows-x86_64, but many sites used it to stage
integer args for a hand-written SysV `__rt_*` runtime helper (reads rdi/
rsi/rdx). Relocate `runtime_helper_int_arg_reg` to abi/registers.rs
(pub(crate), arch-only, extended to 6 args) and swap it in at every such
site: hash/array allocation (__rt_hash_new/__rt_array_new), generators
(__rt_gen_suspend/delegate), fibers (__rt_fiber_start/resume/throw/suspend/
state_eq/construct), Mixed casts (__rt_mixed_cast_*), mktime, the array-
callback cluster (__rt_array_map/filter/reduce/walk/...), and SPL/exception
constructors. Because the helper is arch-only, the swap is byte-identical
off-Windows and only reroutes windows-x86_64. Codegen-emitted callees (the
descriptor invoker, --web handler, dynamic instance-method dispatch) keep
`int_arg_reg_name` — they use MSx64 on both sides. Fixing the fiber cluster
unblocks part of the fiber/generator known-failures on Windows.
Class 3 (14 shims) — a 32-bit Winsock/msvcrt int-status return (eax) was
zero-extended into rax, so a failure (-1) read as a positive 0xFFFFFFFF and
a downstream `test rax,rax; js`/`jl` missed it. Add `cdqe` after the call
in the int-status shims whose consumers sign-test: connect/bind/listen/
shutdown/getsockname/getpeername (split out of the shared socket loop),
setsockopt/recvfrom/sendto/dup/dup2/ioctl/lseek/pselect6. socket/accept
stay untouched (they return a 64-bit SOCKET handle).
Runtime-blob shim gating — the ~55 `__rt_sys_*` shims above are emitted by
`emit_win32_shims` into the per-program runtime object, and the four third-
party families call real library symbols (zlib compress2/..., bzip2 BZ2_*,
pcre2 pcre2_reg*, iconv iconv*). Those libraries are linked only per-program
(`check_result.required_libraries` + phar_archive->z,bz2 + regex->pcre2),
never in the base Windows link (-lkernel32/-lmsvcrt/-lwinmm/-lws2_32/
-lbcrypt/-lshlwapi). Emitting the shims unconditionally therefore made every
program that does not use those features reference symbols with no lib to
resolve them -> `undefined reference` at link -> every windows-x86_64 link
failed (the real wine gate, blind on the macOS/Linux CI, caught it). Gate
the four families on RuntimeFeatures: pcre2 on `regex` (already computed);
new `zlib`/`bzip2`/`iconv` fields (runtime_features.rs) derived in the
pipeline from the same `required_libraries` signal that drives the linking,
so shim emission and library linking stay consistent. The runtime object is
a pure function of (heap, target, RuntimeFeatures) keyed on its assembly
content hash, so gating yields distinct, correct cache objects automatically.
The msvcrt/kernel32/ws2_32 shims stay unconditional (their import libs are
always linked). Verified via nm: a trivial program now emits a runtime blob
with zero third-party references; a gzcompress program emits only the zlib
family.
Restore the ENT_* HTML-escaping constant fold-value registration in
prescan.rs (the import and the `for (name, value) in ENT_INT_CONSTANTS`
loop), which the rebase onto main dropped while resolving the same-file
conflict with the Windows path-constant additions. Without it
`ENT_QUOTES + ENT_HTML5` folded to 0 instead of 51 (htmlspecialchars output
was unaffected — the runtime hardcodes ENT_QUOTES escaping — but bare
constant arithmetic was not).
All emission on Linux-x86_64/macOS/aarch64 is byte-identical (the changes
are gated on windows-x86_64 or route through arch-only helpers). Verified
with zero compiler warnings, the full native codegen suite (5095/5095, 34
ignored), the win32 gating unit tests including a features-off regression
guard, and the windows-x86_64 runtime blobs confirmed clean/precise via nm.
Three windows-x86_64 runtime behaviors this campaign got wrong only surfaced
once the shim-gating link fix let the wine gate actually run them (they were
masked by the earlier undefined-reference link failure); the parity gate's
allow-list flagged all three. (1) chown/chgrp/lchown/lchgrp: the no-op
`__rt_sys_libc_chown`/`__rt_sys_libc_lchown` shim returned C-success (0)
unconditionally, so `chown("/missing", uid)` reported true; PHP wants false on
a nonexistent path. Fix: tail-jump to the existing `__rt_sys_access`
(GetFileAttributesA) existence probe — it takes the path in rdi like chown
does and returns 0 (exists) / -1 (missing), exactly the value the call sites
`cmp eax,0; sete` on; the `jmp` preserves rsp so its `sub rsp,40` still aligns
before GetFileAttributesA. (2) $argv: `__rt_sys_init_argv` used rsi as the
pass-1 scan cursor, so pass 2 restarted from the consumed cursor (the trailing
NUL) and stored zero argv slots, leaving `_global_argv[0]` null and crashing
`__rt_build_argv`'s strlen. Fix: spill the command-line start to the frame's
free 8-byte pad slot `[rsp+32]` (above the 32-byte shadow, untouched by
GetProcessHeap/HeapAlloc) and reload it for the pass-2 restart. (3)
stream_set_blocking(STDIN): STDIN is a console HANDLE, so the fcntl->ioctlsocket
path fails with WSAENOTSOCK; the Class-3 `cdqe` (correct for real socket ioctl
errors) turned the previously-accidental success into a correct-per-ABI false.
PHP's stdio set_option is NOTIMPL on Windows and stream_set_blocking maps that
to true, so a Windows-only helper reports best-effort success (`mov rax,1; ret`)
without a syscall; the cdqe is left intact. All three are windows-x86_64-only.
Verified by objdump of the cross-compiled runtime object plus win32 (56/56) and
stream_set_blocking unit tests; behavior confirmed by the wine parity gate.
d1e9c08 to
6cb893a
Compare
Replace the C1a loud stubs of __rt_proc_open/__rt_proc_close with a real fork/pipe/execve + wait4 implementation for the three POSIX targets (macOS-aarch64, Linux-aarch64, Linux-x86_64). Windows-x86_64 keeps a loud stub, now labelled C1c (the CreateProcessA runtime lands next). Pipe-only: each descriptor must be ["pipe","r"|"w"]; any other shape makes proc_open return -1 (documented C1b limitation). The descriptor count is bounded at 8 and the descriptor_spec array may be indexed (kind 2) or hash (kind 3) — __rt_array_get_mixed_key is representation-agnostic. The child takes the mode-appropriate pipe end (write for "r", read for "w") and the parent end is pushed into the by-ref $pipes array via __rt_array_push_refcounted. proc_close reaps with wait4 and returns the raw exit code (status >> 8) & 0xff, or -1 on failure; the lowerer stamps a -1 sentinel into the resource box so the kind-5 destructor never re-reaps. Target caveats baked into the emitters: the raw macOS fork syscall returns the child pid in BOTH parent and child (disambiguated by comparing getpid before/after), SYS_close is 6 on macOS (not 3, which is read), and the Linux svc does not set flags so an explicit cmp precedes each conditional branch. Raw syscalls are emitted directly to keep the helper self-contained. The by-ref pipes ABI passes $pipes by value-pointer, so the helper must not reallocate the array; the caller pre-sizes $pipes to capacity 4 and n<=4 appends in place, which covers the pipe-only test surface. n in 5..8 is a lowerer-side follow-up. Tests: proc_open returns a resource, proc_close returns the exit status; the by-ref pipe readback test is #[ignore]d because the checker infers $pipes[n] as never (by-ref pipe writes are not yet type-tracked). The Windows PE compile-only test is retargeted to the C1c stub.
…teProcessA) Replace the C1c loud stubs of the Windows-x86_64 __rt_proc_open/__rt_proc_close with a real process-spawning runtime built on CreatePipe + CreateProcessA + WaitForSingleObject + GetExitCodeProcess, completing proc_open on all four supported targets (the three POSIX ones landed in C1b). __rt_proc_open (emit_proc_open_win32_x86_64) mirrors the C1b pipe-only shape: the descriptor_spec parsing, validation (indexed/hash kind, 1<=n<=8), the "pipe"/"r" comparisons, the owned-box decref sequence, and the by-ref $pipes push via __rt_array_push_refcounted are the same shared x86_64 internal-helper calls as the Linux path. Only the OS mechanism differs: CreatePipe replaces pipe2 (with an inheritable SECURITY_ATTRIBUTES; the parent end is made non-inheritable via SetHandleInformation so the child cannot deadlock on it), STARTUPINFOA.hStdInput/hStdOutput/hStdError with STARTF_USESTDHANDLES replaces dup2 (descriptor index i wires the child end into fd i for i<3; i>=3 still gets a real inheritable pipe pushed into $pipes but has no numbered-fd slot, a documented C1c limitation), and CreateProcessA replaces fork/execve. The command string is wrapped as `cmd.exe /s /c "<command>"` in a HeapAlloc'd writable buffer (matching php-src's string-form proc_open and the C1b /bin/sh -c shell), so no argv->cmdline escaping is needed for the string surface. SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX) suppresses the modal dialogs that would hang a headless runner; any fd 0/1/2 without a descriptor is redirected to NUL; CREATE_NO_WINDOW keeps the child windowless. On success the parent closes every child end plus the unused thread handle and returns hProcess (the lowerer boxes it as a kind-5 resource); every failure path closes the handles opened so far, frees the cmdbuf, and returns -1. The three Win32 structs (SECURITY_ATTRIBUTES, STARTUPINFOA, PROCESS_INFORMATION) use standard C upward layout: field at byte offset F lives at base+F where base is the lowest stack address and the pointer passed to the API, matching the existing pselect6 fd_set convention. __rt_proc_close (emit_proc_close_win32_x86_64) reaps via WaitForSingleObject(INFINITE) + GetExitCodeProcess + CloseHandle, returning the child exit code or -1; the lowerer's -1 sentinel keeps the kind-5 destructor from re-reaping. Six Win32 imports added (CreatePipe/CreateProcessA/ WaitForSingleObject/GetExitCodeProcess/SetHandleInformation/SetErrorMode). The Windows PE compile-only tests cover the single- and three-pipe descriptor paths; the POSIX/aarch64 proc_open/proc_close emitters are byte-identical.
Fix seven windows-x86_64 builtin shims whose behavior diverged from php-src, each verified against its runtime consumer and the target.rs struct layout. Every Win32 struct/out-param is laid out upward (field at base+offset); non-Windows emitters stay byte-identical. - rename(): overwrite an existing target via MoveFileExA (MOVEFILE_REPLACE_EXISTING), so php-src's write-temp-then-rename works, and translate the Win32 BOOL result to the POSIX status __rt_rename tests with `cmp eax, 0` (a successful rename previously reported failure, and a failed rename reported success). - link(): translate the CreateHardLinkA BOOL to POSIX (0/-1) so a successful hard link no longer reports failure. - disk_free_space()/disk_total_space(): __rt_sys_statfs was a stub that never called the API and left the caller's statfs buffer uninitialized; fill f_bsize/f_blocks/f_bavail from GetDiskFreeSpaceExA. - sys_get_temp_dir(): return the real Windows temp path from GetTempPathA (owned, heap-stamped string) instead of the hardcoded "/tmp". - php_uname(): fill the utsname buffer from GetComputerNameA / GetNativeSystemInfo instead of calling a nonexistent msvcrt `uname` (which self-recursed through the shim). - touch(): apply the requested atime/mtime via SetFileTime instead of ignoring the timestamps (the utimensat shim was a no-op). - fileinode(): synthesize st_ino from GetFileInformationByHandle's nFileIndexHigh:nFileIndexLow pair instead of leaving it 0. Adds GetDiskFreeSpaceExA, GetTempPathA, GetComputerNameA, GetNativeSystemInfo, GetFileInformationByHandle, GetSystemTimeAsFileTime, SetFileTime and MoveFileExA to WIN32_IMPORTS. Only the Windows arms and the Windows branch of lower_sys_get_temp_dir change. Full codegen suite 5102 passed / 0 failed / 34 ignored; win32 unit 58/0 and windows_pe 29/0; warning-clean.
…guide (W9)
The 5585-LOC win32/mod.rs mixed ~75 shim emitters, a 762-LOC C-symbol block,
and a 1100-LOC test module in a single mod.rs — against the "keep mod.rs slim,
split aggressively" file-size policy. Split it into a 212-LOC orchestrator plus
cohesive sibling submodules. This is a PURE MOVE: the emitted assembly is
byte-for-byte identical.
- mod.rs (212) orchestrator: emit_win32_shims, emit_main_wrapper,
emit_fd_to_handle, X86_64_HEAP_MAGIC_HI32, mod/use plumbing
- imports.rs (340) WIN32_IMPORTS, emit_win32_imports, windows_c_shim_name
- shims_fs.rs (693) fd / file / dir family
- shims_time.rs (307) clock / env / time family
- shims_net.rs (840) socket / winsock / dns family
- shims_compress.rs zlib, bzip2, iconv
- shims_pcre.rs (139) pcre2-posix, malloc / free
- shims_misc.rs (657) exit / argv / mmap / math-fp / dup / kill / uname / ...
- shims_c_symbols.rs the c_symbols delegate block + msvcrt passwd lookup
- tests.rs (1113) the 57 win32 unit tests
Verified byte-identical: the emit_win32_shims + emit_main_wrapper +
emit_fd_to_handle dump (2868 lines) has the same SHA-256 before and after the
split. No emitter.instruction string, comment, label, or byte offset changed —
only item location and visibility (fns called by the orchestrator became
pub(super); the two externally-referenced items keep pub(crate) via re-export).
Also documents the hard-won Windows x86_64 (MSx64) ABI rules in AGENTS.md:
shadow space, rsi/rdi non-volatility, 40/56-byte stack alignment, the upward
struct/out-param layout rule, BOOL->POSIX status translation, Class-3 sign
extension, and the ntdll-not-in-link-set caveat.
win32 unit 57/0, windows_pe 29/0, warning-clean.
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.
Closes #379.
Overview
Implements complete Windows x86_64 PE/COFF cross-compilation support. The compiler produces standalone Windows .exe binaries from PHP source, cross-compiled from macOS/Linux using MinGW-w64.
Win32 API Coverage
All POSIX syscalls and C library functions are mapped to real Win32 API implementations:
Genuine platform limitations (no Win32 equivalent exists):
Architecture: SysV-to-MSx64 Shim Layer
Instead of rewriting ~650 syscall sites across ~100 runtime files, a shim layer converts the existing SysV calling convention to MSx64 ABI before calling Win32 API functions. All Windows-specific complexity lives in one module.
Verified Output
CI
New
windows-pe-cross-compilejob on ubuntu-24.04 with MinGW-w64:Toolchain
Windows Codegen Parity Gate
On top of the PE cross-compile, this PR adds a curated no-regression gate so Windows codegen parity can only improve, never silently regress. The full codegen suite now runs under
windows-x86_64(MinGW + Wine) viaELEPHC_TEST_TARGET=windows-x86_64intests/codegen/support/.tests/codegen/support/windows_codegen_allowlist.txtlocks the 3118 known-good tests (2 wine-flakyinclude_pathstests excluded). Thewindows-codegen-parityjob (16 shards) emits JUnit; thewindows-codegen-gateaggregator asserts every allow-listed test passed (intersection-assert). New and known-failing tests do not gate — so parity can only rise.scripts/gen_windows_codegen_allowlist.py(generate/gate) keeps the lists deterministic and fails on stale input.docs/compiling/targets.mdgains a "Windows codegen parity gate" section.The harness is a byte-identical no-op when
ELEPHC_TEST_TARGETis unset, so native macOS/Linux tests are unaffected.