Skip to content

Cross Compiling for Ubuntu 20.04 armhf with V8 8.x

Günter Obiltschnig edited this page Oct 23, 2023 · 4 revisions

This article describes how to cross-compile macchina.io (with V8 version 8.x) for an 32-bit ARM-based machine (armhf architecture) running Ubuntu 20.04.

Prerequisites

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

  • git
  • build-essential
  • libssl-dev
  • python (3.x)
  • crossbuild-essential-armhf

Preparing the Host System

Cross-compiling V8 for a 32-bit ARM target needs a 32-bit toolchain on the host too. So in addition to installing the cross-compiler for ARM, we also need to install support for building and running 32-bit Intel (i386) programs, as during the V8 build process, code generators are built that must run with the same bit width as the target system.

First, we prepare the package manager to add i386 packages.

$ sudo dpkg --add-architecture i386
$ sudo apt update

Then we add the i386 packages.

$ sudo apt install -y binutils-multiarch libc6-dev:i386 libstdc++-9-dev:i386 gcc-9-multilib g++-9-multilib 

Then 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/ focal main restricted
deb [arch=armhf] http://ports.ubuntu.com/ focal-updates main restricted
deb [arch=armhf] http://ports.ubuntu.com/ focal universe
deb [arch=armhf] http://ports.ubuntu.com/ focal-updates universe
deb [arch=armhf] http://ports.ubuntu.com/ focal multiverse
deb [arch=armhf] http://ports.ubuntu.com/ focal-updates multiverse
deb [arch=armhf] http://ports.ubuntu.com/ focal-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 and i386 architectures. This is done by inserting [arch=amd64,i386] 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,i386] 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,i386] 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 python-is-python3

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 --branch feature/v8-8.x

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-Focal-RPi with the following content:

#
# X-Ubuntu-Focal--RPi
#
# Make settings for Raspberry Pi (2 or newer) Ubuntu 20.04
#
#

#
# 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-vfpv4

#
# 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=gnu++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-Focal-RPi 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-Focal-RPi 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