Some distributions, such as Fedora, only provide cross-compiling support for bare-metal targets such as the Linux kernel, but the header and library files needed for user land applications are not provided. In such a case, you can simply copy the missing files from your target system.
If your distribution does not come with the required toolchain, then you can build it from source. For the compiler, you can use clang or gcc/g++. The gcc and g++ compilers must be configured to produce code for the target system and the binutils tools need to handle files for the target system. Moreover, the C and C++ libraries need to be compiled with this toolchain. The steps vary by operating system and host and target architecture. On the web, you can find instructions if you search for gcc cross-compile <architecture>.
With this preparation, you are almost ready to cross-compile the sample application (including the LLVM libraries) except for one little detail. LLVM uses the TableGen tool during the build. During cross-compilation, everything is compiled for the target architecture, including this tool. You can use llvm-tblgen from the build in Chapter 1 or you can compile only this tool. Assuming you are in the directory that contains the clone of this book’s GitHub repository, type the following:
$ mkdir build-host
$ cd build-host
$ cmake -G Ninja \
-DLLVM_TARGETS_TO_BUILD=”X86″ \
-DLLVM_ENABLE_ASSERTIONS=ON \
-DCMAKE_BUILD_TYPE=Release \
../llvm-project/llvm
$ ninja llvm-tblgen
$ cd ..
These steps should be familiar by now. A build directory is created and entered. The cmake command creates the build files for LLVM only for the X86 target. To save space and time, a release build is done but assertions are enabled to catch possible errors. Only the llvm-tblgen tool is compiled with ninja.
With the llvm-tblgen tool at hand, you can now start the cross-compilation process. The CMake command line is very long, so you may want to store the command in a script file. The difference from previous builds is that more information must be provided:
$ mkdir build-target
$ cd build-target
$ cmake -G Ninja \
-DCMAKE_CROSSCOMPILING=True \
-DLLVM_TABLEGEN=../build-host/bin/llvm-tblgen \
-DLLVM_DEFAULT_TARGET_TRIPLE=aarch64-linux-gnu \
-DLLVM_TARGET_ARCH=AArch64 \
-DLLVM_TARGETS_TO_BUILD=AArch64 \
-DLLVM_ENABLE_ASSERTIONS=ON \
-DLLVM_EXTERNAL_PROJECTS=tinylang \
-DLLVM_EXTERNAL_TINYLANG_SOURCE_DIR=../tinylang \
-DCMAKE_INSTALL_PREFIX=../target-tinylang \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc-12 \
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++-12 \
../llvm-project/llvm
$ ninja
Again, you create a build directory and enter it before running the CMake command. Some of these CMake parameters have not been used before and require some explanation:
- CMAKE_CROSSCOMPILING set to ON tells CMake that we are cross-compiling.
- LLVM_TABLEGEN specifies the path to the llvm-tblgen tool to use. This is the one from the previous build.
- LLVM_DEFAULT_TARGET_TRIPLE is the triple of the target architecture.
- LLVM_TARGET_ARCH is used for JIT code generation. It defaults to the architecture of the host. For cross-compiling, this must be set to the target architecture.
- LLVM_TARGETS_TO_BUILD is the list of targets for which LLVM should include code generators. The list should at least include the target architecture.
- CMAKE_C_COMPILER and CMAKE_CXX_COMPILER specify the C and C++ compilers used for the build, respectively. The binaries of the cross-compilers are prefixed with the target triple and are not found automatically by CMake.
With the other parameters, a release build with assertions enabled is requested and our tinylang application is built as part of LLVM. Once the compilation process has finished, the file command can demonstrate that we have created a binary for ARMv8. Specifically, we can run $ file bin/tinylang and check that the output says ELF 64-bit object for the ARM aarch64 architecture.