Skip to content

Commit 567c447

Browse files
authored
Merge pull request jellyfin#1150 from jellyfin/appimage-ubuntu
Fix AppImage crashes on Ubuntu and other non-Arch distros
2 parents 451417d + 6ea2a43 commit 567c447

3 files changed

Lines changed: 85 additions & 7 deletions

File tree

‎dev/appimage/Dockerfile‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ RUN rm -rf /AppDir/usr/lib/dri \
8989
/AppDir/usr/lib/libOpenGL.so* \
9090
/AppDir/usr/lib/libgbm.so* \
9191
/AppDir/usr/lib/libvulkan.so* \
92-
/AppDir/usr/lib/libvdpau.so* \
9392
/AppDir/usr/lib/libdrm.so* \
9493
/AppDir/usr/lib/libdrm_*.so* \
9594
/AppDir/usr/lib/libxshmfence.so* \
@@ -120,14 +119,19 @@ RUN cp /AppDir/usr/share/applications/org.jellyfin.JellyfinDesktop.desktop /AppD
120119
ln -sf org.jellyfin.JellyfinDesktop.svg /AppDir/.DirIcon
121120

122121
# Wrap QtWebEngineProcess so it also uses the bundled ld-linux.
123-
# Without this, Chromium's renderer/GPU subprocesses would use the host's
124-
# dynamic linker and hit the same glibc mismatch.
125-
COPY dev/appimage/QtWebEngineProcess.wrapper /tmp/QtWebEngineProcess.wrapper
122+
# This MUST be a statically-linked binary, not a shell script. The parent
123+
# exports LD_LIBRARY_PATH pointing to the bundled (Arch) libs; a script
124+
# wrapper would be interpreted by /bin/bash loaded via the host's ld-linux,
125+
# which then pulls in the bundled glibc — an ABI mismatch that segfaults
126+
# on distros whose glibc differs from the build host.
127+
COPY dev/appimage/webengine-wrapper.c /tmp/webengine-wrapper.c
128+
RUN gcc -static -O2 -o /tmp/webengine-wrapper /tmp/webengine-wrapper.c && \
129+
strip /tmp/webengine-wrapper
126130
RUN set -e && \
127131
webengine_bin="$(find /AppDir/usr/lib -name QtWebEngineProcess -type f)" && \
128132
if [ -n "$webengine_bin" ]; then \
129133
mv "$webengine_bin" "${webengine_bin}.real" && \
130-
cp /tmp/QtWebEngineProcess.wrapper "$webengine_bin" && \
134+
cp /tmp/webengine-wrapper "$webengine_bin" && \
131135
chmod +x "$webengine_bin"; \
132136
fi
133137

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
#!/bin/bash
2-
# APPDIR is exported by AppRun
2+
SELF="$(readlink -f "$0")"
3+
4+
# Derive APPDIR from script location if not inherited from AppRun.
5+
# The script is always under $APPDIR/usr/lib/..., so stripping from
6+
# /usr/lib/ onward gives us the AppDir root.
7+
: "${APPDIR:="${SELF%%/usr/lib/*}"}"
8+
39
LIBRARY_PATH="${APPDIR}/usr/lib"
410
LIBRARY_PATH="${LIBRARY_PATH}:/usr/lib64:/usr/lib:/lib64:/lib"
511
LIBRARY_PATH="${LIBRARY_PATH}:/usr/lib/x86_64-linux-gnu:/lib/x86_64-linux-gnu"
612

713
exec "$APPDIR/usr/lib/ld-linux-x86-64.so.2" \
814
--inhibit-cache \
915
--library-path "${LIBRARY_PATH}" \
10-
"$(readlink -f "$0").real" "$@"
16+
"${SELF}.real" "$@"

‎dev/appimage/webengine-wrapper.c‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Statically-linked wrapper for QtWebEngineProcess.
3+
*
4+
* Qt WebEngine launches this as a subprocess. Because the parent sets
5+
* LD_LIBRARY_PATH to the AppImage's bundled libraries, a shell-script
6+
* wrapper would crash: the host's /bin/bash gets loaded by the host's
7+
* dynamic linker, which then pulls in the *bundled* glibc — an ABI
8+
* mismatch that segfaults on distros whose glibc differs from the build
9+
* host (e.g. Ubuntu vs Arch).
10+
*
11+
* A static binary sidesteps the problem entirely: it has no shared
12+
* library dependencies, so LD_LIBRARY_PATH is irrelevant. It simply
13+
* re-execs the real QtWebEngineProcess binary through the bundled
14+
* ld-linux with an explicit --library-path.
15+
*/
16+
17+
#include <limits.h>
18+
#include <stdio.h>
19+
#include <stdlib.h>
20+
#include <string.h>
21+
#include <unistd.h>
22+
23+
int main(int argc, char *argv[])
24+
{
25+
char self[PATH_MAX];
26+
ssize_t len = readlink("/proc/self/exe", self, sizeof(self) - 1);
27+
if (len < 0)
28+
_exit(127);
29+
self[len] = '\0';
30+
31+
/* Derive APPDIR: this binary lives under $APPDIR/usr/lib/...,
32+
so the first /usr/lib/ in the path marks the boundary. */
33+
char *p = strstr(self, "/usr/lib/");
34+
if (!p)
35+
_exit(127);
36+
37+
int appdir_len = (int)(p - self);
38+
39+
char ldlinux[PATH_MAX];
40+
snprintf(ldlinux, sizeof(ldlinux),
41+
"%.*s/usr/lib/ld-linux-x86-64.so.2", appdir_len, self);
42+
43+
char real_bin[PATH_MAX + 8];
44+
snprintf(real_bin, sizeof(real_bin), "%s.real", self);
45+
46+
char lib_path[PATH_MAX * 2];
47+
snprintf(lib_path, sizeof(lib_path),
48+
"%.*s/usr/lib"
49+
":/usr/lib64:/usr/lib:/lib64:/lib"
50+
":/usr/lib/x86_64-linux-gnu:/lib/x86_64-linux-gnu",
51+
appdir_len, self);
52+
53+
/* argv: ld-linux --inhibit-cache --library-path <path> <real> [original args…] */
54+
char **new_argv = calloc((size_t)(argc + 5), sizeof(char *));
55+
if (!new_argv)
56+
_exit(127);
57+
58+
new_argv[0] = ldlinux;
59+
new_argv[1] = "--inhibit-cache";
60+
new_argv[2] = "--library-path";
61+
new_argv[3] = lib_path;
62+
new_argv[4] = real_bin;
63+
for (int i = 1; i < argc; i++)
64+
new_argv[i + 4] = argv[i];
65+
66+
execv(ldlinux, new_argv);
67+
_exit(127);
68+
}

0 commit comments

Comments
 (0)