Lab Book
free electrons
http://free-electrons.com
About this document
Updates to this document can be found on http://free-electrons.com/doc/training/embedded-linux/.
This document was generated from LaTeX sources found onhttp://git.free-electrons.com/ training-materials.
More details about our training sessions can be found onhttp://free-electrons.com/training.
Copying this document
© 2004-2016, Free Electrons,http://free-electrons.com.
This document is released under the terms of the Creative Commons CC BY-SA 3.0 license. This means that you are free to download, distribute and even modify it, under certain conditions.
Training setup
Download files and directories used in practical labs
Install lab data
For the different labs in this course, your instructor has prepared a set of data (kernel images, kernel configurations, root filesystems and more). Download and extract its tarball from a terminal:
cd
wget http://free-electrons.com/doc/training/embedded-linux/embedded-linux-labs.tar.xz sudo tar Jvxf embedded-linux-labs.tar.xz
sudo chown -R <user>.<group> embedded-linux-labs
Note thatrootpermissions are required to extract the character and block device files contained in this lab archive. This is an exception. For all the other archives that you will handle during the practical labs, you will never need root permissions to extract them. If there is another exception, we will let you know.
Lab data are now available in an embedded-linux-labsdirectory in your home directory. For each lab there is a directory containing various data. This directory will also be used as working space for each lab, so that the files that you produce during each lab are kept separate. You are now ready to start the real practical labs!
Install extra packages
Ubuntu comes with a very limited version of the vieditor. Install vim, a improved version of this editor.
sudo apt-get install vim
More guidelines
Can be useful throughout any of the labs
• Read instructions and tips carefully. Lots of people make mistakes or waste time because they missed an explanation or a guideline.
• Always read error messages carefully, in particular the first one which is issued. Some people stumble on very simple errors just because they specified a wrong file path and didn’t pay enough attention to the corresponding error message.
• Never stay stuck with a strange problem more than 5 minutes. Show your problem to your colleagues or to the instructor.
• You should only use the rootuser for operations that require super-user privileges, such as: mounting a file system, loading a kernel module, changing file ownership, configuring
the network. Most regular tasks (such as downloading, extracting sources, compiling...) can be done as a regular user.
• If you ran commands from a root shell by mistake, your regular user may no longer be able to handle the corresponding generated files. In this case, use thechown -Rcommand to give the new files back to your regular user.
Building a cross-compiling toolchain
Objective: Learn how to compile your own cross-compiling toolchain
for the uClibc C library
After this lab, you will be able to: • Configure thecrosstool-ngtool
• Execute crosstool-ngand build up your own cross-compiling toolchain
Setup
Go to the$HOME/embedded-linux-labs/toolchaindirectory.
Install needed packages
Install the packages needed for this lab:
sudo apt-get install autoconf automake libtool libexpat1-dev \ libncurses5-dev bison flex patch curl cvs texinfo git bc \ build-essential subversion gawk python-dev gperf unzip \ pkg-config wget help2man
Tip: instead of typing all the above package names, fetch the electronic version of these instruc-tions and copy/paste the long command line!
Getting Crosstool-ng
Let’s download the 1.20.0 release of Crosstool-ng, through its git source repository:
git clone git://crosstool-ng.org/crosstool-ng cd crosstool-ng/
git checkout -b crosstool-ng-1.22.0
Note that if cloning through git:// doesn’t work due to network restrictions, you can clone throughhttp://instead.
Installing Crosstool-ng
We can either install Crosstool-ng globally on the system, or keep it locally in its download direc-tory. We’ll choose the latter solution. As documented indocs/2\ -\ Installing\ crosstool-NG.txt, do:
autoreconf
./configure --enable-local make
make install
./ct-ng help
Configure the toolchain to produce
A single installation of Crosstool-ng allows to produce as many toolchains as you want, for different architectures, with different C libraries and different versions of the various components. Crosstool-ng comes with a set of ready-made configuration files for various typical setups: Crosstool-ng calls themsamples. They can be listed by using./ct-ng list-samples.
We will use thearm-cortexa5-linux-uclibcgnueabihfsample. It can be loaded by issuing:
./ct-ng arm-cortexa5-linux-uclibcgnueabihf
Then, to refine the configuration, let’s run themenuconfiginterface:
./ct-ng menuconfig
InPath and misc options:
• ChangePrefix directoryto /usr/local/xtools/${CT_TARGET}. This is the place where the toolchain will be installed.
• Change Maximum log level to seeto DEBUG so that we can have more details on what happened during the build in case something went wrong.
InToolchain options:
• SetTuple’s aliasto arm-linux. This way, we will be able to use the compiler as arm-linux-gcc instead of arm-cortexa5-linux-uclibcgnueabihf-gcc, which is much longer to type.
InC-library:
• Enable theIPv6support. InDebug facilities:
• Make sure thatgdb,straceandltrace) are enabled. • Remove the remaining options (dmallocandduma).
• Ingdboptions, make sure that theCross-gdbandBuild a static gdbserveroptions are enabled; the other options are not needed.
Explore the different other available options by traveling through the menus and looking at the help for some of the options. Don’t hesitate to ask your trainer for details on the available options. However, remember that we tested the labs with the configuration described above. You might waste time with unexpected issues if you customize the toolchain configuration.
Produce the toolchain
First, create the directory /usr/local/xtools/ and change its owner to your user, so that Crosstool-ng can write to it.
Then, create the directory$HOME/srcin which Crosstool-NG will save the tarballs it will down-load.
Nothing is simpler:
And wait!
Known issues
Source archives not found on the Internet
It is frequent that Crosstool-ng aborts because it can’t find a source archive on the Internet, when such an archive has moved or has been replaced by more recent versions. New Crosstool-ng versions ship with updated URLs, but in the meantime, you need work-arounds.
If this happens to you, what you can do is look for the source archive by yourself on the Internet, and copy such an archive to the src directory in your home directory. Note that even source archives compressed in a different way (for example, ending with.gz instead of .bz2) will be fine too. Then, all you have to do is run./ct-ng buildagain, and it will use the source archive that you downloaded.
ppl-0.10.2 compiling error with gcc 4.7.1
If you are using gcc 4.7.1, for example in Ubuntu 12.10 (not officially supported in these labs), compilation will fail inppl-0.10.2 with the below error:
error: ’f_info’ was not declared in this scope
One solution is to add the-fpermissiveflag to theCT_EXTRA_FLAGS_FOR_HOSTsetting (inPath and misc options -> Extra host compiler flags).
Testing the toolchain
You can now test your toolchain by adding/usr/local/xtools/arm-cortexa5-linux-uclibcgnueabihf/ bin/to yourPATHenvironment variable and compiling the simplehello.cprogram in your main
lab directory witharm-linux-gcc.
You can use thefilecommand on your binary to make sure it has correctly been compiled for the ARM architecture.
Cleaning up
To save about 6 GB of storage space, do a./ct-ng cleanin the Crosstool-NG source directory. This will remove the source code of the different toolchain components, as well as all the gen-erated files that are now useless since the toolchain has been installed in/usr/local/xtools.
Bootloader - U-Boot
Objectives: Set up serial communication, compile and install the
U-Boot bootloader, use basic U-Boot commands, set up TFTP
com-munication with the development workstation.
As the bootloader is the first piece of software executed by a hardware platform, the installation procedure of the bootloader is very specific to the hardware platform. There are usually two cases:
• The processor offers nothing to ease the installation of the bootloader, in which case the JTAG has to be used to initialize flash storage and write the bootloader code to flash. Detailed knowledge of the hardware is of course required to perform these operations. • The processor offers a monitor, implemented in ROM, and through which access to the
memories is made easier.
The Xplained board, which uses the SAMA5D3 SoCs, falls into the second category. The monitor integrated in the ROM reads the MMC/SD card to search for a valid bootloader before looking at the internal NAND flash for a bootloader. In case nothing is available, it will operate in a fallback mode, that will allow to use an external tool to reflash some bootloader through USB. Therefore, either by using an MMC/SD card or that fallback mode, we can start up a SAMA5D3-based board without having anything installed on it.
Downloading Atmel’s flashing tool
Go to the~/embedded-linux-labs/bootloaderdirectory.
We’re going to use that fallback mode, and its associated tool,sam-ba. We first need to download this tool, from Atmel’s website1.
wget http://www.atmel.com/Images/sam-ba_2.15.zip unzip sam-ba_2.15.zip
To runsam-ba, you will need to install the below libraries:
sudo apt-get install libxss1 libxft2
Setting up serial communication with the board
Plug the USB-to-serial cable on the Xplained board. The blue end of the cable is going toGND
onJ23, red on RXD and green on TXD. When plugged in your computer, a serial port should appear,/dev/ttyUSB0.
You can also see this device appear by looking at the output ofdmesg.
To communicate with the board through the serial port, install a serial communication program, such aspicocom:
sudo apt-get install picocom
You also need to make your user belong to thedialoutgroup to be allowed to write to the serial console:
sudo adduser $USER dialout
You need to log out and in again for the group change to be effective.
Runpicocom -b 115200 /dev/ttyUSB0, to start serial communication on/dev/ttyUSB0, with a baudrate of 115200.
You can now power-up the board by connecting the micro-USB cable to the board, and to your PC at the other end. If a system was previously installed on the board, you should be able to interact with it through the serial line.
If you wish to exitpicocom, press[Ctrl][a]followed by [Ctrl][x].
AT91Bootstrap Setup
The boot process is done in two steps with the ROM monitor trying to execute a first piece of software, called AT91Bootstrap, from its internal SRAM, that will initialize the DRAM, load
U-Bootthat will in turn load Linux and execute it.
As far as bootloaders are concerned, the layout of the NAND flash will look like:
• Offset0x0for the first stage bootloader is dictated by the hardware: the ROM code of the SAMA5D3 looks for a bootloader at offset0x0 in the NAND flash.
• Offset 0x40000 for the second stage bootloader is decided by the first stage bootloader. This can be changed by changing the AT91Bootstrap configuration.
• Offset0xc0000of the U-Boot environment is decided by U-Boot. This can be changed by modifying the U-Boot configuration.
The first item to compile is AT91Bootstrap that you can fetch from Atmel’s GitHub account:
git clone git://github.com/linux4sam/at91bootstrap.git cd at91bootstrap
Then, we first need to configure the build system for our setup. We’re going to need a few pieces of information for this:
• Which board you want to run AT91Bootstrap on • Which device should AT91Bootstrap will be stored on • What component you want AT91Boostrap to load
You can get the list of the supported boards by listing theboard directory. You’ll see that in each of these folders, we have a bunch ofdefconfig files, that are the supported combinations. In our case, we will load U-Boot, from NAND flash (nfin thedefconfig file names).
After finding the rightdefconfig file, load it usingmake <defconfig_filename> (just the file name, without the directory part).
In recent versions of AT91Bootstrap, you can now run make menuconfig to explore options available in this program.
The next thing to do is to specific the cross-compiler prefix (the part before gcc in the cross-compiler executable name):
export
CROSS_COMPILE=arm-linux-You can now start compiling usingmake2.
At the end of the compilation, you should have a file called sama5d3_xplained-nandflashboot-uboot-*.bin, in the binariesfolder.
In order to flash it, we need to do a few things. First, remove theNAND CSjumper on the board. It’s next to the pin header closest to the Micro-USB plug. Now, press the RESET button. On the serial port, you should seeRomBoot.
Put the jumper back.
Then, startsam-ba(orsam-ba_64if using a 64 bit installation of Ubuntu). Run the executable from where it was extracted. You’ll get a small window. Select thettyACM0connection, and the
at91sama5d3x-ekboard. HitConnect. You need to:
• Hit theNANDFlashtab
• In theScripts choices, selectEnable NandFlashand hit Execute
• SelectErase All, and execute the command
• Then, select and executeEnable OS PMECC parametersin order to change the NAND ECC parameters to what RomBOOT expects. Change the number of ECC bits to4, and the ECC offset to36.
• Finally, send the image we just compiled using the commandSend Boot File
AT91Bootstrap should be flashed now, keepsam-baopen, and move to the next section.
U-Boot setup
Download U-Boot:
wget ftp://ftp.denx.de/pub/u-boot/u-boot-2015.04.tar.bz2
2You can speed up the compiling by using the-jXoption withmake, where X is the number of parallel jobs used for compiling. Twice the number of CPU cores is a good value.
We’re going to use a specific U-Boot version, 2015.04, which we have tested to work on the Atmel Xplained board. More recent versions may also work, but we have not tested them. Extract the source archive and get an understanding of U-Boot’s configuration and compilation steps by reading theREADMEfile, and specifically theBuilding the Softwaresection.
Basically, you need to:
• Set theCROSS_COMPILEenvironment variable;
• Run make <NAME>_defconfig, where <NAME> is the name of your board as declared in
boards.cfg. There are two flavors of the Xplained configuration: one to run from the SD card (sama5d3_xplained_mmc) and one to run from the NAND flash (sama5d3_xplained_ nandflash). Since we’re going to boot on the NAND, use the latter. Note that for our platform, both these choices are sharing most of their configuration, that is defined in
include/configs/sama5d3_xplained.h. Read this file to get an idea of how a U-Boot configuration file is written;
• Now that you have a valid initial configuration, you can now run make menuconfig to further edit your bootloader features.
• Finally, run make, which should build U-Boot.
Now, in sam-ba, in the Send File Name field, set the path to the u-boot.bin that was just compiled, and set the address to0x40000. Click on the Send Filebutton.
You can now exitsam-ba.
Testing U-Boot
Reset the board and check that it boots your new bootloaders. You can verify this by checking the build dates:
AT91Bootstrap 3.7.1 (Wed Oct 28 06:48:23 CET 2015) NAND: ONFI flash detected
NAND: Manufacturer ID: 0x2c Chip ID: 0x32 NAND: Disable On-Die ECC
NAND: Initialize PMECC params, cap: 0x4, sector: 0x200 NAND: Image: Copy 0x80000 bytes from 0x40000 to 0x26f00000 NAND: Done to load image
U-Boot 2015.04 (Oct 28 2015 - 07:11:52) CPU: SAMA5D36 Crystal frequency: 12 MHz CPU clock : 528 MHz Master clock : 132 MHz DRAM: 256 MiB NAND: 256 MiB MMC: mci: 0
*** Warning - bad CRC, using default environment In: serial
Err: serial Net: gmac0
Error: gmac0 address not set. , macb0
Error: macb0 address not set. Hit any key to stop autoboot: 0
Interrupt the countdown to enter the U-Boot shell:
U-Boot #
In U-Boot, type thehelpcommand, and explore the few commands available.
Setting up Ethernet communication
Later on, we will transfer files from the development workstation to the board using the TFTP protocol, which works on top of an Ethernet connection.
To start with, install and configure a TFTP server on your development workstation, as detailed in the bootloader slides.
With a network cable, connect the Ethernet port labelled ETH0/GETH of your board to the one of your computer. If your computer already has a wired connection to the network, your instructor will provide you with a USB Ethernet adapter. A new network interface, probably
eth1or eth2, should appear on your Linux system.
To configure this network interface on the workstation side, click on theNetwork Managertasklet on your desktop, and selectEdit Connections.
In the IPv4 Settingstab, press the Add button and then choose theManualmethod to make the interface use a static IP address, like 192.168.0.1(of course, make sure that this address belongs to a separate network segment from the one of the main company network).
You can use 255.255.255.0as Netmask, and leave theGatewayfield untouched (if you click on the Gateway box, you will have to type a valid IP address, otherwise you won’t be allowed to click on theApplybutton).
Now, configure the network on the board in U-Boot by setting theipaddrand serverip envi-ronment variables:
setenv ipaddr 192.168.0.100 setenv serverip 192.168.0.1
setenv ethaddr 12:34:56:ab:cd:ef
In case the board was previously configured in a different way, we also turn off automatic booting after commands that can be used to copy a kernel to RAM:
setenv autostart no
To make these settings permanent, save the environment:
saveenv
Now reset your board3.
You can then test the TFTP connection. First, put a small text file in the directory exported through TFTP on your development workstation. Then, from U-Boot, do:
tftp 0x22000000 textfile.txt
Caution: known issue in Ubuntu 12.04 and later (still present in Ubuntu 14.04): if download through tftp doesn’t work, you may have to stop the tftpd-hpaserver and start it again every time you boot your workstation:
sudo service tftpd-hpa restart
The problem is Ubuntu starts this server too early, before the preconditions it needs are met. When you restart the service long after the machine has booted, this issue is no longer present. If it still doesn’t work, another (radical!) workaround is to reinstall thetftpd-hpaserver!
sudo apt-get install --reinstall tftpd-hpa
So far, we haven’t had the time yet to investigate the root cause of the issue that is addressed by this last workaround.
The tftp command should have downloaded the textfile.txt file from your development workstation into the board’s memory at location0x220000004.
You can verify that the download was successful by dumping the contents of the memory:
md 0x22000000
We will see in the next labs how to use U-Boot to download, flash and boot a kernel.
Rescue binaries
If you have trouble generating binaries that work properly, or later make a mistake that causes you to loose your bootloader binaries, you will find working versions underdata/in the current lab directory.
3Resetting your board is needed to make yourethaddrpermanent, for obscure reasons. If you don’t, U-boot will complain thatethaddris not set.
4This location is part of the board DRAM. If you want to check where this value comes from, you can check the Atmel SAMA5D3 datasheet athttp://www.atmel.com/tools/ATSAMA5D3-XPLD.aspx, following theDocuments
link. It’s a big document (more than 1,800 pages). In this document, look forMemory Mapping and you will find the SoC memory map. You will see that the address range for the memory controller (DDRC S) starts at0x20000000and ends at0x3fffffff. This shows that the0x22000000address is within the address range for RAM. You can also try with other values in the same address range, knowing that our board only has 256 MB of RAM (that’s0x10000000, so the physical RAM probably ends at0x30000000).
Kernel sources
Objective: Learn how to get the kernel sources and patch them.
After this lab, you will be able to:
• Get the kernel sources from the official location • Apply kernel patches
Setup
Create the$HOME/embedded-linux-labs/kerneldirectory and go into it.
Get the sources
Go to the Linux kernel web site (http://www.kernel.org/) and identify the latest stable version. Just to make sure you know how to do it, check the version of the Linux kernel running on your machine.
We will uselinux-4.3.x, which this lab was tested with.
To practice with thepatchcommand later, download the full 4.2 sources. Unpack the archive, which creates alinux-4.2directory. Remember that you can usewget <URL> on the command line to download files.
Apply patches
Download the 2 patch files corresponding to the latest 4.3 stable release: a first patch to move from 4.2 to 4.3 and a second patch to move from 4.3 to 4.3.x.
Without uncompressing them (!), apply the 2 patches to the Linux source directory.
View one of the 2 patch files with vior gvim(if you prefer a graphical editor), to understand the information carried by such a file. How are described added or removed files?
Kernel - Cross-compiling
Objective: Learn how to cross-compile a kernel for an ARM target
platform.
After this lab, you will be able to:
• Set up a cross-compiling environment
• Configure the kernel Makefile accordingly
• Cross compile the kernel for the Atmel SAMA5D3 Xplained ARM board
• Use U-Boot to download the kernel
• Check that the kernel you compiled starts the system
Setup
Go to the$HOME/embedded-linux-labs/kerneldirectory.
Install the packagelibqt4-devwhich is needed for the xconfigkernel configuration interface.
Target system
We are going to cross-compile and boot a Linux kernel for the Atmel SAMA5D3 Xplained board.
Kernel sources
We will re-use the kernel sources downloaded and patched in the previous lab.
Cross-compiling environment setup
To compile Linux, you need to have a compiling toolchain. We will use the cross-compiling toolchain that we previously produced, so we just need to make it available in the PATH:
export PATH=/usr/local/xtools/arm-cortexa5-linux-uclibcgnueabihf/bin:$PATH
Also, don’t forget to either:
• Define the value of the ARCH and CROSS_COMPILE variables in your environment (using
export)
• Or specify them on the command line at every invocation of make, i.e: make ARCH=... CROSS_COMPILE=... <target>
Linux kernel configuration
By runningmake help, find the proper Makefile target to configure the kernel for the Xplained board (hint: the default configuration is not named after the board, but after the SoC name). Once found, use this target to configure the kernel with the ready-made configuration.
Don’t hesitate to visualize the new settings by runningmake xconfigafterwards!
In the kernel configuration, as an experiment, change the kernel compression from Gzip to XZ. This compression algorithm is far more efficient than Gzip, in terms of compression ratio, at the expense of a higher decompression time.
Cross compiling
You’re now ready to cross-compile your kernel. Simply run:
make
and wait a while for the kernel to compile. Don’t forget to usemake -j<n>if you have multiple cores on your machine!
Look at the end of the kernel build output to see which file contains the kernel image. You can also see the Device Tree.dtbfiles which got compiled. Find which.dtbfile corresponds to your board.
Copy the linux kernel image and DTB files to the TFTP server home directory.
Load and boot the kernel using U-Boot
We will use TFTP to load the kernel image on the Xplained board:
• On your workstation, copy the zImage and DTB files to the directory exposed by the TFTP server.
• On the target (in the U-Boot prompt), load zImage from TFTP into RAM at address 0x21000000:
tftp 0x21000000 zImage
• Now, also load the DTB file into RAM at address 0x22000000:
tftp 0x22000000 at91-sama5d3_xplained.dtb
• Boot the kernel with its device tree:
bootz 0x21000000 - 0x22000000
You should see Linux boot and finally crashing. This is expected: we haven’t provided a working root filesystem for our device yet.
You can now automate all this every time the board is booted or reset. Reset the board, and specify a differentbootcmd:
setenv bootcmd ’tftp 0x21000000 zImage; tftp 0x22000000 at91-sama5d3_xplained.dtb; bootz 0x21000000 - 0x22000000’ saveenv
Flashing the kernel and DTB in NAND flash
In order to let the kernel boot on the board autonomously, we can flash the kernel image and DTB in the NAND flash available on the Xplained board.
After storing the first stage bootloader, U-boot and its environment variables, we will keep special areas in NAND flash for the DTB and Linux kernel images:
So, let’s start by erasing the corresponding 128 KiB of NAND flash for the DTB:
nand erase 0x140000 0x20000 (NAND offset) (size)
Then, let’s erase the 5 MiB of NAND flash for the kernel image:
nand erase 0x160000 0x500000
Then, copy the DTB and kernel binaries from TFTP into memory, using the same addresses as before.
Then, flash the DTB and kernel binaries:
nand write 0x22000000 0x140000 0x20000 (RAM addr) (NAND offset) (size) nand write 0x21000000 0x160000 0x500000
Power your board off and on, to clear RAM contents. We should now be able to load the DTB and kernel image from NAND and boot with:
nand read 0x22000000 0x140000 0x20000 (RAM addr) (offset) (size) nand read 0x21000000 0x160000 0x500000 bootz 0x21000000 - 0x22000000
Write a U-Boot script that automates the DTB + kernel download and flashing procedure. Finally, using editenv bootcmd, adjust bootcmd so that the Xplained board boots using the kernel in flash.
Now, reset the board to check that it boots fine from NAND flash. Check that this is really your own version of the kernel that’s running.
Tiny embedded system with
Busy-Box
Objective: making a tiny yet full featured embedded system
After this lab, you will:
• be able to configure and build a Linux kernel that boots on a directory on your workstation, shared through the network by NFS.
• be able to create and configure a minimalistic root filesystem from scratch (ex nihilo, out of nothing, entirely hand made...) for the Xplained board
• understand how small and simple an embedded Linux system can be. • be able to install BusyBox on this filesystem.
• be able to create a simple startup script based on /sbin/init. • be able to set up a simple web interface for the target.
• have an idea of how much RAM a Linux kernel smaller than 1 MB needs.
Lab implementation
While (s)he develops a root filesystem for a device, a developer needs to make frequent changes to the filesystem contents, like modifying scripts or adding newly compiled programs.
It isn’t practical at all to reflash the root filesystem on the target every time a change is made. Fortunately, it is possible to set up networking between the development workstation and the target. Then, workstation files can be accessed by the target through the network, using NFS. Unless you test a boot sequence, you no longer need to reboot the target to test the impact of script or application updates.
Setup
Kernel configuration
We will re-use the kernel sources from our previous lab, in$HOME/embedded-linux-labs/kernel/. In the kernel configuration built in the previous lab, verify that you have all options needed for booting the system using a root filesystem mounted over NFS, and if necessary, enable them and rebuild your kernel.
Setting up the NFS server
Create anfsrootdirectory in the current lab directory. Thisnfsrootdirectory will be used to store the contents of our new root filesystem.
Install the NFS server by installing the nfs-kernel-server package if you don’t have it yet. Once installed, edit the/etc/exportsfile as root to add the following line, assuming that the IP address of your board will be192.168.0.100:
/home/<user>/embedded-linux-labs/tinysystem/nfsroot 192.168.0.100(rw,no_root_squash,no_subtree_check)
Make sure that the path and the options are on the same line. Also make sure that there is no space between the IP address and the NFS options, otherwise default options will be used for this IP address, causing your root filesystem to be read-only.
Then, restart the NFS server:
sudo service nfs-kernel-server restart
Booting the system
First, boot the board to the U-Boot prompt. Before booting the kernel, we need to tell it that the root filesystem should be mounted over NFS, by setting some kernel parameters.
Use the following U-Boot command to do so,in just 1 line
setenv bootargs console=ttyS0,115200 root=/dev/nfs ip=192.168.0.100:::::eth0 nfsroot=192.168.0.1:/home/<user>/embedded-linux-labs/tinysystem/nfsroot rw
Of course, you need to adapt the IP addresses to your exact network setup. Save the environment variables (withsaveenv).
You will later need to make changes to thebootargsvalue. Don’t forget you can do this with theeditenv command.
Now, boot your system. The kernel should be able to mount the root filesystem over NFS:
VFS: Mounted root (nfs filesystem) on device 0:13.
If the kernel fails to mount the NFS filesystem, look carefully at the error messages in the console. If this doesn’t give any clue, you can also have a look at the NFS server logs in/var/log/syslog. However, at this stage, the kernel should stop because of the below issue:
[ 7.476715] devtmpfs: error mounting -2
This happens because the kernel is trying to mount thedevtmpfsfilesystem in/dev/in the root filesystem. To address this, create adev directory undernfsrootand reboot.
Now, the kernel should complain for the last time, saying that it can’t find an init application:
Kernel panic - not syncing: No working init found. Try passing init= option to kernel. See Linux Documentation/init.txt for guidance.
Obviously, our root filesystem being mostly empty, there isn’t such an application yet. In the next paragraph, you will add Busybox to your root filesystem and finally make it usable.
Root filesystem with Busybox
Download the sources of the latest BusyBox 1.23.x release.
To configure BusyBox, we won’t be able to use make xconfig, which is currently broken for BusyBox in Ubuntu 14.04, because of Qt library dependencies.
We are going to usemake gconfigthis time. Before doing this, install the required packages:
sudo apt-get install libglade2-dev
Now, configure BusyBox with the configuration file provided in thedata/directory (remember that the Busybox configuration file is.configin the Busybox sources).
If you don’t use the BusyBox configuration file that we provide, at least, make sure you build BusyBox statically! Compiling Busybox statically in the first place makes it easy to set up the system, because there are no dependencies on libraries. Later on, we will set up shared libraries and recompile Busybox.
Build BusyBox using the toolchain that you used to build the kernel.
Going back to the BusyBox configuration interface specify the installation directory for Busy-Box5. It should be the path to yournfsrootdirectory.
Now run make installto install BusyBox in this directory.
Try to boot your new system on the board. You should now reach a command line prompt, allowing you to execute the commands of your choice.
Virtual filesystems
Run theps command. You can see that it complains that the/proc directory does not exist. The ps command and other process-related commands use the proc virtual filesystem to get their information from the kernel.
From the Linux command line in the target, create theproc, sys and etc directories in your root filesystem.
Now mount theprocvirtual filesystem. Now that/procis available, test again thepscommand. Note that you can also now halt your target in a clean way with thehaltcommand, thanks to
procbeing mounted6.
System configuration and startup
The first user space program that gets executed by the kernel is/sbin/initand its configuration file is/etc/inittab.
In the BusyBox sources, read details about/etc/inittabin theexamples/inittabfile. Then, create a /etc/inittab file and a /etc/init.d/rcS startup script declared in /etc/ inittab. In this startup script, mount the /procand/sysfilesystems.
5You will find this setting inInstall Options -> BusyBox installation prefix.
6haltcan find the list of mounted filesystems in/proc/mounts, and unmount each of them in a clean way before shutting down.
Any issue after doing this?
Switching to shared libraries
Take the hello.c program supplied in the lab data directory. Cross-compile it for ARM, dynamically-linked with the libraries, and run it on the target.
You will first encounter a very misleading not found error, which is not because the hello
executable is not found, but because something else is not found using the attempt to execute this executable. What’s missing is theld-uClibc.so.0executable, which is the dynamic linker required to execute any program compiled with shared libraries. Using thefindcommand (see examples in your command memento sheet), look for this file in the toolchain install directory, and copy it to thelib/directory on the target.
Then, running the executable again and see that the loader executes and finds out which shared libraries are missing.
If you still get the same error message, work, just try again a few seconds later. Such a delay can be needed because the NFS client can take a little time (at most 30-60 seconds) before seeing the changes made on the NFS server.
Similarly, find the missing libraries in the toolchain and copy them tolib/on the target. Once the small test program works, we are going to recompile Busybox without the static compilation option, so that Busybox takes advantages of the shared libraries that are now present on the target.
Before doing that, measure the size of thebusyboxexecutable.
Then, build Busybox with shared libraries, and install it again on the target filesystem. Make sure that the system still boots and see how much smaller thebusybox executable got.
Implement a web interface for your device
Replicatedata/www/ to the/wwwdirectory in your target root filesystem. Now, run the BusyBox http server from the target command line:
/usr/sbin/httpd -h /www/
It will automatically background itself.
If you use a proxy, configure your host browser so that it doesn’t go through the proxy to connect to the target IP address, or simply disable proxy usage. Now, test that your web interface works well by openinghttp://192.168.0.100on the host.
Filesystems - Block file systems
Objective: configure and boot an embedded Linux system relying on
block storage
After this lab, you will be able to: • Manage partitions on block storage. • Produce file system images.
• Configure the kernel to use these file systems • Use the tmpfs file system to store temporary files
Goals
After doing the A tiny embedded system lab, we are going to copy the filesystem contents to the MMC flash drive. The filesystem will be split into several partitions, and your sama5d3 X-plained board will be booted with this MMC card, without using NFS anymore.
Setup
Throughout this lab, we will continue to use the root filesystem we have created in the $HOME/ embedded-linux-labs/tinysystem/nfsroot directory, which we will progressively adapt to use block filesystems.
Filesystem support in the kernel
Recompile your kernel with support for SquashFS and ext3. Update your kernel image in NAND flash.
Boot your board with this new kernel and on the NFS filesystem you used in this previous lab7.
Now, check the contents of /proc/filesystems. You should see that ext3 and SquashFS are now supported.
Prepare the MMC card
We’re going to use an MMC card for our block device.
Plug the MMC card your instructor gave you on your workstation. Type the dmesgcommand to see which device is used by your workstation. In case the device is /dev/sdb, you will see something like
sd 3:0:0:0: [sdb] 3842048 512-byte hardware sectors: (1.96 GB/1.83 GiB)
If your PC has an internal MMC/SD card reader, the device may also been seen as/dev/mmcblk0, and the first partition asmmcblk0p18. You will see that the MMC/SD card is seen in the same
way by the board.
In the following instructions, we will assume that your MMC/SD card is seen as/dev/sdbby your PC workstation.
Caution: read this carefully before proceeding. You could destroy existing parti-tions on your PC!
Do not make the confusion between the device that is used by your board to repre-sent your MMC/SD disk (probably/dev/sda), and the device that your workstation uses when the card reader is inserted (probably/dev/sdb).
So, don’t use the/dev/sdadevice to reflash your MMC disk from your workstation. People have already destroyed their Windows partition by making this mistake.
You can also runcat /proc/partitionsto list all block devices in your system. Again, make sure to distinguish the SD/MMC card from the hard drive of your development workstation! Type themountcommand to check your currently mounted partitions. If MMC/SD partitions are mounted, unmount them:
$ sudo umount /dev/sdb*
Then, clear possible MMC/SD card contents remaining from previous training sessions:
$ sudo dd if=/dev/zero of=/dev/sdb bs=1M count=256
Now, let’s use thecfdiskcommand to create the partitions that we are going to use:
$ sudo cfdisk /dev/sdb
In thecfdiskinterface, delete existing partitions, then create three primary partitions, starting from the beginning, with the following properties:
• One partition, 64MB big, with theFAT16partition type.
• One partition, 8 MB big9, that will be used for the root filesystem. Due to the geometry
of the device, the partition might be larger than 8 MB, but it does not matter. Keep the
Linuxtype for the partition.
• One partition, that fills the rest of the MMC card, that will be used for the data filesystem. Here also, keep theLinuxtype for the partition.
PressWritewhen you are done.
To make sure that partition definitions are reloaded on your workstation, remove the MMC card and insert it again.
Data partition on the MMC disk
Using the mkfs.ext3 create a journaled file system on the third partition of the MMC disk (Caution: one again, make sure you’re not touching hard disk partitions instead of the MMC ones!). Remember that you can use the-Loption to set a volume name for the partition. Move the contents of the/www/upload/files directory (in your target root filesystem) into this new
8This is not always the case with internal MMC/SD card readers. On some PCs, such devices are behind an internal USB bus, and thus are visible in the same way external card readers are
9For the needs of our system, the partition could even be much smaller, and 1 MB would be enough. However, with the 8 GB SD cards that we use in our labs, 8 MB will be the smallest partition thatcfdiskwill allow you to create.
partition. The goal is to use the third partition of the MMC card as the storage for the uploaded images.
Connect the MMC disk to your board. You should see the MMC partitions in/proc/partitions. Mount this third partition on/www/upload/files.
Once this works, modify the startup scripts in your root filesystem to do it automatically at boot time.
Reboot your target system and with the mount command, check that /www/upload/files is now a mount point for the third MMC disk partition. Also make sure that you can still upload new images, and that these images are listed in the web interface.
Adding a tmpfs partition for log files
For the moment, the upload script was storing its log file in /www/upload/files/upload.log. To avoid seeing this log file in the directory containing uploaded files, let’s store it in/var/log
instead.
Add the/var/log/directory to your root filesystem and modify the startup scripts to mount a
tmpfsfilesystem on this directory. You can test yourtmpfsmount command line on the system before adding it to the startup script, in order to be sure that it works properly.
Modify thewww/cgi-bin/upload.cfgconfiguration file to store the log file in/var/log/upload. log. You will lose your log file each time you reboot your system, but that’s OK in our system. That’s whattmpfsis for: temporary data that you don’t need to keep across system reboots. Reboot your system and check that it works as expected.
Making a SquashFS image
We are going to store the root filesystem in a SquashFS filesystem in the second partition of the MMC disk.
In order to create SquashFS images on your host, you need to install the squashfs-tools
package. Now create a SquashFS image of your NFS root directory.
Finally, using theddcommand, copy the file system image to the second partition of the MMC disk.
Booting on the SquashFS partition
In the U-boot shell, configure the kernel command line to use the second partition of the MMC disk as the root file system. Also add therootwaitboot argument, to wait for the MMC disk to be properly initialized before trying to mount the root filesystem. Since the MMC cards are detected asynchronously by the kernel, the kernel might try to mount the root filesystem too early withoutrootwait.
Check that your system still works. Congratulations if it does!
Store the kernel image and DTB on the MMC card
Finally, copy the zImage kernel image and DTB to the first partition of the MMC card and adjust thebootcmdof U-Boot so that it loads the kernel and DTB from the MMC card instead of loading them from the NAND.
You’ll first need to format the first partition, using:
sudo mkfs.vfat -F 16 -n boot /dev/sdb1
It will create a new FAT16 partition, calledboot. Remove and plug the MMC card. You can now copy the kernel image and Device Tree to it.
In U-boot, you can load a file from a FAT filesystem using a command like
fatload mmc 0:1 0x21000000 filename
Which will load the file namedfilenamefrom the first partition of the first MMC device to the system memory at the address0x21000000.
Going further
At this point our board still uses the bootloaders (at91bootstrap and U-Boot) stored in the NAND flash. Let’s try to have everything on our MMC card.
The ROM code can load the first stage bootloader from an SD card, from a file namedBOOT.BIN
located in the first FAT partition of an MMC card.
For this you will need to recompile at91bootstrap to support booting from an MMC card. When testing, do not forget to remove the NAND CS jumper!
Filesystems - Flash file systems
Objective: Understand flash and flash file systems usage and their
integration on the target
After this lab, you will be able to:
• Prepare filesystem images and flash them. • Define partitions in embedded flash storage.
Setup
Stay in$HOME/embedded-linux-labs/tinysystem. Install themtd-utilspackage, which will be useful to create UBIFS and UBI images.
Goals
Instead of using an external MMC card as in the previous lab, we will make our system use its internal flash storage.
We will create an MTD partition to be attached to the UBI layer (the partitions previously used to store the kernel image and the DTB should be merged with this UBI partition).
The kernel and DTB images will be stored in two separatestatic(read-only) UBI volumes. The root filesystem will be a UBI volume storing a UBIFS filesystem mounted read-only, the web server upload data will be stored in another UBI volume storing a UBIFS filesystem mounted read/write. These volumes will bedynamicvolumes and will be 16 MiB large.
Which gives the following layout:
Enabling NAND flash and filesystems
First, make sure your kernel has support for UBI and UBIFS, and also the option allowing us to pass the partition table through the command line: (CONFIG_MTD_CMDLINE_PARTS).
Recompile your kernel if needed. We will update your kernel image on flash in the next section.
Filesystem image preparation
To prepare filesystem images, we won’t use what you stored on the MMC/SD card during the previous lab. We will get back to the filesystem sources that you have in $HOME/embedded-linux-labs/tinysystem/nfsroot.
To runmkfs.ubifs, you will need to find the Logical Erase Block (LEB) size that UBI will use. A solution to get such information is to list default MTD partitions on the target (cat /proc/ mtd), and attach the last partition to UBI. In case, the last partition ismtd5, you will run:
ubiattach -m 5 /dev/ubi_ctrl
Doing this, you will get details in the kernel log about the MTD page size and the LEB size that UBI will use.
Using such information, prepare prepare a UBIFS filesystem image containing the files stored in the/www/upload/filesdirectory.
Modify the /etc/init.d/rcS file under nfsroot to mount a UBI volume called data 10 on
/www/upload/files.
Once done, create a UBIFS image of your root filesystem.
UBI image preparation
Create aubinizeconfig file where you will define the 4 volumes described above, then use the
ubinizetool to generate your UBI image.
Warning: do not use the autoresize flag (vol_flags=autoresize): U-Boot corrupts the UBI metadata when trying to expand the volume.
Remember that some of these volumes are static (read-only) and some are not.
MTD partitioning and flashing
Look at the default MTD partitions in the kernel log. They do not match the way we wish to organize our flash storage. Therefore, we will define our own partitions at boot time, on the kernel command line.
Redefine the partitions in U-Boot using themtdidsandmtdpartsenvironment variables. Once done, execute themtdpartscommand and check the partition definitions.
You can now safely erase the UBI partition without risking any corruption on other partitions. Download the UBI image (usingtftp) you have created in the previous section and flash it on the UBI partition.
When flashing the UBI image, use thetrimffsversion of the commandnand write11.
10We will create it when runningubinizein the next section
11The commandnand write.trimffsskips the blank sectors instead of writing them. It is needed because the algorithm used by the hardware ECC for the SAMA5D3 SoC generates a checksum with bytes different from
0xFFif the page is blank. Linux only checks the page, and if it is blank it doesn’t erase it, but as the OOB is not blank it leads to ECC errors. More generally it is not recommended writing more than one time on a page and its OOB even if the page is blank.
Because of a bug in the UBI layer implemented by U-Boot, you’ll have to reboot the board after flashing the UBI image.
Loading kernel and DTB images from UBI and booting it
From U-Boot, retrieve the kernel and DTB images from their respective UBI volumes and try to boot them. If it works, you can modify yourbootcmdaccordingly.
Set the bootargsvariable so that:
• Themtdpartsenvironment variable contents are passed to the kernel through its command line.
• The UBI partition is automatically attached to the UBI layer at boot time
• The root filesystem is mounted from the root volume, and is mounted read-only (kernel parameterro).
Boot the target, and check that your system still works as expected. Your root filesystem should be mounted read-only, while the data filesystem should be mounted read-write, allowing you to upload data using the web server.
Going further
Resizing an existing volume and creating a new one
In some cases you might need to adapt your NAND partitioning without re-flashing everything. Thanks to UBI this is possible.
From Linux, resize thedata volume to occupy 128 MiB, and then create a newlog volume of 16MiB. Mount this volume as a UBIFS filesystem and see what happens.
Update your init script to mount the UBIlog volume on /var/log. Reboot your system and check that thelog is correcly mounted.
Using
squashfs
for the root filesystem
Root filesystems are often a sensitive part of your system, and you don’t want it to be corrupted, hence some people decide to use a read-only file system for their rootfs and use another file system to store their auxiliary data.
squashfsis one of these read-only file systems. However,squashfsexpects to be mounted on a block device.
Use theubiblklayer to emulate a read-only block device on top of a static UBI volume to mount a squashfsfilesystem as the root filesystem:
• First create asquashfsimage with your rootfs contents
• Then create a new static volume to store your squashfs and update it with your squashfs image
• Enable and setup theubiblklayer • Boot on your new rootfs
Atomic update
UBI also provides an atomic update feature, which is particularly useful if you need to safely upgrade sensitive parts of your system (kernel, DTB or rootfs).
Duplicate the kernel volume and create a U-Boot script to fallback on the second kernel volume if the first one is corrupted:
• First create a new static volume to store your kernel backup • Flash a valid kernel on the backup volume
• Modify yourbootcmdto fallback to the backup volume if the first one is corrupted • Now try to update the kernel volume and interrupt the process before it has finished and
see what happens (unplug the platform)
• Create a shell script to automate kernel updates (executed in Linux). Be careful, this script should also handle the case where the backup volume has been corrupted (copy the contents of the kernel volume into the backup one)
Third party libraries and
applica-tions
Objective: 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 filesys-tem built in theA tiny embedded systemlab to add theALSAlibraries and tools and an audio playback application using these libraries.
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.
Audio support in the Kernel
Recompile your kernel with audio support. The options we want are: CONFIG_SOUND,CONFIG_SND,
CONFIG_SND_USBandCONFIG_SND_USB_AUDIO.
At this stage, the easiest solution to update your kernel is probably to get back to copying it to RAM from tftp. Anyway, we will have to modify U-Boot environment variables, as we are going to switch back to NFS booting anyway.
Make sure that your board still boots with this new kernel.
Figuring out library dependencies
We’re going to integrate thealsa-utilsandogg123executables. As most software components, they in turn depend on other libraries, and these dependencies are different depending on the configuration chosen for them. In our case, the dependency chain foralsa-utilsis quite simple, it only depends on thealsa-liblibrary.
The dependencies are a bit more complex for ogg123. It is part of vorbis-tools, that depend onlibaoandlibvorbis. libao in turn depends onalsa-lib, and libvorbisonlibogg.
libao,alsa-utilsandalsa-libare here to abstract the use ofALSA, one of the Audio Subsys-tems supported in Linux. vorbis-tools, libvorbis andlibogg are used to handle the audio files encoded using the Ogg codec, which is quite common.
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 theA tiny embedded system 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 theconfigure script(by running./configure --help). • By running theconfigurescript, 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 withalsa-lib, then continue withalsa-utils,libao,libogg, andlibvorbis, to finally compilevorbis-tools.
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;
• Atargetspace, in which we will only copy the required files from thestagingspace: binaries and libraries, after stripping, configuration files needed at runtime, etc. This target space will take a lot less space than thestaging 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
targetspace will contain only what’s needed for execution.
So, in$HOME/embedded-linux-labs/thirdparty, create two directories: stagingandtarget. For the target, we need a basic system with BusyBox, device nodes and initialization scripts. We will re-use the system built in theA tiny embedded systemlab, so copy this system in the target directory:
cp -a $HOME/embedded-linux-labs/tinysystem/nfsroot/* target/
Note that for this lab, a lot of typing will be required. To save time typing, we advise you to copy and paste commands from the electronic version of these instructions.
Testing
Make sure the target/ directory is exported by your NFS server to your board by modifying
/etc/exportsand restarting your NFS server.
Make your board boot from this new directory through NFS.
alsa-lib
alsa-libis a library supposed to handle the interaction with the ALSA subsystem. It is available at http://alsa-project.org. Download version 1.0.28, and extract it in $HOME/embedded-linux-labs/thirdparty/.
By looking at theconfigurescript, we see that it has been generated byautoconf(the header contains a sentence likeGenerated by GNU Autoconf 2.62). Most of the time,autoconfcomes withautomake, that generates Makefiles fromMakefile.amfiles. So alsa-lib uses a rather com-mon build system. Let’s try to configure and build it:
./configure make
You can see that the files are getting compiled with gcc, which generates code for x86 and not for the target platform. This is obviously not what we want, so we clean-up the object and tell the configure script to use the ARM cross-compiler:
make clean
CC=arm-linux-gcc ./configure
Of course, thearm-linux-gcccross-compiler must be in yourPATHprior to running the configure script. TheCCenvironment variable is the classical name for specifying the compiler to use. Quickly, you should get an error saying:
checking whether we are cross compiling... configure: error: in ‘.../thirdparty/alsa-lib-1.0.28’: configure: error: cannot run C compiled programs.
If you meant to cross compile, use ‘--host’. See ‘config.log’ for more details
If you look at theconfig.logfile, you can see that theconfigurescript 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 aconfigurescript, and that’s why it tests so early that it’s actually doable, and bails out if not.
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--buildand--hostoptions, as described in the help of theconfigurescript:
System types:
--build=BUILD configure for building on BUILD [guessed]
--host=HOST cross-compile to build programs to run on HOST [BUILD]
The --buildoption 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
returni686-pc-linux-gnu. See http://www.gnu.org/software/autoconf/manual/html_node/ Specifying-Names.htmlfor more details on these options.
So, let’s override the value of the--hostoption:
CC=arm-linux-gcc ./configure --host=arm-linux
Theconfigurescript should end properly now, and create a Makefile. Run themakecommand, and you should see that at some point it is failing because of a missing Python header. You can get rid of this error by disabling Python support in theconfigurescript by using the
--disable-pythonoption. Start make again, and this time you should have the compilation ending fine.
Look at the result of compiling insrc/.libs: a set of object files and a set of libasound.so*
files.
The libasound.so* files are a dynamic version of the library. The shared library itself is
libasound.so.2.0.0, it has been 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
And creates the symbolic linkslibasound.soandlibasound.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 -lLIBNAME option to the compiler, which will look for a file namedlib<LIBNAME>.so. In our case, the compilation option is-lasoundand the name of the library file islibasound.so. So, thelibasound.so
symlink is needed at compile time;
• libasound.so.2is needed because it is theSONAMEof the library. SONAMEstands 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 theSONAMEof a library, you can use:
arm-linux-readelf -d libasound.so.2.0.0
and look at the(SONAME)line. You’ll also see that this library needs the C library, because of the(NEEDED)line onlibc.so.0.
The mechanism of SONAME allows to change the library without recompiling the applications linked with this library. Let’s say that a security problem is found in the alsa-lib release that provides libasound 2.0.0, and fixed in the next alsa-lib release, which will now provide libasound 2.0.1.
You can just recompile the library, install it on your target system, change thelibasound.so.2
link so that it points tolibasound.so.2.0.1and restart your applications. And it will work, because your applications don’t look specifically forlibasound.so.2.0.0but for theSONAME libasound.so.2.
However, it also means that as a library developer, if you break the ABI of the library, you must change theSONAME: change fromlibasound.so.2to libasound.so.3.
Finally, the last step is to tell the configure script where the library is going to be installed. Mostconfigurescripts 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.). But in our system, we simply want the libraries to be installed in the/usrprefix, so let’s tell theconfigurescript about this:
CC=arm-linux-gcc ./configure --host=arm-linux --disable-python --prefix=/usr make
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 theprefix(where the application or library will be running on the target system) from the location where the application or library will be installed on your host while building the root filesystem.
For example, libasound will be installed in $HOME/embedded-linux-labs/thirdparty/target/ usr/lib/because this is the directory where we are building the root filesystem, but once our target system will be running, it will see libasound in/usr/lib.
The prefix corresponds to the path in the target system and never on the host. So, one shouldneverpass a prefix like$HOME/embedded-linux-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 will 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, and be different from the configuration prefix.
We now only have the installation process left to do. First, let’s make the installation in thestaging space:
make DESTDIR=$HOME/embedded-linux-labs/thirdparty/staging install
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 thetargetspace:
1. Create thetarget/usr/libdirectory, it will contain the stripped version of the library 2. Copy the dynamic version of the library. Onlylibasound.so.2andlibasound.so.2.0.0
are needed, sincelibasound.so.2is theSONAMEof the library andlibasound.so.2.0.0
is the real binary:
• cp -a staging/usr/lib/libasound.so.2* target/usr/lib
3. Strip the library:
And we’re done with alsa-lib!
Alsa-utils
Download alsa-utils from the ALSA offical webpage. We tested the lab with version 1.0.28. Once uncompressed, we quickly discover that the alsa-utils build system is based on theautotools, so we will work once again with a regularconfigurescript.
As we’ve seen previously, we will have to provide the prefix and host options and the CC variable:
CC=arm-linux-gcc ./configure --host=arm-linux --prefix=/usr
Now, we should quiclky get an error in the execution of theconfigurescript:
checking for libasound headers version >= 1.0.27... not present. configure: error: Sufficiently new version of libasound not found.
Again, we can check inconfig.logwhat theconfigurescript is trying to do:
configure:7130: checking for libasound headers version >= 1.0.27 configure:7192: arm-linux-gnueabihf-gcc -c -g -O2 conftest.c >&5
conftest.c:15:28: fatal error: alsa/asoundlib.h: No such file or directory
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: there are not in the default directory/usr/include/, but in the/usr/includedirectory of ourstaging space. The help text of the configurescript says:
CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if you have headers in a nonstandard directory <include dir>
Let’s use it:
CPPFLAGS=-I$HOME/embedded-linux-labs/thirdparty/staging/usr/include \ CC=arm-linux-gcc \
./configure --host=arm-linux --prefix=/usr
Now, it should stop a bit later, this time with the error:
checking for libasound headers version >= 1.0.27... 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 (as can be seen from the-lasoundoption): alsa-utils usesalsa-lib, so theconfigure script wants to make sure this library is already installed. Unfortunately, theldlinker 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 (instaging/usr/lib). This-Loption can be passed to the linker by using theLDFLAGS at configure time, as told by the help text of theconfigurescript:
LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a nonstandard directory <lib dir>
Let’s use thisLDFLAGS variable:
LDFLAGS=-L$HOME/embedded-linux-labs/thirdparty/staging/usr/lib \ CPPFLAGS=-I$HOME/embedded-linux-labs/thirdparty/staging/usr/include \ CC=arm-linux-gcc \
Once again, it should fail a bit further down the tests, this time complaining about a missing
curses helper header. cursesor ncursesis a graphical framework to design UIs in the terminal. This is only used byalsamixer, one of the tools provided byalsa-utils, that we are not going to use. Hence, we can just disable the build ofalsamixer.
Of course, if we wanted it, we would have had to build ncursesfirst, just like we builtalsa-lib. We will also need to disable support forxmltothat generates the documentation.
LDFLAGS=-L$HOME/embedded-linux-labs/thirdparty/staging/usr/lib \ CPPFLAGS=-I$HOME/embedded-linux-labs/thirdparty/staging/usr/include \ CC=arm-linux-gcc \
./configure --host=arm-linux --prefix=/usr \ --disable-alsamixer --disable-xmlto
Then, run the compilation withmake. Hopefully, it works!
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):
make DESTDIR=/tmp/alsa-utils/ install
TheDESTDIR 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 inDESTDIR/configuration-prefix.
Now, let’s see what has been installed in/tmp/alsa-utils/:
./lib/udev/rules.d/90-alsa-restore.rules ./usr/bin/aseqnet ./usr/bin/aseqdump ./usr/bin/arecordmidi ./usr/bin/aplaymidi ./usr/bin/aconnect ./usr/bin/alsaloop ./usr/bin/speaker-test ./usr/bin/iecset ./usr/bin/aplay ./usr/bin/amidi ./usr/bin/amixer ./usr/bin/alsaucm ./usr/sbin/alsaconf ./usr/sbin/alsactl ./usr/share/sounds/alsa/Side_Left.wav ./usr/share/sounds/alsa/Rear_Left.wav ./usr/share/sounds/alsa/Noise.wav ./usr/share/sounds/alsa/Front_Right.wav ./usr/share/sounds/alsa/Front_Center.wav ./usr/share/sounds/alsa/Side_Right.wav ./usr/share/sounds/alsa/Rear_Right.wav ./usr/share/sounds/alsa/Rear_Center.wav ./usr/share/sounds/alsa/Front_Left.wav ./usr/share/locale/ru/LC_MESSAGES/alsaconf.mo ./usr/share/locale/ja/LC_MESSAGES/alsaconf.mo ./usr/share/locale/ja/LC_MESSAGES/alsa-utils.mo ./usr/share/locale/fr/LC_MESSAGES/alsa-utils.mo
./usr/share/locale/de/LC_MESSAGES/alsa-utils.mo ./usr/share/man/fr/man8/alsaconf.8 ./usr/share/man/man8/alsaconf.8 ./usr/share/man/man1/aseqnet.1 ./usr/share/man/man1/aseqdump.1 ./usr/share/man/man1/arecordmidi.1 ./usr/share/man/man1/aplaymidi.1 ./usr/share/man/man1/aconnect.1 ./usr/share/man/man1/alsaloop.1 ./usr/share/man/man1/speaker-test.1 ./usr/share/man/man1/iecset.1 ./usr/share/man/man1/aplay.1 ./usr/share/man/man1/amidi.1 ./usr/share/man/man1/amixer.1 ./usr/share/man/man1/alsactl.1 ./usr/share/alsa/speaker-test/sample_map.csv ./usr/share/alsa/init/ca0106 ./usr/share/alsa/init/hda ./usr/share/alsa/init/test ./usr/share/alsa/init/info ./usr/share/alsa/init/help ./usr/share/alsa/init/default ./usr/share/alsa/init/00main So, we have:
• The udev rules inlib/udev
• The alsa-utils binaries in/usr/binand/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 thestaging space:
make DESTDIR=$HOME/embedded-linux-labs/thirdparty/staging/ install
Then, let’s install only the necessary files in thetargetspace, manually:
cd ..
cp -a staging/usr/bin/a* staging/usr/bin/speaker-test target/usr/bin/ cp -a staging/usr/sbin/alsa* target/usr/sbin arm-linux-strip target/usr/bin/a* arm-linux-strip target/usr/bin/speaker-test arm-linux-strip target/usr/sbin/alsactl mkdir -p target/usr/share/alsa/pcm cp -a staging/usr/share/alsa/alsa.conf* target/usr/share/alsa/ cp -a staging/usr/share/alsa/cards target/usr/share/alsa/ cp -a staging/usr/share/alsa/pcm/default.conf target/usr/share/alsa/pcm/