Skip to content

LINK: fatal error LNK1181 on Windows when /LIBPATH contains spaces and command line is under ~6KB #157457

@peakschris

Description

@peakschris

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions