java - 本机库在某些 JVM 上无法正常工作
问题描述
我正在尝试使用 JNA 在 java 中使用本机库。库加载,我可以调用方法,但在某些 JVM 发行版上,本机库的行为不符合预期。一些结果:
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
这里预期的行为是通过获取硬件信息LC_get_hardware_info
然后退出而没有错误。
似乎没有 JDK9+ 发行版可以工作,一些 JDK8 发行版可以工作,有些不能(编辑)所有经过测试的 JVM 都是 64 位的。
代码(编辑:不调用LC_get_hardware_info
不会改变行为,只是在成功的情况下没有输出),返回的值SenseLC
是错误代码:
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
只是将返回码映射到字符串
预期行为:
所有对SenseLC
return 0
( LC_SUCCESS
) 的调用,在调用 之后ret = lib.LC_get_hardware_info(handle.getValue(), hi);
,System.out.println(hi);
打印:
LC_hardware_info{
developerNumber={somenumber}
serialNumber={somenumber}
setDate={somenumber}
reservation=0
}
失败行为:
调用int ret = lib.LC_open(0, 0, handle);
成功(ret=0
调用后),运行后ret = lib.LC_get_hardware_info(handle.getValue(), hi);
ret=3
(LC_INVALID_PARAMETER
),程序输出为:
Read: LC_INVALID_PARAMETER
似乎某些 JDK 上的本机代码返回了不正确的句柄。
编辑 1:添加 LC_hardware_info 和 SenseLC 类
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}";
}
}
感测LC:
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);
}
编辑 2库头文件: lc.h,部分sdk (Clave2)
编辑 3:更新了 JNA 映射,问题仍然存在。旧代码可以在这里找到
解决方案
您在不同的 JVM 上得到不同结果的事实表明存在未定义的行为。在它看起来有效的情况下,你只是走运了。
正如评论中所讨论的,您的问题可能是lc_handle_t
. 头文件表明它是 64 位机器上的指针大小,以及int
32 位机器上的大小。因此,您应该PointerByReference
在看到* lc_handle_t
时使用(打开以获取手柄时),然后getValue()
在返回的手柄上使用lc_handle_t
.
该类型也出现在结构的reservation
字段中LC_hardware_info
,应该映射到Pointer
.
推荐阅读
- jquery - 选择更改未运行
- python - python的if条件中的列表计数方法
- clang - Clang Windows LD 设置库路径不起作用?
- java - 压缩多个 excel 文件 Java
- javascript - 如何使用 emscripten 生成独立的 WebAssembly
- angularjs - 加载资源失败:将 Angular 应用程序推送到云代工厂时,服务器响应状态为 404(未找到)
- matlab - 单例注册表类
- python - 无法导入张量流:没有名为“张量流”的模块
- python - Python 脚本完成但给出 [2]+ Killed
- javascript - 为什么 push 显示类型为“any[]”的参数不可分配给类型为“never”的参数错误?