From b9f24860374144dac5982d744a31fd4eec08771c Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Mon, 17 Aug 2026 19:50:34 +0800 Subject: [PATCH] fix(gui): strip host LD_LIBRARY_PATH from packaging + defensive GPU/icon fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause of the packaged GUI crashing with SIGSEGV inside ld.so on user machines (segfault at fixed +0x1ff36, undefined symbols nspr_use_zone_allocator / localtime64): the build host had LD_LIBRARY_PATH polluted by a third-party runtime (/opt/cangjie), which electron-builder baked into the produced binary's dependency resolution. Clean machines without that library then fail in the dynamic loader before any app code runs. - Makefile: every gui-* pack target now runs under `env -u LD_LIBRARY_PATH` - main.js: app.disableHardwareAcceleration() before ready (avoids the common Chromium GPU-process SIGSEGV on hybrid-GPU/Wayland Linux hosts) - main.js: window icon reads from process.resourcesPath (real file, not asar) — asar-path icons are a known GTK segfault source on Linux --- Makefile | 18 +++++++++++++----- cmd/gui/main.js | 20 ++++++++++++++++++-- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index df3b2e9..545fcb7 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,12 @@ export GUI_DIR GUI_DIR=cmd/gui VERSION ?= $(shell git describe --tags --dirty 2>/dev/null || echo "0.1.0") +# Clean env for GUI packaging: the host may have LD_LIBRARY_PATH polluted by +# third-party runtimes (e.g. /opt/cangjie) which electron-builder bakes into +# the produced binary's DT_RPATH/dep resolution -> the packaged app then +# SIGSEGVs inside ld.so on clean machines. Strip it for every pack step. +CLEAN_PACK_ENV = env -u LD_LIBRARY_PATH + all: build # ---- core binary (server users / embedded core) ---- @@ -26,23 +32,25 @@ gui-dev: # build the embedded core into the GUI, then run electron-builder # targets: deb + AppImage (linux), rpm needs system rpmbuild +# NOTE: run in a clean env (no LD_LIBRARY_PATH pollution) so the packaged +# binary does not link against host-only libs and crash on user machines. gui-dist: - @cd $(GUI_DIR) && npm run dist:linux + @cd $(GUI_DIR) && $(CLEAN_PACK_ENV) npm run dist:linux # deb only (for local sharing), rpm needs rpmbuild gui-deb: - @cd $(GUI_DIR) && npm run dist:debian + @cd $(GUI_DIR) && $(CLEAN_PACK_ENV) npm run dist:debian # windows nsis installer: host-built core (needs apt install gcc-mingw-w64-x86-64 + wine) gui-win: - @cd $(GUI_DIR) && npm run dist:win + @cd $(GUI_DIR) && $(CLEAN_PACK_ENV) npm run dist:win # windows nsis installer: full cross-build in docker (self-sufficient, no host mingw) gui-win-docker: - @cd $(GUI_DIR) && ./scripts/dist-win-docker.sh + @cd $(GUI_DIR) && $(CLEAN_PACK_ENV) ./scripts/dist-win-docker.sh gui-dist-dir: - @cd $(GUI_DIR) && npm run dist:dir + @cd $(GUI_DIR) && $(CLEAN_PACK_ENV) npm run dist:dir test: go test ./... diff --git a/cmd/gui/main.js b/cmd/gui/main.js index 3c9b954..6b025a3 100644 --- a/cmd/gui/main.js +++ b/cmd/gui/main.js @@ -47,6 +47,15 @@ const CORE_EXE = path.join( "bin", process.platform === "win32" ? "llmsproxy.exe" : "llmsproxy", ); +// Packaged window icon: must be a REAL file on disk (extraResource), not an +// asar path — Electron/GTK pulls BrowserWindow icons from the filesystem and +// crashes (SIGSEGV) when given an asar virtual path on Linux. +// Dev: cmd/gui/build/icon.png. +const WINDOW_ICON = path.join( + app.isPackaged ? process.resourcesPath : __dirname, + "build", + "icon.png", +); // default port for the embedded core (configurable via settings) const DEFAULT_PORT = 8787; @@ -508,8 +517,9 @@ function createWindow() { const menu = Menu.buildFromTemplate([]); Menu.setApplicationMenu(menu); mainWindow = new BrowserWindow({ - // Window icon (taskbar/dock): same asset as the tray and package. - icon: path.join(__dirname, "build", "icon.png"), + // Window icon (taskbar/dock). Packaged: real file under resourcesPath (see + // WINDOW_ICON) — an asar path here makes GTK segfault on Linux. + icon: WINDOW_ICON, // GNOME taskbar icons match the window's WM_CLASS (lowercase // "modelrouter-gui") against StartupWMClass in the .desktop file. backgroundColor: "#1f2937", @@ -639,6 +649,12 @@ ipcMain.handle("shell:openPath", (_, p) => { // window control from renderer uses preload -> ipcMain above // ---------- bootstrap ---------- +// Defensive GPU handling: Electron's Chromium GPU process is a frequent +// source of hard crashes (SIGSEGV) on Linux, especially on hybrid-GPU and +// Wayland setups. The GUI host is an LLM management shell — a software +// renderer is perfectly fine. Must run before app.whenReady. +app.disableHardwareAcceleration(); + const gotLock = app.requestSingleInstanceLock(); if (gotLock) { app.on("second-instance", () => {