Rustup on Windows: How to select MSVC version

On my machine I have two versions of visual studio installed: VS2019 and VS2022. They differ in the compilers and also in the libraries they link to. Now I want to compile the wasmer (Webassembly Virtual machine) c-api into a library that can be consumed by a C++ project. The thing is that I need to specifically control which version of the VS library versions the resulting wasmer.lib is being linked against. So I was looking how the toolchain of rustup can be modified. I also tried to modify the path but I do not have any executables of Visual studio in my path.

How can I specify the visual studio version that is used by cargo?

Dangerous half-knowledge here, but I used to cross compile to windows a while back. I remember that you can set the linker in cargo in the [target] table. Maybe for you, this would look something like:

[target.x86_64-pc-windows-msvc]
linker = "path/to/your/link.exe"

Thanks jofas, as far as I understand this the new path to the link executable should also use a different version of the c runtime? If this is the case then this could work. I have assumed to set the toolset somewhere.

When using your key in the projects cargo.toml file I get this

warning: C:\USRTMP\dev\OpenSource\01_Scripting\wasmer\lib\c-api\Cargo.toml: unused manifest key: target.x86_64-pc-windows-msvc.linker

And after testing I got the same linker errors in my C++ project

Sounds like I used the wrong toolchain triple as an example. You can run rustup toolchain list to get the toolchain you are using. My output currently looks like this:

stable-x86_64-unknown-linux-gnu (default)

so if I wanted to change to a different linker than cc (i.e. use clang instead of gcc), I'd write the following inside my Cargo.toml:

[target.x86_64-unknown-linux-gnu]
linker = "/usr/bin/clang"

I assumed you'd have two linker.exe executables on your computer, one for VS2019 and one for VS2022. Your problem is that your toolchain uses the wrong linker per default. I think if you change the target.<your-toolchain>.linker field in your Cargo.toml, you should be able to link to the right ABI.

EDIT: So sorry, wanted to try it out on my own machine and realized that I don't have to change my Cargo.toml file, but my cargo configuration file. In my case, I added the above code snippet to my $HOME/.cargo/config.toml file and it did the trick

Now I think the config key in config.toml works, but still when I open the resulting lib file and search for e.g. winrt, I get this

-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22000.0\\cppwinrt, other dependencies are similar

So the Windows SDK version is 10.0.22000.0, but what I need is 10.0.18362.0. The expected platform toolset should be v142

How can I achieve this?

Hmm, sorry that my suggestion didn't help. I hoped that explicitly setting the linker to the one shipped as part of the VS environment you need would solve the problem. I guess there are system variables involved which have an impact on the linker and how it chooses which ABIs to link to. Here is a link to the MSVC linker reference, maybe you can find the right options to link to the ABI you need? I don't know enough about linkers in general and msvc in particular to be of any help.

Having two versions of the same toolchain installed is a complete hassle in my experience. Tried that once with two minor versions of Python3 without setting a virtual environment and it completely backfired on me :sweat_smile:. Have you considered building your application in a Docker container? According to the documentation, you can install VS build tools inside a Windows Docker container.

I totally agree that two toolchains are a mess. I would stop it if this would be possible, but I need them for two different projects.
I tried to first execute the batch "developer prompt 2019" and hoped that the relevant environment options are picked up by cargo, but it didn't.

Also setting the linker dir to the specific sdk version did not help

[target.1.63-x86_64-pc-windows-msvc]
rustflags = [ "-Clink-args=-LibDir=C:/Program Files (x86)/Windows Kits/10/Lib/10.0.18362.0/ucrt/x64"]

If I understand the problem correctly, you shouldn't have any executables like link.exe, cl.exe on your PATH; nor should you try to pass absolute paths to VS executables to rust. Instead you should use vcvarsall.bat to load the appropriate environment into your current shell, then build your rust project as normal (you may still need set e.g. linker = "link.exe"). vcvarsall.bat accepts arguments both for a version of Windows SDK and for a version of the toolset.

1 Like

@yuriy0 you are correct. My process now is that I first start the Developer Command Prompt for Visual Studio 2019. When I then check the path of link.exe it is correctly located in

C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\bin\Hostx86\x86\link.exe

For a long time I thought that the presence of the string

"C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22000.0\\ucrt"

means, that this version is required as a dependency is not correct since I do not have this path on my system.

When I use the resulting lib in a windows c++ program, I can correctly execute it. But for my use case I want to replace the kernel32.lib by a replacement that is provided by my realtime operating system supplier (Intervalzero RTX). Therefore, the symbols required by wasmer.lib should exactly match the provided symbols of this replacement kernel32.lib. This is not the case, since I get these linker errors:

Error	LNK2001	unresolved external symbol __imp_ReleaseSRWLockExclusive	WasmerTest	C:\Users\schoetbi\source\repos\WasmerTest\WasmerTest\wasmer.lib(dynasmrt-a7e4b265862ce86a.dynasmrt.83b58e04-cgu.14.rcgu.o)	1	
.. many more 

Error	LNK2001	unresolved external symbol GetFileInformationByHandle	WasmerTest	C:\Users\schoetbi\source\repos\WasmerTest\WasmerTest\wasmer.lib(same_file-45198a06d190a74d.same_file.397d7d37-cgu.6.rcgu.o)	1	

So as I understand this, the problem is not what link.exe is used but what version of the kernel32.lib is used in the linker step.

I checked where kernrel32.libs are located on my system:

C:\Program Files (x86)\Windows Kits>rg --files | rg -i kernel32.lib
10\Lib\10.0.19041.0_\um\x86\kernel32.Lib
10\Lib\10.0.19041.0_\um\arm64\kernel32.Lib
10\Lib\10.0.19041.0_\um\arm\kernel32.Lib
10\Lib\10.0.19041.0_\um\x64\kernel32.Lib
10\Lib\10.0.18362.0\um\x86\kernel32.Lib
10\Lib\10.0.18362.0\um\x64\kernel32.Lib
10\Lib\10.0.18362.0\um\arm64\kernel32.Lib
10\Lib\10.0.18362.0\um\arm\kernel32.Lib

Then I renamed 10.0.19041.0 to 10.0.19041.0_ in both the lib and the include folder and made

cargo clean
make build-capi
make package-capi

to regenerate wasmer.lib. But still the symbol requirements and the linker errors stayed the same.

I'm afraid this is where my knowledge ends - I've never found myself hacking on the windows kernel. My suggestion would be to contact your supplier, surely they have a process for building applications for their runtime.

You might also look at the implementation of vcvarsall.bat and see how it handles setting up the environment for the windows SDK, then try to replicate that logic with the paths to your own libs.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.