2
0
mirror of synced 2025-02-20 13:38:20 +00:00

go.mobile/app: entry point for android shared libraries

This is the minimum amount of machinery necessary to get
libgojni.so loading and callable from an Android App.

To keep impact on the Go runtime small, we construct an
auxv array here from /proc/self/auxv.

LGTM=minux, adonovan
R=adonovan, golang-codereviews, minux, bradfitz
CC=golang-codereviews
https://golang.org/cl/107500043
This commit is contained in:
David Crawshaw 2014-07-10 07:29:36 -04:00
parent 1fabe7702b
commit d3521b7338
3 changed files with 181 additions and 0 deletions

38
app/Go.java Normal file
View File

@ -0,0 +1,38 @@
package go;
import android.content.Context;
// Go is an entry point for libraries compiled in Go.
// In an app's Activity.onCreate, call:
//
// Go.init(getApplicationContext());
//
// When the function returns, it is safe to start calling
// Go code.
public final class Go {
// init loads libgojni.so and starts the runtime.
public static void init(Context context) {
if (running) {
return;
}
running = true;
// TODO(crawshaw): setenv TMPDIR to context.getCacheDir().getAbsolutePath()
// TODO(crawshaw): context.registerComponentCallbacks for runtime.GC
System.loadLibrary("gojni");
new Thread() {
public void run() {
Go.run();
}
}.start();
Go.waitForRun();
}
private static boolean running = false;
private static native void run();
private static native void waitForRun();
}

121
app/android.go Normal file
View File

@ -0,0 +1,121 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build android
package app
/*
#cgo LDFLAGS: -llog
#include <android/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <jni.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
pthread_cond_t go_started_cond;
pthread_mutex_t go_started_mu;
int go_started;
static int (*_rt0_arm_linux1)(int argc, char** argv);
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
_rt0_arm_linux1 = (int (*)(int, char**))dlsym(RTLD_DEFAULT, "_rt0_arm_linux1");
if (_rt0_arm_linux1 == NULL) {
__android_log_print(ANDROID_LOG_FATAL, "Go", "missing _rt0_arm_linux1");
}
pthread_mutex_lock(&go_started_mu);
go_started = 0;
pthread_mutex_unlock(&go_started_mu);
pthread_cond_init(&go_started_cond, NULL);
return JNI_VERSION_1_6;
}
// Runtime entry point.
JNIEXPORT void JNICALL
Java_go_Go_run(JNIEnv* env, jclass clazz) {
// Defensively heap-allocate argv0, for setenv.
char* argv0 = strdup("gojni");
// Build argv, including the ELF auxiliary vector, which is loaded
// from /proc/self/auxv. While there does not appear to be any
// spec for this format, there are some notes in
//
// Phrack, V. 0x0b, Issue 0x3a, P. 0x05.
// http://phrack.org/issues/58/5.html
//
// For our needs, we don't need to know the format beyond the fact
// that argv is followed by a meaningless envp, then a series of
// null terminated bytes that make up auxv.
struct {
char* argv[2];
char* envp[2];
char* auxv[1024];
} x;
x.argv[0] = argv0;
x.argv[1] = NULL;
x.envp[0] = argv0;
x.envp[1] = NULL;
int fd = open("/proc/self/auxv", O_RDONLY, 0);
if (fd == -1) {
__android_log_print(ANDROID_LOG_FATAL, "Go", "cannot open /proc/self/auxv: %s", strerror(errno));
}
int n = read(fd, &x.auxv, sizeof x.auxv - 1);
if (n < 0) {
__android_log_print(ANDROID_LOG_FATAL, "Go", "error reading /proc/self/auxv: %s", strerror(errno));
}
if (n == sizeof x.auxv - 1) { // x.auxv should be more than plenty.
__android_log_print(ANDROID_LOG_FATAL, "Go", "/proc/self/auxv too big");
}
close(fd);
x.auxv[n] = NULL;
int32_t argc = 1;
_rt0_arm_linux1(argc, x.argv);
}
// Used by Java initialization code to know when it can use cgocall.
JNIEXPORT void JNICALL
Java_go_Go_waitForRun(JNIEnv* env, jclass clazz) {
pthread_mutex_lock(&go_started_mu);
while (go_started == 0) {
pthread_cond_wait(&go_started_cond, &go_started_mu);
}
pthread_mutex_unlock(&go_started_mu);
__android_log_print(ANDROID_LOG_INFO, "Go", "gojni has started");
}
*/
import "C"
import "unsafe"
func run() {
// TODO(crawshaw): replace os.Stderr / os.Stdio.
ctag := C.CString("Go")
cstr := C.CString("android.Run started")
C.__android_log_write(C.ANDROID_LOG_INFO, ctag, cstr)
C.free(unsafe.Pointer(ctag))
C.free(unsafe.Pointer(cstr))
// Inform Java that the program is initialized.
C.pthread_mutex_lock(&C.go_started_mu)
C.go_started = 1
C.pthread_cond_signal(&C.go_started_cond)
C.pthread_mutex_unlock(&C.go_started_mu)
select {}
}

22
app/app.go Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package app hooks into the Android runtime via cgo/JNI.
//
// Typical use of this package is in a shared library loaded by
// a mobile app:
//
// package main
//
// import "google.golang.org/mobile/app"
//
// func main() {
// app.Run()
// }
package app
// Run starts the process.
func Run() {
run()
}