Go JNI Exception 0xc0000005 signal arrived during external code execution

3

I am currently getting this error message if I run the code using CGO. Using almost the same code in pure C, I don't get the error message.

Go (CGO) case: The code below will compile without errors but will give out an error when executed.

package main

/*
#cgo CFLAGS: -Id:/jdk/include -Id:/jdk/include/win32
#cgo LDFLAGS: -Ld:/jdk/jre/bin/server -ljvm

#include <jni.h>

JNIEnv* create_vm(JavaVM **jvm)
{
    JNIEnv* env;
    printf("*env\n");
    JavaVMInitArgs args;
    printf("args\n");
    JavaVMOption options;
    printf("options\n");
    args.version = JNI_VERSION_1_8;
    args.nOptions = 0;
    printf("set values\n");
    int rv;
    rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
    printf("jni create java vm\n");
    if (rv != JNI_OK) {
        printf("Failed to create Java VMn");
    } else {
        printf("Launched JVM! :)\n");
    }
    return env;
}

void invoke_class(JNIEnv* env)
{
    jclass hello_world_class;
    jmethodID main_method;
    jmethodID square_method;
    jmethodID power_method;
    jint number=20;
    jint exponent=3;
    hello_world_class = (*env)->FindClass(env, "helloWorld");
    main_method = (*env)->GetStaticMethodID(env, hello_world_class, "main", "([Ljava/lang/String;)V");
    square_method = (*env)->GetStaticMethodID(env, hello_world_class, "square", "(I)I");
    power_method = (*env)->GetStaticMethodID(env, hello_world_class, "power", "(II)I");
    (*env)->CallStaticVoidMethod(env, hello_world_class, main_method, NULL);
    printf("%d squared is %d\n", number,
        (*env)->CallStaticIntMethod(env, hello_world_class, square_method, number));
    printf("%d raised to the %d power is %d\n", number, exponent,
        (*env)->CallStaticIntMethod(env, hello_world_class, power_method, number, exponent));
}

void test()
{
    printf("START TEST\n");
    JavaVM *jvm;
    printf("*jvm\n");
    JNIEnv *env;
    printf("*env\n");
    env = create_vm(&jvm);
    printf("create_vm\n");
    invoke_class(env);
    printf("invoke class\n");
}
*/
import "C"

func main() {
    C.test()
}

Output of the CGO code above

START TEST
*jvm
*env
*env
args
options
set values
Exception 0xc0000005 0x0 0x0 0x50003b6
PC=0x50003b6
signal arrived during external code execution

main._Cfunc_test()
        command-line-arguments/_obj/_cgo_gotypes.go:43 +0x48
main.main()
        D:/Projects/Go/src/loable.tech/go-jasper/main.go:66 +0x27

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
        D:/Go/src/runtime/asm_amd64.s:2197 +0x1
rax     0x6
rbx     0x3100800
rcx     0xcafebabe
rdi     0x1
rsi     0x0
rbp     0x51e052a0
rsp     0x72f678
r8      0x500047b
r9      0x72f840
r10     0x2
r11     0x72f9b0
r12     0x3d8
r13     0x6
r14     0x0
r15     0xf1
rip     0x50003b6
rflags  0x210246
cs      0x33
fs      0x53
gs      0x2b

Now here's the confusing part. When I use C instead of go and using almost the same code as above, everything works as expected.

Pure C code

#include <jni.h>

JNIEnv* create_vm(JavaVM **jvm)
{
    JNIEnv* env;
    printf("*env\n");
    JavaVMInitArgs args;
    printf("args\n");
    JavaVMOption options;
    printf("options\n");
    args.version = JNI_VERSION_1_8;
    args.nOptions = 0;
    printf("set values\n");
    int rv;
    rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
    printf("jni create java vm\n");
    if (rv != JNI_OK) {
        printf("Failed to create Java VMn");
    } else {
        printf("Launched JVM! :)\n");
    }
    return env;
}

void invoke_class(JNIEnv* env)
{
    jclass hello_world_class;
    jmethodID main_method;
    jmethodID square_method;
    jmethodID power_method;
    jint number=20;
    jint exponent=3;
    hello_world_class = (*env)->FindClass(env, "helloWorld");
    main_method = (*env)->GetStaticMethodID(env, hello_world_class, "main", "([Ljava/lang/String;)V");
    square_method = (*env)->GetStaticMethodID(env, hello_world_class, "square", "(I)I");
    power_method = (*env)->GetStaticMethodID(env, hello_world_class, "power", "(II)I");
    (*env)->CallStaticVoidMethod(env, hello_world_class, main_method, NULL);
    printf("%d squared is %d\n", number,
        (*env)->CallStaticIntMethod(env, hello_world_class, square_method, number));
    printf("%d raised to the %d power is %d\n", number, exponent,
        (*env)->CallStaticIntMethod(env, hello_world_class, power_method, number, exponent));
}

void main()
{
    printf("START TEST\n");
    JavaVM *jvm;
    printf("*jvm\n");
    JNIEnv *env;
    printf("*env\n");
    env = create_vm(&jvm);
    printf("create_vm\n");
    invoke_class(env);
    printf("invoke class\n");
}

This is the output of the C code above, which is the output that I am expecting.

START TEST
*jvm
*env
*env
args
options
set values
jni create java vm
Launched JVM! :)
create_vm
Hello, World
20 squared is 400
20 raised to the 3 power is 8000
invoke class
java
c
go
java-native-interface
cgo
asked on Stack Overflow Mar 15, 2018 by juantamad

1 Answer

0

Not at my usual machine so can't verify, but I think this has something to do with how the JVM startup works. This answer provides some info on that. My guess is that golang has some mechanism for processing signals/exceptions and that that interferes with the JVMs signal/exception handling. Maybe it is possible to do something by explicitly handling signals in golang. There is a starting guide for signals in golang here. Again this is just conjecture though, since I can't verify it at the moment.

answered on Stack Overflow Mar 15, 2018 by Emil H

User contributions licensed under CC BY-SA 3.0