From 29897ab2baa560389dd739a61ff785b3c4d6be89 Mon Sep 17 00:00:00 2001 From: Sergio Date: Mon, 31 Aug 2026 16:11:50 +0000 Subject: [PATCH] gtk4: el `--export-dynamic` venia del .pc de gmodule, y estatico destapo un bug de upstream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Los 8 ejecutables decian static y enlazaban musl dinamicamente. La huella que lo delata sin mirar el build: `readelf --dyn-syms` daba 21475 entradas contra ~2700 de un binario dinamico normal — eso es --export-dynamic, no un enlace corriente. NO venia del meson de gtk (ahi no aparece la palabra) sino de un .pc DE DEPENDENCIA: `gmodule-2.0.pc` trae literalmente `Libs: -Wl,--export-dynamic`, y `meson.build:426` pedia `dependency('gmodule-2.0')`. El wrapper de cc del lab (`sandbox.rs:597`) tira el `-static` ante --export-dynamic ⇒ lo ponia el lab y lo sacaba el lab, por un flag heredado de una dep. No hace falta parchear el .pc de glib: upstream ya publica la misma libreria sin el flag. `gmodule-no-export-2.0.pc` trae el `-lgmodule-2.0 -pthread` de verdad y los otros dos solo hacen Requires sobre el añadiendo --export-dynamic. Es la palanca prevista, no un apaño — de hecho `gio-2.0.pc` de la propia glib ya usa la variante no-export. REGLA GENERAL: cualquier receta que enlace gmodule hereda --export-dynamic y pierde el -static SIN AVISO. Si link=static no pega y hay glib de por medio, mirar los .pc de las deps antes que el build del proyecto. Y entonces aparecio un bug que SOLO existe en estatico: los 8 morian con SIGSEGV hasta en `--help`. Backtrace con gdb: g_module_symbol (module=0x0, symbol_name="gtk_progress_get_type") _gtk_module_has_mixed_deps (module_to_check=0x0) at gtk/gtkmain.c:474 do_pre_parse_initialization () at gtk/gtkmain.c:498 gtk_init_check () at gtk/gtkmain.c:635 `_gtk_module_has_mixed_deps(NULL)` hace `g_module_open(NULL,0)` y pasa el resultado a `g_module_symbol` sin comprobarlo. En musl ESTATICO `dlopen(NULL)` devuelve NULL siempre ⇒ deref de nulo en el offset 8, en el arranque. Con libc dinamica el bug no se ve nunca. Cuarto parche de la receta: la comprobacion que falta. Devolver FALSE es correcto ademas de seguro — en un estatico no hay modulos cargables, asi que no puede haber simbolos de GTK 2/3 mezclados. Control: 423 ficheros intactos, NEEDED=0 en los ocho, .dynsym de 21475 a 0, los ocho arrancan, gtk4-path-tool hace trabajo real, y `builder-tool validate` devuelve EXACTAMENTE el mismo «Could not initialize windowing system» que daba el binario dinamico anterior. Cae en cascada el re-hash de 7 dependientes; se reconstruyen a continuacion. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01ACUcwo9mZsE5ocYVE9npih --- recipes/gtk4.toml | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/recipes/gtk4.toml b/recipes/gtk4.toml index 4cf3ae32..1f870e85 100644 --- a/recipes/gtk4.toml +++ b/recipes/gtk4.toml @@ -43,10 +43,51 @@ link = "static" # En todo el árbol hay 5 objetivos compartidos: `libgtk-4.so`, printbackend-{cpdb,cups,file} y # media-gstreamer. cpdb/cups/media ya los quitan las flags; los otros dos, estos dos seds. # +# ── Parche 3 (2026-08-31): `gmodule-no-export-2.0`, o los 8 ejecutables salen DINÁMICOS ───────── +# `link = "static"` era mentira y los 8 `tools/` enlazaban musl dinámicamente (audit estricto). +# Huella que lo delata sin mirar el build: `readelf --dyn-syms` daba **21 475 entradas** contra +# ~2 700 de un binario dinámico normal ⇒ eso es `--export-dynamic`, no un enlace corriente. +# +# NO venía del meson de gtk —ahí no aparece la palabra— sino de un `.pc` DE DEPENDENCIA: +# `gmodule-2.0.pc` trae literalmente `Libs: -Wl,--export-dynamic`, y `meson.build:426` pedía +# `dependency('gmodule-2.0')`. El wrapper de cc del lab (`sandbox.rs:597`) tira el `-static` +# inyectado ante `--export-dynamic` ⇒ lo ponía el lab y lo sacaba el lab, por un flag heredado. +# +# **No hace falta parchear el `.pc` de glib**: upstream ya publica la misma librería sin el flag. +# `gmodule-no-export-2.0.pc` tiene el `-lgmodule-2.0 -pthread` de verdad, y los otros dos +# (`gmodule-2.0`, `gmodule-export-2.0`) son wrappers que sólo hacen `Requires:` sobre él y añaden +# `-Wl,--export-dynamic`. Pedir el `no-export` es la palanca prevista, no un apaño. +# +# Cuesta cero acá: `gmodule_dep` se declara una vez y se usa en un solo sitio (`gtk/meson.build:1002`), +# y los dos consumidores de `g_module_open` —`gtkbuilderscope.c` y `gtkmain.c`— son caminos de +# `dlopen` que un binario estático musl no puede tomar igual. +# +# ⚠ REGLA GENERAL: cualquier receta que enlace gmodule hereda `--export-dynamic` y pierde el +# `-static` SIN AVISO. Si `link = "static"` no pega y hay glib de por medio, mirar los `.pc` de las +# deps antes que el build del proyecto. +# +# ── Parche 4: el bug de upstream que SÓLO existe en estático ──────────────────────────────────── +# Con los 8 ejecutables ya estáticos, TODOS morían con SIGSEGV (rc=139) hasta en `--help`, donde el +# binario dinámico imprimía el uso. No era el enlace: es un fallo de gtk. Backtrace con gdb: +# g_module_symbol (module=0x0, symbol_name="gtk_progress_get_type") at gmodule.c:855 +# _gtk_module_has_mixed_deps (module_to_check=0x0) at gtk/gtkmain.c:474 +# do_pre_parse_initialization () at gtk/gtkmain.c:498 +# gtk_init_check () at gtk/gtkmain.c:635 +# `_gtk_module_has_mixed_deps(NULL)` hace `module = g_module_open (NULL, 0)` y pasa el resultado a +# `g_module_symbol` **sin comprobarlo**. En musl ESTÁTICO `dlopen(NULL)` devuelve NULL siempre ⇒ +# desreferencia de nulo en el offset 8, en el arranque de `gtk_init_check`. Con libc dinámica +# `dlopen(NULL)` funciona y el bug no se ve nunca: es de los que sólo existen de un lado. +# El parche es la comprobación que falta. Devolver FALSE es lo correcto además de lo seguro: en un +# binario estático no hay módulos cargables, así que no puede haber símbolos de GTK 2/3 mezclados. +# `gtk/gtkbuilderscope.c:188` tiene el otro `g_module_open(NULL,…)` del árbol y ése SÍ está bien +# (devuelve NULL a un getter y va guardado por `g_module_supported()`). +# # ⚠ El artefacto deja de traer `libgtk-4.so`. Es lo coherente con `link = "static"` y con la fase # install, que ya fabricaba `libgtk-4.a` como el producto real; la .so se construía e instalaba sin # que nadie la usara. Las colas wlr/gnome tienen sus PROPIAS recetas de gtk4 y no se ven afectadas. configure = ''' +sed -i "s/dependency('gmodule-2.0'/dependency('gmodule-no-export-2.0'/" meson.build +sed -i 's| if (g_module_symbol (module, "gtk_progress_get_type", &func))| if (module == NULL)\n return FALSE;\n&|' gtk/gtkmain.c sed -i 's/libgtk_dep/libgtk_static_dep/g' tools/meson.build sed -i "/^libgtk = shared_library('gtk-4',/,/^)/ s/^ install: true,$/ install: false,\n build_by_default: false,/" gtk/meson.build sed -i "/^shared_module('printbackend-file',/,/^)/ s/^ install: true,$/ install: false,\n build_by_default: false,/" modules/printbackends/meson.build