Cross compiling a C++ project for Beaglebone
I am thinking of getting into Embedded Linux, so I have ordered a Beaglebone. I don’t want to use Python to program it as I want to do some real-time processing with it. For that, I have to figure out how to compile C++ programs on the host PC instead of doing it on the Beaglebone as it has very little ram.
Initially, I wanted to do the cross-compilation using QEMU
but quickly gave up the idea as there is no out-of-the-box QEMU
system for Beaglebone. Raspberry pi has a nice QEMU
system that runs out of the box.
I will revisit the idea of using QEMU
once I got accompanied on the procedure on building Linux kernel.
Everything new has to start simple, so, a simple that I have in mind is to compile and run a LED blinking application using libgpiod library which is the preferred method for driving GPIOs from user-space.
I built and installed the libgpiod
library on the Beaglebone
itself. I can also install it using sudo apt install libgpiod
but I don’t want to do that as it was a very old version.
Compiling and installing libgpiod
The libgpiod
library can be built and installed using the following procedure.
git clone https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/
You will checkout the master branch. Feel free to checkout a release if you are more comfortable like that using
debian@BeagleBone:~/libgpiod$ git branch -a
master
* v2.0.x
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/v0.1.x
remotes/origin/v0.2.x
remotes/origin/v0.3.x
remotes/origin/v1.0.x
remotes/origin/v1.1.x
remotes/origin/v1.2.x
remotes/origin/v1.3.x
remotes/origin/v1.4.x
remotes/origin/v1.5.x
remotes/origin/v1.6.x
remotes/origin/v2.0.x
debian@BeagleBone:~/libgpiod$ git checkout remotes/origin/v2.0.x
Configure and compile it using the following commands
./autogen.sh --enable-tools=yes --enable-bindings-cxx=yes --prefix=/usr/local
make
sudo make install
go
Note
If you get an error like this
debian@BeagleBone:~/libgpiod$ ./autogen.sh --enable-tools=yes
--enable-bindings-cxx=yes --prefix=/usr/local
./autogen.sh: 12: autoreconf: not found
You need to install autoconf
.
debian@BeagleBone:~/libgpiod$ sudo apt install autoconf
If debian complains that it was not able to find it
[sudo] password for debian:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
E: Unable to locate package autoconf
If debian is not able to find the package, then update your packages using
sudo apt update
and try again.
These are all the packages that I installed to build libgpiod
. Note that it is a fresh installation of debian
on beaglebone
sudo apt install pkg-config
sudo apt install autoconf
sudo apt install autoconf-archive
sudo apt install build-essential
# Or a single command
sudo apt install pkg-config autoconf autoconf-archieve build-essential
If you don’t want to use C++, then remove the --enable-bindings-cxx=yes
in the above command lines.
Then try compiling libgpiod again.
./autogen.sh --enable-tools=yes --enable-bindings-cxx=yes --prefix=/usr/local
The above command will take sometime. After it is done, execute
make
sudo make install
The libgpiod
comes with a bunch of useful command line tools for driving and testing GPIOs.
debian@BeagleBone:~/libgpiod$ gpiodetect
If you get an error like this
gpiodetect: error while loading shared libraries: libgpiod.so.3: cannot open shared object file: No such file or directory
Then the executable is not able to find the library. libgpiod gives a hint when we executed sudo make install
----------------------------------------------------------------------
Libraries have been installed in:
/usr/local/lib
If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the '-LLIBDIR'
flag during linking and do at least one of the following:
- add LIBDIR to the 'LD_LIBRARY_PATH' environment variable
during execution
- add LIBDIR to the 'LD_RUN_PATH' environment variable
during linking
- use the '-Wl,-rpath -Wl,LIBDIR' linker flag
- have your system administrator add LIBDIR to '/etc/ld.so.conf'
See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------
All we have to do is add the lib
path to our library path.
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
Note
The above command only works for the current terminal. If you want the changes to persist, add it to ~/.bashrc
.
E.g. If you want to drive P9-12 pin on the Beaglebone-Black
then you would set the offset to 28 and the gpiochip as gpiochip1
gpioset gpiochip1 28=1
CMAKE_SYSROOT and sshfs
To cross-compile a C++ project, CMake needs to find the libraries in the target. The CMAKE_SYSROOT
variable will be used to let the CMake know where the target system libraries are located in.
I thought of using rsync
to copy the required libraries from Beaglebone
to the host PC, but, I used sshfs
to mount the Beaglebone root folder in the host pc.
cd ~
mkdir Beaglebone-Sysroot
sshfs debian@192.168.7.2:/ /home/kalyan/Beaglebone-Sysroot/ -o transform_symlinks
Info
To unmount the mounted sshfs
file system, you can use the command
fusermount -u /home/kalyan/Beaglebone-Sysroot
/home/kalyan/Beaglebone-Sysroot/
path needs to be the folder that you have created and it will be used as a mount point where the root file system of beaglebone will be mounted.Installing Cross-compiler
I installed the cross-compiler using sudo apt install crossbuild-essential-armhf
because the compiler that I downloaded from the GNU website doesn’t has the correct sysroot
setup and the builds were failing.
CMake toolchain file
This is the CMake toolchain file that I am using
# Set the system name
set(CMAKE_SYSTEM_NAME Linux)
# C Compiler
set(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabihf-g++)
# Sysroot location
# It is mounted using sshfs
set(CMAKE_SYSROOT /home/kalyan/Beaglebone-Sysroot)
# These lines are necessary to let cmake know to only look in the
# beaglebone for the libraries instead of looking in host pc.
# sshfs debian@192.168.7.2:/ /home/kalyan/Beaglegone-Sysroot/ -o transform_symlinks
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
The project can be found in github.
Info
Go to the next section if you were not able to run the compiled programs on Beaglebone. If you see an error like this
./ConstexprJoinArray: /lib/arm-linux-gnueabihf/libc.so.6: version `GLIBC_2.34' not found (required by ./ConstexprJoinArray)
Tip
Can also use rsync. I copied entire Beaglebones file system
rsync --info=progress2 -vaHAX --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found"} debian@192.168.0.202:/ /home/kalyan/Beaglebone-Sysroot-rsync/
It seems that there is an issue with certain symlinks, specifically the symlinks libpthread.so
and librt.so
. These symlinks currently use absolute paths, which can cause problems. To address this, it is necessary to delete these symlinks and recreate them using relative paths.
One possible solution is to create a clean sysroot by cloning it, which can help resolve this issue. By properly specifying the rootfs in CMake, it is expected that CMake would handle these absolute links. However, it appears that CMake is unable to utilize these symlinks effectively.
I wanted to express my sincere gratitude for providing the Python script that effectively solves the issue I was facing with absolute symlinks in the root filesystem when cross-compiling. The script you shared, which can be found here, has proven to be incredibly helpful in resolving this problem.
This Python script takes a sysroot directory as input and transforms all the absolute symlinks into relative ones, making the sysroot usable within another system. It accomplishes this by recursively traversing the directory structure and identifying symlinks that have absolute paths. It then replaces those absolute paths with relative ones based on the provided sysroot directory.
This ensures that the sysroot can be utilized seamlessly for cross-compiling purposes.
import sys
import os
# Take a sysroot directory and turn all the abolute symlinks and turn them into
# relative ones such that the sysroot is usable within another system.
if len(sys.argv) != 2:
print("Usage is " + sys.argv[0] + "<directory>")
sys.exit(1)
topdir = sys.argv[1]
topdir = os.path.abspath(topdir)
def handlelink(filep, subdir):
link = os.readlink(filep)
if link[0] != "/":
return
if link.startswith(topdir):
return
#print("Replacing %s with %s for %s" % (link, topdir+link, filep))
print("Replacing %s with %s for %s" % (link, os.path.relpath(topdir+link, subdir), filep))
os.unlink(filep)
os.symlink(os.path.relpath(topdir+link, subdir), filep)
for subdir, dirs, files in os.walk(topdir):
for f in files:
filep = os.path.join(subdir, f)
if os.path.islink(filep):
#print("Considering %s" % filep)
handlelink(filep, subdir)
GLIBC version Mismatch
What actually happened is version mismatch with GLIBC. This error can occur when you try to run a cross-compiled executable on a target system that has a different version of glibc than the one used for cross-compiling.
Remember that we have downloaded the cross-compiler
without checking the version of gcc
that we have on Beaglebone using sudo apt install crossbuild-essential-armhf
.
If I check the version of compiler on the target
Target
debian@BeagleBone:~/ExamplePrograms$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabihf/10/lto-wrapper
Target: arm-linux-gnueabihf
Configured with: ../src/configure -v --with-pkgversion='Debian 10.2.1-6' --with-bugurl=file:///usr/share/doc/gcc-10/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-10 --program-prefix=arm-linux-gnueabihf- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libitm --disable-libquadmath --disable-libquadmath-support --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-sjlj-exceptions --with-arch=armv7-a --with-fpu=vfpv3-d16 --with-float=hard --with-mode=thumb --disable-werror --enable-checking=release --build=arm-linux-gnueabihf --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.2.1 20210110 (Debian 10.2.1-6)
and compare it to host then we can see that the host has a newer version of the compiler.
Host
kalyan@DESKTOP-TT2VIL5:~/Beaglebone-Sysroot$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 11.3.0-1ubuntu1~22.04' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-11-xKiWfi/gcc-11-11.3.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-11-xKiWfi/gcc-11-11.3.0/debian/tmp-gcn/usr --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04)
We need to compile the correct version of compiler ourselves. It can be done by following this guide Cross-compiler using crosstool-ng for Beaglebone | blog (parzival2.github.io)