开发者

Passing pointer from C to Java becomes NULL

开发者 https://www.devze.com 2023-03-21 17:51 出处:网络
I\'m working on an Android app for x86 that requires some integration with C. I\'ve been using swig/JNI to do the trick, and things have been running smoothly for the most part. However, pointers have

I'm working on an Android app for x86 that requires some integration with C. I've been using swig/JNI to do the trick, and things have been running smoothly for the most part. However, pointers have been giving me some errors.

My issue is that I am able to successfully reference variable addresses in the emulator (ARM) but on a device (x86), things do not go as well.

Using the example from this link, I discovered that the address of any allocated variable in C become开发者_开发知识库s NULL once this address passes over to Java. For example...

Swig-generated JNI:

SWIGEXPORT jlong JNICALL Java_exampleJNI_new_1intp(JNIEnv *jenv, jclass jcls) {
  jlong jresult = 0 ;
  int *result = 0 ;
  (void)jenv;
  (void)jcls;
  result = (int *)new_intp();
  LOGI("Result is %x", result);
  *(int **)&jresult = result; 
  LOGI("JResult is %x", jresult);
  return jresult;
}

Source file containing new_intp():

static int *new_intp() {
  return (int *) calloc(1,sizeof(int));
}

I have print statements checking the value of the address as it originates in C and passes over to Java. In new_intp(), the new variable is allocated a good looking address, but once this value returns to JNI and gets cast as a jlong, it turns to NULL.

In other words, *(int **)&jresult = result;causes jresult to be 0.

Why does this happen? Is there some particularity of x86 that disallows JNI to work with pointers? Or is it because I'm testing it on a physical device rather than an emulator?

Regards


Actually, this is a pointer-aliasing issue. SWIG is using old-school C pointer techniques which don't work in newer GCCs when optimization is on. Buried in the SWIG docs it specifically says what to do:

Important

If you are going to use optimisations turned on with gcc (for example -O2), ensure you also compile with -fno-strict-aliasing. The GCC optimisations have become more aggressive from gcc-4.0 onwards and will result in code that fails with strict aliasing optimisations turned on. See the C/C++ to Java typemaps section for more details.


It looks to me like it could be an endianness issue.

*(int **)&jresult = result; 

If int is 32 bits, and jresult is 64 bits, then on a big-endian architecture this could give unexpected results.

&jresult is a pointer to a 64-bit value, so if you cast it to a pointer to a 32-bit value, then you're pointing at the lower-addressed of the two constituent 32-bit words. In a big-endian system this will be the most significant word. So after writing a 32-bit word to the most significant end of the 64-bit value, you get a 64-bit value which is 2^32 times bigger than what you expect.

If your LOGI call is treating the parameter as a 32-bit int, and implicitly down-casting it from a 64-bit value, then it will give 0.

Can you see if this works instead?

jresult = (jlong) result; 
0

精彩评论

暂无评论...
验证码 换一张
取 消