13

I am trying to cross-compile C applications from Linux (64 bit) to Windows (64 bit) using Clang. I read the page on cross-compilation, which wasn't too helpful.

As a simple test, I have the following code in test.c:

#include <stdio.h>

int main()
{
        puts("hello world");
        return 0;
}

My best guess so far is clang -o test -target x86_64-win64-?ABI? test.c. However, I have no idea what ABI Windows 64 bit uses. When I run clang with the target triple x86_64-win64-abcdefg, it seems to compile fine--that is, it finishes without error and results in something that is a somewhat-valid binary. That doesn't make any sense, considering abcdefg is definitely not a valid ABI. The resulting binary is far too big for such a small program, and Windows seems to think it's a 16-bit program (???). Disassembling it reveals references to "linux" and "gnu", so it would seem Clang is not even trying to compile for Windows.

Targeting win32 (x86_64-win32-???ABI???) results in the following error message:

test.c:1:10: fatal error: 'stdio.h' file not found
#include <stdio.h>
         ^
1 error generated.

This error, if I'm not mistaken, is the result of it not knowing where to look for system files. I assume Clang does store Windows header files somewhere, since it claims to be able to cross-compile; but where? If it doesn't, is there somewhere I can download them?

Is there a list of all the architectures, systems, and ABI's Clang supports somewhere? The list on the cross-compilation page is not comprehensive.

The page also suggests using -mcpu=..., but a warning suggests that is outdated. Instead, as the warning recommends, I tried -mtune=x86_64. This seems to have no effect. Is this even necessary, considering the architecture is specified in the target triple?

I have seen some literature that suggests I need lld, LLVM's experimental linker. Is this the case? I have had some issues compiling lld, and would like to avoid it if possible.

Rogue
  • 636
  • 8
  • 22
  • 1
    "_If you choose a parameter that Clang doesn’t know, like blerg, it’ll ignore and assume unknown, which is not always desired, so be careful._" And that's probably what happened when you chose `abcdefg`. – DYZ Feb 05 '17 at 06:48
  • @DYZ ah okay, thank you. If only there were a list of valid parameters or some message to indicate that it doesn't know! – Rogue Feb 05 '17 at 15:43
  • not an answer, but I found https://github.com/tpoechtrager/wclang -- haven't tried it yet. "Cross compile source code easily for Windows with clang on Linux/Unix" – Jeff Epler Feb 22 '17 at 19:32
  • wclang works for me, invoking clang-3.5 to build object files, and binutils ld to link. Trying to use lld, via -fuse-ld=lld, gets me an error: "Windows targets are not supported on the ELF frontend: i386pep". This is too bad, because what I wish I had was a faster cross-linker than binutils ld, which takes >10 minutes linking the main executable at $DAY_JOB. – Jeff Epler Feb 22 '17 at 19:43
  • @JeffEpler Make sure you are specifying that you want to cross-link for Windows when you run lld. You may not be specifying the correct ABI? – Rogue Feb 22 '17 at 23:00

3 Answers3

4

Your best option to develop Window binaries using clang is Mingw-w64, as you need more than just a compiler to compile for another system. You also need a linker for that system as well as libraries to link against, so basically you need an SDK for the platform you are targeting and Mingw-w64 comes with everything you require.

https://www.mingw-w64.org/downloads/

You can install it on a Linux system or macOS system and cross compile or you can install it directly on a Windows system and compile natively, without the requirement to have anything like the SDK of Visual Studio (VS). Actually the same code should compile with any installation of Mingw-w64 regardless of the system you are using for building it.

Please note that Mingw does not give you a POSIX API on Windows. You will have the standard C/C++ API available that every platform must support and for everything else, you have to use native Windows API just like you'd have to when developing software with VS. As not everyone may understand what I've just said, here's an example:

You can use fopen() to open a file as that is a standard C API function that every platform supports. But you cannot use open() to open a file, as that is a POSIX function defined in unistd.h and this header doesn't natively exist on Windows (not unless you have installed a POSIX subsystem which is not even available for all Windows version).

In Windows you have windows.h and instead of fopen() you can use the function CreateFile(), which despite its name does not always create a file, it can also open existing ones for reading, and then you will get a HANDLE that you need to pass to CloseHandle() once you are done with it (which is like close() on UNIX systems).

If you would like to get a POSIX-like API on Windows with no requirement of users having to install one, so you can share the same code between your Windows and Linux projects, appropriate wrappers do exist for that but that is not related to the compiler or SDK you are using. These are just Windows libraries you are liking against and that implement some fraction of the POSIX API on top of the Windows API; which sometimes comes with caveats. It's the opposite of Wine which implements most of the Windows API on top of POSIX and other native system APIs.

So you see, what makes porting C/C++ code hard is not the language itself but the libraries that act as a layer between your code and the system below it, as they differ from system to system, even between POSIX or POSIX-like systems. There are fundamental differences between Linux, FreeBSD, and macOS, despite the fact that they share a lot of the same API, too. And if you want to test your Windows binaries after the build, you either need a real Windows environment to do so or at least an emulated one like Wine does provide.

Mecki
  • 125,244
  • 33
  • 244
  • 253
  • I prefer native compilation because I have to sign the outputs. After all, if you have to sign natively, then you might as well compile natively too. – Jeff Holt Nov 29 '21 at 15:40
  • @JeffHolt Mingw is probably not the best option if you want to develop professional code in the first place but if you just code as a hobby and want to create some simple C binaries for Windows, it's good enough. And it's supported by CMake, so if you have your code working in Mingw and use CMake, you can just instruct it to create a VS project for you on the fly which you can then use to make the final distribution build in VS easily. – Mecki Nov 29 '21 at 16:06
1

The question is a bit old, but I hope this answer will help someone else.

Hypothetically, what you need is a cross-compiler that runs on Linux but builds a Windows executable using Visual Studio provided headers and libraries. The link you provided redirects you to this page which tells you how to generate one. Hypothetically, you can build LLVM version 7 like this:

cmake -G Ninja -H. -B../_bin \
    -DLLVM_ENABLE_PROJECTS="llvm;clang;lld" \
    -DCMAKE_INSTALL_PREFIX=/opt/llvm-win32 \
    -DLLVM_TARGETS_TO_BUILD=X86 \
    -DCMAKE_BUILD_TYPE=Release \
    -DCLANG_TABLEGEN=/mnt/data/projects/llvm-org/dl/clang-tblgen-7 \
    -DLLVM_TABLEGEN=/mnt/data/projects/llvm-org/dl/llvm-tblgen-7 \
    -DLLVM_DEFAULT_TARGET_TRIPLE=i686-windows-msvc \
    -DLLVM_TARGET_ARCH=i686 \
    -DLLVM_TARGETS_TO_BUILD=X86 \

You may have to tweak clang/lib/Driver/ToolChains/MSVC.cpp source file so the shell separator is changed from ; to : because on Unix shells, colon is used for separating multiple paths in variable.

One more thing. LLD defaults to Windows 7 in COFF format. You may either statically or creatively modify Configuration::MajorOSVersion and Configuration::MinorOSVersion in lld/COFF/Config.h if you like to compile for an older Windows. Check Windows Versions for the correct version you like to support. Perhaps, an environment variable OS_VER could carry this information.

But even then, that's just the first step. You still would need Visual Studio installed somewhere. This is where LLVM/clang will look for even basic headers like stdio.h or libcmt.lib. Hypothetically, if you have a Visual Studio 6 or later installed somewhere you could use that. Just make sure to rename all the files and directories to small-case (again, because Linux).

After that, triggering the compiler is simple. Something like:

export VCINSTALLDIR=/mnt/data/projects/VC98
__inc="${VCINSTALLDIR}/include"
__inc="${__inc}:${VCINSTALLDIR}/atl/include"
__inc="${__inc}:${VCINSTALLDIR}/mfc/include"
export INCLUDE=${__inc}

__lib="${VCINSTALLDIR}/lib"
__lib="${__lib}:${VCINSTALLDIR}/mfc/lib"
export LIB="${__lib}"

export OS_VER="4.10" # Windows 98

exec /opt/win32/bin/clang \
    -fms-compatibility \
    -fms-extensions \
    -fmsc-version=1200 \
    -fuse-ld=lld \
    $*

in a shell script will do. The INCLUDE and LIB variables are honored by LLVM. Save it as something like i686-windows-msvc6-cc and you are good to go. Hypothetically, you can now invoke the compiler on your test source like:

i686-windows-msvc6-cc -o test.exe test.c

Then, boot back into Windows or load your VM and execute test.exe.

Note this is all hypothetical because MSVC terms and conditions don't allow you to fool around with their SDK like this. This could likely also be the reason why no one distributes a cross-compiler like this either. You can of course, try this hypothetically for educational purposes. Your best options is to use the one generated with MinGW instead like Mecki suggested.

Unmanned Player
  • 1,109
  • 9
  • 30
-7

I've installed mobaxterm on my windows 10 machine. There is a free version. It provides an xserver. It contains an installation of cygwin and you can start a local terminal. you just type: apt-get install clang and clang is ready to compile and finds stdio.h without complaining.

But if you intend to run the resulting executable not on mobaxterm/cygwin but inside windows itself, you need to compile with mingwin instead.

renataflow
  • 55
  • 6
  • 1
    This doesn't answer the question. The question is about building from Linux, not from Windows. – lvella Sep 26 '20 at 18:19