I have a Java application in a JAR that needs to communicate via serial using JSSC (On Ubuntu in a SOPine board). It is throwing the following java.lang.UnsatisfiedLinkError exception:
Exception in thread "CommThread" java.lang.UnsatisfiedLinkError: /root/.jssc/linux/libjSSC-2.7_armsf.so: /root/.jssc/linux/libjSSC-2.7_armsf.so: cannot open shared object file: No such file or directory at java.base/java.lang.ClassLoader$NativeLibrary.load0(Native Method) at java.base/java.lang.ClassLoader$NativeLibrary.load(ClassLoader.java:2430) at java.base/java.lang.ClassLoader$NativeLibrary.loadLibrary(ClassLoader.java:2487) at java.base/java.lang.ClassLoader.loadLibrary0(ClassLoader.java:2684) at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2617) at java.base/java.lang.Runtime.load0(Runtime.java:767) at java.base/java.lang.System.load(System.java:1831) at jssc.SerialNativeInterface.<clinit>(SerialNativeInterface.java:172) at jssc.SerialPort.<init>(SerialPort.java:118) at com.company.comm.transport.serial.CommSerial.initialize(CommSerial.java:51) at com.company.product.adapters.comm.Communicator.connect(Communicator.java:73) at com.company.product.services.comm.CommThread.run(CommThread.java:18)
TL;DR: Skip to the answer if you don’t want to read several day’s worth of troubleshooting.
I exported my JAR file in Eclipse with the jssc.jar added to the project’s Java Build Path’s Libraries in the Classpath and its included in the exported entries.
I did confirm that the file libjSSC-2.7_armsf.so is there:
sudo ls –la /root/.jssc/linux/ drwxr-xr-x 2 root root 4096 Sep 23 14:41 . drwxr-xr-x 3 root root 4096 Sep 20 15:39 .. -rw-r--r-- 1 root root 10539 Sep 20 15:39 libjSSC-2.7_armsf.so
The closest answer to my issue that I can find is the Stack Overflow question unsatisfied link error runnable jar referencing jssc library. I confirmed that the SOPine board I’m using has a VFPv4 Floating Point Unit, contains the directory /lib/arm-linux-gnueablhf/, and I have the JDK bellsoft-java11:armhf (From dpkg -l) installed, so I am not clear on why it is using libjSSC-2.7_armsf.so instead of libjSSC-2.7_armhf.so, which is in the same jssc.jar.
I tried adding libjSSC-2.7_armhf.so to the /root/.jssc/linux/ directory, installing libjssc-java to linux, and modifying LD_LIBRARY_PATH and -Djava.library.path to point to another directory, but it throwing the same exception that it cannot locate libjSSC-2.7_armsf.so.
I’m using the following command to run the JAR file with the pine user:
sudo java -jar product-0.1.0.jar &> output-0.1.0_1.txt
Edit 10/2/2019:
I tried to change the JSSC shared library it’s using by deleting the libjSSC-2.7_armsf.so, installing jssc locally using sudo apt install libjssc-java
, and using LD_LIBRARY_PATH and -Djava.library.path to focus my program at running the new library, but it is still asking for libjSSC-2.7_armsf.so. I’m going to configure the Eclipse project to not package the JSSC JAR file with my project’s exported JAR file and try running it again.
Edit 10/7/2019: Last week, I decided to create a simple HelloSerial.java program to reduce potential dependencies. The code is below:
import jssc.SerialPortList; public class HelloSerial { static { System.out.println("HelloSerial: loading JSSC"); try { System.load("/root/.jssc/linux/libjSSC-2.7_armhf.so"); System.out.println("HelloSerial: loading JSSC"); } catch (UnsatisfiedLinkError e) { System.err.println("Native core library failed to load.n" + e); System.exit(1);; } } public static void main(String[] args) { System.out.println("HelloSerial: start"); String[] portNames = SerialPortList.getPortNames(); System.out.println("HelloSerial: listing " + portNames.length + " port names"); for(int i = 0; i < portNames.length; i++){ System.out.println(portNames[i]); } System.out.println("HelloSerial: end"); } }
Here is the output from compiling and running it within the Linux system:
$ sudo javac -classpath .:jssc.jar HelloSerial.java $ sudo java -cp jssc.jar:. HelloSerial HelloSerial: loading JSSC HelloSerial: loading JSSC HelloSerial: start Exception in thread "main" java.lang.UnsatisfiedLinkError: /root/.jssc/linux/libjSSC-2.7_armsf.so: /root/.jssc/linux/libjSSC-2.7_armsf.so: cannot open shared object file: No such file or directory at java.base/java.lang.ClassLoader$NativeLibrary.load0(Native Method) at java.base/java.lang.ClassLoader$NativeLibrary.load(ClassLoader.java:2430) at java.base/java.lang.ClassLoader$NativeLibrary.loadLibrary(ClassLoader.java:2487) at java.base/java.lang.ClassLoader.loadLibrary0(ClassLoader.java:2684) at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2617) at java.base/java.lang.Runtime.load0(Runtime.java:767) at java.base/java.lang.System.load(System.java:1831) at jssc.SerialNativeInterface.<clinit>(SerialNativeInterface.java:172) at jssc.SerialPortList.<clinit>(SerialPortList.java:43) at HelloSerial.main(HelloSerial.java:20)
Even though I’m explicitly using System.load to load the hard float version of the shared library, it is still defaulting to try using the soft float. What could be forcing it to use the soft float version? All of the libraries, including the JDK, are using either armhf, arm64, or all in dpkg -l
.
Edit 10/8/2019: I found that JSSC uses logic in its SerialNativeInterface class to determine which shared library to use (This is just showing the relevant lines of code):
79 static { 80 String libFolderPath; 81 String libName; 82 83 String osName = System.getProperty("os.name"); 84 String architecture = System.getProperty("os.arch"); 85 String userHome = System.getProperty("user.home"); 86 String fileSeparator = System.getProperty("file.separator"); 87 String tmpFolder = System.getProperty("java.io.tmpdir"); ... 118 else if(architecture.equals("arm")) {//since 2.1.0 119 String floatStr = "sf"; 120 >>>> if(javaLibPath.toLowerCase().contains("gnueabihf") || javaLibPath.toLowerCase().contains("armhf")){ 121 floatStr = "hf"; 122 } 123 else { ... 139 } 140 architecture = "arm" + floatStr; 141 } 142 143 libFolderPath = libRootFolder + fileSeparator + ".jssc" + fileSeparator + osName; 144 libName = "jSSC-" + libVersion + "_" + architecture; 145 libName = System.mapLibraryName(libName);
The important part is on line 120, where the ARM’s library is choosen be soft float or hard float based on whether the java.library.path includes a gnueabihf or armhf path. Including the following line of code shows those paths:
System.out.println("HelloSerial: java.library.path " + System.getProperty("java.library.path"));
This outputted:
HelloSerial: java.library.path /usr/java/packages/lib:/lib:/usr/lib
It looks like gnueabihf or armhf paths like /lib/gnueabihf
aren’t being used, so I need to add one as a placeholder.
Using sudo java -D.java.library.path=".:/lib/gnueabihf" -jar HelloSerial-1.0.0.jar
works.
I’ll see if I can permanently add /lib/gnueabihf
to java.library.path in the future.
Advertisement
Answer
JSSC uses logic in its SerialNativeInterface class to determine which shared library to use. For OS’s with ARM architecture, it checks the java.library.path to see if it contains gnueabihf or armhf in the paths. If not, it will use the soft float shared library instead.
My current java.library.path doesn’t contain either, so I added the existing /lib/gnueabihf
directory to the path using the following command:
$ sudo java -D.java.library.path=".:/lib/gnueabihf" -jar HelloSerial-1.0.0.jar
There are other ways to load a path that are listed in this article or you can search online for that info. You can use $ java -XshowSettings:properties
to confirm that it is included in the java.library.path.