Simple cross-compilation with --target fails

Hi, I am trying to cross compile a project on Linux for Windows using clang. I am having a hard time with it so far, with two main problems:

  • how to create the proper sysroot
  • how to make clang link

I’ll start with the second problem first. I created a very simple test.cpp:

int main() {
    return 0;
}

Which I then compile with:

clang++ -v --target=x86_64-pc-windows-msvc test.cpp

which results in:

Debian clang version 15.0.2-1
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: /usr/bin
 "/usr/lib/llvm-15/bin/clang" -cc1 -triple x86_64-pc-windows-msvc19.20.0 -emit-obj -mrelax-all -mincremental-linker-compatible --mrelax-relocations -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name test.cpp -mrelocation-model pic -pic-level 2 -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -mllvm -treat-scalable-fixed-error-as-warning -v -fcoverage-compilation-dir=/media/data/sysroot-win -resource-dir /usr/lib/llvm-15/lib/clang/15.0.2 -internal-isystem /usr/lib/llvm-15/lib/clang/15.0.2/include -fdeprecated-macro -fdebug-compilation-dir=/media/data/sysroot-win -ferror-limit 19 -fno-use-cxa-atexit -fms-extensions -fms-compatibility -fms-compatibility-version=19.20 -std=c++14 -fdelayed-template-parsing -fcxx-exceptions -fexceptions -fcolor-diagnostics -faddrsig -o /tmp/test-36b16b.o -x c++ test.cpp
clang -cc1 version 15.0.2 based upon LLVM 15.0.2 default target x86_64-pc-linux-gnu
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/llvm-15/lib/clang/15.0.2/include
End of search list.
 "/usr/bin/ld" -out:a.exe -defaultlib:libcmt -defaultlib:oldnames -libpath:lib/amd64 -libpath:atlmfc/lib/amd64 -nologo /tmp/test-36b16b.o
/usr/bin/ld: Error: unable to disambiguate: -defaultlib:libcmt (did you mean --defaultlib:libcmt ?)
clang: error: linker command failed with exit code 1 (use -v to see invocation)

It works fine without the target argument. Any idea of how to do such a simple cross-compilation with clang?

GNU LD doesn’t know about cross-compile. Add -fuse-ld=lld to use lld instead that know about cross-compiles.

But there is more to this. If you want to cross compile for windows you probably want something like llvm-mingw GitHub - mstorsjo/llvm-mingw: An LLVM/Clang/LLD based mingw-w64 toolchain

Thanks a lot! That llvm-mingw sounds great. I will play with it and report back once I know more :slight_smile:

It could be that you are interested in cross-compiling to Windows using the Microsoft runtimes and headers rather than replacements for them as you are using the triple x86_64-unknown-windows-msvc rather than x86_64-unknown-windows-gnu which is the GNU environment (MinGW) which has its own SDK and runtime. In such a case, you would need lld as bfd does not provide the necessary support in the linker for working with the import libraries and some of the necessary metadata emission IIRC. You will need to figure out how to get a copy of the Windows SDK and some of the MSVC toolset as the VisualC runtime is actually part of the MSVC toolset rather than the SDK.

thank you, you are correct. I am interested in using the Microsoft runtimes/headers, and I got a copy of the original on the Linux machine. However, here are my results. In the directory with the MS sysroot, there is the test.cpp, a folder called /llvm-mingw contains its latest release.

This works:

/llvm-mingw/bin/clang++ --target=x86_64-pc-windows-msvc -fuse-ld=lld -I include/ -I include/ucrt/ -L lib/x64 -L lib/ucrt/x64/ -L lib/um/x64/ test.cpp

Using mingw32 generates an error:

sysroot-win$ /llvm-mingw/bin/x86_64-w64-mingw32-g++ --target=x86_64-pc-windows-msvc -fuse-ld=lld -I include/ -I include/ucrt/ test.cpp 
lld-link: warning: ignoring unknown argument '--as-needed'
lld-link: warning: ignoring unknown argument '-lunwind'
lld-link: warning: ignoring unknown argument '--no-as-needed'
lld-link: error: could not open '/llvm-mingw/lib/clang/15.0.0/lib/windows/clang_rt.builtins-x86_64.lib': No such file or directory
clang-15: error: linker command failed with exit code 1 (use -v to see invocation)

I saw that the file /llvm-mingw/lib/clang/15.0.0/lib/windows/clang_rt.builtins-x86_64.a exists, so I tried calling it to emit a static lib, but that completely crashes the compiler:

sysroot-win$ ../llvm-mingw/bin/x86_64-w64-mingw32-g++ --target=x86_64-pc-windows-msvc -fuse-ld=lld -I include/ -I include/ucrt/ --emit-static-lib test.cpp
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.      Program arguments: /llvm-mingw/bin/clang --start-no-unused-arguments --driver-mode=g++ -target x86_64-w64-mingw32 -rtlib=compiler-rt -unwindlib=libunwind -stdlib=libc++ -fuse-ld=lld --end-no-unused-arguments --target=x86_64-pc-windows-msvc -fuse-ld=lld -I include/ -I include/ucrt/ --emit-static-lib test.cpp
1.      Compilation construction
2.      Building compilation jobs
3.      Building compilation jobs
 #0 0x00007f75af92d6ef (/llvm-mingw/bin/../lib/libLLVM-15.so+0x92d6ef)
 #1 0x00007f75af92af8c (/llvm-mingw/bin/../lib/libLLVM-15.so+0x92af8c)
 #2 0x00007f75aec20aa0 (/lib/x86_64-linux-gnu/libc.so.6+0x3daa0)
 #3 0x00007f75b5f0ddcc clang::driver::Driver::BuildJobsForActionNoCache(clang::driver::Compilation&, clang::driver::Action const*, clang::driver::ToolChain const*, llvm::StringRef, bool, bool, char const*, std::map<std::pair<clang::driver::Action const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>, llvm::SmallVector<clang::driver::InputInfo, 4u>, std::less<std::pair<clang::driver::Action const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>, std::allocator<std::pair<std::pair<clang::driver::Action const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>> const, llvm::SmallVector<clang::driver::InputInfo, 4u>>>>&, clang::driver::Action::OffloadKind) const (/llvm-mingw/bin/../lib/libclang-cpp.so.15+0x230ddcc)
 #4 0x00007f75b5f0f72a clang::driver::Driver::BuildJobsForAction(clang::driver::Compilation&, clang::driver::Action const*, clang::driver::ToolChain const*, llvm::StringRef, bool, bool, char const*, std::map<std::pair<clang::driver::Action const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>, llvm::SmallVector<clang::driver::InputInfo, 4u>, std::less<std::pair<clang::driver::Action const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>, std::allocator<std::pair<std::pair<clang::driver::Action const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>> const, llvm::SmallVector<clang::driver::InputInfo, 4u>>>>&, clang::driver::Action::OffloadKind) const (/llvm-mingw/bin/../lib/libclang-cpp.so.15+0x230f72a)
 #5 0x00007f75b5f1000e clang::driver::Driver::BuildJobs(clang::driver::Compilation&) const (/llvm-mingw/bin/../lib/libclang-cpp.so.15+0x231000e)
 #6 0x00007f75b5f1c930 clang::driver::Driver::BuildCompilation(llvm::ArrayRef<char const*>) (/llvm-mingw/bin/../lib/libclang-cpp.so.15+0x231c930)
 #7 0x0000559db241032e clang_main(int, char**) (/llvm-mingw/bin/clang+0x1032e)
 #8 0x00007f75aec0c20a (/lib/x86_64-linux-gnu/libc.so.6+0x2920a)
 #9 0x00007f75aec0c2bc __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x292bc)
#10 0x0000559db240ca6a _start (/llvm-mingw/bin/clang+0xca6a)
Segmentation fault

So, first question: can I somehow fix the error with mingw not finding the .lib?

Second question: I had to link/rename some libraries, like uuid.lib -> Uuid.Lib because linux is case sensitive and was looking for the non-capital-letter version of the lib. Do I have to do that with all the libraries that are not found? Or is there another way?

Third question: I am currently trying to compile Qt on Linux for Windows using llvm-mingw. Lots of libraries are not being found, so I am wondering if nobody has done that before or if I am just missing a huge piece that automates all the problems I have?

You’re mixing files and use cases quite wildly here. MSVC-style and mingw style environments use different lib naming conventions. You generally shouldn’t be mixing files from such environments with each other unless you really know what you’re getting yourself into.

Bringing up a whole cross compilation environment from scratch is quite a bit of work; if you want to know how to do that for a mingw environment, have a look at the scripts in the llvm-mingw repo, starting e.g. at the toplevel script: llvm-mingw/build-all.sh at 5ad257fed02b5c3086eabadb8b482a62254321e6 · mstorsjo/llvm-mingw · GitHub

If you want to build with MSVC headers and libs, then ignore all of llvm-mingw - don’t mix it in here, it will only confuse you. @tobiashieta’s comment was more of a suggestion for a prepackaged ready solution.

The issue with the MSVC headers and libs is that they aren’t redistributable, so others can’t build a premade package with everything in place and redistribute it publicly.

This is indeed an issue with the WinSDK.

One way to do it, is to run some scripts over the SDK to lowercase all files (and their internal include directives) - the install scripts in GitHub - mstorsjo/msvc-wine: Scripts for setting up and running MSVC in Wine on Linux does this.

Alternatively, when building with Clang, it’s possible to use Clang’s VFS overlay feature to handle the case insensitivity here. See e.g. llvm-project/WinMsvc.cmake at 15eaefa5fe3608b03f1abefc31129efaf9eab88e · llvm/llvm-project · GitHub for how this is done in the cmake files for building LLVM itself with such a setup.

For linking with lld, I’m not entirely sure if there are the same options available for using a case insensitivity VFS overlay. It looks like the same WinMsvc.cmake above creates a directory of symlinks to do the name mappings: llvm-project/WinMsvc.cmake at 15eaefa5fe3608b03f1abefc31129efaf9eab88e · llvm/llvm-project · GitHub

Alternatively, if you’ve got lowercased files as if installed with msvc-wine above, it’s enough to just add the relevant directories to $INCLUDE and $LIB. I pushed a branch on msvc-wine that includes a script that does this for you: msvc-wine/msvcenv-native.sh at env-native · mstorsjo/msvc-wine · GitHub So with an install from msvc-wine, with an environment set up with the msvcenv-native.sh script, it should be enough to just call clang-cl or clang --target x86_64-windows-msvc and lld-link with any clang/lld build.

It’s quite possible to build Qt for Windows on Linux using llvm-mingw - but it seems like you have somehow intermingled the install of the prepackaged llvm-mingw with your own setup. With llvm-mingw everything you need should be available from the get go without needing to meddle with anything in the toolchain package at all.

If you’ve extracted a release package of llvm-mingw and have added it to your $PATH, then you can build Qt 5.x like this:

./configure -xplatform win32-clang-g++ -device-option CROSS_COMPILE=x86_64-w64-mingw32- -release -opensource -confirm-license -nomake examples -silent -opengl desktop
make -j$(nproc)

For Qt 6.x, the procedure is a bit more complex, as you need to build the native Qt tools manually first:

mkdir build-native
cd build-native

cmake \
    -G Ninja \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX=$(pwd) \
    -DQT_BUILD_EXAMPLES=OFF \
    -DQT_BUILD_TESTS=OFF \
    -DINPUT_opengl=no \
    ../qtbase
ninja host_tools
QT_HOST_PATH="$(pwd)"

cd ..
mkdir build-cross
cd build-cross

cmake \
    -G Ninja \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX=$(pwd) \
    -DCMAKE_CROSSCOMPILING=TRUE \
    -DCMAKE_SYSTEM_NAME=Windows \
    -DCMAKE_C_COMPILER=x86_64-w64-mingw32-clang \
    -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-clang++ \
    -DCMAKE_RC_COMPILER=x86_64-w64-mingw32-windres \
    -DQT_QMAKE_TARGET_MKSPEC=win32-clang-g++ \
    -DQT_HOST_PATH=$QT_HOST_PATH \
    -DQT_BUILD_EXAMPLES=OFF \
    -DQT_BUILD_TESTS=OFF \
    ../qtbase
ninja

I haven’t tried cross compiling Qt with clang in MSVC mode so I can’t help with that.

1 Like

Thank you very much, this is a great answer! You confirmed what I was suspecting when the msvc headers didn’t work.

The issue with the MSVC headers and libs is that they aren’t redistributable, so others can’t build a premade package with everything in place and redistribute it publicly.

Yeah, I don’t care about redistributing it, I only want the resulting exe for the CI to run the unit tests.

I didn’t know about your msvc-wine project. I will try that one before continuing with the cross compilation – it sounds too promising and maybe that is the faster road to success :smiley:

And last, I am trying to build Qt 6.2.4. I already managed to build it successfully with Debian mingw-w64, but then I discovered that mingw-w64 is gcc 10.3, and that one is too old to compile my project :frowning: So that is why I had to move to clang.