Summary
When linking on Windows with an MSVC toolchain where the linker library search path contains directories with spaces (e.g. C:\Program Files (x86)\Windows Kits\...), rustc produces LNK1181: cannot open input file 'Files.obj' for short compilations.
Root cause
exec_linker in compiler/rustc_codegen_ssa/src/back/link.rs tries direct invocation first and only falls back to a response file when the estimated command-line length exceeds ~6KB (very_likely_to_exceed_some_spawn_limit, Windows threshold: > 1024 * 6). The response file path correctly wraps each argument in "..." (the Escape struct, is_like_msvc = true), which link.exe handles correctly. But for short compilations — most library crates — the direct path is taken, and CreateProcess receives /LIBPATH:C:\Program Files (x86)\... as a single argument. Link.exe's own command-line parser then splits on the space, interpreting Files as a separate token and failing with LNK1181.
Specifically, rustc invokes link.exe via Rust's std::process::Command::arg() which uses CreateProcess. Even though each argument is a distinct OS-level argument (correctly represented in the CreateProcess command string with surrounding quotes), link.exe's internal parser re-splits the command string on whitespace when it encounters an argument of the form /LIBPATH:path with spaces — because the colon-prefixed switch value is not itself quoted.
How to reproduce
Any Windows build where the linker's LIB environment variable contains paths with spaces — standard for Windows SDK installations. The issue is triggered by -LIBPATH: flags being explicitly placed on the command line for hermetic toolchain precedence (e.g. via bazelbuild/rules_rust#3256).
The linker invocation looks like:
link.exe ... "-LIBPATH:C:\apps\MSVC\lib\x64" "-LIBPATH:C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\lib\um\x64" ...
Link.exe splits the second argument at the space, treating Files as a separate input file:
LINK : fatal error LNK1181: cannot open input file 'Files.obj'
Fix
In exec_linker, always use the response file when the linker is MSVC-like and the host is Windows. The response file path already exists and correctly quotes all arguments via the Escape struct (is_like_msvc = true):
// compiler/rustc_codegen_ssa/src/back/link.rs, in exec_linker()
// Before (line ~1697 in 1.96.0):
if !cmd.very_likely_to_exceed_some_spawn_limit() {
// After:
// On Windows with MSVC, always use a response file so that link.exe receives
// arguments with spaces correctly quoted. The Escape struct already handles
// MSVC response-file quoting (`is_like_msvc = true`).
let needs_rsp = sess.target.is_like_msvc && cfg!(windows);
if !needs_rsp && !cmd.very_likely_to_exceed_some_spawn_limit() {
The rest of the response file path — UTF-16 BOM encoding, MSVC "..."-style quoting, @linker-arguments — is already correct and handles spaces properly. This change makes it unconditionally active on Windows/MSVC rather than as a large-command-line fallback.
Affected versions
Confirmed in 1.90.0, 1.96.0, and current master (as of 2026-06-04). The response file fallback path itself is correct — only the threshold guard that bypasses it for short command lines is the problem.
Summary
When linking on Windows with an MSVC toolchain where the linker library search path contains directories with spaces (e.g.
C:\Program Files (x86)\Windows Kits\...), rustc producesLNK1181: cannot open input file 'Files.obj'for short compilations.Root cause
exec_linkerincompiler/rustc_codegen_ssa/src/back/link.rstries direct invocation first and only falls back to a response file when the estimated command-line length exceeds ~6KB (very_likely_to_exceed_some_spawn_limit, Windows threshold:> 1024 * 6). The response file path correctly wraps each argument in"..."(theEscapestruct,is_like_msvc = true), which link.exe handles correctly. But for short compilations — most library crates — the direct path is taken, andCreateProcessreceives/LIBPATH:C:\Program Files (x86)\...as a single argument. Link.exe's own command-line parser then splits on the space, interpretingFilesas a separate token and failing with LNK1181.Specifically, rustc invokes link.exe via Rust's
std::process::Command::arg()which usesCreateProcess. Even though each argument is a distinct OS-level argument (correctly represented in the CreateProcess command string with surrounding quotes), link.exe's internal parser re-splits the command string on whitespace when it encounters an argument of the form/LIBPATH:path with spaces— because the colon-prefixed switch value is not itself quoted.How to reproduce
Any Windows build where the linker's
LIBenvironment variable contains paths with spaces — standard for Windows SDK installations. The issue is triggered by-LIBPATH:flags being explicitly placed on the command line for hermetic toolchain precedence (e.g. via bazelbuild/rules_rust#3256).The linker invocation looks like:
Link.exe splits the second argument at the space, treating
Filesas a separate input file:Fix
In
exec_linker, always use the response file when the linker is MSVC-like and the host is Windows. The response file path already exists and correctly quotes all arguments via theEscapestruct (is_like_msvc = true):The rest of the response file path — UTF-16 BOM encoding, MSVC
"..."-style quoting,@linker-arguments— is already correct and handles spaces properly. This change makes it unconditionally active on Windows/MSVC rather than as a large-command-line fallback.Affected versions
Confirmed in 1.90.0, 1.96.0, and current master (as of 2026-06-04). The response file fallback path itself is correct — only the threshold guard that bypasses it for short command lines is the problem.