Skip to content

Cross Compiling for Ubuntu 18.04 armhf

Günter Obiltschnig edited this page Oct 19, 2022 · 4 revisions

This article describes how to cross-compile macchina.io for an 32-bit ARM-based machine (armhf architecture) running Ubuntu 18.04.

Prerequisites

A Ubuntu 18.04 (bionic) host system (VM) with the following packages installed:

  • git
  • build-essential
  • libssl-dev
  • python
  • crossbuild-essential-armhf

Preparing the Host System

First we need to prepare the package manager to install packages required for cross-compiling. For example, the OpenSSL headers and libraries must be available for the armhf architecture.

$ sudo dpkg --add-architecture armhf

The Ubuntu packages for ARM are not available from the default Ubuntu package server, so we have to add a new package source. Create a the file /etc/apt/sources.list.d/arm-cross-compile-sources.list and add the following content:

deb [arch=armhf] http://ports.ubuntu.com/ bionic main restricted
deb [arch=armhf] http://ports.ubuntu.com/ bionic-updates main restricted
deb [arch=armhf] http://ports.ubuntu.com/ bionic universe
deb [arch=armhf] http://ports.ubuntu.com/ bionic-updates universe
deb [arch=armhf] http://ports.ubuntu.com/ bionic multiverse
deb [arch=armhf] http://ports.ubuntu.com/ bionic-updates multiverse
deb [arch=armhf] http://ports.ubuntu.com/ bionic-backports main restricted universe multiverse

This will tell the package manager to look for packages for the specified architecture (armhf) on http://ports.ubuntu.com.

Next, edit /etc/apt/sources.list and change the entries so that they are only used for the amd64 architecture. This is done by inserting [arch=amd64] after every deb statement, e.g.:

# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
# newer versions of the distribution.
deb [arch=amd64] http://archive.ubuntu.com/ubuntu bionic main restricted
# deb-src http://archive.ubuntu.com/ubuntu bionic main restricted

## Major bug fix updates produced after the final release of the
## distribution.
deb [arch=amd64] http://archive.ubuntu.com/ubuntu bionic-updates main restricted
# deb-src http://archive.ubuntu.com/ubuntu bionic-updates main restricted
...

This change will prevent errors when updating the package index, since archive.ubuntu.com only holds the packages for Intel architectures.

Next, update the package index:

$ sudo apt-get update

Install the essential packages for building the host tools:

$ sudo apt-get install -y build-essential libssl-dev python 

Install the packages required for cross-compiling:

$ sudo apt-get install -y crossbuild-essential-armhf
$ sudo apt-get install -y libssl-dev:armhf

Building macchina.io

Get macchina.io Sources from GitHub

$ git clone https://github.com/macchina-io/macchina.io.git

Build the Host Tools

This will build the tools (e.g., BundleCreator) required for building macchina.io.

$ cd macchina.io
$ make -s -j8 hosttools

This takes a couple of minutes. Note: Depending on the number of CPU cores you have on your build machine, adjust the -j8 argument accordingly.

Build for Target

In order to build for the target, a build configuration file must be used. You can find various build configuration files in platform/build/config. Create a new build configuration file X-Ubuntu-Bionic-armhf with the following content:

#
# X-Ubuntu-Bionic-armhf
#
# Make Settings for Cross-Compiling for Ubuntu 18.04 armhf
#
#

#
# General Settings
#
LINKMODE           ?= SHARED
TOOL               ?= arm-linux-gnueabihf
POCO_TARGET_OSNAME  = Linux
POCO_TARGET_OSARCH ?= armv7l
ARCHFLAGS          ?= -march=armv7-a -mfloat-abi=hard -mfpu=neon-vfpv3

#
# Define Tools
#
CC      = $(TOOL)-gcc
CXX     = $(TOOL)-g++
LINK    = $(CXX)
LIB     = $(TOOL)-ar -cr
RANLIB  = $(TOOL)-ranlib
SHLIB   = $(CXX) -shared -Wl,-soname,$(notdir $@) -o $@
SHLIBLN = $(POCO_BASE)/build/script/shlibln
STRIP   = $(TOOL)-strip
DEP     = $(POCO_BASE)/build/script/makedepend.gcc
SHELL   = sh
RM      = rm -rf
CP      = cp
MKDIR   = mkdir -p

#
# Extension for Shared Libraries
#
SHAREDLIBEXT     = .so.$(target_version)
SHAREDLIBLINKEXT = .so

#
# Compiler and Linker Flags
#
CFLAGS          = -std=c99 $(ARCHFLAGS)
CFLAGS32        =
CFLAGS64        =
CXXFLAGS        = -std=c++14 -Wall -Wno-sign-compare $(ARCHFLAGS)
CXXFLAGS32      =
CXXFLAGS64      =
LINKFLAGS       =
LINKFLAGS32     =
LINKFLAGS64     =
STATICOPT_CC    =
STATICOPT_CXX   =
STATICOPT_LINK  = -static
SHAREDOPT_CC    = -fPIC
SHAREDOPT_CXX   = -fPIC
SHAREDOPT_LINK  = -Wl,-rpath,$(LIBPATH)
DEBUGOPT_CC     = -g -D_DEBUG
DEBUGOPT_CXX    = -g -D_DEBUG
DEBUGOPT_LINK   = -g
RELEASEOPT_CC   = -O2 -DNDEBUG
RELEASEOPT_CXX  = -O2 -DNDEBUG
RELEASEOPT_LINK = -O2

#
# System Specific Flags
#
SYSFLAGS = -D_XOPEN_SOURCE=600 -D_REENTRANT -D_THREAD_SAFE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE -DPOCO_HAVE_FD_EPOLL \
	-DPOCO_HAVE_ADDRINFO -DPOCO_HAVE_LIBRESOLV

#
# System Specific Libraries
#
SYSLIBS  = -lpthread -ldl -lrt

You may need to change the ARCHFLAGS setting to match your target device. To find out the CPU features on the target device, run:

$ cat /proc/cpuinfo
processor	: 0
model name	: ARMv7 Processor rev 0 (v7l)
BogoMIPS	: 10.00
Features	: half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32 
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x3
CPU part	: 0xc09
CPU revision	: 0

Under Features you should see the available CPU features, specifically floating-point support (like neon, vfpv3 or vfpv4). If you build with CPU features that are not available on the target machine (e.g., vfpv4 on a Cortex-A8), the resulting executable will abort with a SIGILL (illegal instruction).

The POCO_CONFIG environment (or Makefile) variable should contain the name of the build configuration to use. Therefore, we run:

$ POCO_CONFIG=X-Ubuntu-Bionic-armhf make -s -j8 DEFAULT_TARGET=shared_release

to build macchina.io for the target. The DEFAULT_TARGET=shared_release argument instructs the build system to build the release executables only. If omitted, the build system would build both debug and release executables.

Depending on build machine performance, this takes 20-30 minutes.

Create Tarball for Transfer to Target

To build a directory structure and tarball that can be easily transferred to the target device (e.g., via scp), use the install_runtime make target:

$ POCO_CONFIG=X-Ubuntu-Bionic-armhf make -s install_runtime INSTALLDIR=/home/admin/macchina_runtime

This will create the following directory structure:

macchina_runtime/
    lib/
        bundles/
    bin/
    etc/

Then, pack the macchina_runtime directory into a tarball:

$ tar cfz macchina_runtime.tgz /home/admin/macchina_runtime

Running macchina.io on the Target

Copy the tarball to the target (e.g., using scp) and expand it with:

$ tar xfz macchina_runtime.tgz

Before starting macchina.io, you have to set the LD_LIBRARY_PATH environment variable, so that the macchina.io shared libraries (both in the lib directory and in the codeCache) will be found.

$ export LD_LIBRARY_PATH=/path/to/macchina_runtime/lib:/path/to/macchina_runtime/bin/codeCache

You should also create the codeCache directory before you start macchina.io, so that the dynamic linker will find it. Otherwise, the first start of macchina.io may fail.

$ mkdir /path/to/macchina_runtime/bin/codeCache

Then you can start macchina.io:

$ /path/to/macchina_runtime/bin/macchina --config=/path/to/macchina_runtime/etc/macchina.properties