main.main on Android
main.main is a Go package’s main function. This article will explain you how it is being invoked on Android.
Android’s primary language has been Java and each application was supposed load Dalvik VM and talk to system libraries through Dalvik. For this historical reason, Android operating system has never stabilized its system level libraries. The system libraries was always a moving target and backward compatibility has been handled at the SDK layer. This model is still true as of today. Even though Android supports native applications, they are being loaded by Dalvik or ART and talk through ABI stable (in a similar fashion to SDK) rather than making any direct system calls.
With Go 1.4, a main package became build-able into a Dalvik- or ART- loadable so file. So it became possible to generate Android APKs from a Go package by generating a boilerplate Android application that loads the generated shared library. As of today, gomobile build subcommand does this cumbersome work for you if you set -target=android.
The runtime story is straightforward. Once the shared library is loaded, we are looking for the exported main.main symbol by dlsyming and invoke it. At this point we already have Go runtime initialized — each so file contains its own Go runtime.
In order to target Android, we are using Android NDK’s toolchain and the system library headers provided in the NDK distribution. Due to the limitation of going through ART, we can’t simply cross compile to a binary but need to use the -buildmode=c-shared mode to output everything into a shared library.
In order to explain how this mechanism work, I will reverse engineer an APK that has been built by the gomobile tool.
$ gomobile build -target=android golang.org/x/mobile/example/basic
It will generate basic.apk which is a zip archive. Unzip to see the contents.
$ unzip basic.apk
classes.dex contains a subclass of NativeActivity that does the basic job of loading libbasic.so at runtime when the application is starting. AndroidManifest.xml is the bare minimum manifest that has org.golang.app.GoNativeActivity as its launcher activity. libbasic.so contains runtime, the main package and all of its dependencies.
System.loadLibrary(“basic”) loads the shared library and invokes ANativeActivity_onCreate. ANativeActivity_onCreate is responsible to find the Go package’s main function by looking for main.main.
$ gobjdump -t lib/armeabi/libbasic.so | grep main.main
001bff04 l O .rodata 00000004 main.main.func1.f
00072208 l F .text 000003bc main.main.func1
00071d14 g F .text 00000034 main.main
onCreate dlsyms main.main, calls it and the application code written in Go runs. Since ANativeActivity_onCreate is called each time the activity is recreated, we are ensuring that main.main won’t be called again on the later onCreate events.