I'm trying to use a native library in java using JNA. The library loads, and I can call methods, but on some JVM distributions the native library does not behave as expected. Some results:
JDK distributions Behaves as expected
Oracle JDK 1.8.0_191 yes
Liberica JDK 1.8u192 no
Liberica JDK 1.8u275 no
AdoptOpenJDK OpenJ9 1.8u272 yes
AdoptOpenJDK OpenJ9 15.0.1 no
AdoptOpenJDK Hotspot 1.8u275 no
AdoptOpenJDK Hotspot 14.0.1 no
Here expected bahaviour is getting getting hardware info via LC_get_hardware_info and then exiting without errors.
No JDK9+ distributions seems to work, some JDK8 distributions work, some don't (edit) all tested JVMs are 64-bit.
The code (edit: not calling LC_get_hardware_info does not change behaviour, just there's no output in case of success), values returned by SenseLC are error codes:
public static void main(String[] args) {
System.setProperty("jna.library.path", System.getProperty("user.dir"));
SenseLC lib = SenseLC.INSTANCE;
PointerByReference handle = new PointerByReference(new Memory(8));
int ret = lib.LC_open(0, 0, handle);
if (ret != SenseLC.LC_SUCCESS) {
System.out.println("Open: " + SenseLCUtil.getCodeName(ret));
return;
}
System.out.println("Handle: " + handle.getValue());
LC_hardware_info hi = new LC_hardware_info();
ret = lib.LC_get_hardware_info(handle.getValue(), hi);
if (ret != SenseLC.LC_SUCCESS) {
System.out.println("Read: " + SenseLCUtil.getCodeName(ret));
return;
}
System.out.println(hi);
ret = lib.LC_close(handle.getValue());
if (ret != SenseLC.LC_SUCCESS) {
System.out.println("Close: " + SenseLCUtil.getCodeName(ret));
return;
}
}
SenseLCUtil.getCodeName just maps return codes to strings
Expected bahavior:
All calls to SenseLC return 0 (LC_SUCCESS), after invoking ret = lib.LC_get_hardware_info(handle.getValue(), hi);, System.out.println(hi); prints:
LC_hardware_info{
developerNumber={somenumber}
serialNumber={somenumber}
setDate={somenumber}
reservation=0
}
Failure behaviour:
Invoking int ret = lib.LC_open(0, 0, handle); is successfull (ret=0 after call), then after running ret = lib.LC_get_hardware_info(handle.getValue(), hi); ret=3 (LC_INVALID_PARAMETER), and the program output is:
Read: LC_INVALID_PARAMETER
It seems as if an incorrect handle is returned from native code on some JDKs.
Edit 1: added LC_hardware_info and SenseLC classes
LC_hardware_info:
@FieldOrder({
"developerNumber",
"serialNumber",
"setDate",
"reservation"
})
public class LC_hardware_info extends Structure {
public int developerNumber;
public byte[] serialNumber = new byte[8];
public int setDate;
public Pointer reservation;
public String getSerial() {
return new String(serialNumber, StandardCharsets.UTF_8);
}
@Override
public String toString() {
return "LC_hardware_info{"
+ "\ndeveloperNumber=" + developerNumber
+ "\nserialNumber=" + getSerial()
+ "\nsetDate=" + setDate
+ "\nreservation=" + reservation
+ "\n}";
}
}
SenseLC:
public interface SenseLC extends Library {
int LC_SUCCESS = 0; // Successful
int LC_OPEN_DEVICE_FAILED = 1; // Open device failed
int LC_FIND_DEVICE_FAILED = 2; // No matching device was found
int LC_INVALID_PARAMETER = 3; // Parameter Error
int LC_INVALID_BLOCK_NUMBER = 4; // Block Error
int LC_HARDWARE_COMMUNICATE_ERROR = 5; // Communication error with hardware
int LC_INVALID_PASSWORD = 6; // Invalid Password
int LC_ACCESS_DENIED = 7; // No privileges
int LC_ALREADY_OPENED = 8; // Device is open
int LC_ALLOCATE_MEMORY_FAILED = 9; // Allocate memory failed
int LC_INVALID_UPDATE_PACKAGE = 10; // Invalid update package
int LC_SYN_ERROR = 11; // thread Synchronization error
int LC_OTHER_ERROR = 12; // Other unknown exceptions
SenseLC INSTANCE = (SenseLC) Native.load("Sense_LC", SenseLC.class);
int LC_open(int vendor, int index, PointerByReference handle);
int LC_close(Pointer handle);
int LC_passwd(Pointer handle, int type, byte[] passwd);
int LC_read(Pointer handle, int block, byte[] buffer);
int LC_write(Pointer handle, int block, byte[] buffer);
int LC_encrypt(Pointer handle, byte[] plaintext, byte[] ciphertext);
int LC_decrypt(Pointer handle, byte[] ciphertext, byte[] plaintext);
int LC_set_passwd(Pointer handle, int type, byte[] passwd, int retries);
int LC_change_passwd(Pointer handle, int type, byte[] oldpasswd, byte[] newpasswd);
int LC_get_hardware_info(Pointer handle, LC_hardware_info info);
int LC_get_software_info(LC_software_info info);
int LC_Hmac(Pointer handle, byte[] text, int textlen, byte[] digest);
int LC_Hmac_software(byte[] text, int textlen, byte[] key, byte[] digest);
int LC_update(Pointer handle, byte[] buffer);
int LC_set_key(Pointer handle, int type, byte[] key);
int LC_gen_update_pkg(byte[] serial, int block, byte[] buffer, byte[] key, byte[] uptPkg);
}
Edit 2 library header : lc.h, part of the sdk (Clave2)
Edit 3: Updated JNA mappings, problem persist. Old code can be found here