4

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 becomes 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

digitalmouse12
  • 155
  • 2
  • 13

2 Answers2

12

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.

Community
  • 1
  • 1
paleozogt
  • 6,393
  • 11
  • 51
  • 94
2

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; 
Graham Borland
  • 60,055
  • 21
  • 138
  • 179
  • @digitalmouse12 perhaps some part of the system doesn't know it's supposed to be little endian - remember x86 android is not official. Try dumping out the raw value of jresult (the pointer, not its target) from memory byte-by-byte and see if there is a difference between the direct assignment case and the pointer hackery case. When I do this on 32 bit desktop linux, I don't get a difference, but that's not your target system. – Chris Stratton Jul 20 '11 at 17:39
  • 1
    @Graham Borland: actually this is a [pointer-aliasing](http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) issue. – paleozogt Sep 22 '11 at 16:48