Skip to content

fix(linux): auto-detect and handle .relr.dyn sections for AppImage builds#4772

Merged
leaanthony merged 1 commit into
v3-alphafrom
fix/appimage-strip-compatibility
Dec 12, 2025
Merged

fix(linux): auto-detect and handle .relr.dyn sections for AppImage builds#4772
leaanthony merged 1 commit into
v3-alphafrom
fix/appimage-strip-compatibility

Conversation

@leaanthony

@leaanthony leaanthony commented Dec 12, 2025

Copy link
Copy Markdown
Member

Summary

  • Adds proactive detection of modern Linux toolchains that use .relr.dyn ELF sections
  • Automatically disables stripping (NO_STRIP=1) when detected to prevent linuxdeploy failures
  • Adds documentation explaining the issue and automatic handling

Problem

Modern Linux distributions (Arch Linux, Fedora 39+, Ubuntu 24.04+) compile libraries with .relr.dyn ELF sections for more efficient relocations. The linuxdeploy tool bundles an older strip binary that cannot process these sections, causing AppImage builds to fail with:

Strip call failed: unknown type [0x13] section `.relr.dyn'

This is a known issue: linuxdeploy#272

Solution

Rather than always disabling stripping (which increases AppImage size by ~20-40%), this PR:

  1. Proactively detects if system GTK libraries have .relr.dyn sections using readelf
  2. Only disables stripping on affected systems
  3. Logs an INFO message with a link to documentation when this happens

Test plan

  • Tested AppImage build on Arch Linux (affected system) - builds successfully
  • Verify older distributions still get stripped binaries

Fixes #4642

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • AppImage builds now auto-detect modern Linux ELF section compatibility and disable binary stripping when needed to prevent build failures, with clearer build output for troubleshooting.
  • Documentation

    • Added guidance on AppImage compatibility, the trade-off of slightly larger images when stripping is disabled, and the option to provide a newer strip tool to reduce size.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Dec 12, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds documentation and runtime detection to avoid stripping AppImage GTK libraries with .relr.dyn ELF sections by setting NO_STRIP=1 at packaging time; updates AppImage build logic to detect such sections and adjusts error output printing. No other behavioral API changes.

Changes

Cohort / File(s) Summary
Documentation
docs/src/content/docs/guides/build/linux.mdx
Adds AppImage strip compatibility guidance describing .relr.dyn detection, the NO_STRIP=1 workaround, trade-offs (larger AppImages), and option to use a newer strip to reduce size.
AppImage Build Logic
v3/internal/commands/appimage.go
Adds hasRelrDynSections() which uses readelf to scan common GTK-related libraries for .relr.dyn and, if present, sets NO_STRIP=1 before running linuxdeploy. Replaces println(output) with fmt.Println(string(output)).
Changelog
v3/UNRELEASED_CHANGELOG.md
Documents the AppImage build mitigation entry about auto-detecting .relr.dyn and disabling stripping.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Verify hasRelrDynSections() readelf invocation, parsing, and failure modes.
  • Review the list of library paths scanned for completeness and platform variations.
  • Confirm environment variable is set early enough and does not conflict with other packaging steps.
  • Validate change from println to fmt.Println(string(...)) preserves intended output encoding.

Suggested labels

Bug, Linux, Documentation, v3-alpha

Suggested reviewers

  • atterpac

Poem

🐰 I sniffed the ELF and found a clue,
A tiny section hiding from view.
I set NO_STRIP with a gentle hop,
Saved AppImages from a packaging flop —
Hooray! 🥕📦

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR claims to fix issue #4642, which involves AppImage build failures on modern Linux distributions. However, #4642 describes an icon mismatch problem in the desktop file/symlink configuration, not .relr.dyn section handling. The .relr.dyn fix addresses a different failure mode than the root cause identified in #4642. Verify whether #4642 was actually caused by .relr.dyn sections or the icon mismatch. If the true root cause is icon mismatch (as described in #4642), this PR addresses a symptom but not the actual issue that was reported.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Out of Scope Changes check ❓ Inconclusive The PR appropriately addresses .relr.dyn ELF section detection and stripping behavior for AppImage builds. However, the claimed fix for #4642 appears to target a different problem than what #4642 describes, suggesting a potential scope mismatch with the linked issue. Clarify the relationship between the .relr.dyn fix and issue #4642. If #4642's root cause is truly the icon mismatch described in the issue details, consider whether this PR should reference a different issue or if #4642 involves multiple failure modes.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: auto-detection of .relr.dyn ELF sections and automatic handling via NO_STRIP=1 to fix AppImage build failures on modern Linux distributions.
Description check ✅ Passed The PR description adequately covers the problem, solution, and test plan. However, the template requires checking for a linked issue, which is present (Fixes #4642), and documentation updates are claimed but changelog updates are shown in the summary rather than specifically mentioning website/changelog.mdx.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/appimage-strip-compatibility

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 275ea95 and a7f16d2.

📒 Files selected for processing (3)
  • docs/src/content/docs/guides/build/linux.mdx (1 hunks)
  • v3/UNRELEASED_CHANGELOG.md (1 hunks)
  • v3/internal/commands/appimage.go (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • v3/UNRELEASED_CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/src/content/docs/guides/build/linux.mdx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Mihara
Repo: wailsapp/wails PR: 4481
File: v3/internal/commands/updatable_build_assets/linux/nfpm/nfpm.yaml.tmpl:29-30
Timestamp: 2025-08-08T10:25:32.415Z
Learning: For Wails v3 Linux nfpm template, Debian/Ubuntu WebKitGTK runtime package names differ: Debian 12 (Bookworm) uses libwebkit2gtk-4.1-0 while Ubuntu 22.04 (Jammy) uses libwebkit2gtk-4.0-37. Prefer an OR dependency "libwebkit2gtk-4.1-0 | libwebkit2gtk-4.0-37" in v3/internal/commands/updatable_build_assets/linux/nfpm/nfpm.yaml.tmpl to support both.
📚 Learning: 2024-10-08T22:11:37.054Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 3763
File: v3/internal/commands/appimage_testfiles/main.go:295-299
Timestamp: 2024-10-08T22:11:37.054Z
Learning: In `v3/internal/commands/appimage_testfiles/main.go`, `time.Sleep` is used within a goroutine and does not block the UI thread.

Applied to files:

  • v3/internal/commands/appimage.go
🧬 Code graph analysis (1)
v3/internal/commands/appimage.go (2)
v3/internal/term/term.go (2)
  • Infof (64-66)
  • Println (109-111)
v3/internal/s/s.go (2)
  • SETENV (84-88)
  • EXEC (269-280)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (1)
v3/internal/commands/appimage.go (1)

201-205: Printing linuxdeploy output on failure is a solid improvement.
fmt.Println(string(output)) will actually show the combined stdout/stderr from s.EXEC, which should make failures debuggable.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Missing Changelog Update

Hi @leaanthony, please update v3/UNRELEASED_CHANGELOG.md with a description of your changes.

This helps us keep track of changes for the next release.

…ilds

Modern Linux distributions (Arch, Fedora 39+, Ubuntu 24.04+) compile
libraries with .relr.dyn ELF sections. The bundled strip binary in
linuxdeploy cannot process these sections, causing AppImage builds to fail.

This commit:
- Adds hasRelrDynSections() to proactively detect modern toolchains
- Automatically disables stripping (NO_STRIP=1) when detected
- Fixes error output to properly display as string
- Adds documentation explaining the issue and workaround

Fixes #4642

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@leaanthony leaanthony force-pushed the fix/appimage-strip-compatibility branch from 275ea95 to a7f16d2 Compare December 12, 2025 21:20
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Dec 12, 2025

Copy link
Copy Markdown

Deploying wails with  Cloudflare Pages  Cloudflare Pages

Latest commit: a7f16d2
Status: ✅  Deploy successful!
Preview URL: https://ff11ba03.wails.pages.dev
Branch Preview URL: https://fix-appimage-strip-compatibi.wails.pages.dev

View logs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
docs/src/content/docs/guides/build/linux.mdx (1)

148-160: Make the “newer strip” path actionable (or link to upstream docs).
Right now it says “configure linuxdeploy to use it” but doesn’t explain how or where to learn it. Consider adding either (a) a short snippet with the exact env var / mechanism (if stable), or (b) a link to linuxdeploy docs/issues describing overriding the bundled strip.

v3/internal/commands/appimage.go (1)

260-280: hasRelrDynSections() should key off the actual GTK lib path (or at least GTK version), and quote paths.
Hardcoding just GTK3 paths makes this brittle across GTK2/GTK4 and across distros/libdirs; also readelf -S %s should quote the path to avoid shlex splitting edge-cases.

Concrete refactor (still cheap): derive a candidate GTK lib path from lddString based on DeployGtkVersion, then fallback to your current list.

-func hasRelrDynSections() bool {
+func hasRelrDynSections(deployGtkVersion string, lddString string) bool {
+    // Try to use the exact lib path from ldd first (more robust than hardcoded libdirs)
+    // Example ldd line: "libgtk-3.so.0 => /usr/lib/... (0x...)"
+    var gtkNeedle string
+    switch deployGtkVersion {
+    case "2":
+        gtkNeedle = "libgtk-x11-2.0.so.0 => "
+    case "3":
+        gtkNeedle = "libgtk-3.so.0 => "
+    case "4":
+        gtkNeedle = "libgtk-4.so.1 => "
+    }
+    if gtkNeedle != "" {
+        if i := strings.Index(lddString, gtkNeedle); i >= 0 {
+            rest := lddString[i+len(gtkNeedle):]
+            if fields := strings.Fields(rest); len(fields) > 0 && strings.HasPrefix(fields[0], "/") {
+                if hasRelrDynInFile(fields[0]) {
+                    return true
+                }
+            }
+        }
+    }
+
     // Check common GTK library that will be bundled
     testLibs := []string{
         "/usr/lib/libgtk-3.so.0",
         "/usr/lib64/libgtk-3.so.0",
         "/usr/lib/x86_64-linux-gnu/libgtk-3.so.0",
     }
 
     for _, lib := range testLibs {
         if _, err := os.Stat(lib); err == nil {
-            output, err := s.EXEC(fmt.Sprintf("readelf -S %s", lib))
+            output, err := s.EXEC(fmt.Sprintf("readelf -S %q", lib))
             if err == nil && strings.Contains(string(output), ".relr.dyn") {
                 return true
             }
         }
     }
     return false
 }
+
+func hasRelrDynInFile(path string) bool {
+    if _, err := os.Stat(path); err != nil {
+        return false
+    }
+    out, err := s.EXEC(fmt.Sprintf("readelf -S %q", path))
+    return err == nil && strings.Contains(string(out), ".relr.dyn")
+}

(And if readelf is missing, consider at least logging once, or adding a fallback like “retry linuxdeploy with NO_STRIP=1 when the failure contains .relr.dyn”, to avoid silent false negatives.)

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38c89e0 and 275ea95.

📒 Files selected for processing (2)
  • docs/src/content/docs/guides/build/linux.mdx (1 hunks)
  • v3/internal/commands/appimage.go (2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: Mihara
Repo: wailsapp/wails PR: 4481
File: v3/internal/commands/updatable_build_assets/linux/nfpm/nfpm.yaml.tmpl:29-30
Timestamp: 2025-08-08T10:25:32.415Z
Learning: For Wails v3 Linux nfpm template, Debian/Ubuntu WebKitGTK runtime package names differ: Debian 12 (Bookworm) uses libwebkit2gtk-4.1-0 while Ubuntu 22.04 (Jammy) uses libwebkit2gtk-4.0-37. Prefer an OR dependency "libwebkit2gtk-4.1-0 | libwebkit2gtk-4.0-37" in v3/internal/commands/updatable_build_assets/linux/nfpm/nfpm.yaml.tmpl to support both.
📚 Learning: 2025-08-08T10:25:32.415Z
Learnt from: Mihara
Repo: wailsapp/wails PR: 4481
File: v3/internal/commands/updatable_build_assets/linux/nfpm/nfpm.yaml.tmpl:29-30
Timestamp: 2025-08-08T10:25:32.415Z
Learning: For Wails v3 Linux nfpm template, Debian/Ubuntu WebKitGTK runtime package names differ: Debian 12 (Bookworm) uses libwebkit2gtk-4.1-0 while Ubuntu 22.04 (Jammy) uses libwebkit2gtk-4.0-37. Prefer an OR dependency "libwebkit2gtk-4.1-0 | libwebkit2gtk-4.0-37" in v3/internal/commands/updatable_build_assets/linux/nfpm/nfpm.yaml.tmpl to support both.

Applied to files:

  • docs/src/content/docs/guides/build/linux.mdx
📚 Learning: 2025-08-08T11:10:17.787Z
Learnt from: Mihara
Repo: wailsapp/wails PR: 4481
File: v3/internal/commands/updatable_build_assets/linux/nfpm/nfpm.yaml.tmpl:37-38
Timestamp: 2025-08-08T11:10:17.787Z
Learning: In Wails v3 nfpm templates for RPM (RHEL-family), do not use a boolean OR between webkit2gtk3 and webkit2gtk4.1. Binaries built against WebKitGTK 4.1 are not ABI-compatible with 4.0, so the dependency must be pinned to the exact ABI: use webkit2gtk4.1 for 4.1 builds (e.g., RHEL/Rocky 10) and webkit2gtk3 for 4.0 builds (RHEL/Rocky 8/9).

Applied to files:

  • docs/src/content/docs/guides/build/linux.mdx
📚 Learning: 2024-10-08T22:11:37.054Z
Learnt from: leaanthony
Repo: wailsapp/wails PR: 3763
File: v3/internal/commands/appimage_testfiles/main.go:295-299
Timestamp: 2024-10-08T22:11:37.054Z
Learning: In `v3/internal/commands/appimage_testfiles/main.go`, `time.Sleep` is used within a goroutine and does not block the UI thread.

Applied to files:

  • v3/internal/commands/appimage.go
🧬 Code graph analysis (1)
v3/internal/commands/appimage.go (2)
v3/internal/term/term.go (2)
  • Infof (64-66)
  • Println (109-111)
v3/internal/s/s.go (2)
  • SETENV (84-88)
  • EXEC (269-280)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Analyze (go)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (1)
v3/internal/commands/appimage.go (1)

201-205: Good: print linuxdeploy output as text on failure.
Switching to fmt.Println(string(output)) makes the error output readable and consistent with s.EXEC returning bytes.

Comment on lines +193 to +200

// Check if system libraries use .relr.dyn sections (modern toolchains)
// If so, disable stripping as linuxdeploy's bundled strip can't handle them
if hasRelrDynSections() {
term.Infof("Detected modern toolchain (.relr.dyn sections), disabling stripping for compatibility. See: https://v3.wails.io/guides/build/linux#appimage-strip-compatibility")
s.SETENV("NO_STRIP", "1")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd /tmp && find . -type f -name "appimage.go" | head -5

Repository: wailsapp/wails

Length of output: 40


🏁 Script executed:

cat /tmp/appimage.go 2>/dev/null || (find . -type f -name "appimage.go" -path "*/v3/internal/commands/*" | head -1 | xargs cat 2>/dev/null)

Repository: wailsapp/wails

Length of output: 7519


🏁 Script executed:

find . -type f -name "appimage.go" | grep -E "v3|commands"

Repository: wailsapp/wails

Length of output: 92


🏁 Script executed:

fd appimage.go

Repository: wailsapp/wails

Length of output: 90


🏁 Script executed:

# List files in the expected directory structure
ls -la v3/internal/commands/ 2>/dev/null | head -20 || echo "Directory not found, searching..."
fd -t f appimage.go

Repository: wailsapp/wails

Length of output: 1320


🏁 Script executed:

# Search for the file and read it
if [ -f "v3/internal/commands/appimage.go" ]; then
  wc -l v3/internal/commands/appimage.go
fi

Repository: wailsapp/wails

Length of output: 94


🏁 Script executed:

git ls-files | grep -i appimage | head -10

Repository: wailsapp/wails

Length of output: 549


🏁 Script executed:

# Get the appimage.go file
git ls-files | grep appimage.go

Repository: wailsapp/wails

Length of output: 90


🏁 Script executed:

# Find and display the appimage.go file
git show HEAD:v3/internal/commands/appimage.go | head -300

Repository: wailsapp/wails

Length of output: 7519


🏁 Script executed:

# Get the full file
git show HEAD:v3/internal/commands/appimage.go > /tmp/appimage_full.txt 2>&1
wc -l /tmp/appimage_full.txt
head -20 /tmp/appimage_full.txt

Repository: wailsapp/wails

Length of output: 370


🏁 Script executed:

# Search for hasRelrDynSections function
git show HEAD:v3/internal/commands/appimage.go | grep -n -A 20 "hasRelrDynSections"

Repository: wailsapp/wails

Length of output: 1534


🏁 Script executed:

# Get lines around 193-200 and the hasRelrDynSections function
git show HEAD:v3/internal/commands/appimage.go | sed -n '180,220p'

Repository: wailsapp/wails

Length of output: 1499


🏁 Script executed:

# Get the hasRelrDynSections implementation
git show HEAD:v3/internal/commands/appimage.go | sed -n '250,290p'

Repository: wailsapp/wails

Length of output: 950


🏁 Script executed:

# Let me get the full context - search for the function definition
git show HEAD:v3/internal/commands/appimage.go | grep -n "func hasRelrDynSections"

Repository: wailsapp/wails

Length of output: 94


🏁 Script executed:

# Get the entire file to understand context
git show HEAD:v3/internal/commands/appimage.go 2>&1 | tail -100

Repository: wailsapp/wails

Length of output: 2807


🏁 Script executed:

# Simple approach - get full file and search
git show HEAD:v3/internal/commands/appimage.go 2>&1 | wc -l

Repository: wailsapp/wails

Length of output: 61


🏁 Script executed:

# Get the file in full
git show HEAD:v3/internal/commands/appimage.go 2>&1 > /tmp/appimage_check.txt
cat /tmp/appimage_check.txt | head -1

Repository: wailsapp/wails

Length of output: 74


🏁 Script executed:

# Direct approach
git ls-files | xargs grep -l "hasRelrDynSections" 2>/dev/null

Repository: wailsapp/wails

Length of output: 92


🏁 Script executed:

# Search in v3 directory
find v3 -name "*.go" -type f 2>/dev/null | xargs grep -l "hasRelrDynSections" 2>/dev/null

Repository: wailsapp/wails

Length of output: 90


🏁 Script executed:

# Try simpler git command
git log --oneline -1
git diff HEAD~1 HEAD -- v3/internal/commands/appimage.go 2>/dev/null | head -100

Repository: wailsapp/wails

Length of output: 140


🏁 Script executed:

# Check if file exists and read it
test -f v3/internal/commands/appimage.go && cat v3/internal/commands/appimage.go | head -50

Repository: wailsapp/wails

Length of output: 1210


🏁 Script executed:

git ls-files --full-name | grep -E "v3.*appimage"

Repository: wailsapp/wails

Length of output: 763


🏁 Script executed:

# List all files
git ls-files | head -20

Repository: wailsapp/wails

Length of output: 740


🏁 Script executed:

# Check repository structure
ls -la | head -20

Repository: wailsapp/wails

Length of output: 1262


🏁 Script executed:

# Try to find appimage.go in the repo
find . -name "appimage.go" -type f 2>/dev/null

Repository: wailsapp/wails

Length of output: 92


🏁 Script executed:

# Search for NO_STRIP usage patterns
rg "NO_STRIP" --type go -B 2 -A 2

Repository: wailsapp/wails

Length of output: 457


🏁 Script executed:

# Search for hasRelrDynSections
rg "hasRelrDynSections" --type go -B 3 -A 15

Repository: wailsapp/wails

Length of output: 2784


🏁 Script executed:

# Search for the conditional block mentioned in the review
rg "Detected modern toolchain" --type go -B 5 -A 5

Repository: wailsapp/wails

Length of output: 902


🏁 Script executed:

# Look for DeployGtkVersion and lddString
rg "DeployGtkVersion|lddString" --type go -B 2 -A 2

Repository: wailsapp/wails

Length of output: 1269


🏁 Script executed:

# Check general NO_STRIP patterns in the codebase
rg "os\.Getenv.*NO_STRIP|NO_STRIP.*os\.Getenv" --type go -B 3 -A 3

Repository: wailsapp/wails

Length of output: 40


🏁 Script executed:

# Search for readelf usage (mentioned in the review's analysis)
rg "readelf" --type go -B 5 -A 10

Repository: wailsapp/wails

Length of output: 781


🏁 Script executed:

# Look for GTK path patterns
rg "/usr/lib.*gtk|gtk.*lib|relr\.dyn" --type go -B 3 -A 3

Repository: wailsapp/wails

Length of output: 5769


Respect user-provided NO_STRIP and detect .relr.dyn across all supported GTK versions.

The auto-detection has two limitations: (1) it unconditionally forces NO_STRIP=1 even if the user explicitly configured their build environment to strip successfully, and (2) hasRelrDynSections() checks only GTK3 library paths, leaving GTK2 and GTK4 builds vulnerable to the same linuxdeploy failure.

- // Check if system libraries use .relr.dyn sections (modern toolchains)
- // If so, disable stripping as linuxdeploy's bundled strip can't handle them
- if hasRelrDynSections() {
-     term.Infof("Detected modern toolchain (.relr.dyn sections), disabling stripping for compatibility. See: https://v3.wails.io/guides/build/linux#appimage-strip-compatibility")
-     s.SETENV("NO_STRIP", "1")
- }
+ // Check if system libraries use .relr.dyn sections (modern toolchains).
+ // If so, disable stripping as linuxdeploy's bundled strip can't handle them.
+ // Respect a user-provided NO_STRIP to allow explicit overrides.
+ if os.Getenv("NO_STRIP") == "" && hasRelrDynSections(DeployGtkVersion, lddString) {
+     term.Infof("Detected modern toolchain (.relr.dyn sections), disabling stripping for compatibility. See: https://v3.wails.io/guides/build/linux#appimage-strip-compatibility")
+     s.SETENV("NO_STRIP", "1")
+ }

Also test on a GTK4-linked setup to ensure detection still triggers when it should.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
v3/internal/commands/appimage.go lines 193-200: the current logic
unconditionally sets NO_STRIP when hasRelrDynSections() returns true and only
inspects GTK3 paths; update it to first respect any user-provided NO_STRIP (do
not overwrite if os.Getenv("NO_STRIP") is non-empty) and only set NO_STRIP="1"
when it's unset and relr.dyn sections are detected; expand hasRelrDynSections()
(or add a helper) to search library paths for GTK2, GTK3 and GTK4 installations
(typical pkg-config paths and common lib dirs) so detection covers all supported
GTK versions, and run/verify this on a GTK4-linked environment to confirm
detection triggers as expected.

@sonarqubecloud

Copy link
Copy Markdown

@leaanthony leaanthony merged commit e0cd242 into v3-alpha Dec 12, 2025
63 checks passed
@leaanthony leaanthony deleted the fix/appimage-strip-compatibility branch December 12, 2025 22:56
Grantmartin2002 pushed a commit to Grantmartin2002/wails that referenced this pull request Apr 29, 2026
…ilds (wailsapp#4772)

Modern Linux distributions (Arch, Fedora 39+, Ubuntu 24.04+) compile
libraries with .relr.dyn ELF sections. The bundled strip binary in
linuxdeploy cannot process these sections, causing AppImage builds to fail.

This commit:
- Adds hasRelrDynSections() to proactively detect modern toolchains
- Automatically disables stripping (NO_STRIP=1) when detected
- Fixes error output to properly display as string
- Adds documentation explaining the issue and workaround

Fixes wailsapp#4642

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
leaanthony added a commit that referenced this pull request May 20, 2026
…5475) (#5476)

* fix(v3/appimage): detect GTK stack from binary before bundling (#5475)

Since alpha.93, the default Linux build target is GTK4 / WebKitGTK 6.0
(GTK3 / WebKit2GTK 4.1 is now the legacy `-tags gtk3` path). The AppImage
bundler still hard-coded the GTK3 set of runtime files, so on a runner
with only the GTK4 toolchain installed `wails3 generate appimage` aborted
with:

    ERROR  Unable to locate all required files: libwebkit2gtkinjectedbundle.so

Move the ldd-based version detection in front of `findGTKFiles`, run it
on the source binary (not the post-copy path, which `filepath.Join` mis-
handles when `-binary` includes a path), and pick the injected-bundle
filename to match: `libwebkitgtkinjectedbundle.so` for GTK4, the older
`libwebkit2gtkinjectedbundle.so` for GTK3. `WebKitWebProcess` and
`WebKitNetworkProcess` are unchanged - both stacks ship them.

Also extend `hasRelrDynSections` to probe `libgtk-4.so.1` (plus aarch64
paths) so the strip workaround introduced in #4772 still triggers on
modern toolchains regardless of which stack the user built against.

Verified on Ubuntu 26.04 lin-node1:

- Default (GTK4): `go build` then `wails3 generate appimage` bundles
  `libwebkitgtkinjectedbundle.so` under `webkitgtk-6.0/injected-bundle/`
  in the AppDir; the resulting AppImage launches under xvfb and logs
  `GTK=4.22.2 WebKitGTK=2.52.3`.
- Legacy (`-tags gtk3`): same flow bundles
  `libwebkit2gtkinjectedbundle.so` under `webkit2gtk-4.1/injected-bundle/`
  and the AppImage launches with `Webkit2Gtk=v2.52.3` and `-tags=gtk3`.

Two pre-existing unrelated issues observed during verification, flagged
here so reviewers don't conflate them with this fix:

- `wails3 generate appimage` with a relative `-builddir` races on the
  AppRun download goroutine because `s.CD(BuildDir)` is called between
  the goroutine launch and its file write. Workaround: pass absolute
  paths.
- The post-bundle `s.MOVE` expects the AppImage to be named after
  `normaliseName(filepath.Base(Binary))`, but `linuxdeploy` names it
  after the desktop `Name=` field, so the move fails when those don't
  match. Workaround: align `Name=` with the binary basename. Will be
  addressed in a follow-up.

Refs #5475.

* fix(v3/appimage): absolute input paths and forced output filename

Two pre-existing bugs surfaced while verifying #5475:

1. `wails3 generate appimage` with a relative `-builddir` would fail
   inside the AppRun download goroutine. The flow does `s.CD(BuildDir)`
   between launching the goroutines and their file writes, so any
   path computed before the CD (e.g. `appDir = filepath.Join(BuildDir,
   "...AppDir")`) was being resolved relative to the wrong CWD by the
   downstream `s.DOWNLOAD(target)` and the `ldd <binary>` shell-out.

   Fix: resolve `-binary`, `-icon`, `-desktopfile`, `-builddir` and
   `-outputdir` to absolute paths up-front, alongside the existing
   `OutputDir` resolution. Now CWD-sensitive operations downstream
   all see stable absolute paths.

2. The post-bundle `s.MOVE(targetFile, OutputDir)` would fail when the
   desktop file's `Name=` field didn't match `normaliseName(basename(
   Binary))`. linuxdeploy's appimage plugin names the output AppImage
   from the desktop `Name=`, but the Go code expected
   `<binary>-<arch>.AppImage` and looked it up by that path.

   Fix: set the `OUTPUT` env var (honored by linuxdeploy-plugin-appimage)
   to the binary-derived name we already expect, so the produced
   filename matches what we then move.

Verified on Ubuntu 26.04 lin-node1:

- Relative `-builddir` + a desktop `Name=Differently Named App` (which
  used to fail twice over): exit 0, AppImage produced at
  `out/appimage-smoke-gtk4-x86_64.AppImage`, launches under xvfb,
  logs `GTK=4.22.2 WebKitGTK=2.52.3`.
- The original absolute-paths flow (both GTK4 default and `-tags gtk3`)
  still produces the matching AppImage in `-outputdir` and exits 0 -
  no regression of the #5475 fix.

Refs #5475.

* fix(v3/appimage): quote shell args and surface ldd output on detect failure

Addresses CodeRabbit and Copilot review on #5476:

- Quote the binary path passed to `ldd` (`%s` -> `%q`). `s.EXEC` splits
  the command string with shlex; an unquoted path with spaces gets
  fragmented and the ldd call fails.
- Same treatment for the `readelf` probe in `hasRelrDynSections` and
  for the `linuxdeploy ... --appdir <dir>` command. The linuxdeploy
  call site was the latent breakage — invoking it with a `-builddir`
  containing a space produced `fork/exec /tmp/space: no such file or
  directory` even after the abs-path resolution from the previous
  commit.
- Include a truncated ldd-output snippet in the
  "unable to determine GTK version" error so users hitting this branch
  can see which libraries the binary actually links against.

Verified on lin-node1 (Ubuntu 26.04):

- Paths with spaces (`-binary "/tmp/space test/my app"` ...): now exit
  0 and produce `out/my-app-x86_64.AppImage`. Previously failed at
  the linuxdeploy step.
- The original absolute-path GTK4 and `-tags gtk3` flows still pass
  and the GTK4 AppImage still launches under xvfb with
  `GTK=4.22.2 WebKitGTK=2.52.3` - no regression.

Refs #5475.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant