简体   繁体   中英

Java.lang.NoSuchFieldError: no "I" field "value" in class "Ljava/lang/Integer;" or its superclasses Android 10

I have received a .so android library from a client and I have to integrate that in my Xamarin Forms project. The library helps the app connect to an IoT device. As the library methods are of the following signature, I decided to write a java wrapper to simplify the parameters and create an aar file. Afterwards, I natively bind the aar and use it as a dll in my project.

It is important to note that the problem in Xamarin only occurs when the Compile Target is > 10. Otherwise, it works fine. My guess is that the latest Updates to non-SDK interfaces broke the application .

Library header:

public static native int ReadParams(String token, StringBuilder serial, StringBuilder ssid, StringBuilder password, StringBuilder sensor, Integer keepAlive);

The problem: The method works fine when called from within a native android application however crashes with the following error from Xamarin Forms. The crash is on the following line in the Java wrapper.

Crash line:

StringBuilder strSerial = new StringBuilder();
StringBuilder strssid = new StringBuilder();
StringBuilder strpassword = new StringBuilder();
StringBuilder strsensor = new StringBuilder();
Integer keepAlive = new Integer(0);
//Crash on below line
int response = EPM002Lib.ReadParams(token, strSerial, strssid, strpassword, strsensor, keepAlive);

The stacktrace:

--- End of managed Java.Lang.IncompatibleClassChangeError stack trace --- java.lang.NoSuchFieldError: no "I" field "value" in class "Ljava/lang/Integer;" or its superclasses at com.esong.lib.EPM002Lib.ReadParams(Native Method) at com.sensorwa.config.configdemo.SquareSdkhelper.ReadParams(SquareSdkhelper.java:32)

I understand that more information regarding the internal functionality of the EPM002Lib.ReadParams params would help, however, the library seems to work with a native android application (even when compiled against Android 10). Please feel free to ask for more information or provide suggestions. Thanks for the help 😄

You're relying on an implementation detail that was not supposed to be relied on.

Now it broke and you get to keep both pieces.

I compiled this file using the Android Aarch64 compiler:

#include <jni.h>

int access_field(JNIEnv *env, jobject obj) {
    jclass cls_Integer = (*env)->FindClass(env, "Ljava/lang/Integer;");
    jfieldID fid_Integer_value = (*env)->GetFieldID(env, cls_Integer, "value", "I");
    return (*env)->GetIntField(env, obj, fid_Integer_value);
}

int access_method(JNIEnv *env, jobject obj) {
    jclass cls_Integer = (*env)->FindClass(env, "Ljava/lang/Integer;");
    jmethodID mid_Integer_value = (*env)->GetMethodID(env, cls_Integer, "intValue", "()I");
    return (*env)->CallIntMethod(env, obj, mid_Integer_value);
}

which results in the following code for access_field :

int access_field(JNIEnv *env, jobject obj) {
   0:   d10103ff    sub sp, sp, #0x40
   4:   a9037bfd    stp x29, x30, [sp,#48]
   8:   9100c3fd    add x29, sp, #0x30
   c:   90000008    adrp    x8, 0 <access_field>
  10:   91000108    add x8, x8, #0x0
  14:   90000002    adrp    x2, 0 <access_field>
  18:   91000042    add x2, x2, #0x0
  1c:   90000003    adrp    x3, 0 <access_field>
  20:   91000063    add x3, x3, #0x0
  24:   f81f83a0    stur    x0, [x29,#-8]
  28:   f81f03a1    stur    x1, [x29,#-16]
    jclass cls_Integer = (*env)->FindClass(env, "Ljava/lang/Integer;");
  2c:   f85f83a9    ldur    x9, [x29,#-8]
  30:   f9400129    ldr x9, [x9]
  34:   f9401929    ldr x9, [x9,#48]
  38:   f85f83a0    ldur    x0, [x29,#-8]
  3c:   aa0803e1    mov x1, x8
  40:   f90007e2    str x2, [sp,#8]
  44:   f90003e3    str x3, [sp]
  48:   d63f0120    blr x9
  4c:   f9000fe0    str x0, [sp,#24]
    jfieldID fid_Integer_value = (*env)->GetFieldID(env, cls_Integer, "value", "I");
  50:   f85f83a8    ldur    x8, [x29,#-8]
  54:   f9400108    ldr x8, [x8]
  58:   f9417908    ldr x8, [x8,#752]
  5c:   f85f83a0    ldur    x0, [x29,#-8]
  60:   f9400fe1    ldr x1, [sp,#24]
  64:   f94007e2    ldr x2, [sp,#8]
  68:   f94003e3    ldr x3, [sp]
  6c:   d63f0100    blr x8
  70:   f9000be0    str x0, [sp,#16]
    return (*env)->GetIntField(env, obj, fid_Integer_value);
  74:   f85f83a8    ldur    x8, [x29,#-8]
  78:   f9400108    ldr x8, [x8]
  7c:   f9419108    ldr x8, [x8,#800]
  80:   f85f83a0    ldur    x0, [x29,#-8]
  84:   f85f03a1    ldur    x1, [x29,#-16]
  88:   f9400be2    ldr x2, [sp,#16]
  8c:   d63f0100    blr x8
  90:   a9437bfd    ldp x29, x30, [sp,#48]
  94:   910103ff    add sp, sp, #0x40
  98:   d65f03c0    ret


}

and for access_method :

int access_method(JNIEnv *env, jobject obj) {
  9c:   d10103ff    sub sp, sp, #0x40
  a0:   a9037bfd    stp x29, x30, [sp,#48]
  a4:   9100c3fd    add x29, sp, #0x30
  a8:   90000008    adrp    x8, 0 <access_field>
  ac:   91000108    add x8, x8, #0x0
  b0:   90000002    adrp    x2, 0 <access_field>
  b4:   91000042    add x2, x2, #0x0
  b8:   90000003    adrp    x3, 0 <access_field>
  bc:   91000063    add x3, x3, #0x0
  c0:   f81f83a0    stur    x0, [x29,#-8]
  c4:   f81f03a1    stur    x1, [x29,#-16]
    jclass cls_Integer = (*env)->FindClass(env, "Ljava/lang/Integer;");
  c8:   f85f83a9    ldur    x9, [x29,#-8]
  cc:   f9400129    ldr x9, [x9]
  d0:   f9401929    ldr x9, [x9,#48]
  d4:   f85f83a0    ldur    x0, [x29,#-8]
  d8:   aa0803e1    mov x1, x8
  dc:   f90007e2    str x2, [sp,#8]
  e0:   f90003e3    str x3, [sp]
  e4:   d63f0120    blr x9
  e8:   f9000fe0    str x0, [sp,#24]
    jmethodID mid_Integer_value = (*env)->GetMethodID(env, cls_Integer, "intValue", "()I");
  ec:   f85f83a8    ldur    x8, [x29,#-8]
  f0:   f9400108    ldr x8, [x8]
  f4:   f9408508    ldr x8, [x8,#264]
  f8:   f85f83a0    ldur    x0, [x29,#-8]
  fc:   f9400fe1    ldr x1, [sp,#24]
 100:   f94007e2    ldr x2, [sp,#8]
 104:   f94003e3    ldr x3, [sp]
 108:   d63f0100    blr x8
 10c:   f9000be0    str x0, [sp,#16]
    return (*env)->CallIntMethod(env, obj, mid_Integer_value);
 110:   f85f83a8    ldur    x8, [x29,#-8]
 114:   f9400108    ldr x8, [x8]
 118:   f940c508    ldr x8, [x8,#392]
 11c:   f85f83a0    ldur    x0, [x29,#-8]
 120:   f85f03a1    ldur    x1, [x29,#-16]
 124:   f9400be2    ldr x2, [sp,#16]
 128:   d63f0100    blr x8
 12c:   a9437bfd    ldp x29, x30, [sp,#48]
 130:   910103ff    add sp, sp, #0x40
 134:   d65f03c0    ret

The main differences are the offsets used in the ldr x8 calls. These are offsets into the function pointer table inside the JNIEnv , more specifically:

  • GetFieldID is at offset 752
  • GetIntField is at offset 800
  • GetMethodID is at offset 264
  • CallIntMethod is at offset 392.

The other difference is the signature passed to the GetIntField or GetMethodID , which is injected at linker time. The object file I dumped is not linked yet, so there are dummy instructions there. It is the fourth argument so it is passed in register x3 .

So, to summarize, you need to do the following:

  • Find the address of the string "()I" somewhere in the library or add it to the string table.
  • You need to find all places where java.lang.Integer#value is accessed.
  • Replace the two function pointer offsets (752 -> 264; 800 -> 392) in the ldr x8 right before the blr x8 call.
  • Find the code that changes x3 and make it point to "()I" instead.

Good luck!

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