【问题标题】:Cross-compiling from Linux to Windows with Clang使用 Clang 从 Linux 交叉编译到 Windows
【发布时间】:2017-06-22 04:58:09
【问题描述】:

我正在尝试使用 Clang 将 C 应用程序从 Linux(64 位)交叉编译到 Windows(64 位)。我读了page on cross-compilation,这并没有太大帮助。

作为一个简单的测试,我在test.c中有如下代码:

#include <stdio.h>

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

到目前为止我最好的猜测是clang -o test -target x86_64-win64-?ABI? test.c。但是,我不知道 ABI Windows 64 位使用什么。当我使用目标三元组x86_64-win64-abcdefg 运行clang 时,它似乎编译得很好——也就是说,它没有错误地完成,并产生了一个有点有效的二进制文件。这没有任何意义,考虑到abcdefg 绝对不是有效的 ABI。生成的二进制文件对于这么小的程序来说太大了,Windows 似乎认为它是一个 16 位程序 (???)。反汇编它会发现对“linux”和“gnu”的引用,所以看起来 Clang 甚至没有尝试为 Windows 编译。

定位 win32 (x86_64-win32-???ABI???) 会导致以下错误消息:

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

如果我没记错的话,这个错误是因为它不知道在哪里寻找系统文件。我假设 Clang 确实将 Windows 头文件存储在某处,因为它声称能够交叉编译;但是哪里?如果没有,有什么地方可以下载吗?

是否有所有架构、系统和 ABI 的 Clang 支持的列表?交叉编译页面上的列表并不全面。

该页面还建议使用-mcpu=...,但警告表明该名称已过时。相反,正如警告所建议的,我尝试了-mtune=x86_64。这似乎没有任何效果。考虑到目标三元组中指定了架构,这甚至是必要的吗?

我看到一些文献表明我需要lld,这是 LLVM 的实验性链接器。是这样吗?我在编译 lld 时遇到了一些问题,如果可能的话,我想避免它。

【问题讨论】:

  • "如果您选择 Clang 不知道的参数,例如 blerg,它将忽略并假定未知,这并不总是需要,所以要小心。"这可能就是您选择 abcdefg 时发生的情况。
  • @DYZ 好的,谢谢。如果只有一个有效参数列表或一些消息表明它不知道!
  • 不是答案,但我找到了github.com/tpoechtrager/wclang——还没有尝试过。 "在 Linux/Unix 上使用 clang 轻松交叉编译 Windows 源代码"
  • wclang 为我工作,调用 clang-3.5 来构建目标文件,并调用 binutils ld 来链接。尝试通过 -fuse-ld=lld 使用 lld 时出现错误:“ELF 前端不支持 Windows 目标:i386pep”。这太糟糕了,因为我希望我有一个比 binutils ld 更快的交联器,在 $DAY_JOB 链接主可执行文件需要超过 10 分钟。
  • @JeffEpler 确保在运行 lld 时指定要为 Windows 交叉链接。您可能没有指定正确的 ABI?

标签: c windows clang llvm-clang


【解决方案1】:

使用 clang 开发 Window 二进制文件的最佳选择是 Mingw-w64,因为您需要的不仅仅是编译器来编译另一个系统。您还需要该系统的链接器以及要链接的库,因此基本上您需要针对您所针对的平台的 SDK,而 Mingw-w64 提供了您所需的一切。

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

你可以安装在 Linux 系统或 macOS 系统上并交叉编译,也可以直接安装在 Windows 系统上并进行原生编译,不需要像 Visual Studio (VS) 的 SDK 之类的东西。实际上,无论您使用什么系统构建它,任何安装的 Mingw-w64 都应该编译相同的代码。

请注意,Mingw 不会在 Windows 上为您提供 POSIX API。您将拥有每个平台都必须支持的标准 C/C++ API,对于其他一切,您必须使用本机 Windows API,就像使用 VS 开发软件时一样。因为不是每个人都可能理解我刚才所说的,这里有一个例子:

您可以使用fopen() 打开文件,因为这是每个平台都支持的标准 C API 函数。但是您不能使用 open() 打开文件,因为这是在 unistd.h 中定义的 POSIX 函数,并且此标头本身并不存在于 Windows 上(除非您安装了甚至不适用于所有 Windows 的 POSIX 子系统版本)。

在 Windows 中你有windows.h 而不是fopen() 你可以使用函数CreateFile(),尽管它的名字并不总是创建一个文件,它也可以打开现有的文件进行阅读,然后你会得到一个HANDLE,你需要在完成后将其传递给CloseHandle()(类似于UNIX 系统上的close())。

如果您想在 Windows 上获得类似 POSIX 的 API,而无需用户安装 API,这样您就可以在 Windows 和 Linux 项目之间共享相同的代码,那么确实存在相应的包装器,但事实并非如此与您使用的编译器或 SDK 有关。这些只是您喜欢的 Windows 库,它们在 Windows API 之上实现了部分 POSIX API;有时会附带一些警告。它与在 POSIX 和其他本机系统 API 之上实现大部分 Windows API 的 Wine 正好相反。

所以你看,让移植 C/C++ 代码变得困难的不是语言本身,而是作为代码和它下面的系统之间的层的库,因为它们因系统而异,即使在 POSIX 或 POSIX 之间也是如此类系统。 Linux、FreeBSD 和 macOS 之间存在根本差异,尽管它们也共享许多相同的 API。如果您想在构建后测试您的 Windows 二进制文件,您需要一个真正的 Windows 环境来执行此操作,或者至少需要一个像 Wine 提供的模拟环境。

【讨论】:

  • 我更喜欢原生编译,因为我必须对输出进行签名。毕竟,如果你必须原生签名,那么你也可以原生编译。
  • @JeffHolt Mingw 可能不是最好的选择,如果您想首先开发专业代码,但如果您只是将编码作为一种爱好并想为 Windows 创建一些简单的 C 二进制文件,那就足够了.它受 CMake 支持,因此,如果您的代码在 Mingw 中工作并使用 CMake,您可以指示它为您即时创建一个 VS 项目,然后您可以使用该项目轻松地在 VS 中构建最终发行版。
【解决方案2】:

这个问题有点老了,但我希望这个答案对其他人有所帮助。

假设您需要一个在 Linux 上运行但使用 Visual Studio 提供的头文件和库构建 Windows 可执行文件的交叉编译器。您提供的链接会将您重定向到 this 页面,该页面告诉您如何生成一个。假设您可以像这样构建 LLVM 版本 7:

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 \

您可能需要调整 clang/lib/Driver/ToolChains/MSVC.cpp 源文件,以便将 shell 分隔符从 ; 更改为 :,因为在 Unix shell 上,冒号用于分隔变量中的多个路径。

还有一件事。 LLD 默认为 COFF 格式的 Windows 7。如果您想为较旧的 Windows 进行编译,您可以静态或创造性地修改 lld/COFF/Config.h 中的 Configuration::MajorOSVersionConfiguration::MinorOSVersion。检查Windows Versions 以获得您想要支持的正确版本。或许,环境变量OS_VER 可以携带此信息。

但即便如此,这只是第一步。您仍然需要在某处安装 Visual Studio。这是 LLVM/clang 甚至会查找基本标头的地方,例如 stdio.hlibcmt.lib。假设您在某处安装了 Visual Studio 6 或更高版本,您可以使用它。只需确保将所有文件和目录重命名为小写(同样,因为 Linux)。

之后,触发编译器就很简单了。比如:

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 \
    $*

在 shell 脚本中就可以了。 LLVM 支持 INCLUDELIB 变量。将其保存为i686-windows-msvc6-cc 之类的内容,您就可以开始使用了。假设您现在可以在测试源上调用编译器,例如:

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

然后,重新启动到 Windows 或加载您的 VM 并执行 test.exe

请注意,这都是假设性的,因为 MSVC 条款和条件不允许您像这样玩弄他们的 SDK。这也可能是没有人分发这样的交叉编译器的原因。当然,您可以出于教育目的假设性地尝试此操作。你最好的选择是使用 MinGW 生成的,而不是像 Mecki 建议的那样。

【讨论】:

    【解决方案3】:

    我已经在我的 Windows 10 机器上安装了 mobaxterm。有一个免费版本。它提供了一个 xserver。它包含 cygwin 的安装,您可以启动本地终端。您只需输入:apt-get install clang,clang 就可以编译并找到 stdio.h 而不会抱怨。

    但是,如果您打算不在 mobaxterm/cygwin 上而是在 windows 内部运行生成的可执行文件,则需要使用 mingwin 编译。

    【讨论】:

    • 这不能回答问题。问题是关于从 Linux 构建,而不是从 Windows 构建。
    猜你喜欢
    • 1970-01-01
    • 2023-01-27
    • 1970-01-01
    • 2017-02-03
    • 2014-01-16
    • 1970-01-01
    • 2018-01-14
    • 2021-11-20
    • 1970-01-01
    相关资源
    最近更新 更多