fix: LNK4291 on Native AOT WinExe with Control Flow Guard#5298
Open
jamescrosswell wants to merge 9 commits into
Open
fix: LNK4291 on Native AOT WinExe with Control Flow Guard#5298jamescrosswell wants to merge 9 commits into
jamescrosswell wants to merge 9 commits into
Conversation
When a consumer publishes a Native AOT WinExe with <ControlFlowGuard>Guard</ControlFlowGuard>, the linker emits LNK4291 for every sentry-native.lib translation unit that contains __try/__except, because the shipped objects carry no /guard:ehcont (EH continuation) metadata. Upstream sentry-native only applies /guard:cf to its sentry_example target, never to the sentry static lib we ship. Build the static lib with /guard:cf (x64 + arm64) and /guard:ehcont (x64 only; ehcont and LNK4291 are both x64 concepts) so the metadata is present and the warnings disappear. Also add windows-config.cmake to the build.yml cache keys: they were keyed only on build-sentry-native.ps1 + the submodule HEAD, so a flags change in the cmake file alone would restore a stale cached lib and the fix would silently not take effect in CI. Fixes #4801 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
build.yml's sentry-native cache key now hashes windows-config.cmake, but that file only had `text=auto`, so it checks out CRLF on the Windows build runner and LF on the macOS/Linux consumers. hashFiles() therefore produced different digests per OS: the win-x64 lib was saved under the CRLF-hash key and every non-Windows consumer (which restores it with fail-on-cache-miss) looked up the LF-hash key and missed. Pin *.cmake to eol=lf like *.ps1 so the hash — and the key — is identical on every runner.
jamescrosswell
commented
Jun 22, 2026
Changelog entries are generated automatically; per maintainer review on #5298 they shouldn't be added by hand.
jamescrosswell
commented
Jun 22, 2026
Per maintainer review on #5298: replace the long inline rationale with a concise pointer to the PR, matching the existing line-1 style.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5298 +/- ##
==========================================
+ Coverage 74.09% 74.11% +0.01%
==========================================
Files 508 508
Lines 18320 18353 +33
Branches 3584 3586 +2
==========================================
+ Hits 13575 13603 +28
- Misses 3872 3879 +7
+ Partials 873 871 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Move the x64-only /guard:ehcont decision out of windows-config.cmake (which
read $ENV{PROCESSOR_ARCHITECTURE}) and into build-sentry-native.ps1, which
already determines target arch via RuntimeInformation.OSArchitecture. The
cmake file just consumes SENTRY_EXTRA_MSVC_FLAGS — one source of truth for
arch, no env-var sniffing inside the cache-init file.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CMake processes -C cache-init scripts before -D cache entries, so SENTRY_EXTRA_MSVC_FLAGS passed via -D was empty when windows-config.cmake ran — silently dropping /guard:cf and /guard:ehcont from the build. Use an environment variable instead, which is visible to -C scripts. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The -C cache-init file + env-var indirection was historical accident. Pass CMAKE_C/CXX_FLAGS_RELWITHDEBINFO straight on the cmake command line, one flag string built where arch detection already lives. Delete the cmake helper file and strip it from build.yml cache keys. This is the one-time cache invalidation across all sentry-native build caches; the build inputs really did change. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Added in this PR to stabilize hashFiles() over windows-config.cmake. That file is gone and out of the cache key, so the rule has no effect. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Collaborator
Author
Minimal repro to verify the fixFor anyone who wants to reproduce the original LNK4291 wall and confirm this PR clears it, here's a self-contained repro: Gist: https://gist.github.com/jamescrosswell/fe9bf0a748b24ab68f4b0541de9f8013 It contains a tiny
Verifyinggit clone https://gist.github.com/jamescrosswell/fe9bf0a748b24ab68f4b0541de9f8013 lnk4291-repro
cd lnk4291-repro
# Run against main — expect ~22 LNK4291 lines against sentry-native.lib obj files
git -C <sentry-dotnet> checkout main
Remove-Item -Recurse -Force <sentry-dotnet>/src/Sentry/Platforms/Native/sentry-native, <sentry-dotnet>/modules/sentry-native/build -ErrorAction SilentlyContinue
./repro.ps1 -SentryRepo <sentry-dotnet>
# Run against this branch — expect "LNK4291 warnings: 0 ✓"
git -C <sentry-dotnet> checkout fix/lnk4291-naot-cfg-ehcont
Remove-Item -Recurse -Force <sentry-dotnet>/src/Sentry/Platforms/Native/sentry-native, <sentry-dotnet>/modules/sentry-native/build -ErrorAction SilentlyContinue
./repro.ps1 -SentryRepo <sentry-dotnet>Requires Windows + .NET 10 SDK + the toolchain |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4801
Problem
Publishing a Native AOT
WinExewith<ControlFlowGuard>Guard</ControlFlowGuard>emitsLNK4291for everysentry-native.libtranslation unit that contains__try/__except:The prebuilt
sentry-native.libwe ship is compiled without/guard:cf//guard:ehcont, so its objects carry no EH-continuation (CFG) metadata. When a consumer links it into a CFG-enabled image, the linker can't find per-function records and falls back to conservative metadata, warning once per object. Upstreamsentry-nativeonly applies/guard:cfto itssentry_exampletest target — never to thesentrystatic lib — so the shipped.libhas zero CFG metadata.Fix
src/Sentry/Platforms/Native/windows-config.cmake— build the static lib with Control Flow Guard metadata:/guard:cfon both x64 and arm64,/guard:ehconton x64 only (EH-continuation metadata — andLNK4291itself — are x64 concepts; gating avoids acl : D9002on the arm64 leg)..github/workflows/build.yml— addwindows-config.cmaketo thesentry-nativecache keys. They were keyed only onbuild-sentry-native.ps1+ the submodule HEAD, so a flags-only change to the cmake file would otherwise restore a stale cached lib and the fix would silently not take effect in CI.No submodule bump and no change to consumer-facing link flags — only the metadata baked into the
.libwe ship.Note to reviewers
Before/after, reproduced locally on Windows (MSVC 14.50, .NET 10):
scripts/build-sentry-native.ps1(run from a VS Developer prompt).WinExethat linkssentry-native.libexactly asSentry.Native.targetsdoes (DirectPInvoke+NativeLibrary+/NODEFAULTLIB:MSVCRT+ thedbghelp/winhttp/Gdi32/Synchronizationlibs), with:sentry_options_new) so the SEH-bearing objects are pulled into the link.dotnet publish -c Release -r win-x64.LNK4291warningssentry_core.obj,sentry_os.obj,sentry_options.obj, …)The published
.exebuilds successfully in both cases; the only difference is the warnings. The rebuilt.libgrows ~12 KB, consistent with the added CFG/EHCONT tables, andCMakeCache.txtshows…/DNDEBUG /guard:cf /guard:ehcont.Tip
Gist to make all of the above easier at #5298 (comment)
🤖 Generated with Claude Code