-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Rust, Windows, and MSVC #1061
Comments
Thanks @retep998. This is going to be a big focus this year and we need to get clear on what the goals are. |
cc me |
The best solution for |
Why does rust require a CRT at all on windows? It's rust not C, it can (and AFAIK, does in most places) just call windows APIs directly? Also, which license prevents open source projects from using the redistributable msvcrt.dlls? |
@Diggsey GPL does not allow you to link GPL code with proprietary code, unless it is a system library in which case there is an exception for that. Since there's no GPL compatible CRTs on Windows, this means the only option for such software is to link dynamically to the system |
@retep998 Thanks for the explanation, although after doing some reading, it seems that the "system library" exception explicitly includes compiler runtimes, from the GPL:
And apparantly other people are interpretting it this way: So as long as you aren't actually distributing msvcrt.dll you could still be GPL compatible. Since the redistributable can be freely downloaded from the microsoft site, it should be possible to prompt the user to install that first as part of any install process? |
@Diggsey Given that, then we should at least be able to use other versions of the CRT so long as we link dynamically. |
I am getting very confused here... What does msvcrt.dll have to do with GPL?? |
@vadimcn GPL forbids distribution of software that falls under GPL if it has any components which are not GPL-compatible, unless they are system libraries which are not distributed with the software. Which means GPL software has to be dynamically linked to msvcrt and cannot be distributed with the CRT redistributable. |
@vadimcn Programs built using VC++ link to msvcrt.dll for their C runtime library, which is the "correct" runtime library to use for new programs: because it's versioned it means microsoft can release new versions without breaking all existing programs. However, programs built using the mingw toolchain link to msvcrt.dll, which is unversioned and only exists for backwards compatibility reasons. The reason is that msvcrt.dll comes preinstalled on all windows machines, whereas versioned crts must be installed separately, and the GPL prohibits you from distributing the proprietary msvcrt.dll with GPL software. Ideally there would be an open source C runtime for windows which would solve all the problems, but AFAIK that doesn't exist. |
@retep998: I kinda doubt that's the reason. All GCC libs, that Rust binaries are linked with, are covered by the GCC runtime library linking exception. |
@vadimcn The problem is not the GCC libraries we use, but rather if someone wants to build GPL software on Windows using Rust. |
Where does it make any distinction about whether you distribute the "system libraries"? |
IIRC in this context the term "system library" is just conservatively interpreted to mean "library that is so prevalent on systems that it need not be shipped", so shipping a library might cause it to be viewed as not being a "system library" and therefore not subject to the exemption. |
I can see it in the GPLv2 now. So discussing the GPL isn't very meaningful without specifying its version it seems... The language there is a bit ambiguous (is the second "component" the same as the first?):
|
@retep998 Just to be clear, it is jemalloc that drags in the |
Perhaps we should break the issue into chunks? I believe jemalloc can be disabled, so we can try to get a working version of MSVC Rust sans jemalloc and debuginfo, then go on to jemalloc, then debuginfo. |
From a technical point of view I managed to build a rust program into an exe using link.exe and the MSVC toolchain in LLVM (it's a hack, but it's in my branch listed above) - there were a couple of things I didn't expect going in:
|
In VS 2015, the CRT is being refactored into a VC runtime and a Universal CRT. |
I have already found a need for integration with Windows resource scripts. When creating executables that link to Common Controls, it's necessary to create a manifest file that tells what version of the Also, it would be nice to be able to add icon files directly to executables in Windows, which I believe is facilitated by resource scripts as well. |
Special thanks to @retep998 for the [excellent writeup](rust-lang/rfcs#1061) of tasks to be done and @ricky26 for initially blazing the trail here! # MSVC Support This goal of this series of commits is to add MSVC support to the Rust compiler and build system, allowing it more easily interoperate with Visual Studio installations and native libraries compiled outside of MinGW. The tl;dr; of this change is that there is a new target of the compiler, `x86_64-pc-windows-msvc`, which will not interact with the MinGW toolchain at all and will instead use `link.exe` to assemble output artifacts. ## Why try to use MSVC? With today's Rust distribution, when you install a compiler on Windows you also install `gcc.exe` and a number of supporting libraries by default (this can be opted out of). This allows installations to remain independent of MinGW installations, but it still generally requires native code to be linked with MinGW instead of MSVC. Some more background can also be found in #1768 about the incompatibilities between MinGW and MSVC. Overall the current installation strategy is quite nice so long as you don't interact with native code, but once you do the usage of a MinGW-based `gcc.exe` starts to get quite painful. Relying on a nonstandard Windows toolchain has also been a long-standing "code smell" of Rust and has been slated for remedy for quite some time now. Using a standard toolchain is a great motivational factor for improving the interoperability of Rust code with the native system. ## What does it mean to use MSVC? "Using MSVC" can be a bit of a nebulous concept, but this PR defines it as: * The build system for Rust will build as much code as possible with the MSVC compiler, `cl.exe`. * The build system will use native MSVC tools for managing archives. * The compiler will link all output with `link.exe` instead of `gcc.exe`. None of these are currently implemented today, but all are required for the compiler to fluently interoperate with MSVC. ## How does this all work? At the highest level, this PR adds a new target triple to the Rust compiler: x86_64-pc-windows-msvc All logic for using MSVC or not is scoped within this triple and code can conditionally build for MSVC or MinGW via: #[cfg(target_env = "msvc")] It is expected that auto builders will be set up for MSVC-based compiles in addition to the existing MinGW-based compiles, and we will likely soon start shipping MSVC nightlies where `x86_64-pc-windows-msvc` is the host target triple of the compiler. # Summary of changes Here I'll explain at a high level what many of the changes made were targeted at, but many more details can be found in the commits themselves. Many thanks to @retep998 for the excellent writeup in rust-lang/rfcs#1061 and @RicK26 for a lot of the initial proof-of-concept work! ## Build system changes As is probably expected, a large chunk of this PR is changes to Rust's build system to build with MSVC. At a high level **it is an explicit non goal** to enable building outside of a MinGW shell, instead all Makefile infrastructure we have today is retrofitted with support to use MSVC instead of the standard MSVC toolchain. Some of the high-level changes are: * The configure script now detects when MSVC is being targeted and adds a number of additional requirements about the build environment: * The `--msvc-root` option must be specified or `cl.exe` must be in PATH to discover where MSVC is installed. The compiler in use is also required to target x86_64. * Once the MSVC root is known, the INCLUDE/LIB environment variables are scraped so they can be reexported by the build system. * CMake is required to build LLVM with MSVC (and LLVM is also configured with CMake instead of the normal configure script). * jemalloc is currently unconditionally disabled for MSVC targets as jemalloc isn't a hard requirement and I don't know how to build it with MSVC. * Invocations of a C and/or C++ compiler are now abstracted behind macros to appropriately call the underlying compiler with the correct format of arguments, for example there is now a macro for "assemble an archive from objects" instead of hard-coded invocations of `$(AR) crus liboutput.a ...` * The output filenames for standard libraries such as morestack/compiler-rt are now "more correct" on windows as they are shipped as `foo.lib` instead of `libfoo.a`. * Rust targets can now depend on native tools provided by LLVM, and as you'll see in the commits the entire MSVC target depends on `llvm-ar.exe`. * Support for custom arbitrary makefile dependencies of Rust targets has been added. The MSVC target for `rustc_llvm` currently requires a custom `.DEF` file to be passed to the linker to get further linkages to complete. ## Compiler changes The modifications made to the compiler have so far largely been minor tweaks here and there, mostly just adding a layer of abstraction over whether MSVC or a GNU-like linker is being used. At a high-level these changes are: * The section name for metadata storage in dynamic libraries is called `.rustc` for MSVC-based platorms as section names cannot contain more than 8 characters. * The implementation of `rustc_back::Archive` was refactored, but the functionality has remained the same. * Targets can now specify the default `ar` utility to use, and for MSVC this defaults to `llvm-ar.exe` * The building of the linker command in `rustc_trans::back::link` has been abstracted behind a trait for the same code path to be used between GNU and MSVC linkers. ## Standard library changes Only a few small changes were required to the stadnard library itself, and only for minor differences between the C runtime of msvcrt.dll and MinGW's libc.a * Some function names for floating point functions have leading underscores, and some are not present at all. * Linkage to the `advapi32` library for crypto-related functions is now explicit. * Some small bits of C code here and there were fixed for compatibility with MSVC's cl.exe compiler. # Future Work This commit is not yet a 100% complete port to using MSVC as there are still some key components missing as well as some unimplemented optimizations. This PR is already getting large enough that I wanted to draw the line here, but here's a list of what is not implemented in this PR, on purpose: ## Unwinding The revision of our LLVM submodule [does not seem to implement][llvm] does not support lowering SEH exception handling on the Windows MSVC targets, so unwinding support is not currently implemented for the standard library (it's lowered to an abort). [llvm]: https://github.com/rust-lang/llvm/blob/rust-llvm-2015-02-19/lib/CodeGen/Passes.cpp#L454-L461 It looks like, however, that upstream LLVM has quite a bit more support for SEH unwinding and landing pads than the current revision we have, so adding support will likely just involve updating LLVM and then adding some shims of our own here and there. ## dllimport and dllexport An interesting part of Windows which MSVC forces our hand on (and apparently MinGW didn't) is the usage of `dllimport` and `dllexport` attributes in LLVM IR as well as native dependencies (in C these correspond to `__declspec(dllimport)`). Whenever a dynamic library is built by MSVC it must have its public interface specified by functions tagged with `dllexport` or otherwise they're not available to be linked against. This poses a few problems for the compiler, some of which are somewhat fundamental, but this commit alters the compiler to attach the `dllexport` attribute to all LLVM functions that are reachable (e.g. they're already tagged with external linkage). This is suboptimal for a few reasons: * If an object file will never be included in a dynamic library, there's no need to attach the dllexport attribute. Most object files in Rust are not destined to become part of a dll as binaries are statically linked by default. * If the compiler is emitting both an rlib and a dylib, the same source object file is currently used but with MSVC this may be less feasible. The compiler may be able to get around this, but it may involve some invasive changes to deal with this. The flipside of this situation is that whenever you link to a dll and you import a function from it, the import should be tagged with `dllimport`. At this time, however, the compiler does not emit `dllimport` for any declarations other than constants (where it is required), which is again suboptimal for even more reasons! * Calling a function imported from another dll without using `dllimport` causes the linker/compiler to have extra overhead (one `jmp` instruction on x86) when calling the function. * The same object file may be used in different circumstances, so a function may be imported from a dll if the object is linked into a dll, but it may be just linked against if linked into an rlib. * The compiler has no knowledge about whether native functions should be tagged dllimport or not. For now the compiler takes the perf hit (I do not have any numbers to this effect) by marking very little as `dllimport` and praying the linker will take care of everything. Fixing this problem will likely require adding a few attributes to Rust itself (feature gated at the start) and then strongly recommending static linkage on Windows! This may also involve shipping a statically linked compiler on Windows instead of a dynamically linked compiler, but these sorts of changes are pretty invasive and aren't part of this PR. ## CI integration Thankfully we don't need to set up a new snapshot bot for the changes made here as our snapshots are freestanding already, we should be able to use the same snapshot to bootstrap both MinGW and MSVC compilers (once a new snapshot is made from these changes). I plan on setting up a new suite of auto bots which are testing MSVC configurations for now as well, for now they'll just be bootstrapping and not running tests, but once unwinding is implemented they'll start running all tests as well and we'll eventually start gating on them as well. --- I'd love as many eyes on this as we've got as this was one of my first interactions with MSVC and Visual Studio, so there may be glaring holes that I'm missing here and there! cc @retep998, @ricky26, @vadimcn, @klutzy r? @brson
I've reorganized the description of this issue slightly and am going to be using it as a sort of metabug to track sub-bugs. |
Based on a binary that @alexcrichton sent me that was built using the new msvc target, it seems that the CRT is being statically linked. Considering we are explicitly linking |
@Diggsey That's pretty amusing. They deserve all the trouble they're enduring and more for not having included a stable C library in their os 30 years ago. It's a "code smell" to me to deliver shared objects alongside executables, especially when they're things like the standard C library, but I managed to link against the evil |
Windows has always included a stable C library, the problems stem from the fact that it must maintain binary compatibility across all versions of windows, and that it combines both the compiler runtime and the language runtime in one. The compiler runtime must, necessarily, be updated for each new version of the compiler. That means the entire runtime must be versioned according to the compiler version, so frequently you need a runtime that is newer than the version of windows on which you are running. This means you can't rely on it being pre-installed, and that is grounds for discounting it as a "core system component", which makes it incompatible with the GPL. The UCRT solves the problem by splitting the compiler runtime and C runtime into separate DLLs, which allows the C runtime part to be the same regardless of the compiler version. Linux doesn't have the problem only because it doesn't attempt to maintain binary compatibility for programs which rely on shared libraries: you can't just copy a program from one linux machine to another and have any guarantee of it working, unless absolutely everything is statically linked and the kernel versions match. For platforms where most software is distributed only in binary form, this obviously can't work. |
@Diggsey Then it is not stable. It is an incorrect presumption that the compiler runtime "must, necessarily, be updated for each new version". I can use an (x86) crt1 Mach-O crosscompiled for OS X 10.1 with El Capitan's
This is so false that I don't even. Linux famously does not break userspace, so kernel versions aren't generally a problem. Moreover, except for rare cases, glibc maintains backwards compatibility. As does OS X, which also guarantees |
The compiler runtime is completely different from the C runtime... Obviously using an old CRT will work, that's the whole point: the CRT has a stable API, the compiler runtime does not. Windows does have a stable C runtime, because as long as you only use the C runtime parts (of any version of msvcrt) it will continue to work forever. The part that changes is the compiler runtime.
Make up your mind, either there is a guarantee or there's not. If there isn't a guarantee, than what I said is true. Windows does provide backwards compatibility guarantees, to a fault. You seem to be missing the important distinction between should work, and does work. |
If you examine the rest of that sentence, instead of cutting it out for cute rhetorical purposes like "Make up your mind", you'd see I'm referring to
Wrong for clang/libSystem. There are (non-MS) platforms that do not make these distinctions, and the existence of the former demonstrates that the latter should not be necessary. Binary compatibility does not have to be an issue, and distinguishing "compiler runtimes" from other stable components is meaningless or inconsequential on platforms that don't suffer from the nuanced design flaws of Windows. Not everyone is forced to accept MS's terminology, nor their justifications for instability. And just to hammer the point home (and because you clearly misunderstood this), the functions in Apple's |
It's worth noting that
|
@retep998 Hah that's quite a good list. But OS X puts all of those in a DLL too. For example for the C++ exception stuff, Edit: But There is simply no justifiable reason for what Microsoft did. |
@retep998 What's the state of this metabug? Looks a bit stale. Can remaining items be moved to rust-lang/rust as individual bugs? |
I went ahead and dropped the P-high tag since this bug is inactive. |
I do update the issue every so often. Feel free to point out anything you feel should be added or was completed and I'll update the list. |
What's left to do re: the first wishlist item? 😜 |
I'm not sure. I'll have to try debugging a Rust program and see how good the local variable info is. |
I have noticed when debugging with MSVC's debugger, stepping into any line that uses |
All of the "Remaining tasks" still appear unfinished, so presumably it's still active. Perhaps the metabug itself could be moved to rust-lang/rust. |
@retep998 I've heard that the COM ABI is slightly different from the usual Win32 ABI, in ways that resulted in MSVC's C (not C++) compiler historically not being able to call it correctly. Is this true / does rustc need to do anything to support it? |
@alexchandel stdcall for C++ methods is slightly different than stdcall for regular functions. Since COM is based on C++ methods, that means that for some of those COM methods, the C equivalent is in fact incorrect. I already opened an issue for this #1342 which is even listed in this meta issue. |
Could someone elaborate in what way the quality of PDB debug info should be improved? |
#2625 covers |
This list has stagnated for a while, and I don't think this repo is really the right place for it anyway. A new list will be maintained the Windows WG repo: rust-windows/wg#3 |
High priority tasks
kind=static-nobundle
stable, or changekind=static
to behave likekind=static-nobundle
.env_clear
actually work.env_clear
does not work on Windows rust#31259Lower priority tasks
dllimport
properly, or just get rid of thedylib
crate type. Correctly handle dllimport on Windows rust#27438cl
with/GL
can be linked properly. Or just stopkind=static
from bundling. MSVC: support LTCG native libraries in rlibs rust#26003-msvc
distribution.-msvc
distribution has unused import libraries rust#31816Command
work better. Command::spawn has weird rules for finding binaries on Windows rust#37519\\?\
where needed on Windows rust#32689Completed tasks
dllimport
properly. Use #[link(kind)] to fix imports from native libs on Windows #1717dllexport
. Add support for DllExport on Windows rust#7196libgcc
and use native/llvm unwinding stuff.librustc_back
how to invokelink.exe
.Wishlist
lld
for cross compilation to Windows with a new pure Rust target without any CRT bits at all. migrate to the LLVM toolchain rust#9367Related links
Command
behave better. Command::spawn has weird rules for finding binaries on Windows rust#37519rust-lang/rust#1768
The text was updated successfully, but these errors were encountered: