2017-12-02
Cross Compiling ATS Programs
A few years ago I wrote a mailing list post on cross compiling ATS to Android. With ATS2 being released the ability to cross compile has been made easier. In this post I'll show how to produce static linux binaries using musl libc and how to build Android and Windows ATS programs by cross compiling on linux.
For these examples I'm using ATS2-0.3.8. I'll use the simplest of 'Hello World' programs in ATS to cross compile. The following is the contents of hello.dats
:
implement main0() = print!("Hello World\n")
To compile:
$ cat hello.dats
implement main0() = print!("Hello World\n")
$ patscc -o hello hello.dats
$ ./hello
Hello World
Static musl libc binaries
musl libc is a lightweight standard library for C programs. I use this instead of glibc because statically linked binaries using glibc have issues using networking functionality. There are no problems using networking routines with musl libc, it's smaller and lightweight and works well statically linked into executables. To build musl libc I used these steps:
$ git clone git://git.musl-libc.org/musl
$ cd musl
$ ./configure
$ make
$ sudo make install
$ export PATH=$PATH:/usr/local/musl/bin/
The ATS compiler, patscc
, compiles ATS code to C. It defaults to using gcc
but takes an atsccomp
command line argument to define an alternative C compiler to use for compiling the generated C code. Unknown command line arguments are passed directly to that C compiler. Given musl libc installed in /usr/local/musl
as above, a static binary can be built with musl-gcc
like so:
$ patscc -o hello -atsccomp "/usr/local/musl/bin/musl-gcc" -static \
-I $PATSHOME/ccomp/runtime -I $PATSHOME \
hello.dats
I pass the -static
flag to produce a statically linked binary and two -I
include paths to find the ATS runtime. These appear to be required if -atsccomp
is used. In this case I use the environment variable PATSHOME
to find the installation directory of ATS. Hopefully that was set at ATS installation time. If this command succeeded then we have a static binary:
$ ./hello
Hello World
$ ldd hello
not a dynamic executable
$ strip hello && ls -l hello
-rwxr-xr-x 1 myuser myuser 18224 Dec 2 23:21 hello
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
Cross compiling Windows binaries
To build Windows compatible binaries on Linux I used mingw-w64. On Ubuntu this is available in the gcc-mingw-w64
package. With that installed a Windows hello
binary can be built with:
$ patscc -o hello.exe -atsccomp "i686-w64-mingw-gcc" \
-I $PATSHOME/ccomp/runtime -I $PATSHOME \
hello.dats
$ file hello.exe
hello.exe: PE32 executable (console) Intel 80386, for MS Windows
Copying to a Windows machine:
C:\Users\Myuser> .\hello
Hello World
Cross compiling Android binaries
To build on Android I generated a standalone toolchain using the Android NDK. This produces standard command line compilers that generate Android compatible binaries. Install the Android NDK - I used android-ndk-r16
- and run:
$ANDROID_NDK/build/tools/make-standalone-toolchain.sh \
--arch=arm --install-dir=$STK_ROOT
Replace ANDROID_NDK
with the path to the Android NDK and STK_ROOT
with the destination where you want the standalone toolchain installed. Use arm64
instead of arm
to build an ARM 64-bit binary.
Now the "Hello World" program can be built using the C compiler from the standalone toolchain. To build on 32-bit using an arm
standalone toolchain, use arm-linux-androideabi-clang
as the C compiler:
$ patscc -o hello -atsccomp $STK_ROOT/bin/arm-linux-androideabi-clang \
-I $PATSHOME/ccomp/runtime -I $PATSHOME \
hello.dats
$ file hello
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
dynamically linked, interpreter /system/bin/linker, not stripped
To build on 64-bit ARM using an arm64
standalone toolchain, use aarch64-linux-android-clang
as the compiler:
$ patscc -o hello -atsccomp $STK_ROOT/bin/aarch64-linux-android-clang \
-I $PATSHOME/ccomp/runtime -I $PATSHOME \
hello.dats
$ file hello
hello64: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV),
dynamically linked, interpreter /system/bin/linker64, not stripped
Notice the use of clang
instead of gcc
. I believe gcc
support for building native Android executables with the NDK is deprecated - I got link errors when I tried.
Copy the hello
executable to an Android phone. It needs to be somewhere writeable on the phone. On a recent non-rooted phone you should be able to use /data/local/tmp
. The following adb
commands work for me when the device is connected:
$ adb push hello /data/local/tmp
$ adb shell
$ cd /data/local/tmp
$ chmod 0755 hello
$ ./hello
Hello World
Producing an actual Android application would require using the ATS FFI to interface with the Android runtime - given the low level nature of ATS, if it can be done in C, it should be able to be done in ATS.