skip to Main Content

Consider the following example project_(CMakeLists.txt):

cmake_minimum_required(VERSION 3.1)

project(CCL LANGUAGES C CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)

find_package(PkgConfig REQUIRED)
find_package(ZLIB)
find_package(PNG)
find_library(MATH_LIBRARY m)
pkg_search_module(OpenEXR OpenEXR)
add_executable(main main.cpp)
if (MATH_LIBRARY)
  target_link_libraries(main PUBLIC ${MATH_LIBRARIES})
endif()

Main.cpp:

#include <iostream>
#include <cmath>

int main(void)
{
    std::cout << "Hello, sin()" << std::sin(30) << std::endl;
    return 0;
}

I want to compile this project with the following CMake toolchain
(aarch64-toolchain.cmake):

# Cross-compilation system information.
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)

# The sysroot contains all the libraries we might need to link against and
# possibly headers we need for compilation.
set(CMAKE_SYSROOT /var/lib/schroot/chroots/ubuntu-focal-arm64)
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_LIBRARY_ARCHITECTURE aarch64-linux-gnu)

# Install path when SYSROOT is read-only.
# set(CMAKE_STAGING_PREFIX aarch64-staging)

# Set the compilers for C, C++ and Fortran.
set(GCC_TRIPLE "aarch64-linux-gnu")
set(CMAKE_C_COMPILER ${GCC_TRIPLE}-gcc-10 CACHE FILEPATH "C compiler")
set(CMAKE_CXX_COMPILER ${GCC_TRIPLE}-g++-10 CACHE FILEPATH "C++ compiler")
set(CMAKE_Fortran_COMPILER ${GCC_TRIPLE}-gfortran CACHE FILEPATH "Fortran compiler")

# Automatically use the cross-wrapper for pkg-config when available.
set(PKG_CONFIG_EXECUTABLE aarch64-linux-gnu-pkg-config CACHE FILEPATH "pkg-config executable")

# Set the architecture-specific compiler flags.
set(ARCH_FLAGS "-mcpu=cortex-a53+crc+simd")
set(CMAKE_C_FLAGS_INIT ${ARCH_FLAGS})
set(CMAKE_CXX_FLAGS_INIT ${ARCH_FLAGS})
set(CMAKE_Fortran_FLAGS_INIT ${ARCH_FLAGS})

# Don't look for programs in the sysroot (these are ARM programs, they won't run
# on the build machine).
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)

# Only look for libraries, headers and packages in the sysroot, don't look on
# the build machine.
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE arm64)

Where the sysroot (/var/lib/schroot/chroots/ubuntu-focal-arm64) was setup using:

name=ubuntu-focal
mk-sbuild --arch=arm64 --skip-proposed --skip-updates --skip-security --name=${name} focal
su - $USER
mk-sbuild --arch=arm64 --skip-proposed --skip-updates --skip-security --name=${name} focal

Configuration-wise, this works fine, however when I try to build this project
find_library ‘correctly’ finds the wrong libm.so library:

$ cmake -DCMAKE_TOOLCHAIN_FILE=../aarch-toolchain.cmake ..
$ make
Scanning dependencies of target main
[ 50%] Building CXX object CMakeFiles/main.dir/main.cpp.o
make[2]: *** No rule to make target '/var/lib/schroot/chroots/ubuntu-focal-arm64/usr/lib/aarch64-linux-gnu/libm.so', needed by 'main'.  Stop.
make[1]: *** [CMakeFiles/Makefile2:76: CMakeFiles/main.dir/all] Error 2
make: *** [Makefile:84: all] Error 2

Looking into the sysroot itself, the library is correctly found, but obviously,
it is a symlink to a file local to the sysroot, not the host:

$ ls -hal /var/lib/schroot/chroots/ubuntu-focal-arm64/usr/lib/aarch64-linux-gnu/
lrwxrwxrwx  1 root root   32 Apr 14  2020 libm.so -> /lib/aarch64-linux-gnu/libm.so.6

It seems to me that CMake is picking up the wrong library (find_library should
really be using the compiler libraries first), so I guess that my toolchain-file
is written incorrectly somehow. How should it be changed to correctly find the
math library, without changing the sysroot or project itself?

(Also, please note that this is an example project to illustrate the problem. I still need to be able to search the sysroot for packages.)


Edits

As requested in the comments, the end state should be a proper aarch64 binary
("main"). Essentially, the build commands below should succeed both with and
without the toolchain, and should yield a functional binary (although, the
aarch64 one naturally only work inside the sysroot).

$ mkdir -p host-build && cd host-build && cmake .. && make 
$ mkdir -p device-build && cd device-build && cmake -DCMAKE_TOOLCHAIN_FILE=../aarch64-toolchain.cmake .. && make

2

Answers


  1. TLDR: There is no easy way, as far as I know.

    Your toolchain file is fine, it’s the symlinks that are bad and they should ideally be relative and not absolute if you wish to use it for building. Below I mention a few options and explain the issue. This is all based on my experience with cross-compiling. Perhaps someone knows more.


    There isn’t anything wrong with your toolchainfile. From my perspective it is configured correctly. This is an issue as you pointed out with the symlinks being relative to your created rootfs and not absolute to the host machine but they are presented as absolute.

    IIRC there is not much you can do here.

    I’ll give you a few options that I know of from personal experience:

    1. Forget cross-compiling and using QEMU chroot into the environment and build there. This is the easiest option.

    2. Get all the required libraries on your host machine (outside of the sysroot) and compile using them – You just have to make sure that you have the exact same libraries as the target machine. Your toolchain file would be then reconfigured to look for libraries among the ones here.
      EDIT: As @josch pointed out in the comments under his answer I should expand on this point to make it more clear to users. However because he already mentioned the most important steps to take when applying this, I will refer the reader to his answer. I will however provide a link to debians official HowTo regarding Multi-arch

    3. The other option would mean fixing your projects CMakeLists to more adapt to the cross-compiling. This would require you to specify the absolute paths to the correct libraries. There is a drawback as you need to always point to the newest library. You could for example create another .cmake file that helps you configure the correct paths and because target_link_libraries() allows you to specify absolute paths, you could use the newly created custom targets.

    4. The last option would be to fix the symlinks (ideally by making them relative). This is in my opinion the preferred way. In the end the rootfs that you will be using for building will be just that, i.e. used for building.

    As far as I know (I might be wrong here) but the mks-build is just a debian tool that is originally used for packaging .deb files and as such wasn’t made to be complimentary to CMake. And if it was then the debian team probably has the toolchain files you are looking for (i.e. I would look for cross-compiling and package guides regarding .deb packages – if they exist they will be included).


    CMAKE_SYSROOT, CMAKE_FIND_ROOT_PATH only add a prefix to specific find_ commands/functions. They pretty much can’t change anything about the target system or the linker behavior (which is the issue here). Which means they are correctly configured. It’s the fake system that is wrongly configured.

    Login or Signup to reply.
  2. You wrote to the debian-cross mailing list so I assume that your question is Debian specific? If so, maybe tag your question as such. Here is how you cross compile any Debian package for amd64 (aarch64) using sbuild:

    sbuild --host=arm64 your_package.dsc
    

    sbuild will take care of setting up your chroot as required and call the build tools with the correct arguments. You do not even need to create a special build chroot for that and can just re-use your existing chroots (sbuild will know what to do with them).

    If instead, you just want to cross-compile some software using CMake on Debian, the process is as simple as installing a cross-compiler and pointing CMake to it:

    $ sudo apt install g++-aarch64-linux-gnu
    $ cmake -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ .
    $ make VERBOSE=1
    ...
    /usr/bin/aarch64-linux-gnu-g++ -rdynamic CMakeFiles/main.dir/main.cpp.o -o main
    ...
    $ file main
    main: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1 ...
    

    That’s it. No need for a sysroot.

    If you haven’t set up arm64 as a foreign architecture on your system, you need to run this once:

    dpkg --add-architecture arm64
    apt-get update
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search