Skip to content

Build-tool plugin executable on Windows uses ABI-incompatible symbols when built with WindowsExperimental.sdk #10041

@tothambrus11

Description

@tothambrus11

A trivial executable target that consumes generated source from a .buildTool() plugin fails during compilation when launching the build plugin when using -static-stdlib SDKROOT=...WindowsExperimental.sdk.

CI matrix:

arch link result
x86_64 dynamic
arm64 dynamic
x86_64 static (-static-stdlib, SDKROOT=…WindowsExperimental.sdk)
arm64 static (same)

Minimal reproducer: https://github.com/tothambrus11/spm-plugin-windows-static

Failed run: https://github.com/tothambrus11/spm-plugin-windows-static/actions/runs/25639157545

Inaccurate (AI) interpretation

Summary

When SDKROOT points at WindowsExperimental.sdk, building any SwiftPM target that uses a .buildTool() plugin produces a plugin executable that fails to start with STATUS_DLL_NOT_FOUND (0xC0000135) because the Windows loader cannot resolve the PackagePlugin.dll it depends on. SwiftPM swallows the loader failure and surfaces only:

error: build planning stopped due to build-tool plugin failures

— the diagnostic-quality side of which is the subject of a companion issue.

Environment

  • Toolchain: swift-DEVELOPMENT-SNAPSHOT-2026-04-30-a (Windows x86_64 and arm64), installed via compnerd/gha-setup-swift
  • SDK: WindowsExperimental.sdk (via SDKROOT=…\Windows.platform\Developer\SDKs\WindowsExperimental.sdk)
  • Build driver: default ("Swift Build") - EDIT: --build-system native doesn't fix this either.
  • Reproduces with both swift build and swift build -Xswiftc -static-stdlib

Reproducer

Public minimal repository: https://github.com/tothambrus11/spm-plugin-windows-static

A trivial executable target consumes generated source from a .buildTool() plugin. CI matrix:

arch link result
x86_64 dynamic
arm64 dynamic
x86_64 static (-static-stdlib, SDKROOT=…WindowsExperimental.sdk)
arm64 static (same)

Failed run: https://github.com/tothambrus11/spm-plugin-windows-static/actions/runs/25639157545

Observed

The "Inspect plugin executable after failure" step in CI shows, on the produced .build\plugins\cache\BuildPlugin.exe:

ExitCode: -1073741515        ← 0xC0000135 STATUS_DLL_NOT_FOUND
--- stdout ---               (empty)
--- stderr ---               (empty)

llvm-readobj --coff-imports enumerates the imported DLLs and resolves each against the PATH the runner has (the same PATH SwiftPM uses to invoke the plugin):

[FOUND]   FoundationEssentials.dll  -> …\Swift\Runtimes\0.0.0\usr\bin\FoundationEssentials.dll
[FOUND]   KERNEL32.dll              -> C:\Windows\system32\kernel32.dll
[MISSING] PackagePlugin.dll                              ← root cause
[FOUND]   swiftCore.dll             -> …\Swift\Runtimes\0.0.0\usr\bin\swiftCore.dll
[FOUND]   swift_Concurrency.dll     -> …\Swift\Runtimes\0.0.0\usr\bin\swift_Concurrency.dll
[FOUND]   VCRUNTIME140.dll          -> …\vcruntime140.dll
[FOUND]   api-ms-win-crt-*-l1-1-0.dll  …

The toolchain does ship PackagePlugin.dll, but at usr\lib\swift\pm\PluginAPI\PackagePlugin.dll — a directory not on PATH and not in the loader's standard search.

Reasoning

  1. The plugin executable's exit code is 0xC0000135 and both stdout and stderr are empty. By Windows semantics this can only originate from the loader, i.e. before any user main() or PackagePlugin runtime entry point runs. So the failure is not a plugin-protocol error or a SwiftPM/plugin handshake problem.
  2. llvm-readobj enumerates the exact DLL imports of the produced binary, and Get-Command resolves each against the live PATH. Exactly one entry is missing — PackagePlugin.dll — and it is the only Swift runtime DLL the plugin imports that is not under …\Runtimes\0.0.0\usr\bin.
  3. The matrix isolates the trigger: same package, same toolchain, same plugin target builds and runs successfully in the dynamic cells (no WindowsExperimental.sdk redirect) and fails in the static cells (with SDKROOT repointed). The only variable is the SDK.

Expected behavior

Either:

  • (a) the build-tool plugin executable produced when targeting WindowsExperimental.sdk should not depend on a PackagePlugin.dll whose location is not in the loader search path (e.g. by linking PackagePlugin statically into plugin executables, or by emitting a manifest / rpath equivalent), or
  • (b) SwiftPM should prepend the toolchain usr\lib\swift\pm\PluginAPI directory to the plugin process's PATH (or use AddDllDirectory / SetDefaultDllDirectories) before invoking the plugin.

(b) appears to be the smaller change and would also benefit any future SDK that ships a similarly placed PackagePlugin.lib import library.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions