Fil-C on Nix

Filnix wiki

mbrock/filnix

ports/overlay.nix

Overlay for Fil-C ported packages
Converts the port list from ../ports.nix into a nixpkgs overlay

λpkgs final prev
let
6portDSL=
import./default.nix inherit pkgs lib pkgs;
7portList=
import../ports.nix inherit pkgs prev final;

Fil-C overrides for a Nix component scope, shared by upstream Nix and Determinate Nix. Boehm GC scans the native stack and data segments, which Fil-C objects do not live in; Fil-C collects garbage itself. seccomp filters and the static busybox sandbox shell are unavailable too.

13nixFilcOverrides=
λnfinal nprev

Fil-C has no LTO; Nix enables it for its release builds.

15mesonComponentOverrides=
λfinalAttrs prevAttrs
let
18base=
nprev.mesonComponentOverridesfinalAttrs prevAttrs
in
//
22preConfigure=
base.preConfigure or prevAttrs.preConfigure or ""
+
appendToVar mesonFlags "-Db_lto=false"

Fil-C has no sigaltstack, and it catches stack overflow itself.

27nix-main=
nprev.nix-main.overrideAttrs
λold
28postPatch=
old.postPatch or ""
+
"\n"
+
substituteInPlace $(find . -path '*/unix/stack.cc') --replace-fail \
'#if defined(SA_SIGINFO) && defined(SA_ONSTACK)' \
'#if defined(SA_SIGINFO) && defined(SA_ONSTACK) && !defined(__FILC__)'
37nix-functional-tests=
nprev.nix-functional-tests.overrideAttrs
λold

The suite runs whichever nix is on PATH. Nixpkgs asks for nix-cli.__spliced.hostHost, which this scope lacks, so it fell back to the build platform's (glibc) Nix: every functional test exercised native Nix, and the Fil-C test plugin could not load into it (undefined pizlonated_* symbols). Fil-C programs run on the build machine, so test the Fil-C Nix.

44nativeBuildInputs=
mapp: if p.pname or "" ≡ "nix" then nfinal.nix-cli else p old.nativeBuildInputs
47postPatch=
old.postPatch or ""
+
"\n"
+
pushd "$(dirname "$(find -L . -path '*/common/vars.sh' -print -quit)")/.." >/dev/null
# The harness enables sandbox tests when `unshare --user` works,
# but this Nix cannot sandbox (no clone; see nix-util below).
# Those tests (remote builds, chroot and overlay stores, ...)
# then build with store paths that only exist inside a sandbox.
substituteInPlace common/vars.sh --replace-fail \
'&& unshare --user true; then' '&& false; then'
popd >/dev/null

Fil-C's libc has no vfork.

62nix-util=
nprev.nix-util.overrideAttrs
λold
63postPatch=
old.postPatch or ""
+
substituteInPlace $(find . -path '*/unix/processes.cc') \
--replace-fail 'allowVfork ? vfork() : fork()' 'fork()'
# Nor clone, which the namespace probes use: report no user,
# mount or PID namespaces.
substituteInPlace $(find . -path '*/linux/linux-namespaces.cc') \
--replace-fail 'bool userNamespacesSupported()
{' 'bool userNamespacesSupported()
{
return false;' \
--replace-fail 'bool mountAndPidNamespacesSupported()
{' 'bool mountAndPidNamespacesSupported()
{
return false;'
79nix-store=
nprev.nix-store.overridewithSandboxShell = false; withAWS = false;.overrideAttrs
λold

Without seccomp, builds refuse to start unless filter-syscalls is off, so make that the default.

87postPatch=
old.postPatch or ""
+
"\n"
+
f=$(find . -path '*/nix/store/local-settings.hh')
sed -i '/Setting<bool> filterSyscalls{/,/"filter-syscalls"/ s/^\( *\)true,$/\1false,/' "$f"
grep -A2 'Setting<bool> filterSyscalls{' "$f" | grep -q false,

Fil-C's loader ignores RUNPATH when dlopening a bare soname, so name libc's nss_dns by path. NixOS's nix.conf check fails on the warning otherwise.

+
substituteInPlace globals.cc --replace-fail \
'dlopen(LIBNSS_DNS_SO,' \
'dlopen("${final.stdenv.cc.libc}/lib/" LIBNSS_DNS_SO,'
103buildInputs=
builtins.filterdep: ¬pkgs.lib.hasInfix"libseccomp" dep.name or "" old.buildInputs
106mesonFlags=
map
λflag
flag ≡ "-Dseccomp-sandboxing=enabled"
→
"-Dseccomp-sandboxing=disabled"
else
old.mesonFlags

A program from the Fil-C sysroot's glibc.

117libcTool=
λname
final.runCommandname meta.mainProgram = name;
mkdir -p $out/bin
ln -s ${final.stdenv.cc.libc}/bin/${name} $out/bin/${name}

Fil-C PHP 8.4 or 8.5 at a given release. Opt-in JIT builds use the in-process LLVM API on staging, or the early C-emitting backend on the released toolchain (docs/php.md and docs/php-port-notes.md).

127mkFilcPhp=
λversion, hash, jit?
version
hash
jitdefault false
let

PHP 8.5 builds Lexbor and OPcache into PHP itself rather than as shared extensions, so their patches and flags move to the core.

136is85=
final.lib.versionAtLeastversion "8.5"
used by php
137opcachePatches=
used by php
141php=
final.callPackage"${prev.path}/pkgs/development/interpreters/php" llvmPackages = prev.llvmPackages // inherit final stdenv;;."php${final.lib.versions.majorversion}${final.lib.versions.minorversion}".overrideinherit version hash; pcre2 = let pcre2 = final.pcre2.overridewithJitSealloc = false;; in if is85 then pcre2.overrideAttrsold: patches = old.patches or [ ] ++ ../patches/pcre2-10.48-filc-match.patch ./patch/pcre2-10.48.patch; configureFlags = old.configureFlags ++ "--disable-jit"; doCheck = true; else pcre2; packageOverrides = _: pprev: let withTests = ext: ext.overrideAttrsold: doCheck = true; checkPhase = '' runHook preCheck sed -i "s|^PHP_TEST_SHARED_EXTENSIONS = |&$( sed -n 's/^\(zend_\)\?extension=/-d &/p' ${php.withExtensionsenabled, …: enabled ++ old.peclDeps or [ ] ++ old.internalDeps or [ ]}/lib/php.ini | tr '\n' ' ' )|" Makefile export NO_INTERACTION=1 SKIP_ONLINE_TESTS=1 SKIP_SLOW_TESTS=1 SKIP_PERF_SENSITIVE=1 # run-tests matches EXPECTF sections with PCRE, which has no # JIT here, so lines with many %s exhausted the default # backtrack limit and passing tests failed. run-tests # itself runs with -n, its parallel workers with no # options; the tests run with -n and see neither. settings="PHP_TEST_SETTINGS=-d open_basedir= -d output_buffering=0 -d memory_limit=-1 -d pcre.backtrack_limit=100000000" mkdir -p "$TMPDIR/run-tests-ini" echo pcre.backtrack_limit=100000000 > "$TMPDIR/run-tests-ini/pcre.ini" export PHP_INI_SCAN_DIR="$TMPDIR/run-tests-ini" USE_ZEND_ALLOC=1 \ make "$settings" test TESTS="-j''${phpTestJobs:-$NIX_BUILD_CORES} -q --show-diff" # Zend's allocator carves request memory out of 2 MiB # chunks, so Fil-C bounds a PHP value by its chunk, and # overflows and use after free between values go # unnoticed. PHP defaults to the system allocator here # (php-8.4-alloc-filc.patch), which gives each allocation # its own bounds; run the tests on both. SKIP_ASAN # skips the tests that need Zend's allocator, as in # upstream's ASan runs, and zendAllocOnlyTests lists those # that need it without saying so, like memory_limit tests. rm -f ${toStringold.zendAllocOnlyTests or [ ]} USE_ZEND_ALLOC=0 SKIP_ASAN=1 \ make "$settings" test TESTS="-j''${phpTestJobs:-$NIX_BUILD_CORES} -q --show-diff" runHook postCheck '';; withoutTests = files: ext: ext.overrideAttrsold: postPatch = old.postPatch or "" + '' rm ${toStringfiles} '';; withPkgConfig = ext: ext.overrideAttrsold: preConfigure = old.preConfigure or "" + '' configureFlagsArray+=("ac_cv_path_PKG_CONFIG=$(command -v $PKG_CONFIG)") '';; withMemcached = ext: ext.overrideAttrsold: phpTestJobs = 1; preCheck = old.preCheck or "" + '' for port in 11211 11212; do ${final.buildPackages.memcached}/bin/memcached -d -p $port -U 0 -u "$(id -un)" done '';; in extensions = pprev.extensions // final.lib.genAttrs"bz2" "dba" "snmp" "sysvmsg" "sysvshm" "tidy" "xsl" "zend_test" "amqp" "ast" "decimal" "ds" "event" "excimer" "gnupg" "igbinary" "imap" "inotify" "mailparse" "maxminddb" "memcache" "memcached" "meminfo" "msgpack" "pcov" "phalcon" "pinba" "protobuf" "pspell" "rdkafka" "redis" "snuffleupagus" "spx" "ssh2" "systemd" "uuid" "uv" "vld" "wikidiff2" "yaml" "zstd" name: withTestspprev.extensions.${name} // dom = pprev.extensions.dom.overrideAttrsold: patches = old.patches or [ ] ++ if is85 then ../patches/php-8.5-dom-filc.patch else ../patches/php-8.4-dom-filc.patch;; filter = pprev.extensions.filter.overrideAttrsold: final.lib.optionalAttrsfinal.lib.versionAtLeastversion "8.4.27" postPatch = old.postPatch or "" + '' substituteInPlace ext/filter/tests/oom_filter_{default_encoded,input,input_array}.phpt \ --replace-fail "'-n'," "'-n', '-d', 'extension=$PWD/ext/filter/modules/filter.so'," '';; pcntl = pprev.extensions.pcntl.overrideAttrsold: postPatch = old.postPatch or "" + '' rm ext/pcntl/tests/pcntl_cpuaffinity.phpt '';; gd = pprev.extensions.gd.overrideAttrsold: configureFlags = old.configureFlags ++ mapf: "php_cv_lib_gd_gdImageCreateFrom${f}=yes" "Png" "Avif" "Webp" "Jpeg" "Xpm" "Bmp" "Tga";; intl = pprev.extensions.intl.overrideAttrsbuildInputs = final.icu;; // final.lib.optionalAttrs¬is85 opcache = pprev.extensions.opcache.overrideAttrsold: patches = old.patches or [ ] ++ opcachePatches ++ final.lib.optionaljit ../patches/php-8.4-opcache-jit-c-filc.patch; // if jit then postPatch = old.postPatch or "" + '' substituteInPlace ext/opcache/jit/zend_jit_c.c \ --replace-fail '#define ZEND_JIT_C_CC "clang"' \ '#define ZEND_JIT_C_CC "${final.gnufilc0}/bin/clang"' ''; else configureFlags = old.configureFlags ++ "--disable-opcache-jit";; // shmop = withTestspprev.extensions.shmop.overrideAttrsold: patches = old.patches or [ ] ++ ../patches/php-8.4-shmop-filc.patch;; ffi = withTestspprev.extensions.ffi.overrideAttrsold: postPatch = old.postPatch or "" + '' # Frees a malloc() result rebuilt from its address as an # integer, which has no capability; Fil-C stops it. rm ext/ffi/tests/gh14626.phpt # Tests that skip without libc.so.6; Fil-C's is .6666. sed -i 's/libc\.so\.6\b/libc.so.6666/g' ext/ffi/tests/*.phpt '';; apcu = withTestspprev.extensions.apcu.overrideAttrsold: patches = old.patches or [ ] ++ ../patches/php-apcu-filc.patch;; amqp = withTestspprev.extensions.amqp.overrideAttrsold: configureFlags = old.configureFlags or [ ] ++ "ac_cv_librabbitmq_version=${final.rabbitmq-c.version}";; gnupg = withTestspprev.extensions.gnupg.overrideAttrsphpTestJobs = 1;; meminfo = withoutTests"tests/bug-github-76_children_items_not_linked_php7.phpt" withTestspprev.extensions.meminfo.overrideAttrsold: patches = old.patches or [ ] ++ ../patches/php-meminfo-undef.patch; // final.lib.optionalAttrsis85 zendAllocOnlyTests = "tests/dump-memory_leak.phpt";; memcache = withMemcachedwithoutTests"tests/035.phpt" "tests/053.phpt" "tests/056.phpt" withTestspprev.extensions.memcache; memcached = withMemcachedwithPkgConfigwithoutTestsmapt: "tests/${t}.phpt" "experimental/extreme_floats" "experimental/getdelayed_cbthrows" "experimental/get_udp" "experimental/session_gc" "experimental/setmulti_badserialize" "experimental/stats_badserver" "experimental/stats" "fetchall_badunserialize" "fetch_badunserialize" "getdelayed_badunserialize" "getmulti_badunserialize" "experimental/serializer/serializer_php_bad_serialize" "experimental/serializer/serializer_php_bad_unserialize" "experimental/serializer_igbinary" "experimental/serializer_json" withTestspprev.extensions.memcached; grpc = withPkgConfigwithTestspprev.extensions.grpc; zstd = withPkgConfigwithTestspprev.extensions.zstd.overrideAttrsold: patches = old.patches or [ ] ++ ../patches/php-zstd-stream-test-memory.patch;; inotify = withTestspprev.extensions.inotify.overrideAttrsold: postPatch = old.postPatch or "" + '' sed -i 's/^ \])$/ ],[:])/' config.m4 '';; igbinary = withoutTests"tests/igbinary_009b_php8.phpt" "tests/__serialize_020.phpt" withTestspprev.extensions.igbinary; msgpack = withoutTests"tests/015.phpt" "tests/015b.phpt" "tests/015e.phpt" "tests/027.phpt" "tests/028.phpt" "tests/029.phpt" "tests/issue132.phpt" "tests/035.phpt" withTestspprev.extensions.msgpack.overrideAttrsold: patches = old.patches or [ ] ++ ../patches/php-msgpack-map-item-double-free.patch ../patches/php-msgpack-enum-refcount.patch;; memprof = pprev.extensions.memprof.overrideAttrsold: meta = old.meta // broken = true;;; luasandbox = withoutTests"tests/timer.phpt" withTestspprev.extensions.luasandbox.overrideAttrszendAllocOnlyTests = "tests/oom-init.phpt";; opentelemetry = withTestspprev.extensions.opentelemetry.overrideAttrsold: patches = old.patches or [ ] ++ ../patches/php-opentelemetry-hook-args-refcount.patch;; mongodb = withoutTests"tests/manager/manager-ctor-server.phpt" withTestspprev.extensions.mongodb; uv = withoutTests"tests/005-uv_listen_cb-not-destroyed.phpt" withTestspprev.extensions.uv; vld = withoutTests"tests/catch-php70.phpt" "tests/foreach-jumps-php70.phpt" "tests/issue00019-php70.phpt" "tests/issue00020-01-php70-64bit.phpt" "tests/issue00020-02-php70-64bit.phpt" "tests/issue00021-php70.phpt" withTestspprev.extensions.vld; xsl = withoutTests"ext/xsl/tests/xslt009.phpt" withTestspprev.extensions.xsl; redis = pprev.extensions.redis.overrideAttrsdoCheck = true; checkPhase = '' runHook preCheck for port in 6379 6380 6381 6382; do ${final.buildPackages.redis}/bin/redis-server --port $port \ --save "" --appendonly no --daemonize yes done for _ in $(seq 100); do ${final.buildPackages.redis}/bin/redis-cli -p 6382 ping && break sleep 0.1 done # These race a background PHP holding a session lock # against fixed waits sized for native start-up; on # busy builders different ones missed their windows. for t in lockReleasedOnClose lockWaitTime lock_ttlLockExpire \ lock_ttlMaxExecutionTime lockHoldCheckBeforeWrite_otherProcessHasLock \ correctLockRetryCount defaultLockRetryCount; do substituteInPlace tests/RedisTest.php \ --replace-fail "function testSession_$t(" "function skipSession_$t(" done # Session tests run these in child processes. export TEST_PHP_EXECUTABLE=${php}/bin/php export TEST_PHP_ARGS="-d extension=$PWD/modules/redis.so" # Also on the system allocator; see withTests. for alloc in 1 0; do for class in Redis RedisArray; do USE_ZEND_ALLOC=$alloc ${php}/bin/php -d extension=$PWD/modules/redis.so \ tests/TestRedis.php --class $class --nocolors done done for port in 6379 6380 6381 6382; do ${final.buildPackages.redis}/bin/redis-cli -p $port shutdown nosave done runHook postCheck '';; imagick = withoutTests"tests/024-ispixelsimilar.phpt" withTestspprev.extensions.imagick.overrideAttrsold: preCheck = old.preCheck or "" + '' export FONTCONFIG_FILE=${final.buildPackages.makeFontsConffontDirectories = final.buildPackages.dejavu_fonts;} XDG_CACHE_HOME=$TMPDIR/cache '';; xdebug = pprev.extensions.xdebug.overrideAttrsold: patches = old.patches or [ ] ++ ../patches/php-xdebug-out-of-bounds.patch; doCheck = true; checkPhase = '' runHook preCheck mkdir ini grep -E '^(zend_)?extension=' ${php.withExtensionsenabled, …: enabled}/lib/php.ini > ini/extensions.ini echo "zend_extension=$PWD/modules/xdebug.so" >> ini/extensions.ini # For run-tests' workers; see withTests. echo pcre.backtrack_limit=100000000 > ini/pcre.ini export PHP_INI_SCAN_DIR=$PWD/ini TEST_PHP_EXECUTABLE=${php}/bin/php \ NO_INTERACTION=1 OPCACHE=no SKIP_SLOW_TESTS=1 SKIP_UNPARALLEL_TESTS=1 SKIP_ONLINE_TESTS=1 run() { $TEST_PHP_EXECUTABLE -n -d pcre.backtrack_limit=100000000 \ run-xdebug-tests.php -j$NIX_BUILD_CORES -q --show-diff \ -d xdebug.mode=develop,debug,coverage,trace,profile "$@" } USE_ZEND_ALLOC=1 run tests # Also on the system allocator; see withTests. Leave out # the debugger tests there: some give the debugged PHP a # second to connect, and on a busy builder one connected # after the client had given up and both waited forever. USE_ZEND_ALLOC=0 run $(ls -d tests/*/ | grep -v '^tests/debugger/$') runHook postCheck '';; snuffleupagus = withTestspprev.extensions.snuffleupagus.overrideAttrsold: patches = old.patches or [ ] ++ ../patches/php-snuffleupagus-scanner-unsigned.patch;; spx = withoutTests"tests/spx_custom_metadata.phpt" withTestspprev.extensions.spx.overrideAttrsold: buildInputs = old.buildInputs or [ ] ++ final.zlib; makeFlags = old.makeFlags or [ ] ++ "SPX_SHARED_LIBADD=-lz";; // final.lib.optionalAttrsis85 decimal = withoutTestsmapt: "tests/php7/${t}.phpt" "cast" "methods/add" "methods/div" "methods/ln" "methods/log10" "methods/mul" "methods/sub" withTestspprev.extensions.decimal; yaml = withoutTests"tests/bug_64019.phpt" "tests/bug_79866.phpt" "tests/yaml_parse_spec_null.phpt" withTestspprev.extensions.yaml;;;.overrideAttrs

PHP 8.5 matches with a PCRE2 whose interpreter avoids Fil-C's slow paths (see the patch); PHP 8.4 keeps Nixpkgs' choice.

Skips the tests that the libtool wrapper and the sandbox's locales break.

Fil-C cannot run the JIT's machine code; PHP is built without it.

PCRE2's own suite (RunTest, RunGrepTest), which the cross-style Fil-C build would otherwise skip.

Bundled extensions build from the unpatched PHP source.

Most PECL extensions build without their tests in Nixpkgs, and tests run with no other extension loaded (run-tests drops extension= lines from the ini), so tests that need session, json, ... skip. Load the default extensions and the extension's own dependencies ahead of it, and print the diff of each failure.

Drop tests that fail the same way without Fil-C or that need what the build sandbox lacks. Paths are relative to where postPatch runs: the PHP source for bundled extensions.

config.m4 looks up plain pkg-config, which a cross build only has under its target prefix.

Memcached servers on ports 11211 and 11212 for the tests.

The tests share the servers.

These new OOM tests spawn CGI with -n, which omits Nixpkgs' shared filter module. Keep testing the OOM, not an undefined filter_input() function.

Pins CPUs 0 and 1, but build sandboxes may exclude CPU 0.

The format probes run programs, so the cross build assumed libgd had none of them; it has all of them.

Nixpkgs pins ICU 73; use the ported default ICU.

The JIT compiles its code with Fil-C at run time.

The native JIT emits machine code, which Fil-C cannot run.

Bundled extensions outside the default set.

PECL extensions.

The version probe runs a program.

The tests share one GnuPG home directory.

The removed test predates PHP 8.2's dynamic property deprecation.

On PHP 8.5's tracked allocator memory_get_usage() ends 426 bytes lower after the dump, natively too.

Unix socket tests want /var/run/memcached; 056 wants a connection timeout where the sandbox has no route.

Fail the same way on x86-64 PHP without Fil-C.

Their SKIPIF includes a file the release lacks.

The stream round-trip holds two copies of PHP_BINARY; Fil-C executables exceed its default 128 MiB budget.

The inotify probe runs a program; the header is enough.

Tests written before PHP 8.4's deprecation notices.

Nixpkgs builds msgpack without session support.

Wants 400 unserializations a second, a native speed.

Judy keeps every node address in a Word_t (JudyMalloc returns one), so the pointers it hands back have no capability and the first allocation memprof records traps in JudyLIns. Porting needs Judy retyped throughout.

Busy-waits on the wall clock and expects that much CPU time; it passes alone but not beside other test jobs.

Fail the same way on x86-64 PHP without Fil-C.

Opens compress.zlib://ext/xsl/tests/..., relative to the PHP source root rather than the extension.

phpredis tests itself with a PHP script against servers on ports 6379 to 6382 rather than with run-tests.

The text tests need fonts and a fontconfig configuration, which the build sandbox lacks. The removed test fails the same way without Fil-C.

Xdebug has its own copy of run-tests. Its debugger tests start PHP themselves, so load the extensions from an ini directory that every PHP started here reads.

config.m4 never substitutes SPX_SHARED_LIBADD, so spx.so was not linked with zlib and only loaded after another module had brought it in. spx_custom_metadata fails the same way without Fil-C.

Tests written before PHP 8.5's warning for NAN coerced to a string and deprecation of null array offsets.

λold
689patches=
old.patches
++

Default to the system allocator so Fil-C bounds each PHP value.

++
final.lib.optionalsis85

Extensions' libraries bound to PHP's own libraries' symbols.

Linear rather than quadratic string appends on the system allocator.

A computed-goto (HYBRID) VM without global registers, inlining only a small hot set of handlers; JIT builds keep the CALL VM. See docs/php.md.

Adds -Wa,--compress-debug-sections, which SaRCAsm rejects.

712separateDebugInfo=
false
713configureFlags=
old.configureFlags
++

Fibers switch stacks with ucontext, like Boost.Context here, instead of the fcontext assembly.

"--disable-fiber-asm"

Overflow-checked arithmetic in C instead of x86 asmgoto, whose callbr crashes FilPizlonatorPass.

"php_cv_have__asm_goto=no"

Fil-C cannot run generated machine code; PCRE warned at the first preg_* call when mapping executable memory failed.

"--without-pcre-jit"

phpdbg's watchpoints would use userfaultfd, which Fil-C does not support; phpdbg died at startup.

"ac_cv_have_decl_UFFDIO_WRITEPROTECT_MODE_WP=no"

The probe runs a program, so the cross build disabled the stack limit check; deep recursion then hit Fil-C's stack overflow trap instead of throwing "Maximum call stack size reached".

"php_cv_have_stack_limit=yes"
++
final.lib.optionalsis85 ∧ ¬jit

OPcache, built in from 8.5 on; see its extension above.

"--disable-opcache-jit"
++
final.lib.optionalsis85

Copies PHP's machine code into huge pages, found through dl_iterate_phdr, which Fil-C's libc lacks; PHP did not link.

"--disable-huge-code-pages"
//
final.lib.optionalAttrsis85 ∧ jit

Toolchains with Fil-C's LLVM JIT (staging) get the in-process backend, which needs the program linked with -enable-filc-jit.

746preConfigure=
old.preConfigure or ""
+
if echo '#include <filc_llvm_jit.h>' | $CC -E -x c - > /dev/null 2>&1; then
export NIX_CFLAGS_LINK="''${NIX_CFLAGS_LINK:-} -enable-filc-jit"
fi
//
final.lib.optionalAttrsis85

Regenerate the VM from the patched generator.

754postPatch=
old.postPatch or ""
+
(cd Zend && ${final.buildPackages.php.unwrapped}/bin/php -n zend_vm_gen.php)

The JIT compiles its code with Fil-C at run time.

+
final.lib.optionalStringjit
substituteInPlace ext/opcache/jit/zend_jit_c.c \
--replace-fail '#define ZEND_JIT_C_CC "clang"' \
'#define ZEND_JIT_C_CC "${final.gnufilc0}/bin/clang"'

The built-in extensions' suites, which the shared ones run in their own builds: OPcache and URI (which parses with Lexbor).

767doCheck=
true
768checkPhase=
runHook preCheck
NO_INTERACTION=1 SKIP_ONLINE_TESTS=1 SKIP_SLOW_TESTS=1 SKIP_PERF_SENSITIVE=1 \
make "PHP_TEST_SETTINGS=-d pcre.backtrack_limit=100000000" test \
TESTS="-j$NIX_BUILD_CORES -q --show-diff ext/opcache/tests ext/uri/tests"

The suites leave the JIT off; check that each backend compiles and runs code (LLVM only where the toolchain has it).

+
final.lib.optionalStringjit
backends=c
if echo '#include <filc_llvm_jit.h>' | $CC -E -x c - > /dev/null 2>&1; then
backends="c llvm"
fi
for backend in $backends; do
jit_out=$(PHP_JIT_BACKEND=$backend PHP_JIT_C_DIR=$TMPDIR PHP_JIT_C_STATS=1 PHP_JIT_C_VERBOSE=1 \
sapi/cli/php -n -d opcache.enable_cli=1 -d opcache.jit=function \
-d opcache.jit_buffer_size=16M \
-r 'function fib($n) { return $n < 2 ? $n : fib($n - 1) + fib($n - 2); } echo fib(20), "\n";' 2>&1)
echo "$jit_out" | grep -v '^zend_jit_\(c\|llvm\): compiled'
echo "$jit_out" | grep -qx 6765
echo "$jit_out" | grep -Eq ' [1-9][0-9]* compiled'
echo "$jit_out" | grep -q "^zend_jit_$backend: compiled"
done
+
runHook postCheck
in
in
//
pkgs.lib.optionalAttrsprev.stdenv.hostPlatform.isFilc

libunwind's unwinder is hand-written assembly without SaRCAsm annotations, and cannot follow Fil-C frames anyway. Packages that check availability (GStreamer, strace, ...) then build without it.

805libunwind=
prev.libunwind.overrideAttrsold: meta = old.meta // badPlatforms = old.meta.badPlatforms or [ ] ++ prev.stdenv.hostPlatform.system;;

Boost 1.87 is the ported release (Context uses ucontext); Nixpkgs' default 1.89 builds its assembly fcontext and fails.

815boost=
final.boost187

mount(8) stopped in libmount's NULL-argument probes. Only the full util-linux has mount(8); util-linuxMinimal (libuuid and libblkid for nearly everything) is made from this one, so leave its pname alone.

820util-linux=
prev.util-linux.overrideAttrsold: pkgs.lib.optionalAttrsold.pname ≡ "util-linux" patches = old.patches or [ ] ++ ../patches/util-linux-libmount-probe-null.patch;

Nixpkgs' openssh is opensshPackages.openssh with /etc/ssh, and its tests build variants with opensshPackages.openssh.override. Port that one, so the variants keep the port, and derive openssh from it as Nixpkgs does (prev.openssh reads this set, and would be ported twice).

833openssh=
final.opensshPackages.openssh.overrideetcDir = "/etc/ssh";
834opensshPackages=
prev.opensshPackages // inherit portDSL.makeOverlayportList final prev // inherit prev.opensshPackages openssh; openssh;

Default PHP; versioned packages remain available (docs/php.md).

844php=
final.php85
845php85=
846version=
"8.5.12"
847hash=
"sha256-/7YO3EN87WO1E35JhNs3moERxQBJxxFRDYuVgnfvwag="

Opt-in: use pkgsFilcStaging for the in-process LLVM JIT. The required API is not in the released Fil-C toolchain yet (docs/php.md).

851php85-jit=
852version=
"8.5.12"
853hash=
"sha256-/7YO3EN87WO1E35JhNs3moERxQBJxxFRDYuVgnfvwag="
854jit=
true

Nixpkgs builds PHP with llvmPackages.stdenv whenever the compiler is Clang, which for Fil-C means a second compiler-rt and libc++ (and a cross GCC) built from source. The Fil-C stdenv is already Clang.

php84 follows PHP's releases; php84_25 keeps 8.4.25, which lacks the fixes for CVE-2026-109618 and CVE-2026-109619, for comparing exploits and fixes (docs/php.md). It is marked insecure so nothing uses it by accident.

865php84=
866version=
"8.4.27"
867hash=
"sha256-slMKh3xyL0sIyeF0owywi6wrfhkD7m6eMV8KqfX4LCQ="

Opt-in: opcache with the JIT, compiled through C (docs/php.md).

870php84-jit=
871version=
"8.4.27"
872hash=
"sha256-slMKh3xyL0sIyeF0owywi6wrfhkD7m6eMV8KqfX4LCQ="
873jit=
true
875php84_25=
mkFilcPhpversion = "8.4.25"; hash = "sha256-lSzMF4Uma5WPSh3PxYDgjqu7efRCItHv8ZK82Y2R9uM=";.overrideAttrsold: meta = old.meta // knownVulnerabilities = "CVE-2026-109618" "CVE-2026-109619";;

PHP 7.4, from fossar/nix-phps, which keeps Nixpkgs' PHP builder working for releases Nixpkgs has dropped. See docs/php.md.

891php74=
let
893nixPhps=
builtins.fetchTarball
894url=
"https://github.com/fossar/nix-phps/archive/9fb06f7d8beb2ec686fc20b9bf7fe7ce25a429a6.tar.gz"
895sha256=
"1p5bvrs9psm4a26bp420902b1i8qa3wz8s8avmrcizrr3d4h0qza"
used by phps
897phps=
import"${nixPhps}/pkgs/phps.nix" prev.path final prev
uses nixPhps

Bundled extensions build from the unpatched PHP source.

899filcOverrides=
λ_ pprev
900extensions=
pprev.extensions
//

The probes run programs, so the cross build assumed an iconv without errno that honours //IGNORE; glibc's sets errno and fails on //IGNORE.

904iconv=
pprev.extensions.iconv.overrideAttrs
λold
905postPatch=
old.postPatch or ""
+
sed -i 's/\(ICONV_SUPPORTS_ERRNO\|ICONV_BROKEN_IGNORE\)\],0/\1],1/' \
ext/iconv/config.m4
910session=
pprev.extensions.session.overrideAttrsold: patches = old.patches or [ ] ++ ../patches/php-7.4-session-cve-2026-109619.patch;
915opcache=
pprev.extensions.opcache.overrideAttrs
λold
916patches=

The shared memory probes run programs, so the cross build found none and opcache could not start. Anonymous mmap works.

919env=
old.env or { } // NIX_CFLAGS_COMPILE = old.env.NIX_CFLAGS_COMPILE or "" + " -DHAVE_SHM_MMAP_ANON=1";

Expects mapping 1 GB of shared memory to fail; the private mapping that replaces it succeeds.

925postPatch=
old.postPatch or ""
+
rm ext/opcache/tests/log_verbosity_bug.phpt
in
phps.php74.overridepackageOverrides = final.lib.composeExtensionsimport"${nixPhps}/pkgs/package-overrides.nix" prev filcOverrides;.overrideAttrs

Replaces nix-phps' own overrides, so apply those first.

λold
937patches=
old.patches
++

PHP's own backport for its end-of-life branch; see docs/php.md.

Default to the system allocator so Fil-C bounds each PHP value.

Adds -Wa,--compress-debug-sections, which SaRCAsm rejects.

945separateDebugInfo=
false
946configureFlags=
old.configureFlags
++

Overflow-checked arithmetic in C instead of x86 asmgoto.

"ac_cv__asm_goto=no"

Fil-C cannot run generated machine code.

"--without-pcre-jit"

Probes that run programs, which the cross build cannot.

"ac_cv_func_getaddrinfo=yes"
"ac_cv_func_fnmatch_works=yes"
"php_cv_sizeof_intmax_t=8"

The ODBC driver builds its bundled Connector/C submodule, whose export map needs the same fix as mariadb-connector-c (see ports.nix).

960unixodbcDrivers=
prev.unixodbcDrivers
//
961mariadb=
prev.unixodbcDrivers.mariadb.overrideAttrs
λold
962postPatch=
old.postPatch or ""

Node.js (V8) is out of scope. Small pure-JS CLIs run on QuickJS through the qnode shim instead; see docs/quickjs-for-node.md.

970qnode=
final.callPackage../packages/qnode { }
971bibtex-tidy=
972package=
final.buildPackages.bibtex-tidy
973bins.bibtex-tidy=
"bibtex-tidy/bin/bibtex-tidy"
975aasvg=
976package=
final.buildPackages.aasvg
977bins.aasvg=
"aasvg/main.js"

YouTube needs a JavaScript runtime; yt-dlp's default, Deno, is V8. QuickJS is one of its supported runtimes. curl-cffi (curl-impersonate needs Go) and secretstorage (cryptography needs Rust) are optional. pycryptodomex is optional too (yt-dlp has a pure-Python AES), and the build's native Python would dlopen its Fil-C ctypes library and fail.

985yt-dlp=
prev.yt-dlp.overridepython3Packages = final.python3Packages; jsRuntime = final.quickjs; withSecretStorage = false;.overridePythonAttrsold: dependencies = builtins.filterd: ¬builtins.elemd.pname or "" "curl-cffi" "pycryptodomex" old.dependencies;

The top-level python3Packages argument splices to the build platform's default Python (3.13), whose hatchling the Fil-C Python 3.12 build cannot import. Use the ported set directly.

The pnpm dependency fetcher's output is platform-independent, but it overrides pnpm-fixup-state-db with pnpm's own Node.js, which loses splicing and pulls in the host (Fil-C) Node.js. Run it natively.

1007fetchPnpmDeps=
prev.lib.makeOverridableargs: final.buildPackages.fetchPnpmDepsargs // prev.lib.optionalAttrsargs ? pnpm pnpm = args.pnpm.__spliced.buildHost or args.pnpm;

overrideScope, so that the plugins build against this GStreamer.

1018gst_all_1=
prev.gst_all_1.overrideScope
λgfinal gprev

Only the optional PTP clock helper uses Rust. The multimedia core is C.

1021gstreamer=
gprev.gstreamer.overridewithRust = false; withLibunwind = false;.overrideAttrs

Native stack unwinding does not understand Fil-C frames.

λold
//
1035doCheck=
true
1036env=
old.env or { } // CK_TIMEOUT_MULTIPLIER = "5";

GType is a pointer in Fil-C's GLib; upstream Fil-C's 1.24.7 patch, rebased onto 1.26.

1043gst-plugins-base=
gprev.gst-plugins-base.overrideAttrsold: patches = old.patches or [ ] ++ ./patch/gst-plugins-base-1.26.11.patch;
1048gst-plugins-good=
gprev.gst-plugins-good.overrideAttrs
λold

Pointer GTypes: pointer once-inits, switches on uintptr_t, and signal parameters without G_SIGNAL_TYPE_STATIC_SCOPE bits.

The deinterlacer's x86 assembly.

1055mesonFlags=
old.mesonFlags ++ "-Dasm=disabled"
1060linphonePackages=
prev.linphonePackages.overrideScope
λlfinal lprev

Python generates sources at build time. As a build input it was the Fil-C Python, which CMake's FindPython3 does not accept as the build machine's interpreter.

1065bc-decaf=
lprev.bc-decaf.overrideAttrs
λold
1069nativeBuildInputs=
old.nativeBuildInputs ++ pkgs.python3
1070buildInputs=
[ ]
1071doCheck=
true
1077qt5=
prev.qt5.overrideScopeimport./qt5.nix inherit pkgs final;
1079qt6=
prev.qt6.overrideScopeimport./qt6.nix inherit pkgs final;

Nix itself.

1082nixComponents=
prev.nixVersions.nixComponents_2_34.overrideScope
pkgs.lib.composeExtensionsnixFilcOverrides
λnfinal nprev
1085nix-expr=
nprev.nix-expr.overrideenableGC = false;.overrideAttrs
λold

The 64-bit Value layout packs tag bits into pointers held as integers, which drops their capabilities; use the plain one.

1090postPatch=
old.postPatch or ""
+
"\n"
+
substituteInPlace $(find . -path '*/nix/expr/value.hh') --replace-fail \
'useBitPackedValueStorage = (ptrSize == 8)' \
'useBitPackedValueStorage = false && (ptrSize == 8)'

nativeBuildInputs' perl splices to the build platform's perl, which cannot load the Fil-C DBI; Fil-C programs run on the build machine.

1101nix-perl-bindings=
nprev.nix-perl-bindings.overrideAttrs
λold
1102nativeBuildInputs=
mapp: if p.pname or "" ≡ "perl" then final.perl else p old.nativeBuildInputs
1105nativeCheckInputs=
final.perlPackages.Test2Harness

sv_setref_pv stores the wrapper through Fil-C's XS pointer table; the typemap read it back with a cast.

1108postPatch=
old.postPatch or ""
+
"\n"
+
substituteInPlace $(find . -path '*/lib/Nix/Store.xs') --replace-fail \
'$var = ($type)SvIV((SV*)SvRV( $arg ));' \
'$var = ($type) zptrtable_decode(Perl_xsub_ptrtable, SvIV((SV*)SvRV( $arg )));'
1120nix=
final.nixComponents.nix-everything

Determinate Systems' fork; see ports/determinate-nix.nix.

1123determinateNixComponents=
import./determinate-nix.nix inherit pkgs final nixFilcOverrides;
1126determinate-nix=
final.determinateNixComponents.nix-everything

Boehm GC's API on Fil-C's own collector (docs/boehm-on-fugc.md).

1129boehmgc=
final.callPackage./boehmgc inherit prev boehmgc;

unixtools takes glibc's getent and getconf only when libc is "glibc", and NetBSD's otherwise, which do not evaluate here. The Fil-C sysroot has glibc's.

1134getent=
libcTool"getent"
1135getconf=
libcTool"getconf"
1136unixtools=
prev.unixtools // inherit final getent getconf;

The emacs30 port has no GUI; the -nox variants come from Nixpkgs' Emacs scope and would miss the port (and bring native compilation along).

1142emacs-nox=
final.emacs30
1143emacs30-nox=
final.emacs30
1145tree-sitter=
final.callPackage./tree-sitter.nix inherit prev tree-sitter;

jemalloc's API on Fil-C's own allocator (docs/jemalloc-on-fugc.md).

1150jemalloc=
final.callPackage./jemalloc inherit prev jemalloc;