简体   繁体   中英

Cross-compiling from Linux to Windows with Clang

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

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM