From 97e4854ccbd808ac012207fd832e8cf673f03148 Mon Sep 17 00:00:00 2001 From: Shaikh Taimoor <60020024+staimoorulhassan@users.noreply.github.com> Date: Fri, 29 May 2026 00:03:26 +0500 Subject: [PATCH] fix(appimage): cd "$APPDIR" in AppRun before exec to fix sharun preload CWD resolution (#2829) Co-authored-by: Taimoor Co-authored-by: sanil-23 --- .../release/strip-appimage-graphics-libs.sh | 82 ++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/scripts/release/strip-appimage-graphics-libs.sh b/scripts/release/strip-appimage-graphics-libs.sh index e2e4faa00..e105582d2 100755 --- a/scripts/release/strip-appimage-graphics-libs.sh +++ b/scripts/release/strip-appimage-graphics-libs.sh @@ -363,6 +363,80 @@ validate_sharun_lib_path() { fi } +# patch_apprun_sharun_cwd — inject `cd "$APPDIR"` into AppRun before the final +# exec call so sharun resolves preload/library paths relative to the AppDir +# rather than the caller's CWD. +# +# Problem (issue #2822): sharun reads its `.preload` entry and library search +# paths relative to the process CWD. When a user launches the AppImage from +# any directory other than the AppDir itself (e.g. double-click from ~/Downloads) +# CWD != AppDir, so ld.so can't find `anylinux.so` (preload) or `libcef.so` +# (LD_LIBRARY_PATH entry). SHARUN_DIR is set correctly — the AppDir IS known — +# but sharun doesn't use it to anchor the preload/library arguments it hands to +# ld.so. +# +# Fix: prepend `cd "$APPDIR"` to the exec line in AppRun so the working +# directory is always the AppDir by the time sharun/the binary runs. This +# mirrors the verified manual workaround from the bug report. +# +# Returns 0 (true) if the AppRun was modified, 1 if no change was needed. +patch_apprun_sharun_cwd() { + local appdir="$1" + if ! uses_sharun_launcher "$appdir"; then + return 1 + fi + + local apprun="$appdir/AppRun" + if [ ! -f "$apprun" ]; then + # Some sharun bundles use the sharun binary directly as the AppDir entry + # point without a separate shell AppRun. Nothing to patch in that case. + return 1 + fi + + # Check if the file is a shell script (not an ELF binary). + local first_bytes + first_bytes="$(LC_ALL=C head -c 2 "$apprun" 2>/dev/null || true)" + if [ "$first_bytes" = $'\x7fE' ]; then + # AppRun is an ELF binary — cannot patch with sed. + return 1 + fi + + # Idempotency guard: skip if we already patched this AppRun. + # Match only the exact patched line — a loose substring (e.g. 'cd.*"$APPDIR"') + # would false-positive on comments like '# cd "$APPDIR"' or unrelated lines + # and leave the real `exec "$@"` unpatched. + local patched_line_re='^[[:space:]]*cd[[:space:]]+"\$APPDIR"[[:space:]]*&&[[:space:]]*exec[[:space:]]+"\$@"[[:space:]]*$' + if grep -Eq "$patched_line_re" "$apprun" 2>/dev/null; then + return 1 + fi + + # Locate the exec line. AppRun scripts generated by lib4bin / sharun + # typically have a line of the form (possibly with leading whitespace): + # exec "$@" + # Patch it to: + # cd "$APPDIR" && exec "$@" + # + # The sed pattern is anchored to end-of-line ($) so trailing content (extra + # args, comments, redirections) doesn't get silently absorbed into the cd && + # exec sequence. + # + # Use a temp file + mv to avoid truncating AppRun mid-write on failure. + local tmp_apprun + tmp_apprun="$(mktemp)" + if sed 's|^\([[:space:]]*\)exec "\$@"[[:space:]]*$|\1cd "$APPDIR" \&\& exec "$@"|' \ + "$apprun" > "$tmp_apprun" \ + && grep -Eq "$patched_line_re" "$tmp_apprun"; then + chmod --reference="$apprun" "$tmp_apprun" + mv "$tmp_apprun" "$apprun" + echo "[strip-libs] patched AppRun: added 'cd \"\$APPDIR\"' before exec to fix sharun CWD preload resolution (issue #2822)" + return 0 + else + rm -f "$tmp_apprun" + echo "[strip-libs] WARNING: could not locate 'exec \"\$@\"' in AppRun — sharun CWD fix not applied; AppImage may fail to launch from non-AppDir CWDs" >&2 + return 1 + fi +} + strip_one_appimage() { local img="$1" local original @@ -386,6 +460,7 @@ strip_one_appimage() { local removed=0 local added_loader=0 local rewrote_libpath=0 + local patched_apprun=0 local lib_roots=() for candidate in \ "$appdir/usr/lib" \ @@ -420,14 +495,17 @@ strip_one_appimage() { if rewrite_sharun_lib_path "$appdir"; then rewrote_libpath=1 fi + if patch_apprun_sharun_cwd "$appdir"; then + patched_apprun=1 + fi validate_sharun_lib_path "$appdir" - if [ "$removed" -eq 0 ] && [ "$added_loader" -eq 0 ] && [ "$rewrote_libpath" -eq 0 ]; then + if [ "$removed" -eq 0 ] && [ "$added_loader" -eq 0 ] && [ "$rewrote_libpath" -eq 0 ] && [ "$patched_apprun" -eq 0 ]; then echo "[strip-libs] No graphics libs or missing sharun interpreter found in $original; leaving unchanged." rm -rf "$workdir" return fi - echo "[strip-libs] Removed $removed file(s), added $added_loader loader file(s); repacking AppImage." + echo "[strip-libs] Removed $removed file(s), added $added_loader loader file(s), patched AppRun=$patched_apprun; repacking AppImage." local rebuilt="$workdir/$name" local appimage_arch