Getting `LNK4098` warning when building with Rust 1.97 on Windows

After I upgraded to Rust 1.97 (which enabled linker warnings by default), one of my projects outputs the following linker warning on Windows when compiling with Cargo and stable-x86_64-pc-windows-msvc:

warning: linker stdout: Library "C:\git\gitlab-time-report\target\debug\deps\gitlab_time_report_cli.lib" and object "C:\git\gitlab-time-report\target\debug\deps\gitlab_time_report_cli.exp" are created.␍      
         LINK : warning LNK4098: library "libcmt.lib" conflicts with use of other libs; use /NODEFAULTLIB:library.
  |
  = note: `#[warn(linker_messages)]` on by default

Now the program works fine, but I'm still intrested in what causes this. Is there a way I can investigate this?

Can you show the full linker invocation? Might need RUSTFLAGS="--print link-args" if they aren't shown already.

I had to use Pastebin, as the full text was hitting forum length limits.

After this, the linker warning above appears.

https://stackoverflow.com/questions/76642111/understanding-warning-lnk4098-defaultlib-libcmt-conflicts-with-use-of-other-l#comment135126036_76642111 suggests that you can try RUSTFLAGS="-Clink-arg=/verbose" to make the linker give more context about the warning.

/verbose is very verbose; it gave me 217k lines of output! A cursory search for libcmt.lib, gitlab_time_report_cli.lib and gitlab_time_report_cli.exp didn't give me any more insight that I have currently...

The only new thing I learnt was that the warning happens on pass 1 and not on pass 2.

I can't even show it to you, as pasting it into Pastebin crashes the tab in my browser :_)

Do you have more than one libcmt.lib on the machine?

Well, there are, but they are all within MSVC directories, so I'm going to assume that this works as intended.

However, according to the /verbose output, only one of these is accessed by the linker.

Searching C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.44.35207\lib\x64\libcmt.lib

libcmt.lib is apparently the static version of the MSVC library, but I checked "rustc -C target-feature=+crt-static src/main.rs" without reproducing the issue (as you'd expect).

If I try to repro with rustc -l libcmt src/main.rs I instead get:

warning: linker stdout: LINK : warning LNK4098: defaultlib 'msvcrt' conflicts with ...

Which is the default -crt-static, e.g. dynamically linked library as expected. Flipping it around, and using rustc -C target-feature=+crt-static -l libcmtd src/main.rs or -l msvcrt I can reproduce the issue.

It's possible some naughty dep could have added a CRT lib with it's build.rs,e.g. cargo::rustc-link-lib but I suspect it's more likely there's a library being built (e.g. with the cc crate) with the wrong flags, which transitively pulls in the wrong CRT.

Use cargo tree -i cc for a quick check, I'm not sure if there's an easier way to find crates that link native code in?

also, pay attention to the static-vcruntime crate, which, as the name suggests, links the static library of vcruntime, which is different from the +crt-static rustc feature.

see their documentation for a comparison:

Here's my cargo tree output. Funnily enough, cc only shows up on Linux targets, and not on MSVC. So it's probably not cc causing this problem.

> cargo tree --workspace --all-features --target=x86_64-pc-windows-msvc -i cc
warning: nothing to print.

To find dependencies that require specific target platforms, try to use option `--target all` first, and then narrow your search scope accordingly.

> cargo tree --workspace --all-features --target=x86_64-unknown-linux-gnu -i cc                   
cc v1.2.58
[build-dependencies]
β”œβ”€β”€ iana-time-zone-haiku v0.1.2
β”‚   └── iana-time-zone v0.1.65
β”‚       └── chrono v0.4.44
β”‚           β”œβ”€β”€ gitlab-time-report v1.3.0 (C:\git\gitlab-time-report\src\gitlab-time-report)
β”‚           β”‚   └── gitlab-time-report-cli v1.3.0 (C:\git\gitlab-time-report\src\gitlab-time-report-cli)
β”‚           β”œβ”€β”€ gitlab-time-report-cli v1.3.0 (C:\git\gitlab-time-report\src\gitlab-time-report-cli)
β”‚           └── serde_with v3.18.0
β”‚               β”œβ”€β”€ charming v0.6.0
β”‚               β”‚   └── gitlab-time-report v1.3.0 (C:\git\gitlab-time-report\src\gitlab-time-report) (*)
β”‚               └── gitlab-time-report v1.3.0 (C:\git\gitlab-time-report\src\gitlab-time-report) (*)
└── openssl-sys v0.9.112
    β”œβ”€β”€ native-tls v0.2.18
    β”‚   β”œβ”€β”€ hyper-tls v0.6.0
    β”‚   β”‚   └── reqwest v0.13.2
    β”‚   β”‚       └── gitlab-time-report v1.3.0 (C:\git\gitlab-time-report\src\gitlab-time-report) (*)
    β”‚   β”œβ”€β”€ reqwest v0.13.2 (*)
    β”‚   └── tokio-native-tls v0.3.1
    β”‚       β”œβ”€β”€ hyper-tls v0.6.0 (*)
    β”‚       └── reqwest v0.13.2 (*)
    └── openssl v0.10.76
        └── native-tls v0.2.18 (*)

The static-vcruntime is not in my dependency tree, neither Cargo.lock nor cargo tree contains it.

did you try /VERBOSE:LIB? it should only print out the libraries the linker is looking for, and usually the line immediately precedding the libcmt.lib is the one that depends on it, which should at least give you a hint where to look at next.

openssl-sys is a pretty good candidate given how funky it's build is - though it is odd it doesn't show up on windows given the cc dependency isn't a target-specific dependency, nor any of the parents AFAIK?

I suppose you could blindly try building with OPENSSL_STATIC=0 and =1 envs but if it's not in the tree it shouldn't make a difference....

That would be msvcrt.lib

 Searching C:\Program Files (x86)\Windows Kits\10\lib\10.0.26100.0\um\x64\winmm.lib:␍
 Searching C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.44.35207\lib\x64\legacy_stdio_definitions.lib:␍
 Searching C:\Program Files (x86)\Windows Kits\10\lib\10.0.26100.0\um\x64\dbghelp.lib:␍
 Searching C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.44.35207\lib\x64\msvcprt.lib:␍
 Searching C:\Program Files (x86)\Windows Kits\10\lib\10.0.26100.0\um\x64\kernel32.lib:␍
 Searching C:\Program Files (x86)\Windows Kits\10\lib\10.0.26100.0\um\x64\ntdll.lib:␍
 Searching C:\Program Files (x86)\Windows Kits\10\lib\10.0.26100.0\um\x64\userenv.lib:␍
 Searching C:\Program Files (x86)\Windows Kits\10\lib\10.0.26100.0\um\x64\ws2_32.lib:␍
 Searching C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.44.35207\lib\x64\msvcrt.lib:␍
 Searching C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.44.35207\lib\x64\libcmt.lib:␍
 Searching C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.44.35207\lib\x64\oldnames.lib:␍
 Searching C:\Program Files (x86)\Windows Kits\10\lib\10.0.26100.0\um\x64\advapi32.lib:␍
 Searching C:\Program Files (x86)\Windows Kits\10\lib\10.0.26100.0\um\x64\uuid.lib:␍
 Searching C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.44.35207\lib\x64\vcruntime.lib:␍
 Searching C:\Program Files (x86)\Windows Kits\10\lib\10.0.26100.0\ucrt\x64\ucrt.lib:␍
 ␍
 Finished searching libraries␍
 LINK : warning LNK4098: defaultlib 'libcmt.lib' conflicts with use of other libs; use /NODEFAULTLIB:library␍

I ran cargo clean and cargo build, but it resulted in the same message on both OPENSSL_STATIC=0 and 1.

well, that is not helpful at all... msvcrt.lib is the very library in conflicts with libcmt.lib.

it seems the print order is different from what I recalled, and these runtime libraries are not pulled in as a direct dependency.

a last resort:

I believe the microsoft linker detects this kind of conflicts using the linker directives embedded in the objects themselves, which are emitted by the compiler based on the frontend specific flags. e.g. /MD or /MT for msvc, -C target-feature=+crt-static for rustc. a quick google search says the embedded linker directives are stored in a section named .drectve and can be examined via the dumpbin utility.

so, try examine every .obj and .lib files (presumably excluding those generated by rustc)

dumpbin /DIRECTIVES legacy_stdio_definitions.lib

you can write a script to specifically look for libcmt, something like:

dumpbin /DIRECTIVES legacy_stdio_definitions.lib | grep -i "libcmt"

I'm not sure, but if the .drectve sections are in plain text, maybe you can just grep through the libraries directly? worth a try.

if this still can't find the offender, then I'm officially out of ideas.

Judging from it's build.rs, I would suspect rusty_v8 is responsible here. It uses precompiled lib files downloaded from their releases which I'd guess contain references to the static CRT.

Yeah, that's the issue. I downloaded the msvc version and extracted rusty_v8.lib then ran dumpbin:

dumpbin /directives rusty_v8.lib

This had a whole lot of output but the import point is it has a lot of linking libcmt:

   Linker Directives
   -----------------
   /DEFAULTLIB:libcmt.lib
   /DEFAULTLIB:oldnames.lib
   /FAILIFMISMATCH:_CRT_STDIO_ISO_WIDE_SPECIFIERS=0
   /alternatename:_Avx2WmemEnabled=_Avx2WmemEnabledWeakValue

   Linker Directives
   -----------------
   /DEFAULTLIB:libcmt.lib
   /DEFAULTLIB:oldnames.lib

   Linker Directives
   -----------------
   /DEFAULTLIB:libcmt.lib
   /DEFAULTLIB:oldnames.lib
   /FAILIFMISMATCH:_CRT_STDIO_ISO_WIDE_SPECIFIERS=0
   /alternatename:_Avx2WmemEnabled=_Avx2WmemEnabledWeakValue

... [it repeats a lot] ...

You can either try passing /NODEFAULTLIB:libcmt.lib to the linker or use +crt-static for your whole build. I think the latter would be the safer option if the library is built for the static CRT. Well unless or until rusty_v8 can fix this.

Bizarre that's not mentioned in the readme!

Closest I could find is [feature] support dynamic crt MD lib for windows. Β· Issue #903 Β· denoland/rusty_v8 Β· GitHub