Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't statically link MSVC runtime #290

Closed
johnterickson opened this issue May 16, 2016 · 12 comments
Closed

Can't statically link MSVC runtime #290

johnterickson opened this issue May 16, 2016 · 12 comments

Comments

@johnterickson
Copy link

johnterickson commented May 16, 2016

Looks like it's hardcoded to dynamically link to the runtime. If someone points me in the right direction, I can send a PR to make a configurable, but I'm not sure where to start. I can confirm that changing it from msvcrt to libcmt makes a statically linked binary.

C:\src\libc>git diff
diff --git a/src/windows.rs b/src/windows.rs
index 21b6e13..3e20aad 100644
--- a/src/windows.rs
+++ b/src/windows.rs
@@ -99,7 +99,7 @@ pub const S_IWRITE: ::c_int = 128;
 pub const S_IREAD: ::c_int = 256;

 #[cfg(target_env = "msvc")] // " if " -- appease style checker
-#[link(name = "msvcrt")]
+#[link(name = "libcmt")]
 extern {}

 extern {

Here's the output of dumpbin for libcmt:


Microsoft (R) COFF/PE Dumper Version 14.00.23506.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file c:\src\RustDrop\target\debug\vsts_drop.exe

File Type: EXECUTABLE IMAGE

  Image has the following dependencies:

    ADVAPI32.dll
    KERNEL32.dll
    USER32.dll
    WS2_32.dll
    SHELL32.dll

and for msvcrt:


Dump of file c:\src\RustDrop\target\debug\vsts_drop.exe

File Type: EXECUTABLE IMAGE

  Image has the following dependencies:

    ADVAPI32.dll
    KERNEL32.dll
    SHELL32.dll
    USER32.dll
    WS2_32.dll
    VCRUNTIME140.dll
    api-ms-win-crt-math-l1-1-0.dll
    api-ms-win-crt-string-l1-1-0.dll
    api-ms-win-crt-time-l1-1-0.dll
    api-ms-win-crt-runtime-l1-1-0.dll
    api-ms-win-crt-environment-l1-1-0.dll
    api-ms-win-crt-heap-l1-1-0.dll
    api-ms-win-crt-convert-l1-1-0.dll
    api-ms-win-crt-stdio-l1-1-0.dll
    api-ms-win-crt-utility-l1-1-0.dll
    api-ms-win-crt-filesystem-l1-1-0.dll
    api-ms-win-crt-conio-l1-1-0.dll
    api-ms-win-crt-locale-l1-1-0.dll

Relevant thread: https://users.rust-lang.org/t/standalone-32bit-windows-app-using-msvc/3103

@alexcrichton
Copy link
Member

Interesting! Linking to msvcrt historically has no real reason for existing beyond "it was the first thing that worked", but I agree that statically linking all of these dependencies is much more "rustic" than not. I fear, though, that this may require changes beyond just this library.

I would guess that a change here is enough for "pure Rust" code, but C code I think has to be compiled differently to account for linking to the CRT differently (e.g. the gcc crate would need to update). Do you know if there's a compatibility story we could have there?

It may also be worth just giving this a spin by building Rust and seeing what happens when you try to run the test suite.

cc @rust-lang/tools
cc @retep998
cc @vadimcn

@retep998
Copy link
Member

Since /MD and /MT affect code generation somewhat (whether to apply dllimport for certain functions and whatnot), this means that to be safe and not encounter any issues, all statically linked C/C++ code for a given binary (either .exe or .dll) must be compiled for and linked against the same CRT. So yes, the gcc crate would have to be able to compile code either with /MD or /MT depending upon which CRT the Rust code will be linked against. There would be some way to decide on this from a higher level and then pass along this information to all the relevant crates, and a standardized environment variable is probably the easiest and quickest way to do this, although there are fancier options.

@retep998
Copy link
Member

In order for the environment variable option to work though, libc would have to remove the #[link(name = "msvcrt")] bit and never hard code which CRT to link to in the code. Instead the only option I can feasibly see is for rustc itself to look at the environment variable (or some other flag or config) and then link in the appropriate CRT. A libc build script wouldn't work because that would only run when you depend on the libc crate and not the prebuilt libc that comes with Rust.

@vadimcn
Copy link

vadimcn commented May 19, 2016

I wouldn't be opposed to making statically linking msvcrt the default.

But if we wanted to make it configurable, I wonder if #[link(alias)] RFC would help here? Granted, it was not intended for replacing lib names in already compiled crates, but hey...

C code I think has to be compiled differently to account for linking to the CRT differently (e.g. the gcc crate would need to update). Do you know if there's a compatibility story we could have there?

Well, as we know, MSVC linker will patch over these differences most of the time (except for data exports, but I don't think msvcrt has any of those). I guess we'll need to do some crater runs to know for sure.

@retep998
Copy link
Member

retep998 commented May 19, 2016

@vadimcn While in many cases the linker can resolve those differences, the object files will still have that version of the CRT specified as a default object to link in, leading to situations where both the static and dynamic CRT are being passed to the linker which is bad. We'd have to specify /NODEFAULTLIB:msvcrt.lib or /NODEFAULTLIB:libcmt.lib as appropriate, which will involve support from rustc to be able to pass these linker flags.

I guess we'll need to do some crater runs to know for sure.

We'd also need crater to actually support Windows first.

@alexcrichton
Copy link
Member

For now I'd be fine with just developing a transition story from dynamically linked to a statically linked CRT. I suspect that the practical impact of switching would be next-to-none as it'd be pretty rare to require dynamic linkage, right? Along those lines I'd want to leave open some vector to request dynamic linkage, but we perhaps don't have to do it just yet.

If we can selectively tell link.exe to ignore msvcrt.lib and libcmt.lib dependencies in object files (via those flags) then we may be able to just change it have everything work.

@retep998
Copy link
Member

retep998 commented May 21, 2016

Dynamic linkage is basically essential if you want to pass CRT objects between binaries. If you allocate something (memory, files, any CRT thing) in a dll/exe that links to one CRT and try to free it (or even use it, files in particular) in a different dll/exe that links to a different CRT then bad things can happen. So keeping an option to dynamically link the CRT is probably important. You don't always need it, but when you do need it you can't really go without it.

I'd personally be happy with just some sort of flag or environment variable to tell rustc which CRT to link, and then it passes /NODEFAULTLIB:blah to the linker for the CRTs that it isn't linking. The environment variable is probably better because then build scripts can follow it too in order to build C/C++ code correctly.

@alexcrichton
Copy link
Member

Solved by https://github.com/rust-lang/rfcs/blob/master/text/1721-crt-static.md and #446, implemented in rust-lang/rust#37545

Susurrus pushed a commit to Susurrus/libc that referenced this issue Mar 26, 2017
Susurrus pushed a commit to Susurrus/libc that referenced this issue Mar 26, 2017
Rename flags to conform to conventions.

Resolves rust-lang#290.
@avkonst
Copy link

avkonst commented Nov 14, 2017

I understand this has been fixed. Could you please let me know what flag should I use with cargo to enable static linkage (i.e. to get rid of runtime dependency to vcruntime140.dll)?

@retep998
Copy link
Member

@avkonst cargo rustc -- -Ctarget-feature=+crt-static

@avkonst
Copy link

avkonst commented Nov 14, 2017

Thanks! I use cargo build and cargo build --release commands? Is the same flag applicable? Is there an option to set in the cargo.toml file?

@avkonst
Copy link

avkonst commented Nov 14, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants