Skip to content

fix: statically link MSVC CRT to remove VC++ Redistributable dependency#427

Merged
DennisDyallo merged 6 commits intomainfrom
dennisdyallo/fix-391
Mar 17, 2026
Merged

fix: statically link MSVC CRT to remove VC++ Redistributable dependency#427
DennisDyallo merged 6 commits intomainfrom
dennisdyallo/fix-391

Conversation

@DennisDyallo
Copy link
Copy Markdown
Collaborator

@DennisDyallo DennisDyallo commented Mar 16, 2026

Summary

Fixes #391Yubico.NativeShims.dll on Windows dynamically linked VCRUNTIME140.dll and 9 api-ms-win-crt-* UCRT forwarding DLLs, causing DllNotFoundException on machines without the VC++ Redistributable installed (e.g., bare Windows 11 Home on new Dell hardware).

Changes

  • Sets CMAKE_MSVC_RUNTIME_LIBRARY to MultiThreaded (/MT) in the WIN32 block of CMakeLists.txt, statically linking the C runtime into the DLL. This is consistent with the existing x64-windows-static vcpkg triplet which already statically links OpenSSL.
  • Bumps cmake_minimum_required from 3.13 to 3.15 — required for CMake policy CMP0091 which enables the CMAKE_MSVC_RUNTIME_LIBRARY variable. Without this, the /MT flag was silently ignored.
  • Adds a CI verification step using dumpbin /imports that inspects all three Windows architecture builds (x64, x86, arm64) and fails the build if any VC++ Redistributable dependencies are detected.
  • Adds inline comments and logical section grouping to CMakeLists.txt for improved readability.
  • Documents the static CRT linkage in Yubico.NativeShims/readme.md.

Passing build

https://github.com/Yubico/Yubico.NET.SDK/actions/runs/23155104306/job/67267560977

Licensing review

Does static CRT linking require special licensing?

No. Static linking with /MT is not governed by the REDIST list. The REDIST list exclusively covers redistribution of separate files (DLLs, merge modules, vcredist installers). With /MT, the CRT code is embedded into the binary — no separate CRT files are distributed.

What does Microsoft's own documentation say?

Microsoft's CRT library features documentation explicitly lists the static CRT libraries as standard build options:

Library Characteristics Option
libucrt.lib Statically links the UCRT into your code /MT
libvcruntime.lib Statically linked into your code /MT
libcmt.lib Statically links the native CRT startup into your code /MT

Key observations:

  • The release static libraries (libucrt.lib, libvcruntime.lib, libcmt.lib) carry no "Not redistributable" marker
  • The debug variants (libucrtd.lib, libvcruntimed.lib, libcmtd.lib) are explicitly marked "Not redistributable"
  • This distinction is intentional — Microsoft permits release static linking while restricting debug
  • Static linking is actually the default compiler behavior: "If you link your program from the command line without a compiler option that specifies a C runtime library, the linker will use the statically linked CRT libraries"

Our build satisfies all requirements

Requirement Status
Licensed Visual Studio user CI builds on windows-2022 runners with VS 2022 Enterprise
Release builds only build-windows.ps1 builds --config Release exclusively
No debug CRT redistributed /MT in Release, /MTd only hypothetically in Debug
No separate CRT files distributed Static linking embeds CRT — nothing to redistribute

Why full static (/MT) over hybrid linking?

Microsoft supports a hybrid approach (static vcruntime, dynamic UCRT). We chose full static because:

  • The affected user's machine was missing both VCRUNTIME140.dll and UCRT forwarders
  • Full static is simpler — no partial dependencies to reason about
  • NativeShims is a thin P/Invoke bridge with minimal CRT surface area
  • The CRT state isolation caveat doesn't apply — NativeShims manages its own memory internally and doesn't share CRT objects across DLL boundaries

Trade-offs

Static (/MT) — this PR Dynamic (/MD) — before
Runtime dependency None beyond Windows system DLLs Requires VC++ Redistributable
Binary size ~200-300KB larger Smaller
CRT security patches Picked up at SDK build time Applied via OS updates
User experience Just works Can fail on bare machines

The security patching trade-off is minimal — NativeShims uses CRT for basic operations (malloc, memcpy), and the SDK ships regular releases that incorporate updated static CRT at build time.

Test plan

  • build-nativeshims workflow passes on Windows (all 3 architectures)
  • CI verification step confirms zero VCRUNTIME or api-ms-win-crt-* imports in built DLLs
  • Linux and macOS builds unaffected (CMake change scoped to WIN32 block)
  • Verify DLL loads on a Windows machine without VC++ Redistributable installed

🤖 Generated with Claude Code

DennisDyallo and others added 6 commits March 16, 2026 10:15
…cy (#391)

NativeShims on Windows dynamically linked VCRUNTIME140.dll and api-ms-win-crt-*
DLLs, causing DllNotFoundException on machines without the VC++ Redistributable.
Sets CMAKE_MSVC_RUNTIME_LIBRARY to /MT (static) and adds CI verification via
dumpbin to prevent regression.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… readme

Improves readability of CMakeLists.txt with section banners, inline comments
explaining compiler/linker flags, and descriptive annotations on source files
and link libraries. Adds static CRT note to NativeShims readme.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The GitHub Actions windows-2022 runner doesn't have dumpbin on PATH by
default. Switched to cmd shell and call vcvars64.bat to set up the VS
developer environment before running the import check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CMAKE_MSVC_RUNTIME_LIBRARY requires CMake policy CMP0091 (introduced in
3.15). With cmake_minimum_required at 3.13, the policy defaulted to OLD
and the /MT flag was silently ignored, leaving the dynamic CRT linkage
in place.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When findstr finds no matches (the success case), it returns exit code 1
which cmd.exe propagates as the script exit code. Adding explicit exit /b 0
after the success path ensures the step passes when no CRT deps are found.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@DennisDyallo DennisDyallo merged commit 0bcb6de into main Mar 17, 2026
6 checks passed
@DennisDyallo DennisDyallo deleted the dennisdyallo/fix-391 branch March 17, 2026 22:24
DennisDyallo added a commit that referenced this pull request Mar 30, 2026
Brings PR #427 (static CRT linking, CMake fixes) and 1.15.2 release
bookkeeping into develop. Resolved conflict in build-nativeshims.yml
by keeping both the CRT verification step (from main) and the updated
upload-artifact v7.0.0 (from develop).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@DennisDyallo DennisDyallo mentioned this pull request Mar 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants