Building GNU libmatheval for ARM

By | January 30, 2014

Overview

GNU libmatheval is a library used for parsing and evaluating symbolic expressions input as text.

A simple and common use of this library is when you have a C program and you have mathematical expressions. For example,

x * y > x^2

contains two variables whose values can vary over time since they could be analog inputs and therefore you need to evaluate it often and execute different actions according to the resulting comparison.

This library can be quite useful in an embedded system that provides some control functionality, but it has the disadvantage that some of its requirements are not straight forward to cross-build and have significant memory and storage footprints.

I managed to build it and run it successfully on an Atmel AT91SAM9260 at 210MHz with 64MB RAM and a rootfs with 128MB NAND flash, so it should be sufficient for many embedded systems, but if you have a slower processor or less memory (it’s still common to find industrial embedded devices with significantly fewer processor horsepower and/or memory) you could have performance issues.
For this article, I’m using the more powerful Atmel Cortex-A5 ATSAMA5D35 with 512MB RAM.

Requirements

I used buildroot for building the toolchain and the rootfs for the evaluation board. Unfortunately, buildroot does not include libmatheval nor some of it’s requirements since they are not typically used in embedded systems.

Building a toolchain and a rootfs with buildroot is a straight-forward task that is well documented. By using it, you can easily build some of libmatheval’s requirements:

  • libtool
  • libgmp
  • libffi
  • pthreads
  • flex

The procedure that I’m describing here is for building libmatheval and the requirements that are not included in buildroot.

First, let’s export common environment variables that make our life easier:

export HOST=arm-linux
export BUILD=i386-linux
export ROOTFS=/home/projects/sama5/rootfs

Now we can start building the requirements:

libuninstring
Package: libunistring 0.9.3

This library is used for advanced text processing. The mathematical expressions we use need advanced text analysis.

./configure --prefix=$ROOTFS --host=$HOST --build=$BUILD --enable-threads=posix

Apply the following patch. The commented code should not be a problem since it’s for glibc < 2.4 and most embedded systems use uclibc or newlib and a newer GCC:

--- a/lib/localename.c  2010-04-03 11:43:57.000000000 +0200
+++ b/lib/localename.c  2014-01-04 23:36:13.392018313 +0100
@@ -2613,10 +2613,10 @@
            See .  */  
         const char *name =
           nl_langinfo (_NL_ITEM ((category), _NL_ITEM_INDEX (-1)));
-        if (name[0] == '\0')
+        /*if (name[0] == '\0')*/
           /* Fallback code for glibc < 2.4, which did not implement
              nl_langinfo (_NL_LOCALE_NAME (category)).  */  
-          name = thread_locale->__names[category];
+        /*name = thread_locale->__names[category];*/
         return name;
 #  endif
 #  if defined __APPLE__ && defined __MACH__ /* MacOS X */

Build and install in $ROOTFS

make
make  DESTDIR=$ROOTFS install

bdw-gc
Package: gc-7.2e

This is a C garbage collector for C malloc or C++ new. As with libunistring, this library is needed for the following requirement (Guile).

Configure, build and install in $ROOTFS:

./configure \
--prefix=$ROOTFS \
--host=$HOST \
--build=$BUILD \
--enable-threads=posix \
--disable-gcj-support

make

make  DESTDIR=$ROOTFS install

guile
Package: guile 1.8.8

Guile is a library, interpreter and compiler of Scheme, a symbolic programming language. It’s an ideal option for evaluating mathematical expressions, but it’s not the best companion for small embedded systems due to its memory and storage footprints. For example, the size of the shared lib libguile.so.17.4.0 in Guile 1.8.8 is ~3.1MB and ~790KB when stripped.

When building it, you must take care that the Guile version that you want to install in your embedded device must be equal or less than the version you have installed in your host since the cross-building process uses the native Guile. In my case I had guile 2.0.5 installed in my host, but I used guile 1.8.8 since I had several problems while trying to build 2.0.x versions.

First, apply this patch that removes the use of csqrt() and calculates it manually. I think this is a problem with my uclibc.

--- guile-1.8.8/libguile/numbers.c	2010-12-13 18:25:01.000000000 +0100
+++ guile-1.8.8.a/libguile/numbers.c	2014-01-20 00:01:38.106510706 +0100
@@ -6168,8 +6168,8 @@
   if (SCM_COMPLEXP (x))
     {
 #if HAVE_COMPLEX_DOUBLE && HAVE_USABLE_CSQRT && defined (SCM_COMPLEX_VALUE)
-      return scm_from_complex_double (csqrt (SCM_COMPLEX_VALUE (x)));
-#else
+        /*return scm_from_complex_double (csqrt (SCM_COMPLEX_VALUE (x)));*/
+        /*#else*/
       double re = SCM_COMPLEX_REAL (x);
       double im = SCM_COMPLEX_IMAG (x);
       return scm_c_make_polar (sqrt (hypot (re, im)),

Configure by specifying where to find ffi and bdw-gc:

 
CPPFLAGS="-I$ROOTFS/include -I$ROOTFS/usr/include" \
LDFLAGS="-L$ROOTFS/lib -L$ROOTFS/usr/lib" \
LIBFFI_CFLAGS="-I$ROOTFS/usr/include" \
LIBFFI_LIBS="-L$ROOTFS/usr/lib" \
BDW_GC_CFLAGS="-I$ROOTFS/usr/include" \
BDW_GC_LIBS="-L$ROOTFS/usr/lib" \
GUILE_FOR_BUILD=/usr/bin/guile \
./configure \
--prefix=$ROOTFS \
--host=$HOST \
--build=$BUILD

Edit the file libguile/Makefile.
Remove the flag -Werror to variable CFLAGS, otherwise it will complain that several variables are set but not used [-Werror=unused-but-set-variable].

 
CFLAGS = -g -O2 -Wall -Wmissing-prototypes

Build and install:

 
make
make install

libmatheval
Package: libmatheval 1.1.11

Now we can finally build libmatheval!

Edit the file Makefile.in and remove the directory tests from the building process:

SUBDIRS = doc lib

Configure, build and install:

CPPFLAGS="-I$ROOTFS/include -I$ROOTFS/usr/include" \
LDFLAGS="-L$ROOTFS/lib -L$ROOTFS/usr/lib" \
./configure \
--prefix=$ROOTFS \
--host=$HOST \
--build=$BUILD

make

make install

For compiling a program tha uses libmatheval you just need to add the linking flag -lmatheval and, of course, include the directories where it was installed, typically /your/rootfs/usr/include and /your/rootfs/usr/lib.

References:

Leave a Reply

Your email address will not be published. Required fields are marked *