Java.lang.NoSuchFieldError: нет поля «I» «значение» в классе «Ljava / lang / Integer;» или его суперклассах Android 10

#java #android #java-native-interface

#java #Android #java-native-интерфейс

Вопрос:

Я получил библиотеку Android .so от клиента, и я должен интегрировать ее в свой проект Xamarin Forms. Библиотека помогает приложению подключаться к устройству Интернета вещей. Поскольку методы библиотеки имеют следующую подпись, я решил написать java-оболочку для упрощения параметров и создания файла aar. После этого я изначально привязываю aar и использую его как dll в своем проекте.

Важно отметить, что проблема в Xamarin возникает только тогда, когда цель компиляции> 10. В противном случае все работает нормально. Я предполагаю, что последние обновления интерфейсов, отличных от SDK, сломали приложение.

Заголовок библиотеки:

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

Проблема:
Метод отлично работает при вызове из собственного приложения Android, однако вылетает со следующей ошибкой из Xamarin Forms. Сбой происходит в следующей строке в оболочке Java.

Аварийная строка:

 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);
  

Трассировка стека:

— Конец управляемого Java.Lang.Несовместимостьclasschangeerror трассировка стека — java.lang.NoSuchFieldError: нет поля «I» «значение» в классе «Ljava / lang / Integer;» или его суперклассов в com.esong.lib.EPM002Lib.ReadParams(собственный метод) в com.sensorwa.config.configdemo.SquareSdkhelper.ReadParams(SquareSdkhelper.java:32)

Я понимаю, что дополнительная информация о внутренней функциональности параметров EPM002Lib.ReadParams помогла бы, однако библиотека, похоже, работает с собственным приложением для Android (даже при компиляции с Android 10). Пожалуйста, не стесняйтесь запрашивать дополнительную информацию или предлагать предложения. Спасибо за помощь 😄

Комментарии:

1. Зачем библиотеке вообще нужен доступ к int полю? Это звучит сломано. Integer предполагается, что значения должны быть неизменяемыми, поэтому библиотека не должна пытаться изменить значение. И если они просто хотели прочитать значение, они должны использовать intValue метод.

2. Да, я понимаю. Тем не менее, мне просто любопытно, как это работает на родном Android :/

3. Ну, не совсем ясно, что вы подразумеваете под «скомпилированным для Android 10» . Если вы имеете в compileSdkVersion виду, то это здесь не совсем уместно. Важна targetSdkVersion (и, конечно, фактическая версия Android, на которой вы запускаете приложение).

4. Да, это действительно compileSdkVersion, и я думаю, что это актуально, поэтому изменения в интерфейсах, отличных от sdk, были внесены в sdk 10. Насколько я понимаю, targetSdkVersion — это версия, на которую нацелено приложение, возможно, с большинством пользователей. база.

5. Похоже, вы неправильно поняли роль compileSdkVersion ; он определяет только, какие API доступны вам во время компиляции. Это то targetSdkVersion , что имеет значение, когда дело доходит до определения того, как применяются такие изменения поведения во время выполнения. На странице, на которую вы ссылались в своем вопросе, даже написано так: «эти интерфейсы принадлежат списку max-target-p ( greylist-max-p ), поэтому ваше приложение может использовать эти интерфейсы, только если оно нацелено на Android 9 (уровень API 28) или ниже» .

Ответ №1:

Я скомпилировал этот файл с помощью компилятора Android Aarch64:

 #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);
}
  

что приводит к следующему коду для 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


}
  

и для 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
  

Основными отличиями являются смещения, используемые в ldr x8 вызовах. Это смещения в таблицу указателей функций внутри JNIEnv , более конкретно:

  • GetFieldID имеет смещение 752
  • GetIntField имеет смещение 800
  • GetMethodID имеет смещение 264
  • CallIntMethod имеет смещение 392.

Другим отличием является подпись, переданная в GetIntField or GetMethodID , которая вводится во время компоновки. Объектный файл, который я сбросил, еще не связан, поэтому там есть фиктивные инструкции. Это четвертый аргумент, поэтому он передается в регистре x3 .

Итак, подводя итог, вам нужно сделать следующее:

  • Найдите адрес строки "()I" где-нибудь в библиотеке или добавьте его в таблицу строк.
  • Вам нужно найти все места, к которым java.lang.Integer#value осуществляется доступ.
  • Замените два смещения указателя функции (752 -> 264; 800 -> 392) в ldr x8 правом углу перед blr x8 звонком.
  • Найдите код, который изменяется x3 , и вместо этого укажите на "()I" него.

Удачи!

Комментарии:

1. Большое вам спасибо, что нашли время ответить. Я постараюсь реализовать это через неделю 🙂

Ответ №2:

Вы полагаетесь на детали реализации, на которые нельзя было полагаться.

Теперь он сломался, и вы можете сохранить обе части.

Комментарии:

1. Волшебной палочки нет, вам придется изменить эту библиотеку, как указано на вашем связанном сайте. Google предупреждал об этом годами.