Cross compiling to an old x86 Linux system
Introduction
I was given the task of developing a IOC which should run in a x86 PC with an old Linux distribution. My development machine was a x86_64 PC running Ubuntu 12.04.
EPICS does a great job cross compiling from a 64-bits host to a 32-bits target, if both have compatible versions of glibc, binutils, kernel, etc. In my case, however, my target had much older versions.
I considered two different solutions:
Create a Virtual Machine and install the target’s Linux distribution. From the Virtual Machine, compile EPICS and my IOC, and then run the IOC in the target.
Build a toolchain configured for my target and use that toolchain to compile both EPICS and the IOC.
The first approach is the easiest, but compiling from inside a VM can be slow and the distribution was not very user friendly. So I took the second path, which I’ll describe in this document.
Overview
I’m assuming you, like me, are running Ubuntu 64 bits. I’m also assuming you already have EPICS base downloaded and compiled. We will go through the steps of downloading, configuring and compiling Crosstool-NG, which will be used to generate our toolchain. Then we will download and compile a couple of libraries needed by EPICS (namely readline and ncurses). Finally, we will compile EPICS base and an example IOC to our target using the newly built toolchain.
Crosstool-NG
Downloading and extracting
First we get the tarball containing the source code and extract it.
wget http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.9.3.tar.bz2
tar -xvf crosstool-ng-1.9.3.tar.bz2
Crosstool-NG has a lot of dependencies, you might want to get them before compiling:
sudo apt-get install gawk bison flex texinfo automake libtool cvs libcurses5-dev build-essential
Compiling
In order to compile:
cd crosstool-ng-1.9.3
./configure --local
make
Configuring
Before configuring Crosstool-NG, I gathered information about my target system:
Kernel Version:
$ uname -r
2.6.27.27
GCC Version:
$ gcc --version
gcc (GCC) 4.2.4
glibc version:
$ /lib/libc.so.6
GNU C Library stable release version 2.7, by Roland McGrath et al.
binutils version:
$ ld --version
GNU ld (Linux/GNU Binutils) 2.18.50.0.9.20080822
Based on this information, and on a lot of trial and error, I configured Crosstool-NG as follows (everything else set as default):
$ ./ct-ng menuconfig
PATHS AND MISC OPTIONS
[*] Use obsolete features
TARGET OPTIONS
Target architecture (x86)
Bitness: (32-bit)
(i686) Architecture Level
OPERATING SYSTEM
Target OS (linux)
Linux kernel version (2.6.27.55)
BINARY UTILITIES
Binutils version (2.17)
C-COMPILER
GCC version (4.2.4)
[*] C++
C-LIBRARY
glibc version (2.6.1)
I tried other configurations, but they crashed the compilation process.
Compiling the toolchain
Now we can compile the toolchain:
./ct-ng build
This will take a while. Go get some coffee or watch a cat video on Youtube.
Once built, the toolchain will be in $HOME/x-tools/i686-unknown-linux-gnu/
It’s a good idea (I think) to put the cross-compiler binaries on your path. Add this to the end of your ~/.bashrc:
PATH=$PATH:$HOME/x-tools/i686-unknown-linux-gnu/bin
Then source your .bashrc, so the changes take effect.
. ~/.bashrc
EPICS dependencies
In order to properly build epics-base to our target system, we have to take care of EPICS dependencies first. Namely, the libraries ‘readline’ and ‘ncurses’.
They will be installed in our toolchain directory. We have to make it writable:
chmod -R +w $HOME/x-tools/i686-unknown-linux-gnu/i686-unknown-linux-gnu/sys-root/usr
Now we can download, configure, compile and install the libraries.
readline
wget ftp://ftp.cwru.edu/pub/bash/readline-6.2.tar.gz
tar -xvf readline-6.2.tar.gz
cd readline-6.2
./configure --prefix=$HOME/x-tools/i686-unknown-linux-gnu/i686-unknown-linux-gnu/sys-root/usr --host=i686-unknown-linux-gnu
make
make install
ncurses
wget ftp://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz
tar -xvf ncurses-5.9.tar.gz
cd ncurses-5.9
./configure --prefix=$HOME/x-tools/i686-unknown-linux-gnu/i686-unknown-linux-gnu/sys-root/usr --host=i686-unknown-linux-gnu
make
make install
Configure cross-compilation in EPICS
We’re almost done. Back to the epics-base directory, open the file: $EPICS_BASE/configure/CONFIG_SITE
Change the line:
CROSS_COMPILER_TARGET_ARCHS=
To:
CROSS_COMPILER_TARGET_ARCHS=linux-686
This tells EPICS base to be compiled both for the host system and for the linux-686 target.
Save and close. Now we will create our own target configuration file, based on a existing file.
cd $EPICS_BASE/configure/os
cp CONFIG_SITE.Common.linux-x86 CONFIG_SITE.Common.linux-686
Open CONFIG_SITE.Common.linux-686
and edit it. Comment out the line:
#COMMANDLINE_LIBRARY = READLINE
And uncomment:
COMMANDLINE_LIBRARY = READLINE_NCURSES
At the end of the file, add the lines:
GNU_DIR=$HOME/x-tools/i686-unknown-linux-gnu
GNU_TARGET=i686-unknown-linux-gnu
This tells EPICS to search for both readline and ncurses libraries during compilation. The last two lines indicate the location of the toolchain and its prefix. Save and close. Last file to edit: CONFIG.Common.linux-686
Change the line that says
VALID_BUILDS = Ioc
To
VALID_BUILDS = Host Ioc
This is needed in order to get caRepeater compiled, according to this source.
Recompile EPICS base
Now, we recompile EPICS base:
make clean uninstall
make
Hopefully, we have everything in place to start developing our IOC’s.
Example IOC
Let’s create a working directory for our programs. I decided to call it ‘apps’:
mkdir ~/apps
Creating
To create an example IOC, we use a Perl script provided by EPICS:
cd ~/apps
mkdir myexample
cd myexample
makeBaseApp.pl -t example myexample
The last command tells the script to create an application named ‘myexample’ using the template (option -t) ‘example’. Now we make our IOC bootable
makeBaseApp.pl -i -t example myexample
It will ask you what the target is. We went to all this trouble to be able to select:
linux-686
For the Application Name, just hit enter.
Configuring
Add this line to the file ~/apps/myexample/configure/CONFIG_SITE
STATIC_BUILD=YES
This will statically link EPICS libraries into our executable.
Now, let’s consider that you will put your IOC in the folder /home of your target system. Edit the file ~/apps/myexample/iocBoot/iocmyexample/envPaths, so it will be:
epicsEnvSet("ARCH","linux-686")
epicsEnvSet("IOC","iocskel")
epicsEnvSet("TOP","/home/myexample")
epicsEnvSet("EPICS_BASE","/home/myexample")
Note that we set EPICS base to coincide with the
cp $EPICS_BASE/bin/linux-686/caRepeater ~/apps/myexample/bin/linux-686/
Compiling
Compile the IOC and prepare it for execution.
make
chmod +x iocBoot/iocmyexample/st.cmd
Note that you won’t be able to run the IOC in your host system, given that it was compiled to our target system. You won’t be able to run it in your target system neither, as your target lacks three libraries: two needed by caRepeater and one needed by the IOC.
First, take care of the libraries needed by caRepeater:
mkdir ~/apps/myexample/lib
mkdir ~/apps/myexample/lib/linux-686/
cp $EPICS_BASE/lib/linux-686/libca.so.3.14 ~/apps/myexample/lib/linux-686
cp $EPICS_BASE/lib/linux-686/libCom.so.3.14 ~/apps/myexample/lib/linux-686
Then copy libreadline from your host’s folder:
~/x-tools/i686-unknown-linux-gnu/i686-unknown-linux-gnu/sys-root/usr/lib/libreadline.so.6.2
To your target’s folder:
/lib/libreadline.so.6
Please note the change in the filename.
Executing
After copying your myexample folder to your target’s /home folder, you can run your IOC:
cd /home/myexample/iocBoot/iocmyexample
./st.cmd
If everything goes as expected, you will have an epics prompt:
epics>
Try listing the records:
epics> dbl
bruno:ai1
bruno:ai2
bruno:ai3
bruno:aiExample
bruno:aiExample1
bruno:aiExample2
bruno:aiExample3
bruno:aSubExample
bruno:calc1
bruno:calc2
bruno:calc3
bruno:calcExample
bruno:calcExample1
bruno:calcExample2
bruno:calcExample3
bruno:compressExample
bruno:subExample
bruno:xxxExample