rust-lang / rfcs

RFCs for changes to Rust
https://rust-lang.github.io/rfcs/
Apache License 2.0
5.89k stars 1.56k forks source link

Rust, Windows, and MSVC #1061

Closed retep998 closed 4 years ago

retep998 commented 9 years ago

High priority tasks

Lower priority tasks

Completed tasks

Wishlist

https://github.com/rust-lang/rust/issues/1768

retep998 commented 9 years ago

cc @brson @ricky26 @vadimcn

brson commented 9 years ago

Thanks @retep998. This is going to be a big focus this year and we need to get clear on what the goals are.

Manishearth commented 9 years ago

cc me

alexchandel commented 9 years ago

The best solution for msvcrt.dll may be to statically link a CRT.

Diggsey commented 9 years ago

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?

retep998 commented 9 years ago

@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 msvcrt.dll which is a system library and thus falls under the exception. Even though Rust is not C, if it statically links to any C code that relies on the CRT, like jemalloc, then it needs to link to the CRT and use the CRT entry points.

Diggsey commented 9 years ago

@retep998 Thanks for the explanation, although after doing some reading, it seems that the "system library" exception explicitly includes compiler runtimes, from the GPL:

However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

And apparantly other people are interpretting it this way: http://lists.gnu.org/archive/html/bug-gnu-utils/2004-01/msg00124.html

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?

retep998 commented 9 years ago

@Diggsey Given that, then we should at least be able to use other versions of the CRT so long as we link dynamically. I'd still like to have the option to statically link to the CRT for software that isn't tied down by GPL though.

vadimcn commented 9 years ago

I am getting very confused here... What does msvcrt.dll have to do with GPL??

retep998 commented 9 years ago

@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.

Diggsey commented 9 years ago

@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.

vadimcn commented 9 years ago

@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. Edit: And I think the same goes for a lot of c++ programs compiled with GCC.

retep998 commented 9 years ago

@vadimcn The problem is not the GCC libraries we use, but rather if someone wants to build GPL software on Windows using Rust.

gkoz commented 9 years ago

@retep998

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.

Where does it make any distinction about whether you distribute the "system libraries"?

kini commented 9 years ago

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.

gkoz commented 9 years ago

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?):

However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

alexchandel commented 9 years ago

@retep998 Just to be clear, it is jemalloc that drags in the msvcrt dependency?

Manishearth commented 9 years ago

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.

ricky26 commented 9 years ago

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:

retep998 commented 9 years ago

In VS 2015, the CRT is being refactored into a VC runtime and a Universal CRT. http://blogs.msdn.com/b/vcblog/archive/2015/03/03/introducing-the-universal-crt.aspx

abonander commented 9 years ago

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 comctl32.dll assembly to load. This can be done manually, but it would be a lot more convenient to automate it with a resource script. However, it may be a more appropriate feature for Cargo than the Rust compiler itself.

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.

alexcrichton commented 9 years ago

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.

retep998 commented 9 years ago

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 msvcrt.lib which should be a dynamically linked CRT, something is weird.

alexchandel commented 9 years ago

Universal CRT

43 years later, Windows finally becomes a C standard library release channel. Sadly, XP isn't slated to receive it, so many engineering systems will miss out.

@retep998 Statically linking any versioned CRT should be the default, right?

retep998 commented 9 years ago

XP no longer receives updates so the Universal CRT won't be installed on XP by default. However the redistributable can still target XP and will install the Universal CRT on XP machines, so XP isn't entirely missing out.

The linker by default will link libcmt.lib which is the statically linked CRT. However Rust is explicitly linking msvcrt.lib which is the dynamically linked CRT. So why it is ignoring that and linking the CRT statically is a question that needs to be answered. Not that I'm opposed to statically linking the CRT, I just wish we knew why it was happening.

alexchandel commented 9 years ago

@retep998 Why is msvcrt used at all? Even for executables that only use libcore, Depends reveals there are still calls to calloc, free, fwrite, malloc, signal, strlen, strncmp, and vfprintf.

Edit: it's now down to memcpy, memcmp, memmove, memset, strlen, and the math functions.

ricky26 commented 9 years ago

@alexchandel It's probably my fault msvcrt is linked - I didn't think too hard about it at the time, I just replaced libraries which were hard-coded in the link stage in rustc with equivalent MSVC ones (I can't remember why I used msvcrt, but I suspect that some functions are inlined in newer CRTs and it probably made it easier to get the initial hack working).

Honestly, I think it should just be linking the compiler-packaged C runtime.

retep998 commented 9 years ago

If you link against MSVC's version of msvcrt.lib you are actually linking to the proper compiler packaged C runtime and not the system msvcrt.dll (the name of an import library does not have to match the DLL it links to). There's nothing wrong with linking to msvcrt.lib, the real problem is that MinGW's version of libmsvcrt.a links to the system msvcrt.dll and not to a versioned redistributable C runtime.

alexchandel commented 9 years ago

@ricky26 But does the standard library even use msvcrt anymore? Can we just not link it by default? It's just a layer of overhead of Win32, and every IO call I can find uses the underlying Win32 API.

@retep998 I'd actually like to distribute a Rust binary that doesn't link against any msvcrt, redistributable or system. Is there any need to link msvcrt.lib, other than to use the VC entry point when linking a VC DLL?

retep998 commented 9 years ago

We use things like malloc and memcpy which are provided by msvcrt. It shouldn't be too difficult to replace them though since malloc is just a wrapper around HeapAlloc and memcpy can be written in pure Rust. Really, the only reason to link msvcrt is if you statically link to C code, in which case you need msvcrt for the entry point to initialize the C runtime. If you dynamically link to a C DLL, then you don't need msvcrt because the DLL has its own entry point that will initialize its C runtime.

EDIT: Actually, we also rely a lot on math functions, and the CRT provides a lot of the math functions.

retep998 commented 9 years ago

Here's a list of all the symbols linked in from msvcr120.dll (some of them are only used by the entry point):

?terminate@@YAXXZ
_XcptFilter
__C_specific_handler
__crtSetUnhandledExceptionFilter
__dllonexit
__getmainargs
__initenv
__set_app_type
__setusermatherr
_aligned_free
_aligned_malloc
_aligned_realloc
_amsg_exit
_calloc_crt
_cexit
_commode
_configthreadlocale
_exit
_fmode
_hypot
_hypotf
_initterm
_initterm_e
_lock
_onexit
_unlock
acos
acosf
asin
asinf
atan
atan2
atan2f
atanf
cbrt
cbrtf
ceil
ceilf
cos
cosf
cosh
coshf
exit
exp
exp2
exp2f
expf
expm1
expm1f
fdim
fdimf
floor
floorf
fma
fmaf
fmax
fmaxf
fmin
fminf
fmod
fmodf
free
frexp
ldexp
log
log10
log10f
log1p
log1pf
log2
log2f
logf
malloc
memcmp
memcpy
memmove
memset
nextafter
nextafterf
pow
powf
realloc
round
roundf
sin
sinf
sinh
sinhf
strlen
tan
tanf
tanh
tanhf
trunc
truncf

As you can see, there's a lot of math stuff. It'll take some investigation to find suitable replacements for all the math stuff and memory operations that perform equally well (the CRT has some fancy assembly optimizations for some of them).

ricky26 commented 9 years ago

@retep998 you're completely correct - I thought that the newer libs all had extra characters at the end.

tkelman commented 9 years ago

You're welcome to use any of openlibm that you may or may not find useful (mostly collected from various bsd's into a standalone library). It's been on my to-do list to work on it to make it buildable by MSVC. The assembly would probably have to be translated from AT&T syntax to Intel, though there are C fallback implementations for most functions (maybe not C that MSVC will be able to compile, but PR's welcome). This evidently came up before in #711.

emberian commented 9 years ago

@tkelman x86-64 and ARM MSVC doesn't have inline assembly.

Using a separate libm has come up before, and if we're going to do it it should definitely be openlibm, but I'm still pretty convinced it's a bad idea. Rust should integrate seamlessly into the platform, and having the system C compiler and Rust using different math functions (with the Rust ones probably of equal or higher quality) seems to be begging for infuriating differences in numerical code.

tkelman commented 9 years ago

The assembly in openlibm isn't inline, it's separate files so you can use ml64 with the right invocation. Would probably want to translate all the makefiles into cmake long-term to make supporting MSVC as straightforward as possible.

Using MSVC (edit: or msvcrt for libm functions https://github.com/JuliaLang/julia/issues/1768) is begging for infuriating inaccurate results in numerical code. This is one of many good reasons the scientific computing community doesn't use MSVC at all.

emberian commented 9 years ago

Maybe it's a good idea to use a separate libm on Windows then! I'm somewhat surprised that MSVC isn't good in that regard, but I guess they don't even have a Fortran compiler ;)

tkelman commented 9 years ago

they don't even have a Fortran compiler ;)

Neither does LLVM, sadly. You may joke about Fortran, but remember that you can't build SciPy, Julia, R, Octave, or Matlab, (or the same set of fundamental libraries if you want to write Rust bindings to them) without a Fortran compiler.

If I can get Julia's test suite to run against msvcrt or msvcr120's libm functions I'll report any incorrect results in #711 - that seems like the more on-topic place to discuss using a different libm anyway.

KindDragon commented 9 years ago

Using MSVC (edit: or msvcrt for libm functions JuliaLang/julia#1768) is begging for infuriating inaccurate results in numerical code. This is one of many good reasons the scientific computing community doesn't use MSVC at all.

It's return correct result in latest version: http://webcompiler.cloudapp.net/

#include <iostream>
#include <math.h>

using namespace std;

int main()
{

   cout << sin(pow (2.0, 64)) << endl; // 0.247261
}
tkelman commented 9 years ago

@KindDragon it's right on 64 bit but wrong on 32.

retep998 commented 9 years ago

After doing a test with replacing the entry point with my own entry point, here are all the symbols from the CRT that we actually link to:

_hypot
_hypotf
acos
acosf
asin
asinf
atan
atan2
atan2f
atanf
cbrt
cbrtf
ceil
ceilf
cos
cosf
cosh
coshf
exp
exp2
exp2f
expf
expm1
expm1f
fdim
fdimf
floor
floorf
fma
fmaf
fmax
fmaxf
fmin
fminf
fmod
fmodf
frexp
ldexp
log
log10
log10f
log1p
log1pf
log2
log2f
logf
memcmp
memcpy
memmove
memset
nextafter
nextafterf
pow
powf
round
roundf
sin
sinf
sinh
sinhf
strlen
tan
tanf
tanh
tanhf
trunc
truncf
retep998 commented 9 years ago

As of https://github.com/rust-lang/rust/pull/26265 we are now down to just math functions and memcmp, memcpy, memset, memmove, and strlen, along with the entry point.

tkelman commented 9 years ago

Nice work (and making rust independent of the msvcrt is a great goal). I got a little busy to run all of Julia's tests against msvcr120's libm, I had to disable fma support and add underscores to hypot and the Bessel functions (which I guess Rust doesn't link to). Still had quite a few unknown function ffi failures that I didn't finish debugging.

Pardon my ignorance of how Rust is using the math functions, but how feasible would it be to make linking to them optional? I'm going to guess that only a subset of Rust libraries are using them at this point?

I also realized that openlibm hasn't been set up to be entirely standalone, it also links to a C runtime right now. The few places it's doing any allocation, string operations, or output could possibly be made optional with some patching.

nagisa commented 9 years ago

Pardon my ignorance of how Rust is using the math functions, but how feasible would it be to make linking to them optional?

Not feasible, unless we can make inherent functions on f64/f32 optional as well, which we can’t. If nothing in the end executable uses them, though, using LTO could make it “optional”.

EDIT: also see https://github.com/rust-lang/rust/issues/26350

alexcrichton commented 9 years ago

@nagisa, @tkelman

On Unix we rely on -ffunction-sections + --gc-sections to omit dependencies on functions like those found in libm, and we may be able to find an equivalent on windows perhaps? (cc https://github.com/rust-lang/rust/issues/13846)

retep998 commented 9 years ago

We should already be using /OPT:REF,ICF on Windows which should do dead code stripping. The only problem is that we export nearly every single symbol in the executable, so link.exe doesn't strip those symbols.

nagisa commented 9 years ago

Trying to rustc on XP will eventually get harder.

mstewartgallus commented 9 years ago

Why are we supporting XP anyway? Are we targeting still supported embedded instances or maybe old Chinese cracked copies of XP?

Gankra commented 9 years ago

@sstewartgallus The big soundbite on this issue is "Windows XP has a bigger userbase than all of Linux for Firefox".

DemiMarie commented 9 years ago

memcpy, memmove, memcmp, strlen, and memset are all provided by ntdll.dll, and have been since Windows 3.51!

alexchandel commented 9 years ago

memcpy, memmove, memcmp, strlen, and memset are all provided by ntdll.dll, and have been since Windows 3.51!

@retep998 Can we switch to these? Also, is it feasible to use your entry point for Rust executables that don't statically link C libraries?

Assuming we do, it would seem we only use msvcrt for math and the entry point when statically linking C libraries. So OpenLibm sounds pretty good.

I'm not too worried about "infuriating differences in numerical code", since openlibm's implementation obeys IEEE 754. It's even preferable that openlibm doesn't share bugs with glibc's/libSystem's/msvcrt's math implementation, because reproducibility (at least in numerical research) is built on compliance to the floating-point standard, not a particular implementation. That's why both MSVC and optimizations that break IEEE 754 are often avoided. Beyond the lack of bugs, the difference would just be speed.

@sstewartgallus WinXP still has a significant userbase in the engineering industry, and it's valuable to target (though I've never had to host rustc on XP).