Project

General

Profile

Actions

Cross-compiling NDN projects for Raspberry Pi » History » Revision 38

« Previous | Revision 38/48 (diff) | Next »
Wentao Shang, 05/21/2014 01:49 PM


Cross-compiling NDN projects for Raspberry Pi

Note: before reading this document, you should already be familiar with the basic concepts of compiling and linking (especially the linking part). If not, you may be interested in reading this great book: Linkers and Loaders

Basic idea

Remember to compile a C/C++ project, we need the source code for the project, the header files for the included libraries, the binary objects of the libraries, and the compiler tools (gcc, as, ld, etc.). The combination of the last three things together is referred to as a building environment. Cross-compiling is no different. To cross compile a project, we first need to setup the building environment and then build the source code in that environment.

Difference between "native compiling" and "cross compiling"

The biggest difference is that for native compiling, you build the binaries that will run on the same platform where you build them. For cross compiling, however, you build the binaries on one platform (called build platform) and run them on another platform (called host or target platform). The platforms may differ in the operating systems (Windows vs. Linux) and/or the CPU architectures (x86_64 vs. arm32).

Raspberry Pi platform information

Raspberry Pi runs on ARMv6 CPU, which is a 32bit chip with hardware float-point support (abbreviated as armhf). There are many operating systems available. The one we are going to use is called Raspbian, which is a port of the Debian "wheezy" Linux distribution.

Creating compiling toolchain for Raspberry Pi

To prepare a building environment, we need to get the gcc/g++ compiler toolchain that will generate binaries for the armhf platform. Raspbian already provided a set of compiling tools on their official github. However, by the time of this writing, those tools only run on 32bit Linux systems. If we want to use 64bit Linux as the build platform, we may need to build our own gcc/g++ toolchain.

The tool we are going to use to build our gcc is ct-ng. It is designed to run on Linux but can be adjusted via some hacks to run on MacOSX. It is pretty easy to use and you may find this article very helpful when building your own toolchain.

Tip: you may use the configuration file from Raspbian github, which will load the "official" configurations for the platform.

Getting libraries ready

After we have the toolchain, the next step is to gather the libraries, including the header files (.h files) and the binaries (.a, .so files). The libraries used by the NDN projects include openssl, Boost, sqlite3, crypto++. Those libraries may recursively depend on other libraries and it will be a big headache to compile all of them from source code and resolve the dependencies manually. Fortunately, Raspbian has a public package repo containing the binaries for most packages available on Debian. So the easy walk-around is to install those libraries directly on Raspberry Pi using apt-get and then copy the relevant files down to our build machine.

On Raspbian, all the header files are in the /usr/include folder, while the binary objects for the libraries are scattered in many places, such as /usr/lib, /lib, etc. A simple way to find the path of a library is to run the following command:

ldconfig -p | grep _library_name_

ldconfig -p will show all the dynamic linking libraries currently available on the system and their locations in the file system.

You may copy all the binaries into the same folder. For example, on the build machine you may have the folder ~/pi/ and inside that folder there are two sub-folders ./include and ./lib. You can copy all the header files into the include folder and the binaries into the lib folder.

Here is the list of library files that you need to get in order to compile the NDN projects. You MAY need other library files for your own project.

.
├── include
│   ├── boost/
│   ├── cryptopp/
│   ├── expat_config.h
│   ├── expat_external.h
│   ├── expat.h
│   ├── openssl/
│   ├── pcap/
│   ├── pcap-bpf.h
│   ├── pcap.h
│   ├── pcap-namedb.h
│   ├── sqlite3ext.h
│   └── sqlite3.h
└── lib
    ├── ld-linux-armhf.so.3
    ├── libboost_chrono.so
    ├── libboost_date_time.so
    ├── libboost_filesystem.so
    ├── libboost_iostreams.so
    ├── libboost_program_options.so
    ├── libboost_regex.so
    ├── libboost_system.so
    ├── libbz2.so.1.0
    ├── libcryptopp.so -> libcrypto++.so
    ├── libcrypto.so
    ├── libcrypto++.so
    ├── libexpat.so
    ├── libicudata.so.48
    ├── libicui18n.so.48
    ├── libicuuc.so.48
    ├── libpcap.so
    ├── libsqlite3.so
    ├── libssl.so
    ├── libz.so.1
    └── pkgconfig

Building the source code

The final step is to compile the projects using the environment we created. The basic idea is to call the cross toolchain and link against the cross-compiled libraries. To do that, we need to export a set of shell variables that are used by the make command. For example, we can export the following variables in the shell:

export AS=/path/to/your/toolchain/as
export LD=/path/to/your/toolchain/ld
export CC=/path/to/your/toolchain/cc
export CXX=/path/to/your/toolchain/c++

This will tell make to use the toolchain we specify instead of the default toolchain.

In order to point the toolchain to the correct location to search libraries, we also need to export CFLAGS/CPPFLAGS and LDFLAGS variables, which will be provided to gcc as options:

export CFLAGS="-I/path/to/your/include/folder -L/path/to/your/lib/folder -Wl,-rpath=/path/to/your/lib/folder"
export LDFLAGS="-L/path/to/your/lib/folder -Wl,-rpath=/path/to/your/lib/folder"

Note that we repeat the LDFLAGS in the CFLAGS. This is because some Makefile script may combine the compiling and linking steps together and use only CFLAGS. (This actually happens when building the NDNx project.)

Another important option is the rpath option. This specifies where the linker will search for recursive dependencies.

Here is a sample script for configuring & compiling NFD. It can be ported to compile other waf based projects as well.

#!/bin/bash

export PATH=/home/wentao/tools/arm-bcm2708/arm-bcm2708hardfp-linux-gnueabi/bin:$PATH

arch="arm-bcm2708hardfp-linux-gnueabi"

export AR="${arch}-ar"
export AS="${arch}-as"
export LD="${arch}-ld"
export CC="${arch}-cc"
export CXX="${arch}-c++"
export RANLIB="${arch}-ranlib"

export CFLAGS="-O2 -std=gnu99 -I/home/wentao/pi/include -L/home/wentao/pi/lib"
export CXXFLAGS="-O2 -g -I/home/wentao/pi/include -L/home/wentao/pi/lib"
export LDFLAGS="-Wl,-rpath=/home/wentao/pi/lib -L/home/wentao/pi/lib"
export PKG_CONFIG_PATH=~/pi/lib/pkgconfig

./waf configure --prefix=/home/wentao/pi --boost-includes=/home/wentao/pi/include --boost-libs=/home/wentao/pi/lib

./waf

After running this script, you may run ./waf install to copy all the generated binaries into the folder you specified in --prefix option at ./waf configure step. In our case, it will be /home/wentao/pi. This is helpful if you are cross-compiling your own libraries (e.g., ndn-cxx) that you want to use to further compile other projects.

Here is the sample script for configuring & compiling NDNx. It can be ported to compile other automake based projects as well.

#!/bin/bash

export PATH=/home/wentao/tools/arm-bcm2708/arm-bcm2708hardfp-linux-gnueabi/bin:$PATH

arch="arm-bcm2708hardfp-linux-gnueabi"

export AR="${arch}-ar"
export AS="${arch}-as"
export LD="${arch}-ld"
export CC="${arch}-cc"
export CXX="${arch}-c++"
export RANLIB="${arch}-ranlib"

export CFLAGS="-O2 -std=gnu99 -I/home/wentao/pi/include -L/home/wentao/pi/lib -Wl,-rpath=/home/wentao/pi/lib"
export LDFLAGS="-L/home/wentao/pi/lib -Wl,-rpath=/home/wentao/pi/lib"

./configure --build="x86_64-linux-gnu" --host="${arch}" --prefix="/home/wentao/pi"

make

Updated by Wentao Shang over 10 years ago · 48 revisions