I recently noticed problems with building with clang in msvc mode when using my distro clang. It seems specific to its configuration, I think that it doesn't affect default clang.
One problem is that --rtlib=libgcc is specified in /etc/clang, which causes clang to produce: ``` clang-15: error: unsupported runtime library 'libgcc' for platform 'MSVC' ``` I think it's a clang bug here: https://github.com/llvm/llvm-project/blob/main/clang/lib/Driver/ToolChains/M... It should probably check for -nodefaultlibs in addition to -nostdlib. It's easy to handle on Wine side by using -nostdlib.
The other problem is that clang for some reason doesn't define `_MSV_VER` by default. I found that it's defined only if I pass `-fuse-ld=lld` to the command line. That's weird because it shouldn't affect compilation step, it should matter only for linking and that's when we pass it. I didn't track it down in clang code itself, but if we can't depend on clang providing it by default, we need to make sure to pass `-fms-compatibility-version` ourselves. The exact version doesn't really matter, we mostly need to make sure that it's defined at all so that our #ifdefs work correctly.
From: Jacek Caban jacek@codeweavers.com
-nostartfiles is not enough if clang is configured with explicit default rtlib. --- aclocal.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/aclocal.m4 b/aclocal.m4 index 080f782b698..f2412497067 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -208,7 +208,7 @@ AC_DEFUN([WINE_TRY_PE_CFLAGS], AC_CACHE_CHECK([whether $CC supports $1], ac_var, [ac_wine_try_cflags_saved=$CFLAGS ac_wine_try_cflags_saved_exeext=$ac_exeext -CFLAGS="$CFLAGS -nostartfiles -nodefaultlibs $1" +CFLAGS="$CFLAGS -nostdlib -nodefaultlibs $1" ac_exeext=".exe" AC_LINK_IFELSE([AC_LANG_SOURCE([[int __cdecl mainCRTStartup(void) { return 0; }]])], [AS_VAR_SET(ac_var,yes)], [AS_VAR_SET(ac_var,no)])
From: Jacek Caban jacek@codeweavers.com
-nostartfiles is not enough if clang is configured with explicit default rtlib. --- tools/winegcc/winegcc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tools/winegcc/winegcc.c b/tools/winegcc/winegcc.c index 71d2b93f0c6..a850cce765f 100644 --- a/tools/winegcc/winegcc.c +++ b/tools/winegcc/winegcc.c @@ -482,7 +482,8 @@ static struct strarray get_link_args( struct options *opts, const char *output_n } if (opts->unicode_app) strarray_add( &flags, "-municode" ); if (opts->nodefaultlibs || opts->use_msvcrt) strarray_add( &flags, "-nodefaultlibs" ); - if (opts->nostartfiles || opts->use_msvcrt) strarray_add( &flags, "-nostartfiles" ); + if (opts->nostartfiles) strarray_add( &flags, "-nostartfiles" ); + if (opts->use_msvcrt) strarray_add( &flags, "-nostdlib" ); if (opts->image_base) strarray_add( &flags, strmake("-Wl,-base:%s", opts->image_base )); if (opts->subsystem) strarray_add( &flags, strmake("-Wl,-subsystem:%s", opts->subsystem ));
From: Jacek Caban jacek@codeweavers.com
The exact version doesn't really matter, but we need to make sure that _MSC_VER is defined at all. Clang not always adds default version, so make sure it's specified. --- configure.ac | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/configure.ac b/configure.ac index 7313fde9a4e..eba345d2c38 100644 --- a/configure.ac +++ b/configure.ac @@ -941,12 +941,13 @@ do esac fi case $llvm_target in - *windows) llvm_cflags="$llvm_cflags -Wl,-subsystem:console" ;; + *windows) llvm_cflags="$llvm_cflags -Wl,-subsystem:console" + llvm_extra_cflags=" -fms-compatibility-version=19" ;; esac WINE_TRY_PE_CFLAGS([-target $llvm_target $llvm_cflags], [target=$llvm_target AS_VAR_SET([${wine_arch}_DELAYLOADFLAG],["-Wl,-delayload,"]) - AS_VAR_APPEND([${wine_arch}_EXTRACFLAGS],[" -target $target"]) + AS_VAR_APPEND([${wine_arch}_EXTRACFLAGS],[" -target $target$llvm_extra_cflags"]) CFLAGS="$CFLAGS -target $target $llvm_cflags"]) AS_VAR_SET([${wine_arch}_TARGET],[$target])]) AS_VAR_POPDEF([wine_cv_crosscc])
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=126791
Your paranoid android.
=== debian11 (32 bit report) ===
d3d9: d3d9ex.c:3115: Test failed: Expected message 0x46 for window 0x1, but didn't receive it, i=1.
ntoskrnl.exe: driver_pnp.c:737: Test failed: expected IRP_MN_REMOVE_DEVICE
The other problem is that clang for some reason doesn't define `_MSC_VER` by default. I found that it's defined only if I pass `-fuse-ld=lld` to the command line. That's weird because it shouldn't affect compilation step, it should matter only for linking and that's when we pass it. I didn't track it down in clang code itself, but if we can't depend on clang providing it by default, we need to make sure to pass `-fms-compatibility-version` ourselves. The exact version doesn't really matter, we mostly need to make sure that it's defined at all so that our #ifdefs work correctly.
This sounds extremely surprising - and I really don't see how `-fuse-ld=lld` could affect this. `_MSC_VER` is pretty much the most basic define for the MSVC target mode... Although I guess that if there's some heuristic for figuring out _what_ to set the define to, that can fail in some cases - maybe? The fix, setting `-fms-compatibility-version=19` is probably quite harmless in any case, but I'd be very much interested in figuring out what really is going on here.
How do I reproduce this situation?
On Fri Nov 25 16:57:37 2022 +0000, Martin Storsjö wrote:
The other problem is that clang for some reason doesn't define
`_MSC_VER` by default. I found that it's defined only if I pass `-fuse-ld=lld` to the command line. That's weird because it shouldn't affect compilation step, it should matter only for linking and that's when we pass it. I didn't track it down in clang code itself, but if we can't depend on clang providing it by default, we need to make sure to pass `-fms-compatibility-version` ourselves. The exact version doesn't really matter, we mostly need to make sure that it's defined at all so that our #ifdefs work correctly. This sounds extremely surprising - and I really don't see how `-fuse-ld=lld` could affect this. `_MSC_VER` is pretty much the most basic define for the MSVC target mode... Although I guess that if there's some heuristic for figuring out _what_ to set the define to, that can fail in some cases - maybe? The fix, setting `-fms-compatibility-version=19` is probably quite harmless in any case, but I'd be very much interested in figuring out what really is going on here. How do I reproduce this situation?
I tested it with:
``` $ echo _MSC_VER | clang -E -target x86_64-windows -fuse-ld=bfd - # 1 "<stdin>" # 1 "<built-in>" 1 # 1 "<built-in>" 3 # 341 "<built-in>" 3 # 1 "<command line>" 1 # 1 "<built-in>" 2 # 1 "<stdin>" 2 _MSC_VER
$ echo _MSC_VER | clang -E -target x86_64-windows -fuse-ld=lld - # 1 "<stdin>" # 1 "<built-in>" 1 # 1 "<built-in>" 3 # 355 "<built-in>" 3 # 1 "<command line>" 1 # 1 "<built-in>" 2 # 1 "<stdin>" 2 1920 ```
Trying a few more cases, it seems that the problem happens when `-fuse-ld` is bfd*, any other (possibly invalid) values work fine.
On Fri Nov 25 16:58:02 2022 +0000, Jacek Caban wrote:
I tested it with:
$ echo _MSC_VER | clang -E -target x86_64-windows -fuse-ld=bfd - # 1 "<stdin>" # 1 "<built-in>" 1 # 1 "<built-in>" 3 # 341 "<built-in>" 3 # 1 "<command line>" 1 # 1 "<built-in>" 2 # 1 "<stdin>" 2 _MSC_VER $ echo _MSC_VER | clang -E -target x86_64-windows -fuse-ld=lld - # 1 "<stdin>" # 1 "<built-in>" 1 # 1 "<built-in>" 3 # 355 "<built-in>" 3 # 1 "<command line>" 1 # 1 "<built-in>" 2 # 1 "<stdin>" 2 1920
Trying a few more cases, it seems that the problem happens when `-fuse-ld` is bfd*, any other (possibly invalid) values work fine.
Thanks!
This led me to this find: https://github.com/llvm/llvm-project/blob/llvmorg-15.0.0/clang/lib/Driver/Dr...
``` if (Args.getLastArgValue(options::OPT_fuse_ld_EQ) .startswith_insensitive("bfd")) TC = std::make_unique<toolchains::CrossWindowsToolChain >( *this, Target, Args); else TC = std::make_uniquetoolchains::MSVCToolChain(*this, Target, Args); ```
I'm not quite sure what the `CrossWindowsToolChain` is - I think it might be something used for the "windows-itanium" case (which is a third windows target in addition to mingw and msvc - it's a setup kinda inbetween msvc and mingw, used in some proprietary setups).
With that in mind, I guess it's better to override `-fuse-ld=lld` so it actually picks the right MSVC toolchain, rather than trying to coerce `CrossWindowsToolChain` to behave as expected.
On Fri Nov 25 18:10:38 2022 +0000, Martin Storsjö wrote:
Thanks! This led me to this find: https://github.com/llvm/llvm-project/blob/llvmorg-15.0.0/clang/lib/Driver/Dr...
if (Args.getLastArgValue(options::OPT_fuse_ld_EQ) .startswith_insensitive("bfd")) TC = std::make_unique<toolchains::CrossWindowsToolChain >( *this, Target, Args); else TC = std::make_unique<toolchains::MSVCToolChain>(*this, Target, Args);
I'm not quite sure what the `CrossWindowsToolChain` is - I think it might be something used for the "windows-itanium" case (which is a third windows target in addition to mingw and msvc - it's a setup kinda inbetween msvc and mingw, used in some proprietary setups). With that in mind, I guess it's better to override `-fuse-ld=lld` so it actually picks the right MSVC toolchain, rather than trying to coerce `CrossWindowsToolChain` to behave as expected.
This is, like the case of `-rtlib` a problem with default parameters that are meant for the default target only, but which end up applying to all cross targets…
On Fri Nov 25 18:19:05 2022 +0000, Martin Storsjö wrote:
This is, like the case of `-rtlib` a problem with default parameters that are meant for the default target only, but which end up applying to all cross targets…
Interesting findings, thanks. I didn't know that such targets even exist... And I agree, it's better to always pass `-fuse-ld` in that case.