2013-05-09
Building Wasp Lisp and MOSREF for Android
Wasp Lisp is a small cross platform lisp by Scott Dunlop that I've written about before and MOSREF is the secure remote injection framework that is one of the applications written with it.
I've been wanting to get Wasp running on Android and Gonk (the low level Android layer of Firefox OS) for debugging and small applications. One of the nice features of Wasp is being able to have a minimal interpreter running on a platform and send byte code from another system to that interpreter which is loaded and run. You can even send the bytecode compiler itself to be loaded and run in the interpreter to get a full system on the target. This is the basis of how MOSREF works where the drone gets sent code to run from the console system.
Standalone Toolkit
Building Wasp for Android requires generating a standalone toolchain from the Android NDK. This results in a vesion of gcc
and libraries that can be run from conventional makefiles and configure scripts.
To generate a standalone toolchain to build Wasp I used:
$NDK_ROOT/build/tools/make-standalone-toolchain.sh \
--platform=android-8 \
--install-dir=$STK_ROOT
Replace NDK_ROOT
with the path to the Android SDK and STK_ROOT
with the destination where you want the standalone toolkit installed. The android-8
in the platform makes the standalone toolkit generate applications for FroYo and above (See STABLE-APIS.html.
Adding $STK_ROOT/bin
to the PATH
will make the compiler available:
export PATH=$PATH:$STK_ROOT/bin
Building libevent for Android
The Wasp virtual machine uses libevent to enable asynchronous operations. Building this for Android using the standalone toolkit is straightforward:
wget https://github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar.gz
tar xvf libevent-2.0.21-stable.tar.gz
cd libevent-2.0.21-stable
./configure --host=arm-linux-androideabi --prefix=$STK_ROOT
make
make install
This configures libevent
to be built using the ARM compilers in the standalone toolkit and to install the resulting libraries in the same install location as the toolkit. This makes it easy for other applications to find and link with the library.
Building the Wasp stub for Android
A Wasp VM stub file is used for generating Wasp applications. The bytecode for an application is appended to the stub file and the result made to be executable. By having stubs for various operating systems and architectures you can create executables specific to them easily. To build the Android stub:
git clone git://github.com/swdunlop/WaspVM.git
cd WaspVM
STRIP=arm-linux-androideabi-strip \
CC=arm-linux-androideabi-gcc \
CFLAGS="-I $STK_ROOT/include -L $STK_ROOT/lib" \
ARCH=arm-linux \
OS=android \
make
To build we define the correct version of the build programs to compile for ARM and set the architecture and OS to stop the build system from picking up the host system version of these. The result is a waspvm-android-arm-linux
stub in the stubs
directory.
Creating the host Wasp programs
To easily create Wasp programs for different platforms we'll need versions of them for the host we are running on. By doing a 'clean' followed by a 'make' on the host we'll clean out the Android build files and rebuild what's needed for the host. The 'clean' process doesn't remove the stub file we created in the previous step which still allows us to do android Wasp programs.
make clean
make
This produces a waspvm-linux-x86_64
stub in my stubs
directory. To generate the Wasp interpreter and other programs:
make repl
Exit out of the repl with (exit)
and there will be a wasp
, waspc
and waspld
program for the host system. Now we can make a version of wasp
for Android:
cd mod
../waspc -exe ../awasp -platform android-arm-linux bin/wasp.ms
The waspc
command takes a stub file (defined by the platform
or stub
argument) and appends the bytecode resulting from the compilation of the Wasp lisp code given as an argument. wasp.ms
is the source for the intepreter. The output is set as awasp
which is our executable for running on Android. We can use a similar command for producing Android versions of the other Wasp programs. MOSREF for example:
../waspc -exe ../amosref -platform android-arm-linux bin/mosref.ms
Running on Android
Running these executables on Android involves pushing them to somewhere writeable on the phone. If you have a rooted Android device (or a Firefox OS device) you can push them pretty much anywhere. On my non-rooted Jellybean Galaxy Note 2 I'm limited to just /data/local/tmp
:
adb push awasp /data/local/tmp
adb shell
cd /data/local/tmp
chmod 0755 awasp
./awasp
>> (+ 1 2 3)
:: 6
This gives you a running Wasp interpreter. To develop and do interesting things on the phone you really need the Wasp mod
files and the stub files copied over:
adb push mod /data/local/tmp
adb push stubs /data/local/tmp
adb shell
cd /data/local/tmp
./awasp
>> (import "lib/http-client")
:: #t
>> (http-response-body (http-get "http://icanhazip.com/"))
:: "xxx.xxx.xxx.xxx\n"
>> (exit)
MOSREF Android drone
It's simple now to create a MOSREF drone that runs on Android. Make sure the MOSREF console has a stubs
directory containing the Android stub then create the drone as:
console> drone drone1 myphone android-arm-linux
Drone executable created.
Listening for drone on 10000...
Copy the created drone1
onto the phone and run it:
adb push mod/drone1 /data/local/tmp
adb shell
cd /data/local/tmp
chmod 0755 drone1
./drone1
Now on the console you can see the drone on the phone:
console> nodes
NODES: console online address: 192.168.1.101 port: 10000
myphone online
console> on myphone sh ls
...directory listing...
console on myphone do (print "hello\n")
...prints hello on the phone 'adb shell' session...
console> on myphone load lib/http-file-server.ms
:: spawn-http-file-server
console> on myphone do (offer-http-file 8080 "/test" "text/plain" "Hello world!")
:: [queue 1F5DD00]
The load
command shown above loads and compiles the Wasp lisp file on the host and sends the compiled bytecode to the drone on the phone. We then run the lisp code on the phone to start an HTTP server to serve data.