R. Lang
R. Lang

Reputation: 11

Using BlueCove-DBus on ARM. Linker error between C library and Java code (JNI) on Linux

Currently I'm attempting to use Bluetooth on my Raspberry Pi 3, After trying to use other libraries that turned out to be incomplete, I settled on BlueCove over DBus (Snapshot 2.1.1) as it appeared to be mostly complete and didn't force the use of a GPL license. Unfortunately the given compiled library did not include natives for ARM so after a lot of trial and error, I compiled the natives for this API along with it's pre-requisites. Or so I thought.

I am receiving an UnsatisfiedLinkError when attempting to use the API. I can confirm the library is being loaded into the JVM and the error appears when calling a native method.

java.lang.UnsatisfiedLinkError: com.intel.bluetooth.BluetoothStackBlueZDBus.getLibraryVersionNative()I at com.intel.bluetooth.BluetoothStackBlueZDBus.getLibraryVersionNative(Native Method) at com.intel.bluetooth.BluetoothStackBlueZDBus.getLibraryVersion(BluetoothStackBlueZDBus.java:160) at com.intel.bluetooth.BlueCoveImpl.detectStack(BlueCoveImpl.java:471) at com.intel.bluetooth.BlueCoveImpl.access$500(BlueCoveImpl.java:69) at com.intel.bluetooth.BlueCoveImpl$1.run(BlueCoveImpl.java:1044) at java.base/java.security.AccessController.doPrivileged(Native Method) at com.intel.bluetooth.BlueCoveImpl.detectStackPrivileged(BlueCoveImpl.java:1042) at com.intel.bluetooth.BlueCoveImpl.getBluetoothStack(BlueCoveImpl.java:1035) at javax.bluetooth.LocalDevice.getLocalDeviceInstance(LocalDevice.java:75) at javax.bluetooth.LocalDevice.getLocalDevice(LocalDevice.java:95) at tech.delta.system_automation.server.bin.connections.bluetooth.RemoteDeviceDiscovery.discoverDevices(RemoteDeviceDiscovery.java:54) at tech.delta.system_automation.server.bin.Autonoma_Server.main(Autonoma_Server.java:40)

Here is where I'm initializing the process

LocalDevice d = LocalDevice.getLocalDevice();

Here is where the code fails in Java within the BlueCove API

private native int getLibraryVersionNative();

public int getLibraryVersion() throws BluetoothStateException {
    int version = getLibraryVersionNative();
    if (version != BLUECOVE_DBUS_VERSION) {
        DebugLog.fatal("BlueCove native library version mismatch " + version + " expected " + BLUECOVE_DBUS_VERSION);
        throw new BluetoothStateException("BlueCove native library version mismatch");
    }
    return version;
}

And here is the C code it's attempting to use

JNIEXPORT jint JNICALL Java_com_intel_bluetooth_BluetoothStackBlueZDBus_getLibraryVersionNative
(JNIEnv *env, jobject peer) {
    return com_intel_bluetooth_BluetoothStackBlueZDBus_BLUECOVE_DBUS_VERSION;
}

Now I can only assume that the code when used on a x86/64 system with the precompiled natives works, but I'm not having much luck and I'll be honest, I have no experience working with JNI, my C knowledge is limited and I haven't written a Makefile in my life. But I suspect it might be a linking issue on the C side.

I'm thinking there is an issue with a flag when compiling the C code which is refusing to work with the jar it's compiled with. The reason why I suspect that is due to having to rename the lib file from libbluecovez.so to libbluecovez_arm.so. But I can't be sure and I'm looking for some more experienced users who might be able to point me in the right direction.

Here is the Makefile for reference

# @version $Revision: 2829 $ ($Author: skarzhevskyy $) $Date: 2009-03-03 03:44:45 -0500 (Tue, 03 Mar 2009) $
#
# Created by Francois Kooman
#
# Use this file in case you don't have ant or maven installed on the system
# Usage: make all
#

BLUECOVE_VERSION=2.1.1-SNAPSHOT

BLUECOVE_JAR=../bluecove/target/bluecove-${BLUECOVE_VERSION}.jar
JAVAH=$(JAVA_HOME)/bin/javah
JAVAC=$(JAVA_HOME)/bin/javac
JAVAC_OPTIONS=-g -source 1.5 -target 1.5
CC=gcc
CFLAGS=-Wall -fPIC -fno-stack-protector # -Werror
# -nodefaultlibs ->  statically linked
CLIBFLAGS=$(CFLAGS) -nodefaultlibs -shared -Wl,-soname,libbluecovez-$(BLUECOVE_VERSION)
SRC_C_DIR=src/main/c
SRC_JAVA_DIR=src/main/java
CLASSES_DIR=target/classes
OBJ_DIR=target/native
JAVACLASSES=com.intel.bluetooth.BluetoothStackBlueZDBus com.intel.bluetooth.BluetoothStackBlueZDBusConsts com.intel.bluetooth.BluetoothStackBlueZDBusNativeTests org.bluecove.socket.LocalSocketImpl
LIBPOSTFIX=`uname -p | grep 64 | sed 's/.*64.*/_x64/g'`

TARGET_LIB=target/libbluecovez$(LIBPOSTFIX).so

DBUS_JAVA_LIBS_DIR=target
DBUS_JAVA_CLASSPATH=$(DBUS_JAVA_LIBS_DIR)/dbus.jar:$(DBUS_JAVA_LIBS_DIR)/unixsockets.jar
CLASSPATH=$(BLUECOVE_JAR):$(DBUS_JAVA_CLASSPATH)

all: classes jni-headers native-lib

classes:
    -@mkdir -p $(CLASSES_DIR)
    -@$(JAVAC) -d $(CLASSES_DIR) $(JAVAC_OPTIONS) -classpath $(CLASSPATH) \
    $(SRC_JAVA_DIR)/org/bluez/*.java $(SRC_JAVA_DIR)/com/intel/bluetooth/*.java $(SRC_JAVA_DIR)/org/bluecove/socket/*.java

jni-headers:
    -@$(JAVAH) -d $(SRC_C_DIR) \
    -classpath $(CLASSPATH):$(CLASSES_DIR) \
    $(JAVACLASSES)

native-lib:
    -@mkdir -p $(OBJ_DIR)
    -@cd $(OBJ_DIR) && \
    $(CC) $(CFLAGS) -c ../../$(SRC_C_DIR)/*.c -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux
    -@$(CC) $(CLIBFLAGS) -o $(TARGET_LIB) $(OBJ_DIR)/*.o
    -@strip $(TARGET_LIB)
    -@cp $(TARGET_LIB) $(CLASSES_DIR)/libbluecovez$(LIBPOSTFIX).so
    -@echo "Native library $(TARGET_LIB) created"
    -@echo "Shared library dependencies:"
    -@ldd -v $(TARGET_LIB)

clean:
    rm -rf target

The Lines I suspect in the Makefile are

CLIBFLAGS=$(CFLAGS) -nodefaultlibs -shared -Wl,-soname,libbluecovez-$(BLUECOVE_VERSION)

and

TARGET_LIB=target/libbluecovez$(LIBPOSTFIX).so

Considering the final jar is called bluecove-bluez-2.1.1-SNAPSHOT.jar and the lib file is called libbluecovez_arm.so

If anyone could offer me some assistance, that would be great.

Update, So the issue is with my Makefile, causing issues with Javah which in turn causes issues with the C compiler and is the root of my problem.

Here is the compilation error

Error: Could not find class file for 'com.intel.bluetooth.BluetoothStackBlueZDBus'.
Error: Could not find class file for 'com.intel.bluetooth.BluetoothStackBlueZDBusConsts'.
Error: Could not find class file for 'com.intel.bluetooth.BluetoothStackBlueZDBusNativeTests'.
Error: Could not find class file for 'org.bluecove.socket.LocalSocketImpl'.
Makefile:40: recipe for target 'jni-headers' failed
make: [jni-headers] Error 1 (ignored)
In file included from ../../src/main/c/BlueCoveBlueZ.c:26:0:
../../src/main/c/BlueCoveBlueZ.h:38:57: fatal error: com_intel_bluetooth_BluetoothStackBlueZDBus.h: No such file or di            rectory
 #include "com_intel_bluetooth_BluetoothStackBlueZDBus.h"
                                                         ^
compilation terminated.
In file included from ../../src/main/c/BlueCoveBlueZ_L2CAP.c:26:0:
../../src/main/c/BlueCoveBlueZ.h:38:57: fatal error: com_intel_bluetooth_BluetoothStackBlueZDBus.h: No such file or di            rectory
 #include "com_intel_bluetooth_BluetoothStackBlueZDBus.h"
                                                         ^
compilation terminated.
In file included from ../../src/main/c/BlueCoveBlueZ_L2CAPServer.c:26:0:
../../src/main/c/BlueCoveBlueZ.h:38:57: fatal error: com_intel_bluetooth_BluetoothStackBlueZDBus.h: No such file or di            rectory
 #include "com_intel_bluetooth_BluetoothStackBlueZDBus.h"
                                                         ^
compilation terminated.
In file included from ../../src/main/c/BlueCoveBlueZ_RFCOMM.c:26:0:
../../src/main/c/BlueCoveBlueZ.h:38:57: fatal error: com_intel_bluetooth_BluetoothStackBlueZDBus.h: No such file or di            rectory
 #include "com_intel_bluetooth_BluetoothStackBlueZDBus.h"
                                                         ^
compilation terminated.
In file included from ../../src/main/c/BlueCoveBlueZ_RFCOMMServer.c:26:0:
../../src/main/c/BlueCoveBlueZ.h:38:57: fatal error: com_intel_bluetooth_BluetoothStackBlueZDBus.h: No such file or di            rectory
 #include "com_intel_bluetooth_BluetoothStackBlueZDBus.h"
                                                         ^
compilation terminated.
In file included from ../../src/main/c/BlueCoveBlueZ_Tests.c:26:0:
../../src/main/c/BlueCoveBlueZ.h:38:57: fatal error: com_intel_bluetooth_BluetoothStackBlueZDBus.h: No such file or di            rectory
 #include "com_intel_bluetooth_BluetoothStackBlueZDBus.h"
                                                         ^
compilation terminated.
In file included from ../../src/main/c/BlueCoveLocalSocket.c:27:0:
../../src/main/c/BlueCoveLocalSocket.h:40:49: fatal error: org_bluecove_socket_LocalSocketImpl.h: No such file or dire            ctory
 #include "org_bluecove_socket_LocalSocketImpl.h"
                                                 ^
compilation terminated.

So I need to modify the Makefile so that it correctly points to the .java files? I'm not exactly sure.

Upvotes: -1

Views: 219

Answers (1)

R. Lang
R. Lang

Reputation: 11

For other people who have the same issue. For some reason the original make file does not work with modern java with the layout of the project. This causes a compiling error leading to the .class files not being made. In turn, this means Javah (Deprecated as of Java 10) to fail when trying to produce header files for the native library to communicate over JNI. The c compiler ignores the lack of header files and compiles anyway. This caused the generic linker error. The file was there but the headers for java were not included in compilation.

To get around this, I compiled the jar Bluecove-bluez on my windows system, removed compiling classes from the make file along with cleaning the target and directly copied the classes to the target from my windows system to the flash drive I was compiling on. Moving the flash drive to the ARM Linux system, compiling just the header files along with the c natives fixed the issue.

Upvotes: 0

Related Questions