Upgrade to nixos-25.11 - #131
Open
jonathanlking wants to merge 9 commits into
Open
Conversation
Bumps the bundled nixpkgs submodule from the previously-recorded
ede5282 (24.05-era) to nixos-25.11's tip at 8fd9daa3, matching the
nixpkgs commit used to test the survey changes earlier in this branch.
With this bump, callers that don't pass `--arg normalPkgs` get the
correct (25.11) nixpkgs by default, and the survey is testable
standalone:
nix-build survey/default.nix -j 8 -A pkgs.libpq \
--arg compiler '"ghc984"'
GHC 9.8.4 is the only viable option for aarch64-musl because it boots from ghc984Binary which has an aarch64-musl Alpine bindist. GHC 9.6.7 is blocked because its boot chain requires ghc902Binary which lacks aarch64-musl. Remove GHC versions no longer in nixpkgs 25.11 (ghc8107, ghc902, ghc928, ghc963, ghc965) from the Cabal version map. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Collaborator
Author
|
Reproducer for the GHC ghci-library AVX2 alignment SIGSEGV that the fixGhc change in this PR works around. # A minimal, self-contained reproducer for the GHC "ghci library" AVX2
# alignment SIGSEGV. It pins nixpkgs at the `nixos-25.11` tip and builds
# against _vanilla_ `pkgs.haskell.compiler.ghc984`, with no
# static-haskell-nix, overlays, or musl in sight. The only nudge it gives
# the compiler is to flip two `.override` arguments that, between them,
# drive GHC's TemplateHaskell-evaluation path into the misaligned
# merged-`.o` regime:
#
# enableShared = false # produce only .a, no .so
# enableRelocatedStaticLibs = true # build .a as -fPIC + ld -r
#
# Under that combination, GHC's build emits per-package merged
# `HS<pkg>-<ver>-<hash>.o` "ghci library" objects via
# `ld -r -T driver/utils/merge_sections.ld`. That linker script catches
# `.rodata.cst32` under its `.rodata` wildcard rule, and loses the
# 32-byte alignment of the AVX2 SIMD constants gcc emits there. At
# TemplateHaskell-evaluation time the RTS linker dutifully maps the
# defective `HSbytestring-*.o`, and `vmovdqa` then faults against the
# now-misaligned constant whenever the AVX2 fast path runs (bytestring's
# `isValidUtf8` over inputs of >= 128 bytes, where `big_strides = len / 128`).
#
# Expected outcome: this derivation _fails to build_, with GHC dying at
# `EXIT=139` (SIGSEGV) while compiling `Main.hs`. Dropping the input size
# to 127 in `Splice.hs` (below the AVX2 threshold) lets it through.
#
# Worth noting: bytestring's `isValidUtf8`, and with it the AVX2 fast
# path, only landed in 0.11.2.0 (PR #423, November 2021; adapted from
# `text`'s simdjson-based work). Only GHCs bundling bytestring
# >= 0.11.2.0 can stumble on the defect via this code path; the
# linker-script bug itself is much older, but stayed harmless until
# AVX2 code arrived that performs aligned loads against the constants
# gcc emits into `.rodata.cst32`.
#
# Usage:
#
# # Save this file as `default.nix` in some directory; referred to
# # below as `./avx2-repro`.
#
# # Reproduce the segfault with a vanilla GHC (the default):
# nix-build ./avx2-repro
#
# # Verify a fix by passing in a patched GHC, e.g. the one produced by
# # static-haskell-nix's `survey` (when run from a static-haskell-nix
# # checkout):
# nix-build ./avx2-repro \
# --arg ghc '(import ./survey { compiler = "ghc984"; useArchiveFilesForTemplateHaskell = true; }).pkgs.ghc'
#
# Nothing in this derivation depends on the rest of static-haskell-nix,
# so it should be suitable for filing upstream (e.g. at
# gitlab.haskell.org/ghc).
{
# Pinned to the tip of nixos-25.11 at the time of writing.
nixpkgsRev ? "8fd9daa3db09ced9700431c5b7ad0e8ba199b575",
nixpkgsSha256 ? "1i4bkzy1siavmxaskp49lgi9s02gam6crb0d0abbbjmsyl39jbsf",
nixpkgsSrc ? builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/${nixpkgsRev}.tar.gz";
sha256 = nixpkgsSha256;
},
pkgs ? import nixpkgsSrc { overlays = []; },
# Default: a vanilla glibc GHC 9.8.4 with the two overrides that nudge
# its TH-eval path into the "load merged ghci-library .o via the RTS
# linker" regime, which is where the alignment bug surfaces. The bug
# itself is independent of libc / target triple; it lives in
# `driver/utils/merge_sections.ld`. Pass in a different GHC (e.g. one
# patched against `merge_sections.ld`, such as the GHC produced by
# static-haskell-nix's `survey`) to confirm a fix.
ghc ? pkgs.haskell.compiler.ghc984.override {
enableShared = false;
enableRelocatedStaticLibs = true;
},
}:
let
spliceHs = pkgs.writeText "Splice.hs" ''
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE BangPatterns #-}
module Splice (s) where
import Language.Haskell.TH
import qualified Data.ByteString as B
-- The TH splice forces `B.isValidUtf8` over a 128-byte buffer at
-- compile time. 128 is the AVX2 fast-path threshold in bytestring's
-- `cbits/is-valid-utf8.c` (`big_strides = len / 128`). Drop the size
-- to 127 and the splice never reaches `vmovdqa` at all.
s :: Q Exp
s = let !r = B.isValidUtf8 (B.replicate 128 65) in [| r |]
'';
mainHs = pkgs.writeText "Main.hs" ''
{-# LANGUAGE TemplateHaskell #-}
module Main where
import Splice (s)
main :: IO ()
main = print $(s)
'';
in
pkgs.stdenv.mkDerivation {
pname = "ghci-library-avx2-alignment-repro";
version = "0";
dontUnpack = true;
nativeBuildInputs = [ ghc ];
buildPhase = ''
runHook preBuild
cp ${spliceHs} Splice.hs
cp ${mainHs} Main.hs
ghc --make \
-hide-all-packages \
-package base \
-package bytestring \
-package template-haskell \
-XHaskell2010 \
Main.hs
# Sanity: the resulting program should print "True".
./Main
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p $out/bin
install -Dm755 ./Main $out/bin/ghci-library-avx2-alignment-repro
runHook postInstall
'';
meta = {
description =
"Tiny TemplateHaskell program that segfaults GHC's RTS linker "
+ "when GHC's ghci-library merge_sections.ld bug is unpatched.";
};
} |
integer-simple has been replaced by ghc-bignum with enableNativeBignum in nixpkgs 25.11. Remove the integer-simple parameter and all conditional code paths that depended on it (~10 locations): - buildPlatformHaskellPackagesWithFixedCabal - stackageExecutables - setupGhcOverlay - add_integer-simple_if_needed helper (deleted entirely) - blaze-textual handleIntegerSimple - cryptonite integer-gmp flag - scientific/x509-validation conditional dontCheck - statify gmp/libffi lib-dirs guards Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Disable pslSupport and http3Support in the curl override: - libpsl: configure's AC_CHECK_LIB link test does `-lpsl` without the transitive deps (libunistring, libidn2) needed for static linking - nghttp3/ngtcp2: new HTTP/3 deps in nixpkgs 25.11 lack static libs in our overlay Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
nixos-25.11 dropped the `pkgs.lzma` attribute alias; the liblzma C library now lives at `pkgs.xz`. The `sdl2-image` static-link rule was referring to it by the old name (via `with final;`), so on nixos-25.11 that evaluation would fail with "attribute 'lzma' missing". The pkg-config name is unchanged (`liblzma`); this is purely an attribute rename.
jonathanlking
force-pushed
the
feature/nixos-25.11
branch
from
May 18, 2026 17:32
d478f59 to
a474187
Compare
nixpkgs split `libpq` out as a top-level package some time after the
postgresql-server LTO/JIT codegen change. The old `postgresql_14`
override here pulled in the full server (systemd, kerberos, pam,
llvm-for-JIT) just to get libpq, and on nixos-25.11's pkgsMusl the
server's `.a` archives now contain LLVM IR bitcode that gold can't
link.
Switch the overlay to override `previous.libpq` directly:
* `gssSupport = false` keeps the existing reason from before
(`gss_init_sec_context` not available on static builds).
* `curlSupport = false` disables PG18's libcurl-backed OAuth
client. Avoiding the dlopen-loaded `libpq-oauth-*.so` keeps the
closure smaller; we don't need OAuth.
* `openssl = previous.openssl` builds libpq against the un-overridden
(shared) openssl, not the static openssl this overlay installs.
With static openssl, libpq's link `-lssl -lcrypto` would resolve
against the static archive and pull OpenSSL's
`ossl_init_register_atexit_ossl_` (and its `atexit` /
`pthread_exit` calls) directly into `libpq.so`. PG18's
`libpq-refs-stamp` policy check in
`src/interfaces/libpq/Makefile` then rejects the build with
"libpq must not be calling any function which invokes exit".
Linking shared openssl keeps those references inside `libssl.so`,
matching upstream `pkgsMusl.libpq`.
* `postInstall = ""` clears libpq.nix's default `rm -f $out/lib/*.a`
so the static archive is retained. nixpkgs PR #519070 adds
`dontDisableStatic` support to libpq.nix; once merged, this should
be removed.
The `final.postgresql` references in `haskellLibsReadyForStaticLinkingOverlay`
(passed to `addStaticLinkerFlagsWithPkgconfig` for `--libs libpq` pkg-config
queries) are switched to `final.libpq` so they resolve directly against the
new attribute, not the un-overridden pkgsMusl postgresql_17.
When GHC is built with `-split-sections`, both the make-based and
hadrian build systems invoke `ld -r` with the linker script
`driver/utils/merge_sections.ld` (in GHC's source tree since at
least 9.0) to produce a per-package merged `HS<pkg>-<ver>-<hash>.o`
("ghci library") object that GHC's RTS linker loads at TH-eval time.
That linker script catches `.rodata.cst32` under the `.rodata`
wildcard, which loses the 32-byte alignment of AVX2 SIMD constants
emitted by gcc into that section. The merged `.o` is therefore
defective: any code that does an aligned 256-bit load against one of
those constants (e.g. bytestring's `isValidUtf8` AVX2 path on inputs
>= 128 bytes) faults at TH-eval time when the RTS linker happens to
map `.rodata` at a non-32-aligned address. Whether the fault
actually fires depends on load-time placement: in some
configurations the constants coincidentally land on a 32-byte
boundary and the defect stays latent.
We make two changes -- either on its own should be enough to prevent
the crash:
1. `postPatch` patches `merge_sections.ld` to preserve
`.rodata.cst32` (and `.cst64`) as their own output sections.
This is a fix for the underlying bug, included to describe the
cause. It is guarded on file existence so a future GHC that
drops `merge_sections.ld` is unaffected.
2. `postFixup` deletes the merged `HS<pkg>-<ver>-<hash>.o` files
after install. The RTS linker then loads the `.a` archive
members instead, where the alignment is preserved per-object.
This is what removes the problem from the toolchain we ship.
The second change has the same effect as upstream GHC commit
https://gitlab.haskell.org/ghc/ghc/-/commit/53038ea9 which (post-9.12)
removes the ghci-library merge step entirely: in both cases no merged
`.o` is loaded, so the RTS linker uses the `.a` archive members. Drop
this `lib.pipe` step once the toolchain moves to a GHC that includes
that commit (>= 9.14).
Two fixes in `survey/default.nix`:
(1) `postgresql-libpq-pkgconfig`: get libpq's extra C libs onto the
static link line of every consumer.
- `addPkgconfigDepend final.openssl`: puts `libssl.pc`/`libcrypto.pc`
on `PKG_CONFIG_PATH` and propagates openssl's lib dir to
transitive consumers' `--extra-lib-dirs`.
- `prePatch` injecting `extra-libraries: pq, pgcommon, pgport, ssl,
crypto` into the `.cabal`. `pq` first, because ld processes static
archives left-to-right and needs libpq's unresolved references on
the table before pulling pgcommon/pgport/ssl/crypto objects.
Routed centrally because nixpkgs's `configuration-nix.nix` wires
every `postgresql-libpq` consumer through this sub-package.
(2) `postgresql = previous.postgresql.override { gssSupport = false;
curlSupport = false; openssl = previous.openssl; }`.
nixpkgs's Haskell overlay drags the full `postgresql_17` into the
closure of `postgresql-simple` (test-tool dep). Default
`gssSupport = true` builds static `krb5-1.22.1`, which fails with a
`master_keyblock` multiple-definition error (pre-GCC-10 `-fno-common`
regression). Shared openssl is needed to pass the same
`libpq-refs-stamp` policy check we already work around for the
standalone `libpq`.
Obsoletes `addStaticLinkerFlagsWithPkgconfig` wrapper for
libpq/openssl consumers.
Postgrest now builds again and verified end-to-end against a real PG 17
server.
jonathanlking
force-pushed
the
feature/nixos-25.11
branch
from
May 18, 2026 20:57
a474187 to
c08bdb1
Compare
Collaborator
Author
|
End-to-end smoke test for the static # 1. Build + verify static linkage
out=$(nix-build survey -A working.postgrest.bin --no-out-link)
file "$out/bin/postgrest" # statically linked, stripped
ldd "$out/bin/postgrest" # not a dynamic executable
# 2. Start postgres, apply a minimal anon-role schema
docker run --rm -d --name pgrest-smoke -p 55432:5432 \
-e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=test postgres:17
until docker exec pgrest-smoke pg_isready -U postgres >/dev/null 2>&1; do sleep 1; done
docker exec -i pgrest-smoke psql -U postgres -d test <<'SQL'
CREATE ROLE web_anon NOLOGIN;
CREATE SCHEMA api;
CREATE TABLE api.things (id serial primary key, name text);
GRANT USAGE ON SCHEMA api TO web_anon;
GRANT SELECT ON api.things TO web_anon;
INSERT INTO api.things(name) VALUES ('hello'), ('world');
SQL
# 3. Run postgrest, query, clean up
PGRST_DB_URI=postgres://postgres:postgres@localhost:55432/test \
PGRST_DB_SCHEMAS=api PGRST_DB_ANON_ROLE=web_anon PGRST_SERVER_PORT=3000 \
"$out/bin/postgrest" &
PGRST_PID=$!
until curl -fsS http://localhost:3000/things >/dev/null 2>&1; do sleep 1; done
curl -fsS http://localhost:3000/things
# Expected: [{"id":1,"name":"hello"}, {"id":2,"name":"world"}]
kill "$PGRST_PID"; docker stop pgrest-smoke |
jonathanlking
marked this pull request as ready for review
May 18, 2026 21:12
Replace single-form `openssl = previous.openssl.override { static = true; };`
and the inline `final.ncurses.override { enableStatic = true; }` with
dual-form derivations that ship both `.so` and `.a` files from a single
output. Pattern: build the static variant separately, copy its `.a` files
into the shared build's `postInstall` (see `test/openssl-both-form-repro/`
and `test/ncurses-both-form-repro/` for standalone reproducers of the
upstream constraint).
Factored as `statify_openssl` and `statify_ncurses` helpers alongside
the existing `statify_zlib` and `statify_curl_including_exe`, matching
the survey's established convention for "produce both shared and static
in one derivation".
`ncurses` is called with `previous.ncurses`, matching that convention.
`openssl` has to be passed `pkgsDontDisableStatic.openssl` instead:
the helper's inner `.override` triggers a dependency walk that, through
`previous`, reaches `curl -> openssl` and ultimately resolves back to
`final.openssl` (the dual-form override under construction), producing
an infinite recursion. ncurses's dep walk doesn't touch any package
overridden by `archiveFilesOverlay`, so it stays cycle-free under
`previous`.
Drop the `openssl = previous.openssl;` workarounds previously needed in
the libpq and postgresql overrides. With shared `libssl.so` available in
`final.openssl`, libpq.so links it dynamically and passes PG18's
`libpq-refs-stamp` policy check without dragging openssl's
`atexit`/`pthread_exit` symbols in.
Verified by building `working.postgrest` end-to-end against a docker
PG 17 instance with a minimal anon-role schema; the resulting statically
linked `postgrest` serves `GET /things` correctly.
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.
Bring static-haskell-nix up to nixos-25.11, with GHC 9.8.4 as the new default compiler. The submodule bump itself is one small commit, but several adaptations were necessary along the way to keep
survey -A workingbuilding, plus several genuinely substantive fixes for problems that only surface against the 25.11 toolchain.The mechanical changes:
nixpkgssubmodule to thenixos-25.11tip.ghc965toghc984, and refresh thedefaultCabalPackageVersionComingWithGhcmap. Each entry was verified against the actualCabal-*directory in the installed GHC.integer-simplesupport entirely. Modern GHCs no longer ship theinteger-simplelibrary, leaving the conditional unreachable.pslSupportandhttp3Supporton curl.libpsl'sAC_CHECK_LIBdoesn't pass transitivelibunistring/libidn2on the static link line, andnghttp3/ngtcp2don't ship a static archive in our overlay.lzmatoxz.The substantive fixes:
Switch the postgres-libpq C dependency from the full
postgresql_17server to the standalonelibpqderivation that nixpkgs split out in 25.11. The smaller closure dodges thesystemd/kerberos/pam/llvm-for-JITbuild inputs, and also dodges the LLVM-bitcode-in-.a-archives thatpostgresql_17's-fltobuild path emits (gold can't link bitcode).Work around a GHC bug where the per-package merged
HS<pkg>-<ver>-<hash>.o"ghci library" objects lose the 32-byte alignment of AVX2 SIMD constants in.rodata.cst32. The cause isdriver/utils/merge_sections.ld's.rodatawildcard catching them. The symptom is that GHC's RTS linker maps the defective object at TemplateHaskell-evaluation time, andvmovdqafaults on the misaligned constant. The workaround patches the linker script to preserve.rodata.cst32(and.cst64) as their own output sections, and also deletes the merged.ofiles after install so the RTS linker uses the per-archive members instead. Either change alone is enough, kept together as defence in depth. Upstream commit https://gitlab.haskell.org/ghc/ghc/-/commit/53038ea9 (post-9.12) removes the merge step entirely. Once on a GHC that contains that commit, this workaround should be dropped. A self-contained reproducer is attached as a comment below.Centralise the libpq / openssl static-link wiring inside
postgresql-libpq-pkgconfig, the Haskell wrapper sub-package that nixpkgs'sconfiguration-nix.nixroutes everypostgresql-libpqconsumer through. Two pieces work together: anaddPkgconfigDepend opensslsopkg-config --static --libs libpqcan resolvelibpq.pc'sRequires.private, and aprePatchthat injectsextra-libraries: pq, pgcommon, pgport, ssl, cryptointo the.cabalso GHC's package-walking aggregation propagates the deps to every transitive consumer's link.pqfirst, because ld walks static archives left-to-right. This replaces theaddStaticLinkerFlagsWithPkgconfigwrappers that the survey previously applied to each libpq consumer. The same change adds apostgresqloverride (gssSupport/curlSupportoff) for packages whose closures pull in the fullpostgresql_17for tests (e.g. viapostgresql-simple'saddTestToolDepends), and addspostgrest,hasql-notifications,squeal-postgresqlto theworkingset'spkgsMuslsection.Build
opensslandncursesin both shared and static form via newstatify_openssl/statify_ncurseshelpers, matching the existingstatify_zlib/statify_curl_including_execonvention.Verified:
nix-build survey -A workinginstantiates and builds.The resulting
postgrestbinary is fully statically linked:Smoke-tested against a docker
postgres:17instance with a minimal anon-role schema, serving GET requests over HTTP and returning the expected JSON.Happy to take feedback on any of this.