9. Third-party libraries and apps
9.1 Objectives
- Learn how to leverage existing libraries and applications: how to configure, compile and install them.
To illustrate how to use existing libraries and applications, we will extend the small root filesystem built in the BusyBox lab to add the ALSA libraries and tools to run basic sound support tests.
ALSA stands for Advanced Linux Sound Architecture, and it's the Linux audio subsystem.
We'll see that manually re-using existing libraries is quite tedious, so that more automated procedures are necessary to make it easier. However, learning how to perform these operations manually will significantly help you when you face issues with more automated tools.
9.2 Required tools
-
Ubuntu packages:
meson
9.3 Figuring out library dependencies
We're going to integrate the alsa-utils
and ipcalc
executables.
In our case, the dependency chain for alsa-utils
is quite simple: it only depends on the alsa-lib
library.
Instead, ipcalc
is standalone and thus doesn't have any dependencies.
Of course, all these libraries rely on the C library, which is not mentioned here, because it is already part of the root filesystem built in the BusyBox lab.
You might wonder how to figure out this dependency tree by yourself. Basically, there are several ways, that can be combined:
- Read the library documentation, which often mentions the dependencies.
- Read the help message of the configure script (by running
./configure --help
). - By running the configure script, compiling and looking at the errors.
To configure, compile and install all the components of our system, we're going to start from the bottom of the tree with alsa-lib
, then continue with alsa-utils
. Then, we will also build ipcalc
.
9.4 Preparation
For our cross-compilation work, we will need two separate spaces:
-
A staging space in which we will directly install all the packages: non-stripped versions of the libraries, headers, documentation and other files needed for the compilation.
This staging space can be quite big, but will not be used on our target, only for compiling libraries or applications; -
A target space, in which we will only copy the required files from the staging space: binaries and libraries, after stripping, configuration files needed at runtime, etc.
This target space will take a lot less space than the staging space, and it will contain only the files that are really needed to make the system work on the target.
To sum up, the staging space will contain everything that's needed for compilation, while the target space will contain only what's needed for execution.
Create the $HOME/embedded-linux-qemu-labs/thirdparty/
directory, and inside create two directories: staging
and target
.
For the target, we need a basic system with BusyBox and initialization scripts.
We're going to re-use the system built in the BusyBox lab, so copy this system in the target
directory.
$ LAB_PATH="$HOME/embedded-linux-qemu-labs/thirdparty"
$ mkdir -p "$LAB_PATH/target/"
$ mkdir -p "$LAB_PATH/staging/"
$ cd $LAB_PATH
$ cp -arv ../tinysystem/nfsroot/* target/
...
9.5 Testing
Make sure the target/
directory is exported by your NFS server to your board by modifying /etc/exports
and restarting your NFS server.
You can of course change our previous /srv/nfs/
symlink to point to this directory.
Make your board boot from this new directory through NFS.
$ sudo rm -f /srv/nfs
$ sudo ln -snv "$LAB_PATH/target/" /srv/nfs
'/srv/nfs' -> '/home/me/embedded-linux-qemu-labs/thirdparty/target/'
$ sudo chown -R tftp:tftp /srv/nfs
$ sudo exportfs -ar
$ sudo systemctl restart nfs-kernel-server
Revert U-Boot to use TFTP and NFS:
...
Hit any key to stop autoboot: 0
=> setenv bootcmd "tftp 0x61000000 zImage; tftp 0x62000000 vexpress-v2p-ca9.dtb; bootz 0x61000000 - 0x62000000"
=> setenv bootargs console=ttyAMA0 root=/dev/nfs ip=${ipaddr}::${serverip}:${netmask}:: nfsroot=${serverip}:${servernfs},nfsvers=3,tcp rw
=> saveenv
=> reset
...
9.6 alsa-lib
alsa-lib
is a library supposed to handle the interaction with the ALSA subsystem.
It is available at https://alsa-project.org/.
9.6.1 Source code
Download version 1.2.7.1
, and extract it in $HOME/embeddedlinux-qemu-labs/thirdparty/
.
$ cd $LAB_PATH
$ label="1.2.7.1"
$ wget "https://www.alsa-project.org/files/pub/lib/alsa-lib-${label}.tar.bz2"
$ tar xfv "alsa-lib-${label}.tar.bz2"
$ mv alsa-lib*/ alsa-lib
$ cd alsa-lib/
Tip: if the website for any of the source packages that we need to download in the next sections is down, a great mirror that you can use is http://sources.buildroot.net/.
9.6.2 Configuration
Back to alsa-lib
sources, look at the configure
script and see that it has been generated by autoconf
(the header contains a sentence like Generated by GNU Autoconf 2.69).
Most of the time, autoconf
comes with automake
, that generates Makefiles from Makefile.am
files.
So alsa-lib
uses a rather common build system.
Let's try to configure and build it:
$ export MAKEFLAGS=-j$(nproc)
$ head -n3 configure
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for alsa-lib 1.2.7.1.
$ ./configure
$ make
If you look at the generated binaries, you'll see that they are x86 ones because we compiled the sources with gcc
, the default compiler.
This is obviously not what we want, so let's clean up the generated objects and tell the configure script to use the ARM cross-compiler.
$ file aserver/aserver.o
aserver/aserver.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), with debug_info, not stripped
$ make clean
$ TC_NAME="arm-training-linux-uclibcgnueabihf"
$ TC_BASE="$HOME/x-tools/$TC_NAME"
$ export PATH="$TC_BASE/bin:$PATH"
$ CC=arm-linux-gcc ./configure
...
checking whether we are cross compiling... configure: error: in `/home/me/embedded-linux-qemu-labs/thirdparty/alsa-lib':
configure: error: cannot run C compiled programs.
If you meant to cross compile, use `--host'.
See `config.log' for more details
Of course, the arm-linux-gcc
cross-compiler must be in your PATH
prior to running the configure script.
The CC
environment variable is the classical name for specifying the compiler to use.
Quickly, you should get an error, as highlighted above.
If you look at the config.log
file, you can see that the configure
script compiles a binary with the cross-compiler and then tries to run it on the development workstation. This is a rather usual thing to do for a configure
script, and that's why it tests so early that it's actually doable, and bails out if not.
...
configure:3719: arm-linux-gcc -o conftest conftest.c >&5
configure:3723: $? = 0
configure:3730: ./conftest
arm-binfmt-P: Could not open '/lib/ld-uClibc.so.0': No such file or directory
configure:3734: $? = 255
configure:3741: error: in `/home/me/embedded-linux-qemu-labs/thirdparty/alsa-lib':
configure:3743: error: cannot run C compiled programs.
If you meant to cross compile, use `--host'.
See `config.log' for more details
...
Obviously, it cannot work in our case, and the scripts exits.
The job of the configure
script is to test the configuration of the system.
To do so, it tries to compile and run a few sample applications to test if this library is available, if this compiler option is supported, etc.
But in our case, running the test examples is definitely not possible.
We need to tell the configure
script that we are cross-compiling, and this can be done using the --build
and --host
options, as described in the help
of the configure
script.
...
System types:
--build=BUILD configure for building on BUILD [guessed]
--host=HOST cross-compile to build programs to run on HOST [BUILD]
...
The --build
option allows to specify on which system the package is built, while the --host
option
allows to specify on which system the package will run.
By default, the value of the --build
option is guessed, and the value of --host
is the same as the value of the --build
option.
The value is guessed using the ./config.guess
script, which on your system should return x86_64-pc-linux-gnu
.
See the autoconf
manual
for more details on these options.
So, let's override the value of the --host
option:
Note that CC
is not required anymore. It is implied by --host
.
The configure
script should end properly now, and create a Makefile
.
However, there is one subtle last issue to handle: because the C library used in our toolchain (uClibc-ng) does not support symbol versioning, we need to tell alsa-lib
about this by passing --without-versioned
.
Without this option, alsa-lib
will build fine, but
it will not work properly at runtime.
9.6.3 Build
So, you should configure alsa-lib
and run
the make command, which should run just fine.
Look at the result of compiling in src/.libs/
: a set of object files and a set of libasound.so*
files.
$ ls -l src/.libs/
total 4316
-rw-rw-r-- 1 me me 36148 Apr 10 22:32 async.o
-rw-rw-r-- 1 me me 15832 Apr 10 22:32 confeval.o
-rw-rw-r-- 1 me me 70572 Apr 10 22:32 confmisc.o
-rw-rw-r-- 1 me me 237316 Apr 10 22:32 conf.o
-rw-rw-r-- 1 me me 19052 Apr 10 22:32 dlmisc.o
-rw-rw-r-- 1 me me 11824 Apr 10 22:32 error.o
-rw-rw-r-- 1 me me 18944 Apr 10 22:32 input.o
lrwxrwxrwx 1 me me 15 Apr 10 22:32 libasound.la -> ../libasound.la
-rw-rw-r-- 1 me me 937 Apr 10 22:32 libasound.lai
lrwxrwxrwx 1 me me 18 Apr 10 22:32 libasound.so -> libasound.so.2.0.0
lrwxrwxrwx 1 me me 18 Apr 10 22:32 libasound.so.2 -> libasound.so.2.0.0
-rwxrwxr-x 1 me me 3986260 Apr 10 22:32 libasound.so.2.0.0
-rw-rw-r-- 1 me me 3532 Apr 10 22:32 names.o
-rw-rw-r-- 1 me me 20552 Apr 10 22:32 output.o
-rw-rw-r-- 1 me me 6472 Apr 10 22:32 shmarea.o
-rw-rw-r-- 1 me me 7340 Apr 10 22:32 socket.o
-rw-rw-r-- 1 me me 7536 Apr 10 22:32 userfile.o
The libasound.so*
files are a dynamic version of the library.
The shared library itself is libasound.so.2.0.0
, which was generated by the following command line:
$ arm-linux-gcc -shared conf.o confmisc.o input.o output.o async.o \
error.o dlmisc.o socket.o shmarea.o userfile.o names.o \
-lm -ldl -lpthread -lrt -Wl,-soname -Wl,libasound.so.2 \
-o libasound.so.2.0.0
It creates the symbolic links libasound.so
and libasound.so.2
.
$ ln -s libasound.so.2.0.0 libasound.so.2
$ ln -s libasound.so.2.0.0 libasound.so
These symlinks are needed for two different reasons:
-
libasound.so
is used at compile time when you want to compile an application that is dynamically linked against the library. To do so, you pass the-l<LIBNAME>
option to the compiler, which will look for a file namedlib<LIBNAME>.so
. In our case, the compilation option is-lasound
and the name of the library file islibasound.so
.
So, thelibasound.so
symlink is needed at compile time. -
libasound.so.2
is needed because it is the SONAME of the library. SONAME stands for Shared Object Name. It is the name of the library as it will be stored in applications linked against this library. It means that at runtime, the dynamic loader will look for exactly this name when looking for the shared library.
So, this symbolic link is needed at runtime.
To know what's the SONAME of a library, you can use:
$ arm-linux-readelf -d src/.libs/libasound.so.2.0.0
Dynamic section at offset 0xdff20 contains 24 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libc.so.0]
0x00000001 (NEEDED) Shared library: [ld-uClibc.so.1]
0x0000000e (SONAME) Library soname: [libasound.so.2]
0x0000000c (INIT) 0x23b1c
0x0000000d (FINI) 0xbc6b4
0x00000019 (INIT_ARRAY) 0xed2fc
0x0000001b (INIT_ARRAYSZ) 4 (bytes)
0x0000001a (FINI_ARRAY) 0xed300
0x0000001c (FINI_ARRAYSZ) 8 (bytes)
0x00000004 (HASH) 0x114
0x6ffffef5 (GNU_HASH) 0x4318
0x00000005 (STRTAB) 0x1021c
0x00000006 (SYMTAB) 0x7a7c
0x0000000a (STRSZ) 52374 (bytes)
0x0000000b (SYMENT) 16 (bytes)
0x00000003 (PLTGOT) 0xf0000
0x00000002 (PLTRELSZ) 7776 (bytes)
0x00000014 (PLTREL) REL
0x00000017 (JMPREL) 0x21cbc
0x00000011 (REL) 0x1ceb4
0x00000012 (RELSZ) 19976 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x6ffffffa (RELCOUNT) 2084
0x00000000 (NULL) 0x0
and look at the (SONAME)
line. You'll also see that this library needs the C library, because of the (NEEDED)
line on libc.so.0
.
The mechanism of SONAME allows to change the library without recompiling the applications linked with this library.
Let's imagine that a security problem is found in the alsa-lib
release that provides libasound.so.2.0.0
, and fixed in the next alsa-lib
release, which will now provide libasound.so.2.0.1
.
You can just recompile the library, install it on your target system, change the libasound.so.2
link so that it points to libasound.so.2.0.1
and restart your applications.
It will work, because your applications don't look specifically for libasound.so.2.0.0
but for the SONAME libasound.so.2
.
However, it also means that as a library developer, if you break the ABI of the library, you must change the SONAME: change from libasound.so.2
to libasound.so.3
.
9.6.4 Installation
Finally, the last step is to tell the configure
script where the library is going to be installed.
Most configure
scripts consider that the installation prefix is /usr/local/
(so that the library is installed in /usr/local/lib/
, the headers in /usr/local/include/
, etc.).
Instead, in our system we simply want the libraries to be installed in the /usr/
prefix, so let's tell the configure
script about this:
For this library, this option may not change anything to the resulting binaries. But, for safety it is always recommended to make sure that the prefix matches where your library will be running on the target system.
Do not confuse the prefix (where the application or library will be running on the target system) with the location where the application or library will be installed on your host while building the root filesystem.
For example, libasound
is installed in $HOME/embedded-linux-qemu-labs/thirdparty/target/usr/lib/
because this is the directory where we are building the root filesystem.
But, once our target system is running, it looks for libasound
in /usr/lib/
.
The prefix corresponds to the path in the target system and never on the host.
So, one should never pass a prefix like $HOME/embedded-linux-qemu-labs/thirdparty/target/usr/
, otherwise at runtime the application or library may look for files inside this directory on the target
system, which obviously doesn't exist!
By default, most build systems install the application or library in the given prefix (/usr/
or /usr/local/
), but with most build systems (including autotools
), the installation prefix can be overridden, different from the configuration prefix.
We now only have the installation process left to do.
First, let's make the installation in the staging space:
Now look at what has been installed by alsa-lib
:
-
Some configuration files in
/usr/share/alsa/
: -
The headers in
/usr/include/
: -
The shared library and its
libtool
(.la
) file in/usr/lib/
: -
A pkgconfig file in
/usr/lib/pkgconfig/
— we'll come back to these later:
Finally, let's install the library in the target
space:
-
Create the
target/usr/lib
directory, it will contain the stripped version of the library. -
Copy the dynamic version of the library. Only
libasound.so.2
andlibasound.so.2.0.0
are needed, sincelibasound.so.2
is the SONAME of the library andlibasound.so.2.0.0
is the real binary. -
Measure the size of the
target/usr/lib/libasound.so.2.0.0
library before stripping. -
Strip the library.
-
Measure the size of the
target/usr/lib/libasound.so.2.0.0
library again after stripping. How many unnecessary bytes were saved?
$ cd $LAB_PATH
$ cp -av staging/usr/lib/libasound.so.2* target/usr/lib/
'staging/usr/lib/libasound.so.2' -> 'target/usr/lib/libasound.so.2'
'staging/usr/lib/libasound.so.2.0.0' -> 'target/usr/lib/libasound.so.2.0.0'
$ du -h target/usr/lib/libasound.so.2.0.0
3.8M target/usr/lib/libasound.so.2.0.0
$ arm-linux-strip target/usr/lib/libasound.so.2.0.0
$ du -h target/usr/lib/libasound.so.2.0.0
904K target/usr/lib/libasound.so.2.0.0
Then, we need to install the alsa-lib
configuration files:
Now, we need to adjust one small detail in one of the configuration files.
Indeed, /usr/share/alsa/alsa.conf
assumes a UNIX group called audio
exists, which is not the case on our very small system; let's set to root instead (group id 0
).
Edit this file and replace defaults.pcm.ipc_gid audio
with defaults.pcm.ipc_gid 0
instead.
And we're done with alsa-lib
!
9.7 alsa-utils
9.7.1 Source code
Download alsa-utils
from the ALSA offical webpage. We tested the lab with version 1.2.6
.
Once uncompressed, we quickly discover that the alsa-utils
build system is based on the autotools, so we will work once again with a regular configure
script.
$ cd $LAB_PATH
$ label="1.2.6"
$ wget "https://www.alsa-project.org/files/pub/utils/alsa-utils-${label}.tar.bz2"
$ tar xfv "alsa-utils-${label}.tar.bz2"
$ mv alsa-utils*/ alsa-utils
$ cd alsa-utils/
9.7.2 Configuration
As we've seen previously, we have to provide the prefix, host options, and the CC
variable.
We should quiclky get an error in the execution of the configure script.
$ ./configure --host=arm-linux --prefix=/usr
...
checking for libasound headers version >= 1.2.5 (1.2.5)... not present.
configure: error: Sufficiently new version of libasound not found.
Again, we can check in config.log
what the configure script is trying to do.
...
configure:8080: checking for libasound headers version >= 1.2.5 (1.2.5)
configure:8127: arm-linux-gcc -c -g -O2 conftest.c >&5
conftest.c:12:10: fatal error: alsa/asoundlib.h: No such file or directory
12 | #include <alsa/asoundlib.h>
| ^~~~~~~~~~~~~~~~~~
compilation terminated.
...
Of course, since alsa-utils
uses alsa-lib
, it includes its header file!
So, we need to tell the C compiler where the headers can be found.
They aren't in the default directory /usr/include/
, but in the equivalent directory of our staging space.
The help
text of the configure
script says:
...
Some influential environment variables:
...
CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
you have headers in a nonstandard directory <include dir>
...
Let's use it! Now, it should stop a bit later, this time with another error.
$ CPPFLAGS=-I"$LAB_PATH/staging/usr/include" \
./configure --host=arm-linux --prefix=/usr
...
checking for libasound headers version >= 1.2.5 (1.2.5)... found.
...
checking for snd_ctl_open in -lasound... no
configure: error: No linkable libasound was found.
The configure script tries to compile an application against libasound
(see the -lasound
option): alsa-utils
uses alsa-lib
, so the configure
script wants to make sure this library is already installed.
Unfortunately, the ld
linker doesn't find it. So, let's tell the linker where to look for libraries using the -L
option followed by the directory where our libraries are (in staging/usr/lib
).
This -L
option can be passed to the linker by using the LDFLAGS
at configure
time, as told by the help text of the configure script:
...
Some influential environment variables:
...
LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
nonstandard directory <lib dir>
...
Let's use this LDFLAGS variable:
$ LDFLAGS=-L"$LAB_PATH/staging/usr/lib" \
CPPFLAGS=-I"$LAB_PATH/staging/usr/include" \
./configure --host=arm-linux --prefix=/usr
...
checking panel.h usability... no
checking panel.h presence... no
checking for panel.h... no
configure: error: required curses helper header not found
Once again, it should fail a bit further down the tests, this time complaining about a missing
curses helper header.
curses
(or ncurses
, new curses) is a graphical framework to design text UIs within the terminal.
This is only used by alsamixer
, one of the tools provided by alsa-utils
, that we are not going to use.
Hence, we can just disable the build of alsamixer
. Of course, if we wanted it, we would have had to build ncurses
first, just like we built alsa-lib
.
9.7.3 Build
Then, run the compilation with make
. Hopefully, it works!
$ LDFLAGS=-L"$LAB_PATH/staging/usr/lib" \
CPPFLAGS=-I"$LAB_PATH/staging/usr/include" \
./configure --host=arm-linux --prefix=/usr --disable-alsamixer
$ make
9.7.4 Installation
Let's now begin the installation process. Before really installing in the staging
directory, let's install in a dummy directory, to see what's going to be installed — this dummy directory will not be used afterwards, it is only to verify what will be installed before polluting the staging space.
The DESTDIR
variable can be used with all Makefiles based on automake
.
It allows to override the installation directory: instead of being installed in the configuration prefix
directory, the files will be installed in $DESTDIR/configuration-prefix
.
Now, let's see what has been installed in /tmp/alsa-utils/
;
$ tree /tmp/alsa-utils/
/tmp/alsa-utils/
├── lib
│ ├── systemd
│ │ └── system
│ │ ├── alsa-restore.service
│ │ ├── alsa-state.service
│ │ └── sound.target.wants
│ │ ├── alsa-restore.service -> ../alsa-restore.service
│ │ └── alsa-state.service -> ../alsa-state.service
│ └── udev
│ └── rules.d
│ └── 90-alsa-restore.rules
├── usr
│ ├── bin
│ │ ├── aconnect
│ │ ├── alsabat
│ │ ├── alsaloop
│ │ ├── alsatplg
│ │ ├── alsaucm
│ │ ├── amidi
│ │ ├── amixer
│ │ ├── aplay
│ │ ├── aplaymidi
│ │ ├── arecord -> aplay
│ │ ├── arecordmidi
│ │ ├── aseqdump
│ │ ├── aseqnet
│ │ ├── axfer
│ │ ├── iecset
│ │ └── speaker-test
│ ├── sbin
│ │ ├── alsabat-test.sh
│ │ ├── alsaconf
│ │ ├── alsactl
│ │ └── alsa-info.sh
│ └── share
│ ├── alsa
│ │ ├── init
│ │ │ ├── 00main
│ │ │ ├── ca0106
│ │ │ ├── default
│ │ │ ├── hda
│ │ │ ├── help
│ │ │ ├── info
│ │ │ └── test
│ │ └── speaker-test
│ │ └── sample_map.csv
│ ├── man
│ │ ├── fr
│ │ │ └── man8
│ │ │ └── alsaconf.8
│ │ ├── man1
│ │ │ ├── aconnect.1
│ │ │ ├── alsabat.1
│ │ │ ├── alsactl.1
│ │ │ ├── alsa-info.sh.1
│ │ │ ├── alsaloop.1
│ │ │ ├── amidi.1
│ │ │ ├── amixer.1
│ │ │ ├── aplay.1
│ │ │ ├── aplaymidi.1
│ │ │ ├── arecord.1 -> aplay.1
│ │ │ ├── arecordmidi.1
│ │ │ ├── aseqdump.1
│ │ │ ├── aseqnet.1
│ │ │ ├── axfer.1
│ │ │ ├── axfer-list.1
│ │ │ ├── axfer-transfer.1
│ │ │ ├── iecset.1
│ │ │ └── speaker-test.1
│ │ ├── man7
│ │ └── man8
│ │ └── alsaconf.8
│ └── sounds
│ └── alsa
│ ├── Front_Center.wav
│ ├── Front_Left.wav
│ ├── Front_Right.wav
│ ├── Noise.wav
│ ├── Rear_Center.wav
│ ├── Rear_Left.wav
│ ├── Rear_Right.wav
│ ├── Side_Left.wav
│ └── Side_Right.wav
└── var
└── lib
└── alsa
24 directories, 62 files
So, we have
- The
systemd
service definitions inlib/systemd/
. - The
udev
rules inlib/udev/
. - The
alsa-utils
binaries in/usr/bin/
and/usr/sbin/
. - Some sound samples in
/usr/share/sounds/
. - The various translations in
/usr/share/locale/
. - The manual pages in
/usr/share/man/
, explaining how to use the various tools. - Some configuration samples in
/usr/share/alsa/
.
Now, let's make the installation in the staging
space:
Then, let's manually install only the necessary files in the target
space. We are only interested in speaker-test
:
$ make DESTDIR="$LAB_PATH/staging" install
$ cd $LAB_PATH
$ cp -a staging/usr/bin/speaker-test target/usr/bin/
$ arm-linux-strip target/usr/bin/speaker-test
You may need to add the missing libraries from the toolchain install directory.
We're finally done with alsa-utils
!
9.7.5 Quick test
Now test that all is working fine by running the speaker-test
utility on your board.
Now you can launch your QEMU machine as usual, with rootfs on NFS, and test speaker-test
in a shell:
# speaker-test -t sine -l 1
speaker-test 1.2.6
Playback device is default
Stream parameters are 48000Hz, S16_LE, 1 channels
Sine wave rate is 440.0000Hz
Rate set to 48000Hz (requested 48000Hz)
Buffer size range from 256 to 16384
Period size range from 64 to 1024
Using max buffer size 16384
Periods = 4
was set period_size = 1024
was set buffer_size = 16384
0 - Front Left
Time per period = 2.577584
There you are: you built and ran your first program depending on a library different from the C library!
9.8 ipcalc
After practicing with autotools based packages, let's build ipcalc
, which is using Meson as build system.
We won't really need this utility in our system, but at least it has no dependencies and therefore offers an easy way to build our first Meson based package.
So, first install the meson
package:
9.8.1 Source code
In the main lab directory, then let's check out the sources through git:
$ cd $LAB_PATH
$ label="1.0.1"
$ git clone https://gitlab.com/ipcalc/ipcalc.git
$ cd ipcalc/
$ git checkout -b embedded-linux-qemu $label
Alternatively, you can retrieve an archived release:
$ cd $LAB_PATH
$ label="1.0.1"
$ wget "https://gitlab.com/ipcalc/ipcalc/-/archive/${label}/ipcalc-${label}.tar.bz2"
$ tar xfv "ipcalc-${label}.tar.bz2"
$ mv ipcalc*/ ipcalc
$ cd ipcalc/
9.8.2 Configuration
To cross-compile with Meson, we need to create a cross file.
Let's create the $LAB_PATH/cross-file.txt
file with the following content:
[binaries]
c = 'arm-linux-gcc'
[host_machine]
system = 'linux'
cpu_family = 'arm'
cpu = 'cortex-a9'
endian = 'little'
Command-line shortcut:
$ cd "$LAB_PATH/ipcalc/"
$ cat > ../cross-file.txt <<'EOF'
[binaries]
c = 'arm-linux-gcc'
[host_machine]
system = 'linux'
cpu_family = 'arm'
cpu = 'cortex-a9'
endian = 'little'
EOF
We also need to create a special directory for building:
9.8.3 Build
We can now have Meson create the Ninja build files for us:
$ meson --cross-file ../../cross-file.txt --prefix /usr ..
The Meson build system
...
C compiler for the host machine: arm-linux-gcc (gcc 11.3.0 "arm-linux-gcc (crosstool-NG 1.25.0.95_7622b49) 11.3.0")
C linker for the host machine: arm-linux-gcc ld.bfd 1.25.0.95
C compiler for the build machine: cc (gcc 11.3.0 "cc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0")
C linker for the build machine: cc ld.bfd 2.38
Build machine cpu family: x86_64
Build machine cpu: x86_64
Host machine cpu family: arm
Host machine cpu: cortex-a9
Target machine cpu family: arm
Target machine cpu: cortex-a9
...
Build targets in project: 1
ipcalc 1.0.1
User defined options
Cross files: ../../cross-file.txt
prefix : /usr
Found ninja-1.10.1 at /usr/bin/ninja
WARNING: Cross file does not specify strip binary, result will not be stripped.
We're finally ready to build ipcalc
:
9.8.4 Installation
And now install ipcalc
into the staging
space.
Check that the staging/usr/bin/ipcalc
file is indeed an ARM executable!
$ DESTDIR="$LAB_PATH/staging" ninja install
[0/1] Installing files.
Installing ipcalc to /home/me/embedded-linux-qemu-labs/thirdparty/staging/usr/bin
$ file "$LAB_PATH/staging/usr/bin/ipcalc"
/home/me/embedded-linux-qemu-labs/thirdparty/staging/usr/bin/ipcalc: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, with debug_info, not stripped
The last thing to do is to copy it to the target space and strip it.
Note that we could have asked ninja install
to strip the executable for us when installing it into the staging
directory.
To do, this, we would have added a strip
entry in the cross file, and passed --strip
to Meson.
However, it's better to keep files unstripped in the staging
space, in case we need to debug them!
9.8.5 Quick test
You can now test that ipcalc
works on the target:
You can now halt
and quit QEMU.
9.9 Final touch
To finish this lab completely, and to be consistent with what we've done before, let's strip the C library and its loader too. Let's compare the size of the binaries to confirm.
$ ls -l target/lib/
total 1248
-r-xr-xr-x 1 me me 69584 Apr 8 15:14 ld-uClibc.so.0
-r-xr-xr-x 1 me me 69584 Apr 8 15:14 ld-uClibc.so.1
-r-xr-xr-x 1 me me 635188 Apr 8 15:14 libc.so.0
-r-xr-xr-x 1 me me 635188 Apr 8 15:14 libc.so.1
$ chmod +w target/lib/*.so.*
$ arm-linux-strip target/lib/*.so.*
$ ls -l target/lib/
total 1040
-rwxrwxr-x 1 me me 66460 Apr 12 20:35 ld-uClibc.so.0
-rwxrwxr-x 1 me me 66460 Apr 12 20:35 ld-uClibc.so.1
-rwxrwxr-x 1 me me 460764 Apr 12 20:35 libc.so.0
-rwxrwxr-x 1 me me 460764 Apr 12 20:35 libc.so.1
9.10 Backup and restore
$ tar cfJv thirdparty-staging.tar.xz staging/
$ tar cfJv thirdparty-target.tar.xz target/
$ cd target/
$ find . -depth -print0 | cpio -ocv0 | xz > ../thirdparty-target.cpio.xz
9.11 Licensing
This document is an extension to: Embedded Linux System Development - Practical Labs - QEMU Variant
— © 2004-2023, Bootlin https://bootlin.com/, CC-BY-SA-3.0
license.