简体   繁体   中英

Char* Corrupted Across JNI Calls

I'm working with a C library (librtmp) on android (Google glass specifically), with bindings created by SWIG.

When I call a native method with a struct containing a char*, return from that method, and call another native method, the char* will not have the same data in it, many times the data will appear to be null, but sometimes it will contain parts of the java or project path or random text (ex: glassvideostreamer/libr , Zunk+ , sun/* ) it will also sometimes contain non-valid unicode code points. The pointer itself keeps pointing to the same memory location between JNI calls, and is correct when the first native method exits, and corrupted when it enters another native method.

Wrapping both methods in a native function and then calling that function works correctly, but then when calling another native method the data will no longer be at the memory location.

Here's my code:

test.i

%module test_wrapper
#define NO_CRYPTO
/* Anything in the following section is added verbatim to the .cxx wrapper file*/
%{
#include "rtmp.h"
%}
test* Alloc_Test();
void Init_Test(test* t, char* data, int len);
void Test_String(test* t);
void Init_And_Test_String(test* t, char* data, int len);

rtmp.h

  typedef struct test {
    char* data;
    int len;
  } test;
  test* Alloc_Test();
  void Init_Test(test* t, char* data, int len);
  void Test_String(test* t);
  void Init_And_Test_String(test* t, char* data, int len);

rtmp.c

test* Alloc_Test(){
  return calloc(sizeof(test));
}
void Init_Test(test* t, char* data, int len){
  t->data = data;
  t->len = len;
  __android_log_print(ANDROID_LOG_DEBUG,"TEST", "String: %.*s  (pointer: %p)", t->len, t->data, t);
}
void Test_String(test* t){
 __android_log_print(ANDROID_LOG_DEBUG,"TEST","%String: %.*s  (pointer: %p)", t->len, t->data, t);
}
void Init_And_Test_String(test* t, char* data, int len){
  Init_Test(t, data, len);
  Test_String(t);
}

finally (for c) here's the generated swig bindings librtmp_wrap.c

SWIGEXPORT jlong JNICALL Java_atellis_glassvideostreamer_librtmp_test_1wrapperJNI_Alloc_1Test(JNIEnv *jenv, jclass jcls) {
  jlong jresult = 0 ;
  test *result = 0 ;

  (void)jenv;
  (void)jcls;
  result = (test *)Alloc_Test();
  *(test **)&jresult = result; 
  return jresult;
}


SWIGEXPORT void JNICALL Java_atellis_glassvideostreamer_librtmp_test_1wrapperJNI_Init_1Test(JNIEnv *jenv, jclass jcls, jlong jarg1, jstring jarg2, jint jarg3) {
  test *arg1 = (test *) 0 ;
  char *arg2 = (char *) 0 ;
  int arg3 ;

  (void)jenv;
  (void)jcls;
  arg1 = *(test **)&jarg1; 
  arg2 = 0;
  if (jarg2) {
    arg2 = (char *)(*jenv)->GetStringUTFChars(jenv, jarg2, 0);
    if (!arg2) return ;
  }
  arg3 = (int)jarg3; 
  Init_Test(arg1,arg2,arg3);
  if (arg2) (*jenv)->ReleaseStringUTFChars(jenv, jarg2, (const char *)arg2);
}


SWIGEXPORT void JNICALL Java_atellis_glassvideostreamer_librtmp_test_1wrapperJNI_Test_1String(JNIEnv *jenv, jclass jcls, jlong jarg1) {
  test *arg1 = (test *) 0 ;

  (void)jenv;
  (void)jcls;
  arg1 = *(test **)&jarg1; 
  Test_String(arg1);
}


SWIGEXPORT void JNICALL Java_atellis_glassvideostreamer_librtmp_test_1wrapperJNI_Init_1And_1Test_1String(JNIEnv *jenv, jclass jcls, jlong jarg1, jstring jarg2, jint jarg3) {
  test *arg1 = (test *) 0 ;
  char *arg2 = (char *) 0 ;
  int arg3 ;

  (void)jenv;
  (void)jcls;
  arg1 = *(test **)&jarg1; 
  arg2 = 0;
  if (jarg2) {
    arg2 = (char *)(*jenv)->GetStringUTFChars(jenv, jarg2, 0);
    if (!arg2) return ;
  }
  arg3 = (int)jarg3; 
  Init_And_Test_String(arg1,arg2,arg3);
  if (arg2) (*jenv)->ReleaseStringUTFChars(jenv, jarg2, (const char *)arg2);
}

on the java side: we just have the generated SWIGTYPE_p test that wraps the struct pointer and code that calls the JNI functions: test_wrapperJNI.java

public class test_wrapperJNI {
  public final static native long Alloc_Test();
  public final static native void Init_Test(long jarg1, String jarg2, int jarg3);
  public final static native void Test_String(long jarg1);
  public final static native void Init_And_Test_String(long jarg1, String jarg2, int jarg3);
}

and test_wrapper.java

public class test_wrapper {
  public static SWIGTYPE_p_test Alloc_Test() {
    long cPtr = test_wrapperJNI.Alloc_Test();
    return (cPtr == 0) ? null : new SWIGTYPE_p_test(cPtr, false);
  }

  public static void Init_Test(SWIGTYPE_p_test t, String data, int len) {
    test_wrapperJNI.Init_Test(SWIGTYPE_p_test.getCPtr(t), data, len);
  }

  public static void Test_String(SWIGTYPE_p_test t) {
    test_wrapperJNI.Test_String(SWIGTYPE_p_test.getCPtr(t));
  }

  public static void Init_And_Test_String(SWIGTYPE_p_test t, String data, int len) {
    test_wrapperJNI.Init_And_Test_String(SWIGTYPE_p_test.getCPtr(t), data, len);
  }

}

putting it all together you get:

System.loadLibrary("TEST");
SWIGTYPE_p_test test = test_wrapper.Alloc_Test();
test_wrapper.Init_Test(test, "Will this string stay the same?", 31);
test_wrapper.Test_String(test);

test = test_wrapper.Alloc_Test();
test_wrapper.Init_And_Test_String(test, "This string will stay the same", 30);

which gets this result (in logcat):

05-11 12:27:33.198    4568-4568/atellis.glassvideostreamer D/dalvikvm﹕ Trying to load lib /data/app-lib/atellis.glassvideostreamer-1/libTEST.so 0x41b597b0
05-11 12:27:33.198    4568-4568/atellis.glassvideostreamer D/dalvikvm﹕ Added shared lib /data/app-lib/atellis.glassvideostreamer-1/libTEST.so 0x41b597b0
05-11 12:27:33.198    4568-4568/atellis.glassvideostreamer D/dalvikvm﹕ No JNI_OnLoad found in /data/app-lib/atellis.glassvideostreamer-1/libTEST.so 0x41b597b0, skipping init
05-11 12:27:38.128    4568-4568/atellis.glassvideostreamer D/TEST﹕ String: Will this string stay the same?  (pointer: 0x59564088)
05-11 12:27:39.940    4568-4568/atellis.glassvideostreamer D/TEST﹕ String:   (pointer: 0x59564088)
05-11 12:27:41.261    4568-4568/atellis.glassvideostreamer D/TEST﹕ String: This string will stay the same  (pointer: 0x59565948)
05-11 12:27:41.261    4568-4568/atellis.glassvideostreamer D/TEST﹕ String: This string will stay the same  (pointer: 0x59565948)

In this case the string becomes blank! As I mentioned above though, it will sometime contain file paths or random text.

typedef struct test {
    char* data;
    int len;
} test;

This specifies no space to actually store the the string data , but merely a pointer to hold its address.

You seem in practice to be pointing it at the memory of various function arguments of likely temporary reservation, and that will not work as that memory can be re-purposed later.

void Init_Test(test* t, char* data, int len){
    t->data = data;

In fact, you explicitly release your claim to that memory:

Init_And_Test_String(arg1,arg2,arg3); 
if (arg2) (*jenv)->ReleaseStringUTFChars(jenv, jarg2, (const char *)arg2);

You must explicitly reserve memory to hold the string, and copy from any temporary storage to there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM