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:
parent
1fabe7702b
commit
d3521b7338
38
app/Go.java
Normal file
38
app/Go.java
Normal 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
121
app/android.go
Normal 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
22
app/app.go
Normal 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()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user