Sniff is a "Scratch-like" programming language that's designed to help Scratchers move gently from Scratch to more conventional languages. They can start writing programs, without having to learn a new language because Sniff is based on Scratch. They learn a little more about variables, compiling, syntax errors (!), and they can have fun controlling real hardware while they're doing it.

Saturday, 17 May 2014

Building GCC for the Yun (in 12 not-easy steps)

The Arduino Yun is a pretty neat bit of kit and should make an ideal place to run Sniff. It has two distinct parts: what is effectivly an Arduino Leonardo, coupled with a Mips Linux host. The trouble is  the Linux side is based on OpenWRT. While this is an OK embedded Linux its designed to run on stuff like routers, where resources are tiny. The Yun processor has 64Mb of Ram, and a 400MHz MIPS processor. With the newly released "overlay" patch, it can (effectively) boot from the SD card, so it has at least 2GB of storage. While that may not sound like a lot, that's a slightly higher spec that the first MIPS machine I used, which supported about 20 users all at the same time. In that context there's no reason why we shouldn't be able to run the Sniff compiler on the Linux side, and have it program the Arduino side...

The rest of this post will be of NO INTEREST AT ALL to anyone who usually reads my blog. It may be of no interest at all to anyone in fact. It's aimed at hardcode Linux/Arduino hackers, with the hope they'll be able to reproduce,and improve on my results (its also essentially my notes, so I can reproduce the build!). This in turn makes Sniff on Yun even cooler!

If you're looking for cool educational computing stuff please just scoll past this.

The main catch to this is that there's no C compiler. Huh? a Unix machine without a C compiler? Well there is a C compiler somewhere, but not one which runs on the Yun. To generate code to run on the Linux side, you need to cross compile it on a different machine. To make matters worse it needs to be Debian Linux! Not that I've got anything against Debian, but it is quite specific...

Step 1: Install Debian in a VirtualBox VM.

I don't have a Debian machine (or a regular Linux machine for that matter - got a FreeBSD box, and a  bunch of Mac's - no the Pi does not count. This is taking several days on a Quad i7!). In any case its nice to have a clean install, just to be safe so I used VirtualBox on the Mac and installed the latest Debian.

apt-get install git subversion build-essential asciidoc \
    fastjar flex gawk libgtk2.0-dev intltool zlib1g-dev \
    genisoimage libncurses5-dev libssl-dev ruby sdcc unzip \
    bison libboost-dev libxml-parser-perl libusb-dev bin86 \
    bcc sharutils openjdk-7-jdk mercurial cvs bzr
The instructions for building on Debian can be found on github but they have a couple of glitches. the first is that it suggest installing the "npm" package. This doesn't exist in my version of Debian. I used the list above, and it worked fine...

Step 3: Clone the Repo


This works as per the instructs

Step 4: Build The YUN Linux OS


The again following the instructions on github don't quite work. They sort of work, but then it randomly crashes out. Restarting it seemed to "solve" the problem. It looks like a race condition, as the build tries to use multiple processes to build elements in parallel. I used the command:

make -j 1 V=s

which seemed to solve the problem. The build is still running after about 24 hours, which is actually a good thing!

Step 5: Check GCC cross compiler


At some point long before the build is complete it will make a working version of gcc that runs on Debian, but generates programs for the Yun!

To use it you need to add it to your PATH, and set up STAGING_DIR so that it can find includes and libs:
export PATH=$PATH:$HOME/Open-Wrt-yun/staging_dir/toolchain*/bin
export STAGING_DIR=$HOME/Open-Wrt-yun/staging_dir

Once you can compile a program:

mips-openwrt-linux-uclibc-gcc hello.c

produces a MIPS executable. You can move this over to the YUN with scp, then ssh and run it!!!!!


Step 6: add "cc"


This long and tangled process doesn't generate "cc" only "gcc". At some point we're going to need both (even though they're the same) so:

cd $HOME/Open-Wrt-yun/staging_dir/toolchain*/bin
ln -s mips-openwrt-linux-uclibc-gcc mips-openwrt-linux-uclibc-cc


Step 7: Download GCC


I went for 4.6.4... the cross compiler is 4.6.2, so I figured I'd stick with 4.6, but take a couple of bug fixes.

Download it and unpack it. Then download the "extra" bits it needs.
cd gcc-4.6.4
./contrib/download_prerequisites
cd ..
mkdir objdir
cd objdir

This also makes a directory to do the actual build in.

Step 8: Build GCC to RUN on Mips

$PWD/../gcc-4.6.4/configure --host=mips-openwrt-linux-uclibc --target=mips-openwrt-linux-uclibc --prefix=/gcc --enable-languages=c,c++

(we;re just building c and c++ as anything more will break later on) and finally 

make

This will crash after a couple of minutes: It turns out theres a REAL bug which affects gcc4.4 and later: it means we need to change mpfr/ mpfr-longlong.h.

#if defined (__mips) && W_TYPE_SIZE == 32

-#if __GNUC__ > 2 || __GNUC_MINOR__ >= 7

+#if __GMP_GNUC_PREREQ (4,4)

+#define umul_ppmm(w1, w0, u, v) \

+  do { \

+    UDItype __ll = (UDItype)(u) * (v); \

+    w1 = __ll >> 32; \

+    w0 = __ll; \

+  } while (0)

+#endif

+#if !defined (umul_ppmm) && __GMP_GNUC_PREREQ (2,7)

 #define umul_ppmm(w1, w0, u, v) \

   __asm__ ("multu %2,%3" : "=l" (w0), "=h" (w1) : "d" (u), "d" (v))

-#else

+#endif

+#if !defined (umul_ppmm)

 #define umul_ppmm(w1, w0, u, v) \

   __asm__ ("multu %2,%3\n\tmflo %0\n\tmfhi %1" \

    : "=d" (w0), "=d" (w1) : "d" (u), "d" (v))

@@ -1034,10 +1043,20 @@


 #endif /* __mips */


At some point during the build of the standard C++ library you'll get an error about fenv.h being missing. This isn't needed so find the relevant include file, and just comment out the include.

Step 9: Install

make install

to "install" it on the Debian machine - if course this doesn't work, but it makes the files. It's set to install in /gcc, so once that's done, tar it up and scp it to the Yun.

You also need binutils. You can either build them (exactly the same way as we built gcc, using the same configure options), or install the pre-built package. I tried both... Not sure if they both work, but I ended up building it myself, and copying it over with gcc.

Step 10: Add the Libraries

You've now got a compiler, but you need includes and libs. Fortunatly we've already built them as part of the OpenWRT build (which has now failed completely trying to make a JavaScript VM - I guess you do need npm for that bit). Copy include and lib from them OpenWRT build on Debian onto the Yun, and put them in /gcc

Step 11: Fix the Libraries

No idea what's going on here, but a bunch of really important files ended up in the wrong place. Fix it with:

cd /gcc/lib/gcc/mips-openwrt-linux-uclibc/4.6.4
for i in ../*.o ../*.a
do
ln -s $i .
done


Step 12: Enjoy!

export PATH=/gcc/bin:$PATH
gcc hello.c
./a.out

You'll get a warning about hard and soft floats. Looks like there's a config error in the compiler, as the Yun doesn't have an FPU, which is going to break a lot of stuff. I need to go back around and start over to fix this, but in the mean time

gcc -msoft-float xxx.c

is a pretty good work around.


Step 1A: Alterative Approach

I deliberately built it to live in the /gcc folder, so you could tar it all up and make it easy to install:

You can download it here.

Gunzip and untag it so that its in /gcc. You'll either need to have turned on the "overlay" expanded file store (so the SD card is holding everything -recommended), or unpack it to the SD, and add a link. Add /gcc/bin to your path.

You might also want to install make with:
opkg update
opkg install make

Conclusion and Disclaimer

It's currently almost completely untested - I've compiled hello world! It works. It could do with being improved. There's a couple of bodges in there, but this is really a proof of concept, and a stop gap until someone integrates all this into the proper build. You can compile C on the Yun, and while the above process is pretty tortured, I managed to get it working in a couple of days. An experienced Linino/OpenWRT/GCC+ hacker should be able to make sense of my notes, and produce something better in a fraction of the time.

Hopefully now we can get past the "it can't be done"stage, and actually make this thing useful...

1 comment:

  1. Hi. I'm using your gcc and I need to link to libcurl but I'm facing the problem here: stackoverflow.com/questions/28329573/gcc-cant-find-curl-library Can you advice?

    ReplyDelete