Skip to content

fix(bindings): strip debug info on Windows to avoid Go 1.25 DWARF5 PE bug#5524

Merged
leaanthony merged 1 commit into
masterfrom
fix/bindings-cgo-windows
Jun 2, 2026
Merged

fix(bindings): strip debug info on Windows to avoid Go 1.25 DWARF5 PE bug#5524
leaanthony merged 1 commit into
masterfrom
fix/bindings-cgo-windows

Conversation

@leaanthony

@leaanthony leaanthony commented May 31, 2026

Copy link
Copy Markdown
Member

Summary

Fixes #4605 and #4551.

Go 1.25 switched to DWARF5 debug format by default. On Windows, the internal linker emits malformed PE section headers when DWARF5 sections are present (golang/go#75077, golang/go#75121). The generated wailsbindings.exe fails to launch with:

fork/exec C:\Users\...\AppData\Local\Temp\wailsbindings.exe: %1 is not a valid Win32 application.

or

fork/exec C:\...\wailsbindings.exe: This version of %1 is not compatible with the version of Windows you're running.

Downgrading to Go 1.24 fixes it because Go 1.24 used DWARF4 by default.

Root cause

golang/go#75077 documents the issue: DWARF5 sections are placed at virtual addresses outside the normal Windows PE address space, making the binary unrunnable. The fix in that issue is to strip debug info entirely with -s -w.

Fix

On Windows, pass -ldflags="-s -w" to the go build that compiles wailsbindings.exe. This strips debug symbols and DWARF sections from the binary, producing clean PE headers. It is appropriate for this binary because:

  • wailsbindings.exe is a temporary tool deleted immediately after use (defer os.Remove(filename)) — it is never debugged
  • The fix works whether or not the user's project uses CGO

The flag is only added on runtime.GOOS == "windows" — no effect on macOS/Linux builds.

Test plan

  • wails dev with Go 1.25.x on Windows (amd64), project without CGO — bindings generate successfully
  • wails dev with Go 1.25.x on Windows (amd64), project with CGO — bindings generate successfully
  • wails dev with Go 1.24 on Windows — still works
  • wails dev on macOS / Linux with Go 1.25 — unchanged (flag not added)
  • go test ./v2/pkg/commands/bindings/... passes

Summary by CodeRabbit

  • New Features

    • Frontend build output is automatically synchronized into embed targets so embedded assets match the built frontend during packaging.
  • Bug Fixes

    • Windows native build now strips debug info for temporary tool binaries to improve build reliability.
    • Cleanup restores placeholder files to keep embed targets consistent after failed builds.
  • Tests

    • Added filesystem tests covering embed discovery, sync, cleanup, and no-op scenarios.

Copilot AI review requested due to automatic review settings May 31, 2026 13:00
@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bf74d4c2-d417-4847-92e9-6eb7482e9f98

📥 Commits

Reviewing files that changed from the base of the PR and between 03fab14 and bf9e5aa.

📒 Files selected for processing (1)
  • v2/pkg/commands/bindings/bindings.go

Walkthrough

Adds SyncFrontendDistToEmbedTarget and calls it from Build to copy frontend dist into discovered Go embed dist targets with deferred cleanup; refactors GenerateBindings to compose build args and appends a Windows-only -ldflags=-s -w before running the build.

Changes

Frontend sync + bindings build

Layer / File(s) Summary
SyncFrontendDistToEmbedTarget implementation
v2/pkg/commands/build/build.go
SyncFrontendDistToEmbedTarget(options *Options) (func(), error) copies custom frontend dist into embed dist targets discovered via staticanalysis.GetEmbedDetails, tracks modified dirs, and returns a cleanup that restores .gitkeep.
Build integration of sync helper
v2/pkg/commands/build/build.go
Build calls SyncFrontendDistToEmbedTarget after frontend compilation, defers its cleanup, and returns on sync error.
Filesystem tests for frontend sync
v2/test/5034/frontend_dir_test.go
Adds tests verifying embed discovery, asset copying (including nested assets), cleanup restoring .gitkeep, no-op when using default frontend dir, and no-op when source dist is absent.
Bindings build args and Windows linker flag
v2/pkg/commands/bindings/bindings.go
GenerateBindings now constructs a buildArgs slice and, on Windows, appends -ldflags=-s -w before invoking shell.RunCommandWithEnv to run go build.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #4605: Bindings Fail to Generate — Windows binding generation compatibility issue that this change targets by adjusting build flags for Windows.

Possibly related PRs

  • wailsapp/wails#5251: Adds SyncFrontendDistToEmbedTarget and matching integration/tests (same helper and test coverage).
  • wailsapp/wails#4263: Also modifies GenerateBindings build invocation and environment handling.
  • wailsapp/wails#4093: Related changes to GenerateBindings; adjusts platform/arch handling for builds.

Suggested labels

Windows, v2-only

Suggested reviewers

  • fbbdev

Poem

🐰 I hopped through build files, neat and spry,
Copied frontend dist where embed trees lie,
Returned a cleanup to tidy the ground,
Tweaked bindings' flags so Windows smiles around,
A rabbit's small fix — now tests hop and sigh.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes changes to build.go adding SyncFrontendDistToEmbedTarget and corresponding tests, which appear unrelated to the Windows bindings DWARF5 issue described in linked issues #4605 and #4551. Review whether build.go and frontend_dir_test.go changes belong in this PR or should be separated into a different issue/PR addressing frontend dist syncing.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title mentions stripping debug info on Windows for a Go 1.25 DWARF5 PE bug, which aligns with the PR description's explanation of the root cause and fix.
Description check ✅ Passed The PR description provides a clear summary of the issue, root cause, fix, and test plan. However, the description template's checklist items are not explicitly checked off.
Linked Issues check ✅ Passed The PR addresses issue #4605 by fixing the Windows bindings compilation issue with Go 1.25. The code changes add Windows-specific DWARF5 debug stripping via -ldflags flags in bindings.go, matching the stated fix.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/bindings-cgo-windows

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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.

Copilot AI 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.

Pull request overview

This PR fixes the bindings helper build by disabling CGO so wailsbindings.exe uses Go’s internal linker on Windows with Go 1.25. It also includes additional custom frontend embed-directory synchronization behavior that is outside the described CGO fix.

Changes:

  • Sets CGO_ENABLED=0 when building the temporary bindings binary.
  • Adds SyncFrontendDistToEmbedTarget and wires it into the build flow after frontend builds.
  • Adds tests for custom frontend dist synchronization behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
v2/pkg/commands/bindings/bindings.go Disables CGO for the bindings helper build environment.
v2/pkg/commands/build/build.go Adds custom frontend dist synchronization and cleanup during builds.
v2/test/5034/frontend_dir_test.go Adds tests covering custom frontend sync/no-op behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread v2/pkg/commands/build/build.go
Comment thread v2/pkg/commands/build/build.go
Comment thread v2/pkg/commands/build/build.go
Comment thread v2/test/5034/frontend_dir_test.go

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@v2/pkg/commands/build/build.go`:
- Around line 213-232: The loop that copies frontendDistDir into each embed
target accumulates syncedDirs but returns noop on a CopyDir error, leaking
partially-copied directories; change the error return inside the loop to return
a cleanup closure bound to the current syncedDirs (which removes those copied
embedDistDir entries and restores any .gitkeep) along with the wrapped error
instead of noop, and ensure the Build caller defers the returned cleanup()
before checking the error so cleanup runs on failure; update references in this
fix around the for loop that uses embedDetails, the syncedDirs slice, the
fs.CopyDir call, and the cleanup()/noop return site.
- Around line 234-243: The cleanup closure in build.go recreates a ".gitkeep"
file but the rest of the codebase (CreateEmbedDirectories and project templates)
use "gitkeep", causing working-tree churn; update the cleanup function to create
"gitkeep" (not ".gitkeep") for each dir in syncedDirs and adjust any test
expectations that currently assert for ".gitkeep" to expect "gitkeep" instead
(refer to the cleanup variable/closure and syncedDirs in build.go to locate the
change).

In `@v2/test/5034/frontend_dir_test.go`:
- Around line 13-30: TestEmbedDiscoveryInTempDir currently only logs results and
never fails; update it to assert the expected outcome from
staticanalysis.GetEmbedDetails: ensure err is nil, assert embedDetails has the
expected count (e.g., 1), and verify the discovered embed path(s) match the
expected value (e.g., "frontend/dist") and/or that d.BaseDir/ d.GetFullPath()
point to the temp project; use t.Fatalf/t.Fatalf or the testify/require helpers
to make the test fail when these conditions are not met.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d6de05f6-f631-40a6-8b81-9107b26fc049

📥 Commits

Reviewing files that changed from the base of the PR and between f83493c and dd5bb82.

📒 Files selected for processing (3)
  • v2/pkg/commands/bindings/bindings.go
  • v2/pkg/commands/build/build.go
  • v2/test/5034/frontend_dir_test.go

Comment thread v2/pkg/commands/build/build.go
Comment thread v2/pkg/commands/build/build.go
Comment thread v2/test/5034/frontend_dir_test.go
@leaanthony leaanthony force-pushed the fix/bindings-cgo-windows branch from eccbd34 to 03fab14 Compare May 31, 2026 14:00

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
v2/pkg/commands/bindings/bindings.go (1)

56-61: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Implementation doesn't match PR title and objectives.

The PR title states "set CGO_ENABLED=0 when building wailsbindings binary" and the objectives explicitly describe setting CGO_ENABLED=0 to force the internal linker. However, CGO_ENABLED=0 is never set in the environment. Instead, the code uses -ldflags=-linkmode=internal (see lines 64-69).

If the bindings binary is pure Go as stated in the PR objectives, setting CGO_ENABLED=0 is the cleaner approach and would align with the PR title. It would also prevent any CGO dependencies from being accidentally introduced.

🔧 Proposed fix to align with PR objectives
 envBuild := os.Environ()
 envBuild = shell.SetEnv(envBuild, "GOOS", runtime.GOOS)
 envBuild = shell.SetEnv(envBuild, "GOARCH", runtime.GOARCH)
+envBuild = shell.SetEnv(envBuild, "CGO_ENABLED", "0")
 // wailsbindings is executed on the build machine.
 // So, use the default C compiler, not the one set for cross compiling.
 envBuild = shell.RemoveEnv(envBuild, "CC")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v2/pkg/commands/bindings/bindings.go` around lines 56 - 61, The bindings
build setup in bindings.go does not follow the stated objective because
CGO_ENABLED is never disabled; update the environment construction in the
bindings build flow to explicitly set CGO_ENABLED=0 alongside the existing
GOOS/GOARCH and CC handling in the build setup. Keep the change centered in the
code that prepares envBuild for wailsbindings so the binary is forced to be pure
Go, and remove or avoid relying on the internal linker flag workaround if it is
only compensating for CGO being enabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@v2/pkg/commands/bindings/bindings.go`:
- Around line 63-70: The Windows-specific comment in
v2/pkg/commands/bindings/bindings.go is misleading about CGO: update the comment
around the buildArgs block (the portion that appends
"-ldflags=-linkmode=internal") to state clearly that the bindings package is
pure-Go and does not require CGO (CgoFiles is empty) and that the internal
linker flag is used only to avoid a Windows PE/loader issue introduced in Go
1.25; remove or rephrase "it still supports CGO" to avoid implying CGO is
required. Optionally, to guarantee a pure-Go build even if unrelated CGO sources
are present, set the environment variable CGO_ENABLED=0 when invoking the go
build for the bindings.

---

Outside diff comments:
In `@v2/pkg/commands/bindings/bindings.go`:
- Around line 56-61: The bindings build setup in bindings.go does not follow the
stated objective because CGO_ENABLED is never disabled; update the environment
construction in the bindings build flow to explicitly set CGO_ENABLED=0
alongside the existing GOOS/GOARCH and CC handling in the build setup. Keep the
change centered in the code that prepares envBuild for wailsbindings so the
binary is forced to be pure Go, and remove or avoid relying on the internal
linker flag workaround if it is only compensating for CGO being enabled.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: eb326d9f-d28f-4412-8fe0-19df0dd48f19

📥 Commits

Reviewing files that changed from the base of the PR and between dd5bb82 and 03fab14.

📒 Files selected for processing (1)
  • v2/pkg/commands/bindings/bindings.go

Comment thread v2/pkg/commands/bindings/bindings.go
@leaanthony leaanthony changed the title fix(bindings): set CGO_ENABLED=0 when building wailsbindings binary fix(bindings): strip debug info on Windows to avoid Go 1.25 DWARF5 PE bug May 31, 2026
@leaanthony

Copy link
Copy Markdown
Member Author

Test results from win-node1 (Windows 11, Build 26200)

Tested on multica@win-node1: Windows 11 24H2 (Build 26200), Go 1.25.0 and 1.25.3, with and without gcc (MinGW 16.1.0 posix-seh).

Scenario Build Run
Go 1.25.0, CGO, default ✅ 0 ✅ 0
Go 1.25.0, CGO, -s -w ✅ 0 ✅ 0
Go 1.25.0, no CGO (internal linker) ✅ 0 ✅ 0
Go 1.25.3, CGO, default ✅ 0 ✅ 0
Go 1.26.2, CGO, default ✅ 0 ✅ 0

PE headers in all cases: MajorOSVersion=6.1, SizeOfHeaders=1536, FileAlignment=512 — all valid.

Conclusion: The bug could not be reproduced on Windows 11. The reported failures were on Windows 10 22H2 (Build 19045). This may be a Windows 10-specific issue, a pre-release-specific issue, or environment-specific (specific gcc version, CGO flags, etc.).

The fix (-ldflags="-s -w") is still the documented workaround for golang/go#75077 and is harmless for a temporary tool binary that is immediately deleted after use. If a Windows 10 + Go 1.25 test environment is available to verify, that would confirm the fix definitively.

… bug

Go 1.25 switched to DWARF5 debug format by default. On Windows, the
internal linker emits malformed PE section headers when DWARF5 sections
are present, producing a binary that Windows refuses to execute:

  fork/exec wailsbindings.exe: %1 is not a valid Win32 application.
  fork/exec wailsbindings.exe: This version of %1 is not compatible
    with the version of Windows you're running.

Pass -ldflags="-s -w" when building wailsbindings.exe on Windows.
Stripping debug info removes DWARF5 sections entirely, producing clean
PE headers. This is appropriate for a temporary tool binary that is
deleted immediately after use and never debugged.

The fix works regardless of whether the user's project uses CGO.

Upstream: golang/go#75077, golang/go#75121
Fixes #4605
Fixes #4551
@leaanthony leaanthony force-pushed the fix/bindings-cgo-windows branch from 6e38440 to c7b5070 Compare June 2, 2026 11:31
@leaanthony

Copy link
Copy Markdown
Member Author

Addressing review feedback:

The PR branch was originally cut before PR #5251 was merged, so it inadvertently included those build.go / test changes in the diff. The branch has now been rebased onto the latest master (which includes the merged #5251) and force-pushed — the PR is now a single commit touching only v2/pkg/commands/bindings/bindings.go.

All review comments about SyncFrontendDistToEmbedTarget, GetEmbedDetails error handling, .gitkeep naming, and missing test assertions are addressed by the merged #5251 and are no longer in scope here.

The CodeRabbit comment about "it still supports CGO" wording referred to an earlier iteration that used -ldflags=-linkmode=internal. That has been superseded by the current -ldflags="-s -w" approach; the comment in the code now accurately describes the DWARF5 root cause.

@leaanthony leaanthony merged commit aa31c67 into master Jun 2, 2026
197 checks passed
@leaanthony leaanthony deleted the fix/bindings-cgo-windows branch June 2, 2026 11:57
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.

Bindings Fail to Generate

3 participants