Superpowered NDK Android: returning int succeeds in "extern" block, fails in called function

234 views Asked by At

I'm trying to figure out how to return an int to Java from a C++ function.

On the Java side, I have the following in a button. I put it in a button to ensure that the app was completely loaded before attempting to execute returnInt().

int testInt;
testInt = returnInt();
Log.i(TAG, "testInt");
Log.i(TAG, String.valueOf(testInt));

And

private native int returnInt();

On the C++ side, returning an int in an extern block succeeds:

extern "C" JNIEXPORT jint Java_com_superpowered_crossexample_MainActivity_returnInt(JNIEnv * __unused javaEnvironment, jobject __unused obj) {
    return 50;
}

But trying the following fails with a SIGILL:

jint SuperpoweredExample::returnInt() {
__android_log_print(ANDROID_LOG_VERBOSE, "CrossExample ", "returnInt");  // this runs
    return 50;  // This fails with SIGILL
}

extern "C" JNIEXPORT jint Java_com_superpowered_crossexample_MainActivity_returnInt(JNIEnv * __unused javaEnvironment, jobject __unused obj) {
 // fails with SIGILL
    example->returnInt();
}

Any idea why the above would produce a SIGILL?

I'm testing with a clone of the CrossExample project provided by Superpowered. (I just used CrossExample as an easy way to have a functioning Superpowered project to play with.)

My project is here:

https://github.com/mhurwicz/myCrossExample

This attempt to return an int is in the myCrossExample1 branch of my project.

If for any reason you wanted it, the original Superpowered project is here:

https://github.com/superpoweredSDK/Low-Latency-Android-Audio-iOS-Audio-Engine

Here's debug output:

Signal = SIGILL (signal SIGILL: illegal instruction operand)
javaEnvironment = {JNIEnv * | 0xb40d9a80} 0xb40d9a80
 [0] = {JNIEnv} 
  functions = {const JNINativeInterface * | 0xb4032a40} 0xb4032a40
   [0] = {const JNINativeInterface} 
    reserved0 = {void * | 0x0} nil
    reserved1 = {void * | 0x0} nil
    reserved2 = {void * | 0x0} nil
    reserved3 = {void * | 0x0} nil
    GetVersion = {jint (*)(JNIEnv *) | 0xb3a505f5} (libart.so`art::CheckJNI::GetVersion(_JNIEnv*))
    DefineClass = {jclass (*)(JNIEnv *, const char *, jobject, const jbyte *, jsize) | 0xb3a51ad0} (libart.so`art::CheckJNI::DefineClass(_JNIEnv*, char const*, _jobject*, signed char const*, int))
    FindClass = {jclass (*)(JNIEnv *, const char *) | 0xb3a51f8c} (libart.so`art::CheckJNI::FindClass(_JNIEnv*, char const*))
    FromReflectedMethod = {jmethodID (*)(JNIEnv *, jobject) | 0xb3a52c2f} (libart.so`art::CheckJNI::FromReflectedMethod(_JNIEnv*, _jobject*))
    FromReflectedField = {jfieldID (*)(JNIEnv *, jobject) | 0xb3a531b4} (libart.so`art::CheckJNI::FromReflectedField(_JNIEnv*, _jobject*))
    ToReflectedMethod = {jobject (*)(JNIEnv *, jclass, jmethodID, jboolean) | 0xb3a536f0} (libart.so`art::CheckJNI::ToReflectedMethod(_JNIEnv*, _jclass*, _jmethodID*, unsigned char))
    GetSuperclass = {jclass (*)(JNIEnv *, jclass) | 0xb3a523d8} (libart.so`art::CheckJNI::GetSuperclass(_JNIEnv*, _jclass*))
    IsAssignableFrom = {jboolean (*)(JNIEnv *, jclass, jclass) | 0xb3a527f0} (libart.so`art::CheckJNI::IsAssignableFrom(_JNIEnv*, _jclass*, _jclass*))
    ToReflectedField = {jobject (*)(JNIEnv *, jclass, jfieldID, jboolean) | 0xb3a53b50} (libart.so`art::CheckJNI::ToReflectedField(_JNIEnv*, _jclass*, _jfieldID*, unsigned char))
    Throw = {jint (*)(JNIEnv *, jthrowable) | 0xb3a53fb0} (libart.so`art::CheckJNI::Throw(_JNIEnv*, _jthrowable*))
    ThrowNew = {jint (*)(JNIEnv *, jclass, const char *) | 0xb3a544c0} (libart.so`art::CheckJNI::ThrowNew(_JNIEnv*, _jclass*, char const*))
    ExceptionOccurred = {jthrowable (*)(JNIEnv *) | 0xb3a549da} (libart.so`art::CheckJNI::ExceptionOccurred(_JNIEnv*))
    ExceptionDescribe = {void (*)(JNIEnv *) | 0xb3a54dd0} (libart.so`art::CheckJNI::ExceptionDescribe(_JNIEnv*))
    ExceptionClear = {void (*)(JNIEnv *) | 0xb3a55200} (libart.so`art::CheckJNI::ExceptionClear(_JNIEnv*))
    FatalError = {void (*)(JNIEnv *, const char *) | 0xb3a55a30} (libart.so`art::CheckJNI::FatalError(_JNIEnv*, char const*))
    PushLocalFrame = {jint (*)(JNIEnv *, jint) | 0xb3a55e3e} (libart.so`art::CheckJNI::PushLocalFrame(_JNIEnv*, int))
    PopLocalFrame = {jobject (*)(JNIEnv *, jobject) | 0xb3a5625d} (libart.so`art::CheckJNI::PopLocalFrame(_JNIEnv*, _jobject*))
    NewGlobalRef = {jobject (*)(JNIEnv *, jobject) | 0xb3a56bd5} (libart.so`art::CheckJNI::NewGlobalRef(_JNIEnv*, _jobject*))
    DeleteGlobalRef = {void (*)(JNIEnv *, jobject) | 0xb3a572d9} (libart.so`art::CheckJNI::DeleteGlobalRef(_JNIEnv*, _jobject*))
    DeleteLocalRef = {void (*)(JNIEnv *, jobject) | 0xb3a5729f} (libart.so`art::CheckJNI::DeleteLocalRef(_JNIEnv*, _jobject*))
    IsSameObject = {jboolean (*)(JNIEnv *, jobject, jobject) | 0xb3a57740} (libart.so`art::CheckJNI::IsSameObject(_JNIEnv*, _jobject*, _jobject*))
    NewLocalRef = {jobject (*)(JNIEnv *, jobject) | 0xb3a56b9b} (libart.so`art::CheckJNI::NewLocalRef(_JNIEnv*, _jobject*))
    EnsureLocalCapacity = {jint (*)(JNIEnv *, jint) | 0xb3a57313} (libart.so`art::CheckJNI::EnsureLocalCapacity(_JNIEnv*, int))
    AllocObject = {jobject (*)(JNIEnv *, jclass) | 0xb3a57b90} (libart.so`art::CheckJNI::AllocObject(_JNIEnv*, _jclass*))
    NewObject = {jobject (*)(JNIEnv *, jclass, jmethodID, ...) | 0xb3a5867c} (libart.so`art::CheckJNI::NewObject(_JNIEnv*, _jclass*, _jmethodID*, ...))
    NewObjectV = {jobject (*)(JNIEnv *, jclass, jmethodID, va_list) | 0xb3a580a5} (libart.so`art::CheckJNI::NewObjectV(_JNIEnv*, _jclass*, _jmethodID*, char*))
    NewObjectA = {jobject (*)(JNIEnv *, jclass, jmethodID, jvalue *) | 0xb3a586a7} (libart.so`art::CheckJNI::NewObjectA(_JNIEnv*, _jclass*, _jmethodID*, jvalue*))
    GetObjectClass = {jclass (*)(JNIEnv *, jobject) | 0xb3a58c7e} (libart.so`art::CheckJNI::GetObjectClass(_JNIEnv*, _jobject*))
    IsInstanceOf = {jboolean (*)(JNIEnv *, jobject, jclass) | 0xb3a59090} (libart.so`art::CheckJNI::IsInstanceOf(_JNIEnv*, _jobject*, _jclass*))
    GetMethodID = {jmethodID (*)(JNIEnv *, jclass, const char *, const char *) | 0xb3a599b0} (libart.so`art::CheckJNI::GetMethodID(_JNIEnv*, _jclass*, char const*, char const*))
    CallObjectMethod = {jobject (*)(JNIEnv *, jobject, jmethodID, ...) | 0xb3a6a042} (libart.so`art::CheckJNI::CallObjectMethod(_JNIEnv*, _jobject*, _jmethodID*, ...))
    CallObjectMethodV = {jobject (*)(JNIEnv *, jobject, jmethodID, va_list) | 0xb3a69fd9} (libart.so`art::CheckJNI::CallObjectMethodV(_JNIEnv*, _jobject*, _jmethodID*, char*))
    CallObjectMethodA = {jobject (*)(JNIEnv *, jobject, jmethodID, jvalue *) | 0xb3a67197} (libart.so`art::CheckJNI::CallObjectMethodA(_JNIEnv*, _jobject*, _jmethodID*, jvalue*))
    CallBooleanMethod = {jboolean (*)(JNIEnv *, jobject, jmethodID, ...) | 0xb3a69f6f} (libart.so`art::CheckJNI::CallBooleanMethod(_JNIEnv*, _jobject*, _jmethodID*, ...))
    CallBooleanMethodV = {jboolean (*)(JNIEnv *, jobject, jmethodID, va_list) | 0xb3a69f05} (libart.so`art::CheckJNI::CallBooleanMethodV(_JNIEnv*, _jobject*, _jmethodID*, char*))
    CallBooleanMethodA = {jboolean (*)(JNIEnv *, jobject, jmethodID, jvalue *) | 0xb3a6712d} (libart.so`art::CheckJNI::CallBooleanMethodA(_JNIEnv*, _jobject*, _jmethodID*, jvalue*))
    CallByteMethod = {jbyte (*)(JNIEnv *, jobject, jmethodID, ...) | 0xb3a69e9b} (libart.so`art::CheckJNI::CallByteMethod(_JNIEnv*, _jobject*, _jmethodID*, ...))
    CallByteMethodV = {jbyte (*)(JNIEnv *, jobject, jmethodID, va_list) | 0xb3a69e31} (libart.so`art::CheckJNI::CallByteMethodV(_JNIEnv*, _jobject*, _jmethodID*, char*))
    CallByteMethodA = {jbyte (*)(JNIEnv *, jobject, jmethodID, jvalue *) | 0xb3a670c3} (libart.so`art::CheckJNI::CallByteMethodA(_JNIEnv*, _jobject*, _jmethodID*, jvalue*))
    CallCharMethod = {jchar (*)(JNIEnv *, jobject, jmethodID, ...) | 0xb3a69dc7} (libart.so`art::CheckJNI::CallCharMethod(_JNIEnv*, _jobject*, _jmethodID*, ...))
    CallCharMethodV = {jchar (*)(JNIEnv *, jobject, jmethodID, va_list) | 0xb3a69d5d} (libart.so`art::CheckJNI::CallCharMethodV(_JNIEnv*, _jobject*, _jmethodID*, char*))
    CallCharMethodA = {jchar (*)(JNIEnv *, jobject, jmethodID, jvalue *) | 0xb3a67059} (libart.so`art::CheckJNI::CallCharMethodA(_JNIEnv*, _jobject*, _jmethodID*, jvalue*))
    CallShortMethod = {jshort (*)(JNIEnv *, jobject, jmethodID, ...) | 0xb3a69cf3} (libart.so`art::CheckJNI::CallShortMethod(_JNIEnv*, _jobject*, _jmethodID*, ...))
    CallShortMethodV = {jshort (*)(JNIEnv *, jobject, jmethodID, va_list) | 0xb3a69c89} (libart.so`art::CheckJNI::CallShortMethodV(_JNIEnv*, _jobject*, _jmethodID*, char*))
    CallShortMethodA = {jshort (*)(JNIEnv *, jobject, jmethodID, jvalue *) | 0xb3a66fef} (libart.so`art::CheckJNI::CallShortMethodA(_JNIEnv*, _jobject*, _jmethodID*, jvalue*))
    CallIntMethod = {jint (*)(JNIEnv *, jobject, jmethodID, ...) | 0xb3a69c20} (libart.so`art::CheckJNI::CallIntMethod(_JNIEnv*, _jobject*, _jmethodID*, ...))
obj = {jobject | 0xbfcc426c} 0xbfcc426c
1

There are 1 answers

10
Steve M On BEST ANSWER

You call returnInt() before SuperpoweredExample(). Therefore you are dereferencing a NULL pointer, example.

Also you didn't return anything from the function declared as returning jint

You need:

extern "C" JNIEXPORT jint Java_com_superpowered_crossexample_MainActivity_returnInt(JNIEnv * __unused javaEnvironment, jobject __unused obj) {
    return example->returnInt();
}