如何使用 LLVM 在 Windows 上为 ARM 编译 C++ 程序?

Posted

技术标签:

【中文标题】如何使用 LLVM 在 Windows 上为 ARM 编译 C++ 程序?【英文标题】:How to compile C++ program on Windows for ARM using LLVM? 【发布时间】:2018-11-28 14:00:35 【问题描述】:

目标仅使用 LLVM 在 Windows 上为 ARM 编译 C++ 程序。

为什么 LLVM 因为许可许可。

我开始怀疑我对 LLVM 的理解是否正确。

在主机上做

    使用 clang(前端)生成中间表示。此表示与目标无关。 使用 llc(后端)生成目标汇编代码。 使用 lld-link.exe 生成可执行文件。

然后在目标机器上执行。

主机Windows 10,64位

目标机器用手臂 cortex-a57 驱动 PX

程序

int main(int argc, char* argv[]) 

    int x=41;
    x++;
    return x;

我检查并编译了 LLVM(使用 Visual Studio 2015,Release build,CPU= x64)

我的尝试

clang.exe -target arm -march=armv8-a -mcpu=cortex-a57 -mfloat-abi=hard  -emit-llvm -c -o main.bc  main.cpp
llc.exe -march=arm -mcpu=cortex-a57 -mattr=a57,armv8-a,v8 -meabi=gnu -o main.s main.bc
lld-link.exe /entry:main /machine:arm main.s

错误

lld-link.exe: error: main.s: unknown file type

然后我尝试在 Windows 上做前端步骤,在 arm 机器上做后端。

clang.exe -target arm -march=armv8-a -mcpu=cortex-a57 -mfloat-abi=hard  -emit-llvm -c -o main.bc  main.cpp
llc.exe -march=arm -mcpu=cortex-a57 -mattr=a57,armv8-a,v8 -meabi=gnu -o main.s main.bc
SCP main.s to the arm machine. SSH and
gcc main.s (using gcc as a test. LLVM should do this.)

错误

main.s: Assembler messages:
main.s:2: Error: unknown pseudo-op: `.syntax'
main.s:3: Error: unknown pseudo-op: `.eabi_attribute'
main.s:9: Error: unknown pseudo-op: `.fpu'
main.s:26: Error: junk at end of line, first unrecognized character is `@'
main.s:29: Error: unknown pseudo-op: `.code'
main.s:31: Error: unknown pseudo-op: `.fnstart'
main.s:32: Error: junk at end of line, first unrecognized character is `@'
main.s:34: Error: operand 1 should be an integer register -- `mov r2,#0'
main.s:41: Error: operand 1 should be an integer or stack pointer register -- `add r0,r0,#1'
main.s:45: Error: unknown mnemonic `bx' -- `bx lr'
main.s:48: Error: unknown pseudo-op: `.cantunwind'
main.s:49: Error: unknown pseudo-op: `.fnend'
main.s:50: Error: junk at end of line, first unrecognized character is `@'

所以我尝试只针对 Windows

clang.exe  -emit-llvm -c -o main.bc  main.cpp
llc.exe -march=x86 -c -o main.s main.bc
ld.lld.exe main.s

错误

ld.lld.exe: error: main.s:1: unknown directive: .text

然后,使用 gcc 代替 ld.lld.exe(再次使用 gcc 作为测试。LLVM 应该这样做。)

clang.exe  -emit-llvm -c -o main.bc  main.cpp
llc.exe -march=x86 -c -o main.s main.bc
gcc main.s -o main.exe

这行得通。测试我输入

main.exe
echo Exit Code is %errorlevel%

返回 42

一般问题

在仅使用 LLVM(没有 gcc,没有从 ARM 下载任何内容)的 Windows 下编译针对 arm CPU 的 C++ 程序的步骤是什么?

具体问题

    自编译 LLVM 附带的工具(例如 clang.exe、llc.exe、lld.exe)能否在 Windows 目标 arm 上编译可执行文件?例如,lld 是否仍在开发中? 为什么我尝试在 Windows 下编译和链接,针对 Windows 失败? 在主机上为目标链接时,头文件和库(例如 libstdc++)来自哪里?我想我需要从手臂机器上得到那些?将它们复制到主机并告诉链接器在哪里可以找到它们?对吗?

更新

所以我最初尝试Cross-compilation using Clang

clang.exe --target=arm --sysroot=c:\code\clang\FromCmdLine main.cpp  -v

结果是

clang.exe: error: linker (via gcc) command failed with exit code 1 (use -v to see invocation)

而-v的细节是

 "C:\\llvm\\clang.exe" -cc1 -triple armv4t-- -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name main.cpp -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -target-cpu arm7tdmi -target-feature +soft-float -target-feature +soft-float-abi -target-feature -fp-only-sp -target-feature -d16 -target-feature -vfp2 -target-feature -vfp3 -target-feature -fp16 -target-feature -vfp4 -target-feature -fp-armv8 -target-feature -neon -target-feature -crypto -target-feature +strict-align -target-abi aapcs -msoft-float -mfloat-abi soft -fallow-half-arguments-and-returns -dwarf-column-info -debugger-tuning=gdb -v -resource-dir "c:\\llvm\\clang\\7.0.0" -isysroot "c:\\code" -fdeprecated-macro -fdebug-compilation-dir "c:\\code" -ferror-limit 19 -fmessage-length 293 -fno-signed-char -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -fcolor-diagnostics -o "C:\\Users\\AppData\\Local\\Temp\\main-b17d06.o" -x c++ main.cpp
clang -cc1 version 7.0.0 based upon LLVM 7.0.0svn default target x86_64-pc-win32
ignoring nonexistent directory "c:\code\usr/local/include"
ignoring nonexistent directory "c:\code\usr/include"
#include "..." search starts here:
#include <...> search starts here:
 C:\llvm\clang\7.0.0\include
End of search list.
 "C:\\MinGW\\bin\\gcc.exe" "--sysroot=c:\\code" -v -o a.out "C:\\Users\\AppData\\Local\\Temp\\main-b17d06.o"
Using built-in specs.
COLLECT_GCC=C:\MinGW\bin\gcc.exe
COLLECT_LTO_WRAPPER=c:/mingw/bin/../libexec/gcc/mingw32/6.3.0/lto-wrapper.exe
Target: mingw32
Configured with: ../src/gcc-6.3.0/configure --build=x86_64-pc-linux-gnu --host=mingw32 --target=mingw32 --with-gmp=/mingw --with-mpfr --with-mpc=/mingw --with-isl=/mingw --prefix=/mingw --disable-win32-registry --with-arch=i586 --with-tune=generic --enable-languages=c,c++,objc,obj-c++,fortran,ada --with-pkgversion='MinGW.org GCC-6.3.0-1' --enable-static --enable-shared --enable-threads --with-dwarf2 --disable-sjlj-exceptions --enable-version-specific-runtime-libs --with-libiconv-prefix=/mingw --with-libintl-prefix=/mingw --enable-libstdcxx-debug --enable-libgomp --disable-libvtv --enable-nls
Thread model: win32
gcc version 6.3.0 (MinGW.org GCC-6.3.0-1)
COMPILER_PATH=c:/mingw/bin/../libexec/gcc/mingw32/6.3.0/;c:/mingw/bin/../libexec/gcc/;c:/mingw/bin/../lib/gcc/mingw32/6.3.0/../../../../mingw32/bin/
LIBRARY_PATH=c:/mingw/bin/../lib/gcc/mingw32/6.3.0/;c:/mingw/bin/../lib/gcc/;c:/mingw/bin/../lib/gcc/mingw32/6.3.0/../../../../mingw32/lib/;c:/mingw/bin/../lib/gcc/mingw32/6.3.0/../../../;c:/code/clang/FromCmdLine/lib/
COLLECT_GCC_OPTIONS='-v' '-o' 'a.out' '-mtune=generic' '-march=i586'
 c:/mingw/bin/../libexec/gcc/mingw32/6.3.0/collect2.exe -plugin c:/mingw/bin/../libexec/gcc/mingw32/6.3.0/liblto_plugin-0.dll -plugin-opt=c:/mingw/bin/../libexec/gcc/mingw32/6.3.0/lto-wrapper.exe -plugin-opt=-fresolution=C:\Users\AppData\Local\Temp\ccufvVIA.res -plugin-opt=-pass-through=-lmingw32 -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lmoldname -plugin-opt=-pass-through=-lmingwex -plugin-opt=-pass-through=-lmsvcrt -plugin-opt=-pass-through=-ladvapi32 -plugin-opt=-pass-through=-lshell32 -plugin-opt=-pass-through=-luser32 -plugin-opt=-pass-through=-lkernel32 -plugin-opt=-pass-through=-lmingw32 -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lmoldname -plugin-opt=-pass-through=-lmingwex -plugin-opt=-pass-through=-lmsvcrt --sysroot=c:\code\clang\FromCmdLine -Bdynamic -o a.out c:/mingw/bin/../lib/gcc/mingw32/6.3.0/../../../crt2.o c:/mingw/bin/../lib/gcc/mingw32/6.3.0/crtbegin.o -Lc:/mingw/bin/../lib/gcc/mingw32/6.3.0 -Lc:/mingw/bin/../lib/gcc -Lc:/mingw/bin/../lib/gcc/mingw32/6.3.0/../../../../mingw32/lib -Lc:/mingw/bin/../lib/gcc/mingw32/6.3.0/../../.. -Lc:/code/clang/FromCmdLine/lib C:\Users\AppData\Local\Temp\main-b17d06.o -lmingw32 -lgcc -lgcc_eh -lmoldname -lmingwex -lmsvcrt -ladvapi32 -lshell32 -luser32 -lkernel32 -lmingw32 -lgcc -lgcc_eh -lmoldname -lmingwex -lmsvcrt c:/mingw/bin/../lib/gcc/mingw32/6.3.0/crtend.o
c:/mingw/bin/../lib/gcc/mingw32/6.3.0/../../../../mingw32/bin/ld.exe: C:\Users\AppData\Local\Temp\main-b17d06.o: Relocations in generic ELF (EM: 40)
C:\Users\AppData\Local\Temp\main-b17d06.o: error adding symbols: File in wrong format

更新

这并不能完全回答我的问题,但它确实帮助我进步。

为了更好地理解,我发现crosstool-NG 很有用,尤其是他们的documentation(第 1 章到第 5 章)。

然后我阅读了cmake cross compiling 文档。

我写了一个小cmake C++测试。

Helloworld.cpp

#include <iostream>
int main(int argc, char *argv[])

   std::cout << "Hello World!" << std::endl;
   return 0;

CMakeLists.txt

cmake_minimum_required(VERSION 2.8.9)
project (hello)
add_executable(hello helloworld.cpp)

针对 cmake 的特定配置。这是来自4。

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(CMAKE_SYSROOT /home/user/x-tools/aarch64-unknown-linux-gnueabi/aarch64-unknown-linux-gnueabi/sysroot/)
set(CMAKE_STAGING_PREFIX /home/user/crosscompile/stage)

set(tools /home/user/x-tools/aarch64-unknown-linux-gnueabi)
set(CMAKE_C_COMPILER $tools/bin/aarch64-unknown-linux-gnueabi-gcc)
set(CMAKE_CXX_COMPILER $tools/bin/aarch64-unknown-linux-gnueabi-g++)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

还有命令行

cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain_file.txt ..

交叉编译到 ARM,程序在 ARM 机器上运行。

但这不使用 LLVM / Clang。要使用 LLVM,我想更改工具链配置以使用

set(tools /usr/bin)
set(CMAKE_C_COMPILER $tools/clang)
set(CMAKE_CXX_COMPILER $tools/clang++)

失败,因为该 bin 文件夹是用于主机的。

我还尝试使用从http://releases.llvm.org/download.html 下载的 AArch64。是的,那也没用。

总之,这就是我们所需要的。

    一个 sysroot 文件夹,其中包含目标系统的 lib 和 include 文件夹。好的,该 sysroot 文件夹中的内容应该比 lib 和 include 更多。 目标系统的工具链(编译器、汇编器、链接器)。

【问题讨论】:

链接器获得main.s 似乎很奇怪。打赌它期望main.o main.s 来自llvm.org/docs/GettingStarted.html#example-with-clang(LLVM 系统入门 -> 使用 clang 的示例)第 6 点(llc hello.bc -o hello.s)。但是,是的,那些家伙不直接调用链接器。 【参考方案1】:

这是我必须做的,为了让概念验证工作,只使用 llvm 进行交叉编译,host=linux x86_64 和 target = DrivePX (arm aarch64)。 (也适用于 host=Windows 10 x86_64。)

我推荐一个像croostool-ng这样的工具,它为你设置了一个交叉编译工具链,但下面的步骤展示了幕后发生的事情,它只使用了llvm。

    在主机上,检出并编译 LLVM,包括 Clang 见http://llvm.org/docs/GettingStarted.html。 这需要很长时间,所以发布版本。还要确保您有足够的空间 (GB)。 告诉 cmake 进行发布构建,例如 CC=gcc CXX=g++ cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release ../llvm/ 然后调用 make。 在主机上调用 clang。 记得设置目标,例如--target=aarch64-linux-gnu 告诉 clang 使用 llvm 链接器,即-fuse-ld=lld clang --target=aarch64-linux-gnu -v main.cpp -o main -fuse-ld=lld 会有很多链接器错误

ld.lld:错误:找不到库 -lgcc

ld.lld:错误:找不到库 -lgcc_s

ld.lld:错误:找不到库 -lc

    还有一些关于缺少 crtX.o 文件的错误,例如

ld.lld:错误:无法打开 crt1.o:没有这样的文件或目录

    在目标机器上找到所有的 libgcc、libc 等文件。 记得复制完整版本的库,例如对于 libgcc_s.so 复制 libgcc_s.so.1 将这些文件复制到主机。 告诉 clang 在哪里可以找到这些库,例如将文件复制到名为 libs 的文件夹中 clang --target=aarch64-linux-gnu -v main.cpp -o main -fuse-ld=lld -L./libs 还请记住,链接器正在寻找例如libgcc_s.so 所以创建符号链接到例如libgcc_s.so.1. 将 crtX.o 文件从目标复制到主机。将它们放在 main.cpp 旁边。 然后又出现一个错误

ld.lld:错误:未定义符号:__libc_csu_fini

该符号位于 libc_nonshared.a 中。这answer 帮助了。

libc.so 是一个链接器脚本,它告诉链接器拉入共享库 libc.so.6 和非共享部分 libc_nonshared.a

在主机上找到并打开 libc.so 并查看 libc.so.6 和 libc_nonshared.a 的位置。 将它们复制到主机。 告诉链接器使用这两个库,即-lc -lc_nonshared clang --target=aarch64-linux-gnu -v main.cpp -o main -fuse-ld=lld -L./libs -lc -lc_nonshared

我的短文本代码只依赖于 libc、libgcc,不需要头文件。如果您的代码需要其他库和头文件,则必须将它们从目标复制到主机。


更新 如果您想了解 libgcc,请阅读 this question。

【讨论】:

我建议将 /usr/lib 从目标复制到文件夹 x 并使用 --sysroot=/path/to/x 告诉 clang/lld。没有选项-L 或将“随机”文件复制到“随机”位置。 (不可否认,复制了更多文件,但更干净恕我直言)。

以上是关于如何使用 LLVM 在 Windows 上为 ARM 编译 C++ 程序?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Windows 上为 WebStorm 或 IntelliJ IDEA 使用 Mac OSX 键盘映射?

如何在 Windows64 上为 Python 3.5 安装 pydotplus

如何在Windows 10上为用户指定硬盘空间配额

如何在 Windows 上为 CUDA 链接库(例如 CUBLAS、CUSPARSE)

如何在 Windows 上为 CUDA 链接库(例如 CUBLAS、CUSPARSE)

如何在 Windows 上为 NetBeans 和 gcc 添加库包含路径?