Thursday, September 08, 2005

Mono on embedded Linux

I am working with a TS-5500 embedded system for the first time, equipped with a PCMCIA slot, an Ethernet port, and a CompactFlash slot, which is used for a 64MB flash drive. We downloaded an image of embedded Linux on the card. This was part of a class to write an AI, self-navigating car in the C language to run on Linux. But being a .NET/Mono and C# fan myself, I wanted to try my hand at building Mono for embedded linux so I could write the AI code in C#.

The "hard drive" flash card only has 64MB, as I mentioned. The embedded linux system did not come with compiling tools either. So all development for this system had to be done in a development OS environment, which when chroot'd into allows one to build programs that can then be freely copied onto the embedded system that can run. Mono would have to be built on a traditional Linux system, inside that development OS area, and installed to some special location that I could then use to copy the installation directory to the embedded device. I crossed my fingers that it would be small enough to fit on the CF drive -- or I'd be buying a bigger one.

I downloaded the sources to Mono 1.1.8.3 and read the README file. I have built Mono several times before on other varieties of Linux, but because embedded linux has so many stripped out features, I was curious about the dependency list. I was pleased to see only two dependencies, both easy to get: glib and pkg-config. The other nice part about the dependencies, at least as it sounded from the README file, is that they were only required to build mono, not necessarily to run mono, which would help keep the size down on the embedded system. One other dependency was required for me, that wasn't listed, because the embedded system didn't have /dev/random, which was required to build
mono. I didn't know how to add /dev/random to this distribution of Linux, so instead I downloaded the alternative, egd. I'll discuss each of these dependencies and their gotchas next.

Each dependency's source code had to be downloaded as a tarball, moved into the development OS directory and extracted with tar xvzf some.tar.tgz, all from outside the development OS environment, meaning without chroot'ing to it. Then chroot'ing into the environment, I built each package with the standard ./configure && make install.

glib would not build because of a gettext dependency. After Googling on the text of the compile error, I found that gettext isn't actually required by glib any more, so its detection within its configure script could be safely disabled. Opening glib's configure script in vi, I searched for "You must have either have gettext support" (double "have" intended), and then modified the "if test" line above it from: if test "$gt_cv_have_gettext" != "yes" ; then to read: if test "$gt_cv_have_gettext" = "XXX" ; then, which essentially disabled the check. glib built and installed without any more trouble.

Once pkg-config and glib were installed, I found building mono still failed, due to /dev/random being missing. I had a /dev/random in my native Linux, which happens to be Gentoo, but inside my chroot'd environment from which I was building mono, no /dev/random was provided. After Googling on the compile error text, I found that last year the mono group wrote a random generator substitute that tied into egd, which I later found out was an entropy generator, good for random numbers. Google Linux helped me find the sources to egd. egd requires Perl, which fortunately
the embedded system already had. Although egd claims to not require an install, I did so anyway, which added SHA support to my system, which seemed to be necessary. Running egd.pl /tmp/entropy after that started the entropy-generating daemon, and set the socket to be at /tmp/entropy. Finally, an environment variable had to be set so that mono's install could find egd as the substitute random number generator: export MONO_EGD_SOCKET=/tmp/entropy.

Finally I could build mono. I set the prefix directory (the directory to install to) to /src/dest, which was empty at that point, so that I could copy /src/dest into / of the embedded system and effectively install mono. Time would tell if that worked.

./configure --prefix=/src/dest
make install

I ran into this error "error while loading shared libraries: libgthread-2.0.so.0" during build. The solution was to set the LD_LIBRARY_PATH environment variable so that the mono compiler could find its dependencies.

I also ran into this error "Unhandled Exception: System.Security.Cryptography.CryptographicException: Couldn't access random source.", which is how I found out that /dev/random is needed, and how I eventually came to the workaround discussed earlier in this blog.

Another error I ran into was that after chroot'ing into the development OS, the /proc filesystem was unreachable from the inside of the development OS. A /proc directory existed, but was empty. So solve this, I had to mount the /proc directory inside the development OS before chroot'ing into the it: sudo mount -t proc none /home/mech/ts5500devOS/proc

So after all these steps, make and make install succeeded. Hurray!

I quickly switched to /src/dest and ran a du -hcs to find out how much space mono wanted to take for install. 86MB. Rats. That's way too big. So I sorted through the directory, removing large libraries and executables from the mono installed directory that I didn't think I'd be using on the embedded system. I got it to 11MB, and my Hello World app still runs on it (though I removed the compiler).

All comments are welcome. Let me know if you found this useful!

2 comments:

  1. Good article. What libraries did you remove to get it so small?

    ReplyDelete
  2. this was a very good article for me in my research to imbedded linux, thanku!

    ReplyDelete