NeuronRobotics / nrjavaserial

A Java Serial Port system. This is a fork of the RXTX project that uses in jar loading of the native code.
Other
344 stars 142 forks source link

ARMv5 crashes the JVM #224

Open neeme-praks-sympower opened 2 years ago

neeme-praks-sympower commented 2 years ago

Machine: ARMv5 machine (uname tells armv5tejl). JVM:

java version "1.8.0_211"
Java(TM) SE Embedded Runtime Environment (build 1.8.0_211-b12, profile compact3, headless)
Java HotSpot(TM) Embedded Client VM (build 25.211-b12, mixed mode)

Versions affected: I tried with org.openhab:nrjavaserial:3.15.0.OH2 and com.neuronrobotics:nrjavaserial:5.2.1 - same result.

I cannot do anything with nrjavaserial, it crashes the JVM.

Simple test program:

import gnu.io.CommPortIdentifier;

import java.util.Enumeration;

public class TestSerial {

  public static void main(String[] args) {
    System.out.println("COM ports:");
    Enumeration identifiers = CommPortIdentifier.getPortIdentifiers();
    while (identifiers.hasMoreElements()) {
      CommPortIdentifier identifier = (CommPortIdentifier) identifiers.nextElement();
      System.out.printf(
          "name=%s, owner=%s, type=%s, isOwned=%s%n",
          identifier.getName(),
          identifier.getCurrentOwner(),
          identifier.getPortType(),
          identifier.isCurrentlyOwned()
      );
    }

  }
}

Result, JVM crash with:

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  java.lang.ClassLoader$NativeLibrary.load(Ljava/lang/String;Z)V+0
j  java.lang.ClassLoader.loadLibrary0(Ljava/lang/Class;Ljava/io/File;)Z+328
j  java.lang.ClassLoader.loadLibrary(Ljava/lang/Class;Ljava/lang/String;Z)V+48
j  java.lang.Runtime.load0(Ljava/lang/Class;Ljava/lang/String;)V+57
j  java.lang.System.load(Ljava/lang/String;)V+7
j  gnu.io.NativeResource.loadResource(Ljava/io/File;)V+41
j  gnu.io.NativeResource.inJarLoad(Ljava/lang/String;)V+33
j  gnu.io.NativeResource.loadLib(Ljava/lang/String;)V+43
j  gnu.io.NativeResource.load(Ljava/lang/String;)V+138
j  gnu.io.SerialManager.<init>()V+23
j  gnu.io.SerialManager.getInstance()Lgnu/io/SerialManager;+10
j  gnu.io.RXTXCommDriver.<clinit>()V+39
v  ~StubRoutines::call_stub
j  java.lang.Class.forName0(Ljava/lang/String;ZLjava/lang/ClassLoader;Ljava/lang/Class;)Ljava/lang/Class;+0
j  java.lang.Class.forName(Ljava/lang/String;)Ljava/lang/Class;+11
j  gnu.io.CommPortIdentifier.<clinit>()V+12
v  ~StubRoutines::call_stub
j  TestSerial.main([Ljava/lang/String;)V+74
v  ~StubRoutines::call_stub

Browsing the source code, I think the problem is with the way NativeResource tries to locate and load the native library (see source).

The issue seems to be that it naively loops through all the 32-bit native library variants, trying to load each until it succeeds. However, the mere fact of trying to load an incorrect native library seems to crash the JVM.

Workaround: extract libNRJavaSerialv5.so from the JAR file (or download from GitHub), place it somewhere in the file system and add a system property to the JVM command line arguments: -DlibNRJavaSerial.userlib=/path/to/native/libNRJavaSerialv5.so (replace /path/to/native with the location where you stored the downloaded native library).

Proper fix: add a system property (e.g. libraryName + ".classpathlib") to force loading a specific native library from the classpath, skipping the incompatible libraries. I'll open a pull request for this.

Even better fix would be to auto-detect the correct version (somehow jSerialComm manages to achieve this) but I'm happy with the system property.