From 2453722a4c7e1d8c8bfe96884e5a3ba88a3c9b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Boros?= Date: Fri, 31 Jul 2026 08:27:44 +0300 Subject: [PATCH 1/3] fix(nix): pin ncurses to old glibc symbol version for cfgetospeed nixpkgs' bumped glibc (2.42) re-versions cfgetospeed, so a freshly built ncurses binds to the new GLIBC_2.42 symbol by default even though nothing about ncurses' own code changed. That breaks the portable CLI bundle on hosts with older glibc (e.g. Ubuntu 24.04's 2.39), since the bundle deliberately excludes glibc itself and relies on the host's. glibc still exports the old (functionally identical) GLIBC_2.17 version of cfgetospeed alongside the new one, so a .symver directive forces ncurses to bind to that instead - verified the patched build against the new glibc drops the GLIBC_2.42 requirement entirely (max symbol version falls back to GLIBC_2.38, matching every other bundled library). --- nix/overlays/default.nix | 7 ++- ...ncurses-cfgetospeed-old-glibc-symver.patch | 55 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 nix/overlays/patches/ncurses-cfgetospeed-old-glibc-symver.patch diff --git a/nix/overlays/default.nix b/nix/overlays/default.nix index c2a6e5976d..68e635f719 100644 --- a/nix/overlays/default.nix +++ b/nix/overlays/default.nix @@ -1,6 +1,6 @@ { self, ... }: { - flake.overlays.default = final: _prev: { + flake.overlays.default = final: prev: { # NOTE: add any needed overlays here. in theory we could # pull them from the overlays/ directory automatically, but we don't # want to have an arbitrary order, since it might matter. being @@ -17,6 +17,11 @@ xmrig = throw "The xmrig package has been explicitly disabled in this flake."; + # Force the pre-2.42 glibc symbol version for cfgetospeed so the portable CLI bundle keeps working on older-glibc hosts. + ncurses = prev.ncurses.overrideAttrs (old: { + patches = (old.patches or [ ]) ++ [ ./patches/ncurses-cfgetospeed-old-glibc-symver.patch ]; + }); + cargo-pgrx = final.callPackage ../cargo-pgrx/default.nix { inherit (final) lib; inherit (final) fetchCrate; diff --git a/nix/overlays/patches/ncurses-cfgetospeed-old-glibc-symver.patch b/nix/overlays/patches/ncurses-cfgetospeed-old-glibc-symver.patch new file mode 100644 index 0000000000..bb11dd8d15 --- /dev/null +++ b/nix/overlays/patches/ncurses-cfgetospeed-old-glibc-symver.patch @@ -0,0 +1,55 @@ +--- a/ncurses/tinfo/lib_baudrate.c 2026-07-31 08:24:24 ++++ b/ncurses/tinfo/lib_baudrate.c 2026-07-31 08:24:34 +@@ -45,6 +45,15 @@ + #endif + + /* ++ * Force the pre-2.42 (functionally identical) glibc symbol version for ++ * cfgetospeed, so binaries built against newer glibc still run on hosts ++ * with older glibc that only export the old-versioned symbol. ++ */ ++#if defined(__linux__) && defined(__GLIBC__) ++__asm__(".symver cfgetospeed,cfgetospeed@GLIBC_2.17"); ++#endif ++ ++/* + * These systems use similar header files, which define B1200 as 1200, etc., + * but can be overridden by defining USE_OLD_TTY so B1200 is 9, which makes all + * of the indices up to B115200 fit nicely in a 'short', allowing us to retain +--- a/ncurses/tinfo/tinfo_driver.c 2026-07-31 08:24:24 ++++ b/ncurses/tinfo/tinfo_driver.c 2026-07-31 08:24:44 +@@ -37,6 +37,15 @@ + #include + #include /* ospeed */ + ++/* ++ * Force the pre-2.42 (functionally identical) glibc symbol version for ++ * cfgetospeed, so binaries built against newer glibc still run on hosts ++ * with older glibc that only export the old-versioned symbol. ++ */ ++#if defined(__linux__) && defined(__GLIBC__) ++__asm__(".symver cfgetospeed,cfgetospeed@GLIBC_2.17"); ++#endif ++ + #if HAVE_NANOSLEEP + #include + #if HAVE_SYS_TIME_H +--- a/progs/tset.c 2026-07-31 08:24:24 ++++ b/progs/tset.c 2026-07-31 08:24:54 +@@ -89,6 +89,16 @@ + #include + #include + #include ++ ++/* ++ * Force the pre-2.42 (functionally identical) glibc symbol version for ++ * cfgetospeed, so binaries built against newer glibc still run on hosts ++ * with older glibc that only export the old-versioned symbol. ++ */ ++#if defined(__linux__) && defined(__GLIBC__) ++__asm__(".symver cfgetospeed,cfgetospeed@GLIBC_2.17"); ++#endif ++ + #include + + #if HAVE_GETTTYNAM From 7a5a3327f9894f61d8e1708e154800f36bdf2274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Boros?= Date: Fri, 31 Jul 2026 08:45:50 +0300 Subject: [PATCH 2/3] fix: guard cfgetospeed symver override to glibc >= 2.42 only The unconditional .symver directive broke the build on older glibc (e.g. 2.40, currently on develop): older glibc only exports one (unversioned-from-this-perspective) definition of cfgetospeed, so requesting the GLIBC_2.17 version explicitly fails to link ("no symbol version section for versioned symbol"). Only glibc >= 2.42 actually carries both the old and new versioned symbols, so the override must only apply then. --- .../ncurses-cfgetospeed-old-glibc-symver.patch | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nix/overlays/patches/ncurses-cfgetospeed-old-glibc-symver.patch b/nix/overlays/patches/ncurses-cfgetospeed-old-glibc-symver.patch index bb11dd8d15..1dd54f7967 100644 --- a/nix/overlays/patches/ncurses-cfgetospeed-old-glibc-symver.patch +++ b/nix/overlays/patches/ncurses-cfgetospeed-old-glibc-symver.patch @@ -1,5 +1,5 @@ --- a/ncurses/tinfo/lib_baudrate.c 2026-07-31 08:24:24 -+++ b/ncurses/tinfo/lib_baudrate.c 2026-07-31 08:24:34 ++++ b/ncurses/tinfo/lib_baudrate.c 2026-07-31 08:41:53 @@ -45,6 +45,15 @@ #endif @@ -8,7 +8,7 @@ + * cfgetospeed, so binaries built against newer glibc still run on hosts + * with older glibc that only export the old-versioned symbol. + */ -+#if defined(__linux__) && defined(__GLIBC__) ++#if defined(__linux__) && defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 42)) +__asm__(".symver cfgetospeed,cfgetospeed@GLIBC_2.17"); +#endif + @@ -17,7 +17,7 @@ * but can be overridden by defining USE_OLD_TTY so B1200 is 9, which makes all * of the indices up to B115200 fit nicely in a 'short', allowing us to retain --- a/ncurses/tinfo/tinfo_driver.c 2026-07-31 08:24:24 -+++ b/ncurses/tinfo/tinfo_driver.c 2026-07-31 08:24:44 ++++ b/ncurses/tinfo/tinfo_driver.c 2026-07-31 08:41:53 @@ -37,6 +37,15 @@ #include #include /* ospeed */ @@ -27,7 +27,7 @@ + * cfgetospeed, so binaries built against newer glibc still run on hosts + * with older glibc that only export the old-versioned symbol. + */ -+#if defined(__linux__) && defined(__GLIBC__) ++#if defined(__linux__) && defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 42)) +__asm__(".symver cfgetospeed,cfgetospeed@GLIBC_2.17"); +#endif + @@ -35,7 +35,7 @@ #include #if HAVE_SYS_TIME_H --- a/progs/tset.c 2026-07-31 08:24:24 -+++ b/progs/tset.c 2026-07-31 08:24:54 ++++ b/progs/tset.c 2026-07-31 08:41:53 @@ -89,6 +89,16 @@ #include #include @@ -46,7 +46,7 @@ + * cfgetospeed, so binaries built against newer glibc still run on hosts + * with older glibc that only export the old-versioned symbol. + */ -+#if defined(__linux__) && defined(__GLIBC__) ++#if defined(__linux__) && defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 42)) +__asm__(".symver cfgetospeed,cfgetospeed@GLIBC_2.17"); +#endif + From 4350e69307b401fb222b91de92f58bf2ff081754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Boros?= Date: Fri, 31 Jul 2026 12:06:43 +0300 Subject: [PATCH 3/3] fix: use runtime dlvsym instead of link-time .symver for cfgetospeed compat The .symver approach broke the final link (ncurses' own -Wl,--version-script conflicts with a link-time versioned external symbol reference), and separately, the naive dlvsym-with-fallback first attempt still linked a bare reference to cfgetospeed for its fallback path, which just re-added the GLIBC_2.42 requirement it was meant to avoid. Resolving both dlvsym and its dlsym fallback dynamically (never referencing the bare cfgetospeed identifier in compiled code) sidesteps the linker entirely and degrades gracefully on any glibc, verified by building the patched ncurses against the bumped glibc: no cfgetospeed reference of any kind remains in the dynamic symbol table, and the max GLIBC symbol requirement drops back to 2.38, matching every other library in the bundle. --- nix/overlays/default.nix | 2 +- ...ncurses-cfgetospeed-old-glibc-compat.patch | 127 ++++++++++++++++++ ...ncurses-cfgetospeed-old-glibc-symver.patch | 55 -------- 3 files changed, 128 insertions(+), 56 deletions(-) create mode 100644 nix/overlays/patches/ncurses-cfgetospeed-old-glibc-compat.patch delete mode 100644 nix/overlays/patches/ncurses-cfgetospeed-old-glibc-symver.patch diff --git a/nix/overlays/default.nix b/nix/overlays/default.nix index 68e635f719..1056e79d2c 100644 --- a/nix/overlays/default.nix +++ b/nix/overlays/default.nix @@ -19,7 +19,7 @@ # Force the pre-2.42 glibc symbol version for cfgetospeed so the portable CLI bundle keeps working on older-glibc hosts. ncurses = prev.ncurses.overrideAttrs (old: { - patches = (old.patches or [ ]) ++ [ ./patches/ncurses-cfgetospeed-old-glibc-symver.patch ]; + patches = (old.patches or [ ]) ++ [ ./patches/ncurses-cfgetospeed-old-glibc-compat.patch ]; }); cargo-pgrx = final.callPackage ../cargo-pgrx/default.nix { diff --git a/nix/overlays/patches/ncurses-cfgetospeed-old-glibc-compat.patch b/nix/overlays/patches/ncurses-cfgetospeed-old-glibc-compat.patch new file mode 100644 index 0000000000..1a1766675c --- /dev/null +++ b/nix/overlays/patches/ncurses-cfgetospeed-old-glibc-compat.patch @@ -0,0 +1,127 @@ +--- a/ncurses/tinfo/lib_baudrate.c 2026-07-31 08:24:24 ++++ b/ncurses/tinfo/lib_baudrate.c 2026-07-31 12:03:39 +@@ -45,6 +45,39 @@ + #endif + + /* ++ * On Linux/glibc, resolve cfgetospeed to the old (functionally identical) ++ * GLIBC_2.17 symbol version at runtime, so binaries built against newer ++ * glibc still run on hosts with older glibc that only export that version. ++ * Done via dlvsym rather than a link-time .symver directive, since the ++ * latter conflicts with ncurses' own --version-script linking. Both dlvsym ++ * and its dlsym fallback are declared directly (rather than via ++ * /_GNU_SOURCE, which may already be locked in by earlier headers ++ * in this file), and neither path calls the bare cfgetospeed identifier, so ++ * no versioned reference to it survives in the compiled output. ++ */ ++#if defined(__linux__) && defined(__GLIBC__) ++extern void *dlvsym(void *handle, const char *symbol, const char *version); ++extern void *dlsym(void *handle, const char *symbol); ++static speed_t ++_supabase_compat_cfgetospeed(const struct termios *t) ++{ ++ static int inited = 0; ++ static speed_t (*fn)(const struct termios *) = NULL; ++ if (!inited) { ++ fn = (speed_t (*)(const struct termios *)) ++ dlvsym((void *) 0, "cfgetospeed", "GLIBC_2.17"); ++ if (!fn) { ++ fn = (speed_t (*)(const struct termios *)) ++ dlsym((void *) 0, "cfgetospeed"); ++ } ++ inited = 1; ++ } ++ return fn ? fn(t) : 0; ++} ++#define cfgetospeed(t) _supabase_compat_cfgetospeed(t) ++#endif ++ ++/* + * These systems use similar header files, which define B1200 as 1200, etc., + * but can be overridden by defining USE_OLD_TTY so B1200 is 9, which makes all + * of the indices up to B115200 fit nicely in a 'short', allowing us to retain +--- a/ncurses/tinfo/tinfo_driver.c 2026-07-31 08:24:24 ++++ b/ncurses/tinfo/tinfo_driver.c 2026-07-31 12:03:50 +@@ -37,6 +37,39 @@ + #include + #include /* ospeed */ + ++/* ++ * On Linux/glibc, resolve cfgetospeed to the old (functionally identical) ++ * GLIBC_2.17 symbol version at runtime, so binaries built against newer ++ * glibc still run on hosts with older glibc that only export that version. ++ * Done via dlvsym rather than a link-time .symver directive, since the ++ * latter conflicts with ncurses' own --version-script linking. Both dlvsym ++ * and its dlsym fallback are declared directly (rather than via ++ * /_GNU_SOURCE, which may already be locked in by earlier headers ++ * in this file), and neither path calls the bare cfgetospeed identifier, so ++ * no versioned reference to it survives in the compiled output. ++ */ ++#if defined(__linux__) && defined(__GLIBC__) ++extern void *dlvsym(void *handle, const char *symbol, const char *version); ++extern void *dlsym(void *handle, const char *symbol); ++static speed_t ++_supabase_compat_cfgetospeed(const struct termios *t) ++{ ++ static int inited = 0; ++ static speed_t (*fn)(const struct termios *) = NULL; ++ if (!inited) { ++ fn = (speed_t (*)(const struct termios *)) ++ dlvsym((void *) 0, "cfgetospeed", "GLIBC_2.17"); ++ if (!fn) { ++ fn = (speed_t (*)(const struct termios *)) ++ dlsym((void *) 0, "cfgetospeed"); ++ } ++ inited = 1; ++ } ++ return fn ? fn(t) : 0; ++} ++#define cfgetospeed(t) _supabase_compat_cfgetospeed(t) ++#endif ++ + #if HAVE_NANOSLEEP + #include + #if HAVE_SYS_TIME_H +--- a/progs/tset.c 2026-07-31 08:24:24 ++++ b/progs/tset.c 2026-07-31 12:03:59 +@@ -89,6 +89,40 @@ + #include + #include + #include ++ ++/* ++ * On Linux/glibc, resolve cfgetospeed to the old (functionally identical) ++ * GLIBC_2.17 symbol version at runtime, so binaries built against newer ++ * glibc still run on hosts with older glibc that only export that version. ++ * Done via dlvsym rather than a link-time .symver directive, since the ++ * latter conflicts with ncurses' own --version-script linking. Both dlvsym ++ * and its dlsym fallback are declared directly (rather than via ++ * /_GNU_SOURCE, which may already be locked in by earlier headers ++ * in this file), and neither path calls the bare cfgetospeed identifier, so ++ * no versioned reference to it survives in the compiled output. ++ */ ++#if defined(__linux__) && defined(__GLIBC__) ++extern void *dlvsym(void *handle, const char *symbol, const char *version); ++extern void *dlsym(void *handle, const char *symbol); ++static speed_t ++_supabase_compat_cfgetospeed(const struct termios *t) ++{ ++ static int inited = 0; ++ static speed_t (*fn)(const struct termios *) = NULL; ++ if (!inited) { ++ fn = (speed_t (*)(const struct termios *)) ++ dlvsym((void *) 0, "cfgetospeed", "GLIBC_2.17"); ++ if (!fn) { ++ fn = (speed_t (*)(const struct termios *)) ++ dlsym((void *) 0, "cfgetospeed"); ++ } ++ inited = 1; ++ } ++ return fn ? fn(t) : 0; ++} ++#define cfgetospeed(t) _supabase_compat_cfgetospeed(t) ++#endif ++ + #include + + #if HAVE_GETTTYNAM diff --git a/nix/overlays/patches/ncurses-cfgetospeed-old-glibc-symver.patch b/nix/overlays/patches/ncurses-cfgetospeed-old-glibc-symver.patch deleted file mode 100644 index 1dd54f7967..0000000000 --- a/nix/overlays/patches/ncurses-cfgetospeed-old-glibc-symver.patch +++ /dev/null @@ -1,55 +0,0 @@ ---- a/ncurses/tinfo/lib_baudrate.c 2026-07-31 08:24:24 -+++ b/ncurses/tinfo/lib_baudrate.c 2026-07-31 08:41:53 -@@ -45,6 +45,15 @@ - #endif - - /* -+ * Force the pre-2.42 (functionally identical) glibc symbol version for -+ * cfgetospeed, so binaries built against newer glibc still run on hosts -+ * with older glibc that only export the old-versioned symbol. -+ */ -+#if defined(__linux__) && defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 42)) -+__asm__(".symver cfgetospeed,cfgetospeed@GLIBC_2.17"); -+#endif -+ -+/* - * These systems use similar header files, which define B1200 as 1200, etc., - * but can be overridden by defining USE_OLD_TTY so B1200 is 9, which makes all - * of the indices up to B115200 fit nicely in a 'short', allowing us to retain ---- a/ncurses/tinfo/tinfo_driver.c 2026-07-31 08:24:24 -+++ b/ncurses/tinfo/tinfo_driver.c 2026-07-31 08:41:53 -@@ -37,6 +37,15 @@ - #include - #include /* ospeed */ - -+/* -+ * Force the pre-2.42 (functionally identical) glibc symbol version for -+ * cfgetospeed, so binaries built against newer glibc still run on hosts -+ * with older glibc that only export the old-versioned symbol. -+ */ -+#if defined(__linux__) && defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 42)) -+__asm__(".symver cfgetospeed,cfgetospeed@GLIBC_2.17"); -+#endif -+ - #if HAVE_NANOSLEEP - #include - #if HAVE_SYS_TIME_H ---- a/progs/tset.c 2026-07-31 08:24:24 -+++ b/progs/tset.c 2026-07-31 08:41:53 -@@ -89,6 +89,16 @@ - #include - #include - #include -+ -+/* -+ * Force the pre-2.42 (functionally identical) glibc symbol version for -+ * cfgetospeed, so binaries built against newer glibc still run on hosts -+ * with older glibc that only export the old-versioned symbol. -+ */ -+#if defined(__linux__) && defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 42)) -+__asm__(".symver cfgetospeed,cfgetospeed@GLIBC_2.17"); -+#endif -+ - #include - - #if HAVE_GETTTYNAM