| =================================================================== |
| How to Cross Compile Compiler-rt Builtins For Arm |
| =================================================================== |
| |
| Introduction |
| ============ |
| |
| This document contains information about building and testing the builtins part |
| of compiler-rt for an Arm target, from an x86_64 Linux machine. |
| |
| While this document concentrates on Arm and Linux the general principles should |
| apply to other targets supported by compiler-rt. Further contributions for other |
| targets are welcome. |
| |
| The instructions in this document depend on libraries and programs external to |
| LLVM, there are many ways to install and configure these dependencies so you |
| may need to adapt the instructions here to fit your own situation. |
| |
| Prerequisites |
| ============= |
| |
| In this use case we will be using cmake on a Debian-based Linux system, |
| cross-compiling from an x86_64 host to a hard-float Armv7-A target. We will be |
| using as many of the LLVM tools as we can, but it is possible to use GNU |
| equivalents. |
| |
| You will need: |
| * A build of LLVM for the llvm-tools and LLVM CMake files. |
| * A clang executable with support for the ``ARM`` target. |
| * ``compiler-rt`` sources. |
| * The ``qemu-arm`` user mode emulator. |
| * An ``arm-linux-gnueabihf`` sysroot. |
| |
| .. note:: |
| An existing sysroot is required because some of the builtins include C library |
| headers and a sysroot is the easiest way to get those. |
| |
| In this example we will be using ``ninja`` as the build tool. |
| |
| See https://compiler-rt.llvm.org/ for information about the dependencies |
| on clang and LLVM. |
| |
| See https://llvm.org/docs/GettingStarted.html for information about obtaining |
| the source for LLVM and compiler-rt. |
| |
| ``qemu-arm`` should be available as a package for your Linux distribution. |
| |
| The most complicated of the prerequisites to satisfy is the ``arm-linux-gnueabihf`` |
| sysroot. In theory it is possible to use the Linux distributions multiarch |
| support to fulfill the dependencies for building but unfortunately due to |
| ``/usr/local/include`` being added some host includes are selected. |
| |
| The easiest way to supply a sysroot is to download an ``arm-linux-gnueabihf`` |
| toolchain from https://developer.arm.com/open-source/gnu-toolchain/gnu-a/downloads. |
| |
| Building compiler-rt builtins for Arm |
| ===================================== |
| |
| We will be doing a standalone build of compiler-rt. The command is shown below. |
| Shell variables are used to simplify some of the options:: |
| |
| LLVM_TOOLCHAIN=<path-to-llvm-install>/ |
| TARGET_TRIPLE=arm-none-linux-gnueabihf |
| GCC_TOOLCHAIN=<path-to-gcc-toolchain> |
| SYSROOT=${GCC_TOOLCHAIN}/${TARGET_TRIPLE}/libc |
| COMPILE_FLAGS="-march=armv7-a" |
| |
| cmake ../llvm-project/compiler-rt \ |
| -G Ninja \ |
| -DCMAKE_AR=${LLVM_TOOLCHAIN}/bin/llvm-ar \ |
| -DCMAKE_NM=${LLVM_TOOLCHAIN}/bin/llvm-nm \ |
| -DCMAKE_RANLIB=${LLVM_TOOLCHAIN}/bin/llvm-ranlib \ |
| -DLLVM_CMAKE_DIR="${LLVM_TOOLCHAIN}/lib/cmake/llvm" \ |
| -DCMAKE_SYSROOT="${SYSROOT}" \ |
| -DCMAKE_ASM_COMPILER_TARGET="${TARGET_TRIPLE}" \ |
| -DCMAKE_ASM_FLAGS="${COMPILE_FLAGS}" \ |
| -DCMAKE_C_COMPILER_TARGET="${TARGET_TRIPLE}" \ |
| -DCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=${GCC_TOOLCHAIN} \ |
| -DCMAKE_C_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \ |
| -DCMAKE_C_FLAGS="${COMPILE_FLAGS}" \ |
| -DCMAKE_CXX_COMPILER_TARGET="${TARGET_TRIPLE}" \ |
| -DCMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=${GCC_TOOLCHAIN} \ |
| -DCMAKE_CXX_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \ |
| -DCMAKE_CXX_FLAGS="${COMPILE_FLAGS}" \ |
| -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" \ |
| -DCOMPILER_RT_BUILD_BUILTINS=ON \ |
| -DCOMPILER_RT_BUILD_LIBFUZZER=OFF \ |
| -DCOMPILER_RT_BUILD_MEMPROF=OFF \ |
| -DCOMPILER_RT_BUILD_PROFILE=OFF \ |
| -DCOMPILER_RT_BUILD_CTX_PROFILE=OFF \ |
| -DCOMPILER_RT_BUILD_SANITIZERS=OFF \ |
| -DCOMPILER_RT_BUILD_XRAY=OFF \ |
| -DCOMPILER_RT_BUILD_ORC=OFF \ |
| -DCOMPILER_RT_BUILD_CRT=OFF \ |
| -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \ |
| -DCOMPILER_RT_EMULATOR="qemu-arm -L ${SYSROOT}" \ |
| -DCOMPILER_RT_INCLUDE_TESTS=ON \ |
| -DCOMPILER_RT_TEST_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \ |
| -DCOMPILER_RT_TEST_COMPILER_CFLAGS="--target=${TARGET_TRIPLE} ${COMPILE_FLAGS} --gcc-toolchain=${GCC_TOOLCHAIN} --sysroot=${SYSROOT} -fuse-ld=lld" |
| |
| .. note:: |
| The command above also enables tests. Enabling tests is not required, more details |
| in the testing section. |
| |
| ``CMAKE_<LANGUAGE>_<OPTION>`` options are set so that the correct ``--target``, |
| ``--sysroot``, ``--gcc-toolchain`` and ``-march`` options will be given to the |
| compilers. |
| |
| The combination of these settings needs to be enough to pass CMake's compiler |
| checks, compile compiler-rt and build the test cases. |
| |
| The flags need to select: |
| * The Arm target (``--target arm-none-linux-gnueabihf``) |
| * The Arm architecture level (``-march=armv7-a``) |
| * Whether to generate Arm (``-marm``, the default) or Thumb (``-mthumb``) instructions. |
| |
| It is possible to pass all these flags to CMake using ``CMAKE_<LANGUAGE>_FLAGS``, |
| but the command above uses standard CMake options instead. If you need to |
| add flags that CMake cannot generate automatically, add them to |
| ``CMAKE_<LANGUAGE>_FLAGS``. |
| |
| When CMake has finished, build with Ninja:: |
| |
| ninja builtins |
| |
| Testing compiler-rt builtins using qemu-arm |
| =========================================== |
| |
| The following options are required to enable tests:: |
| |
| -DCOMPILER_RT_EMULATOR="qemu-arm -L ${SYSROOT}" \ |
| -DCOMPILER_RT_INCLUDE_TESTS=ON \ |
| -DCOMPILER_RT_TEST_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \ |
| -DCOMPILER_RT_TEST_COMPILER_CFLAGS="--target=${TARGET_TRIPLE} -march=armv7-a --gcc-toolchain=${GCC_TOOLCHAIN} --sysroot=${SYSROOT} -fuse-ld=lld" |
| |
| This tells compiler-rt that we want to run tests on ``qemu-arm``. If you do not |
| want to run tests, remove these options from the CMake command. |
| |
| Note that ``COMPILER_RT_TEST_COMPILER_CFLAGS`` contains the equivalent of the |
| options CMake generated for us with the first command. We must pass them |
| manually here because standard options like ``CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN`` |
| do not apply here. |
| |
| When CMake has finished, run the tests:: |
| |
| ninja check-builtins |
| |
| Troubleshooting |
| =============== |
| |
| The cmake try compile stage fails |
| --------------------------------- |
| At an early stage cmake will attempt to compile and link a simple C program to |
| test if the toolchain is working. |
| |
| This stage can often fail at link time if the ``--sysroot=``, ``--target`` or |
| ``--gcc-toolchain=`` options are not passed to the compiler. Check the |
| ``CMAKE_<LANGUAGE>_FLAGS`` and ``CMAKE_<LANGAUGE>_COMPILER_TARGET`` flags along |
| with any of the specific CMake sysroot and toolchain options. |
| |
| It can be useful to build a simple example outside of cmake with your toolchain |
| to make sure it is working. For example:: |
| |
| clang --target=arm-linux-gnueabi -march=armv7a --gcc-toolchain=/path/to/gcc-toolchain --sysroot=/path/to/gcc-toolchain/arm-linux-gnueabihf/libc helloworld.c |
| |
| Clang uses the host header files |
| -------------------------------- |
| On debian based systems it is possible to install multiarch support for |
| ``arm-linux-gnueabi`` and ``arm-linux-gnueabihf``. In many cases clang can successfully |
| use this multiarch support when ``--gcc-toolchain=`` and ``--sysroot=`` are not supplied. |
| Unfortunately clang adds ``/usr/local/include`` before |
| ``/usr/include/arm-linux-gnueabihf`` leading to errors when compiling the hosts |
| header files. |
| |
| The multiarch support is not sufficient to build the builtins you will need to |
| use a separate ``arm-linux-gnueabihf`` toolchain. |
| |
| No target passed to clang |
| ------------------------- |
| If clang is not given a target it will typically use the host target, this will |
| not understand the Arm assembly language files resulting in error messages such |
| as ``error: unknown directive .syntax unified``. |
| |
| You can check the clang invocation in the error message to see if there is no |
| ``--target`` or if it is set incorrectly. The cause is usually |
| ``CMAKE_ASM_FLAGS`` not containing ``--target`` or ``CMAKE_ASM_COMPILER_TARGET`` |
| not being present. |
| |
| Arm architecture not given |
| -------------------------- |
| The ``--target=arm-linux-gnueabihf`` will default to Arm architecture v4t which |
| cannot assemble the barrier instructions used in the ``synch_and_fetch`` source |
| files. |
| |
| The cause is usually a missing ``-march=armv7a`` from the ``CMAKE_ASM_FLAGS``. |
| |
| Compiler-rt builds but the tests fail to build |
| ---------------------------------------------- |
| The flags used to build the tests are not the same as those used to build the |
| builtins. The c flags are provided by ``COMPILER_RT_TEST_COMPILE_CFLAGS`` and |
| the ``CMAKE_C_COMPILER_TARGET``, ``CMAKE_ASM_COMPILER_TARGET``, |
| ``CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN`` and ``CMAKE_SYSROOT`` flags are not |
| applied to tests. |
| |
| Make sure that ``COMPILER_RT_TEST_COMPILE_CFLAGS`` contains all the necessary |
| flags. |
| |
| |
| Modifications for other Targets |
| =============================== |
| |
| Arm Soft-Float Target |
| --------------------- |
| The instructions for the Arm hard-float target can be used for the soft-float |
| target by substituting soft-float equivalents for the sysroot and target. The |
| target to use is: |
| |
| * ``-DCMAKE_C_COMPILER_TARGET=arm-linux-gnueabi`` |
| |
| Depending on whether you want to use floating point instructions or not you |
| may need extra c-flags such as ``-mfloat-abi=softfp`` for use of floating-point |
| instructions, and ``-mfloat-abi=soft -mfpu=none`` for software floating-point |
| emulation. |
| |
| You will need to use an ``arm-linux-gnueabi`` GNU toolchain for soft-float. |
| |
| AArch64 Target |
| -------------- |
| The instructions for Arm can be used for AArch64 by substituting AArch64 |
| equivalents for the sysroot, emulator and target:: |
| |
| -DCMAKE_C_COMPILER_TARGET=aarch64-linux-gnu |
| -DCOMPILER_RT_EMULATOR="qemu-aarch64 -L /path/to/aarch64/sysroot |
| |
| You will also have to update any use of the target triple in compiler flags. |
| For instance in ``CMAKE_C_FLAGS`` and ``COMPILER_RT_TEST_COMPILER_CFLAGS``. |
| |
| Armv6-m, Armv7-m and Armv7E-M targets |
| ------------------------------------- |
| To build and test the libraries using a similar method to Armv7-A is possible |
| but more difficult. The main problems are: |
| |
| * There is not a ``qemu-arm`` user-mode emulator for bare-metal systems. |
| ``qemu-system-arm`` can be used but this is significantly more difficult |
| to setup. This document does not explain how to do this. |
| * The targets to compile compiler-rt have the suffix ``-none-eabi``. This uses |
| the BareMetal driver in clang and by default will not find the libraries |
| needed to pass the cmake compiler check. |
| |
| As the Armv6-M, Armv7-M and Armv7E-M builds of compiler-rt only use instructions |
| that are supported on Armv7-A we can still get most of the value of running the |
| tests using the same ``qemu-arm`` that we used for Armv7-A by building and |
| running the test cases for Armv7-A but using the builtins compiled for |
| Armv6-M, Armv7-M or Armv7E-M. This will test that the builtins can be linked |
| into a binary and execute the tests correctly but it will not catch if the |
| builtins use instructions that are supported on Armv7-A but not Armv6-M, |
| Armv7-M and Armv7E-M. |
| |
| This requires a second ``arm-none-eabi`` toolchain for building the builtins. |
| Using a bare-metal toolchain ensures that the target and C library details are |
| specific to bare-metal instead of using Linux settings. This means that some |
| tests may behave differently compared to real hardware, but at least the content |
| of the builtins library is correct. |
| |
| Below is an example that builds the builtins for Armv7-M, but runs the tests |
| as Armv7-A. It is presented in full, but is very similar to the earlier |
| command for Armv7-A build and test:: |
| |
| LLVM_TOOLCHAIN=<path to llvm install>/ |
| |
| # For the builtins. |
| TARGET_TRIPLE=arm-none-eabi |
| GCC_TOOLCHAIN=<path to arm-none-eabi toolchain>/ |
| SYSROOT=${GCC_TOOLCHAIN}/${TARGET_TRIPLE}/libc |
| COMPILE_FLAGS="-march=armv7-m -mfpu=vfpv2" |
| |
| # For the test cases. |
| A_PROFILE_TARGET_TRIPLE=arm-none-linux-gnueabihf |
| A_PROFILE_GCC_TOOLCHAIN=<path to arm-none-linux-gnueabihf toolchain>/ |
| A_PROFILE_SYSROOT=${A_PROFILE_GCC_TOOLCHAIN}/${A_PROFILE_TARGET_TRIPLE}/libc |
| |
| cmake ../llvm-project/compiler-rt \ |
| -G Ninja \ |
| -DCMAKE_AR=${LLVM_TOOLCHAIN}/bin/llvm-ar \ |
| -DCMAKE_NM=${LLVM_TOOLCHAIN}/bin/llvm-nm \ |
| -DCMAKE_RANLIB=${LLVM_TOOLCHAIN}/bin/llvm-ranlib \ |
| -DLLVM_CMAKE_DIR="${LLVM_TOOLCHAIN}/lib/cmake/llvm" \ |
| -DCMAKE_SYSROOT="${SYSROOT}" \ |
| -DCMAKE_ASM_COMPILER_TARGET="${TARGET_TRIPLE}" \ |
| -DCMAKE_ASM_FLAGS="${COMPILE_FLAGS}" \ |
| -DCMAKE_C_COMPILER_TARGET="${TARGET_TRIPLE}" \ |
| -DCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=${GCC_TOOLCHAIN} \ |
| -DCMAKE_C_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \ |
| -DCMAKE_C_FLAGS="${COMPILE_FLAGS}" \ |
| -DCMAKE_CXX_COMPILER_TARGET="${TARGET_TRIPLE}" \ |
| -DCMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=${GCC_TOOLCHAIN} \ |
| -DCMAKE_CXX_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \ |
| -DCMAKE_CXX_FLAGS="${COMPILE_FLAGS}" \ |
| -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" \ |
| -DCOMPILER_RT_BUILD_BUILTINS=ON \ |
| -DCOMPILER_RT_BUILD_LIBFUZZER=OFF \ |
| -DCOMPILER_RT_BUILD_MEMPROF=OFF \ |
| -DCOMPILER_RT_BUILD_PROFILE=OFF \ |
| -DCOMPILER_RT_BUILD_CTX_PROFILE=OFF \ |
| -DCOMPILER_RT_BUILD_SANITIZERS=OFF \ |
| -DCOMPILER_RT_BUILD_XRAY=OFF \ |
| -DCOMPILER_RT_BUILD_ORC=OFF \ |
| -DCOMPILER_RT_BUILD_CRT=OFF \ |
| -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \ |
| -DCOMPILER_RT_EMULATOR="qemu-arm -L ${A_PROFILE_SYSROOT}" \ |
| -DCOMPILER_RT_INCLUDE_TESTS=ON \ |
| -DCOMPILER_RT_TEST_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \ |
| -DCOMPILER_RT_TEST_COMPILER_CFLAGS="--target=${A_PROFILE_TARGET_TRIPLE} -march=armv7-a --gcc-toolchain=${A_PROFILE_GCC_TOOLCHAIN} --sysroot=${A_PROFILE_SYSROOT} -fuse-ld=lld" \ |
| -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \ |
| -DCOMPILER_RT_OS_DIR="baremetal" \ |
| -DCOMPILER_RT_BAREMETAL_BUILD=ON |
| |
| .. note:: |
| The sysroot used for compiling the tests is ``arm-linux-gnueabihf``, not |
| ``arm-none-eabi`` which is used when compiling the builtins. |
| |
| The Armv6-M builtins will use the soft-float ABI. When compiling the tests for |
| Armv7-A we must include ``"-mthumb -mfloat-abi=soft -mfpu=none"`` in the |
| test-c-flags. We must use an Armv7-A soft-float abi sysroot for ``qemu-arm``. |
| |
| Depending on the linker used for the test cases you may encounter BuildAttribute |
| mismatches between the M-profile objects from compiler-rt and the A-profile |
| objects from the test. The lld linker does not check the profile |
| BuildAttribute so it can be used to link the tests by adding ``-fuse-ld=lld`` to the |
| ``COMPILER_RT_TEST_COMPILER_CFLAGS``. |