英文:
Java.lang.NoSuchFieldError: no "I" field "value" in class "Ljava/lang/Integer;" or its superclasses Android 10
问题
我收到了一个来自客户的 .so Android 库,我需要将其集成到我的 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);
// 崩溃发生在下面这行
int response = EPM002Lib.ReadParams(token, strSerial, strssid, strpassword, strsensor, keepAlive);
堆栈跟踪:
--- 管理的 Java.Lang.IncompatibleClassChangeError 堆栈跟踪结束 ---
java.lang.NoSuchFieldError: 在类 "Ljava/lang/Integer;" 或其父类中没有名为 "value" 的 "I" 字段
at com.esong.lib.EPM002Lib.ReadParams(Native Method) at
com.sensorwa.config.configdemo.SquareSdkhelper.ReadParams(SquareSdkhelper.java:32)
我理解获取关于 EPM002Lib.ReadParams 内部功能的更多信息可能会有帮助,然而,这个库似乎可以在本地 Android 应用程序中正常工作(甚至在针对 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 😄
答案1
得分: 1
你依赖了一个本不应该依赖的实现细节。
现在它崩溃了,而你得保留这两个碎片。
英文:
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.
答案2
得分: 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
<details>
<summary>英文:</summary>
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!
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论