How To cross compile EPICS and a IOC to an old x86 Linux system
- 1 Introduction
- 2 Overview
- 3 Crosstool-NG
- 4 EPICS dependencies
- 5 Configure cross-compilation in EPICS
- 6 Example IOC
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.
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.
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
In order to compile:
cd crosstool-ng-1.9.3 ./configure --local make
Before configuring Crosstool-NG, I gathered information about my target system:
$uname -r 18.104.22.168
$gcc --version gcc (GCC) 4.2.4
$/lib/libc.so.6 GNU C Library stable release version 2.7, by Roland McGrath et al.
$ld --version GNU ld (Linux/GNU Binutils) 22.214.171.124.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):
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 (126.96.36.199) 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:
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:
Then source your .bashrc, so the changes take effect.
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.
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
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:
Change the line:
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
COMMANDLINE_LIBRARY = READLINE_NCURSES
At the end of the file, add the lines:
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
VALID_BUILDS = Host Ioc
This is needed in order to get caRepeater compiled, according to http://www.aps.anl.gov/epics/tech-talk/2012/msg01102.php 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.
Let's create a working directory for our programs. I decided to call it 'apps':
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:
For the Application Name, just hit enter.
Add this line to the file ~/apps/myexample/configure/CONFIG_SITE
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 <top> folder of our IOC. I did it because the IOC depends on the caRepeater program, which would be present in an EPICS base if we had one in our target. Because we don't, I'll simply copy the caRepeater generated by the host to the the /bin folder of our IOC <top> folder:
cp $EPICS_BASE/bin/linux-686/caRepeater ~/apps/myexample/bin/linux-686/
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:
To your target's folder:
Please note the change in the filename.
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:
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