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:
"()I"
somewhere in the library or add it to the string table.java.lang.Integer#value
is accessed.ldr x8
right before the blr x8
call.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.