Monday, November 25, 2013

Mono 3.2.3 on Synology DS210

This is my guide to building Mono on the Synology DS box with the ARM chipset. If you just want the binaries, they are here: Mono 3.2.3 binaries for Synology DS210.

To use it run (installs in /opt/mono3.2.3):

wget "https://dl.dropboxusercontent.com/u/15251537/mono-3.2.3-bin.tar.bz2"
tar -C / xf mono-3.2.3-bin.tar.bz2

Then sprinkle a few symlinks:
ln -s /opt/mono-3.2.3/bin/mono /opt/bin/mono
ln -s /opt/mono-3.2.3/bin/mcs /opt/bin/mcs
ln -s /opt/mono-3.2.3/bin/gmcs /opt/bin/gmcs

If you want to compile Mono 3.2.3 yourself, the guide follows here. 


First step is to grab and extract the Mono source:
wget "http://download.mono-project.com/sources/mono/mono-3.2.3.tar.bz2"
tar xf mono-3.2.3.tar.bz2
cd mono-3.2.3

Then simply run the configure script:
./configure --prefix=/opt/mono-3.2.3

And you should be able to compile, but if you try, you get errors like:
undefined reference to `__sync_add_and_fetch_4'
undefined reference to `__sync_val_compare_and_swap_8'

This is because Mono uses a newer GCC and the methods are not in the old GCC found on Synology, and the cross compile tools version is also too old. Fortunately there is a trick which I adopted from here. I use a newer version of GCC, and the relevant file has moved, so these are the steps:
wget "http://ftp.gnu.org/gnu/gcc/gcc-4.8.2/gcc-4.8.2.tar.bz2"
tar xf gcc-4.8.2.tar.bz2 gcc-4.8.2/libgcc/config/arm/
cd gcc-4.8.2/libgcc/config/arm/
sed -i -e 's/define HIDDEN.*/define HIDDEN/' linux-atomic.c
gcc -g -O2 -MT linux-atomic.lo -MD -MP -MF linux-atomic.Tpo -c linux-atomic.c  -fPIC -DPIC -o linux-atomic.o
cd ../../../../
mv gcc-4.8.2/libgcc/config/arm/*.o .

Now you have the missing methods in the object files so they can be linked into the binaries, except the __sync_val_compare_and_swap_8 method. This method can be obtained from the linux-atomic-64bit.c file in libgcc as well. But it depends on the kernel function call "__kernel_cmpxchg64", which is not included in the kernel running on Synology. Fortunately there is a fallback method, which we can activate by editing mono/utils/atomic.h. Somewhere around the top, just insert this line:
#define BROKEN_64BIT_ATOMICS_INTRINSIC 1

This will make the compilation fall back to a mutex based method instead of using the missing kernel method. To make sure we can compile the libraries that need the atomic methods, export these:
export pedump_LDFLAGS=`pwd`/linux-atomic.o
export mono_boehm_LDFLAGS=`pwd`/linux-atomic.o
export mono_sgen_LDFLAGS=`pwd`/linux-atomic.o
export monodis_LDFLAGS=`pwd`/linux-atomic.o
export monograph_LDFLAGS=`pwd`/linux-atomic.o

The mono_sgen and mono_boehm LDFLAGS are overwritten in the Makefile, so:
nano mono/mini/Makefile

Look for mono_boehm_LDFLAGS and mono_sgen_LDFLAGS and change the "LDFLAGS=" to "LDFLAGS+=".

 And finally the __clear_cache call is also missing, so edit mono/mini/mini-arm.c, line 1032, and comment out the call, so we use the fallback instead:

#ifdef MONO_CROSS_COMPILE
#elif __APPLE__
        sys_icache_invalidate (code, size);
//#elif __GNUC_PREREQ(4, 1)
//      __clear_cache (code, code + size);
#elif defined(PLATFORM_ANDROID)
        const int syscall = 0xf0002;
        __asm __volatile (
                "mov     r0, %0\n"
                "mov     r1, %1\n"
                "mov     r7, %2\n"
                "mov     r2, #0x0\n"
                "svc     0x00000000\n"
                :
                :       "r" (code), "r" (code + size), "r" (syscall)
                :       "r0", "r1", "r7", "r2"
                );
#else
        __asm __volatile ("mov r0, %0\n"
                        "mov r1, %1\n"
                        "mov r2, %2\n"
                        "swi 0x9f0002       @ sys_cacheflush"
                        : /* no outputs */
                        : "r" (code), "r" (code + size), "r" (0)
                        : "r0", "r1", "r3" );
#endif

This should get you almost through, but there seems to be an install file missing, or perhaps just using the wrong path:
ln -s install-sh docs/install-sh

And now run "make" and wait.... If it fails with something about pthreads, you may need this hack:
mkdir /opt/arm-none-linux-gnueabi/lib_disabled
mv /opt/arm-none-linux-gnueabi/lib/libpthread* /opt/arm-none-linux-gnueabi/lib_disabled
cp /lib/libpthread.so.0 /opt/arm-none-linux-gnueabi/lib/
ln -s /opt/arm-none-linux-gnueabi/lib/libpthread.so.0 /opt/arm-none-linux-gnueabi/lib/libpthread.so
ln -s /opt/arm-none-linux-gnueabi/lib/libpthread.so.0 /opt/arm-none-linux-gnueabi/lib/libpthread-2.5.so

Then build again. And that should now build all the way, so you can install: make install If this gives you an error about the EntityFramework.dll no being signed: "Failure adding assembly ./../../class/lib/net_4_5/EntityFramework.dll to the cache: Strong name cannot be verified for delay-signed assembly"

Then you need to manually sign the assembly:
mono/mini/mono mcs/class/lib/build/sn.exe -R mcs/class/lib/net_4_5/EntityFramework.dll mcs/class/mono.snk

Run "make install" again, and finally test:

> /opt/mono-3.2.3/bin/mono --version
Mono JIT compiler version 3.2.3 (tarball Thr Nov 21 10:05:16 CET 2013)
Copyright (C) 2002-2012 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
        TLS:           __thread
        SIGSEGV:       normal
        Notifications: epoll
        Architecture:  armel,vfp+fallback
        Disabled:      none
        Misc:          softdebug
        LLVM:          supported, not enabled.
        GC:            sgen

Happy building :)