I want to use 3rd party shared libs from my Java app via JNI.
(I am running on Manjaro Linux, using command line tools at the moment)
I can build and run this all fine if I do not create a dependency in myLib.so on the 3rd party libraries.
As soon as I create a dependency from myLib.so to 3rd party lib the Java invocation fails with
java: symbol lookup error: myLib.so: undefined symbol: acOpenSystem
I have tried setting the library path to the locations of the libraries, but this still fails.
java -Djava.library.path=.:$ARENA_LIBS:$ARENALIBS2 MyClass
I am sure that there is something simple that I am missing, but I cannot seem to divine the answer from the web. Please help. Even a link to a blindingly obvious resource that I failed to find would be welcome ;-)
Thanks
Edit: Here is the source. HelloWorld -> Foo -> Bar Bar.c
#include <stdio.h>
void bar(void)
{
puts("\nHello, I am a shared library called BAR");
}
bar.h
#ifndef bar_h__
#define bar_h__
extern void bar(void);
#endif // bar_h_
foo.c:
#include <jni.h>
#include <stdio.h>
#include "bar.h"
#include "HelloWorld.h"
// https://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html
void foo(void)
{
puts("\nHello, I am a shared library");
bar();
}
JNIEXPORT void JNICALL Java_HelloWorld_foo
(JNIEnv *, jobject) {
foo();
}
foo.h:
#ifndef foo_h__
#define foo_h__
extern void foo(void);
#endif // foo_h_
HelloWorld.h:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: foo
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_foo
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
HelloWorld.java:
import java.io.File;
class HelloWorld {
public static void main(String[] args) {
try {
File file = new File("libFoo.so");
String path = file.getAbsoluteFile().toPath().toString();
System.out.println("path = "+ path);
System.load(path);
System.out.println("Hello from java app"); // Display the string.
new HelloWorld().foo();
} catch (Exception e) {
e.printStackTrace();
}
}
private native void foo();
}
And my script file:
#!/bin/sh
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
# https://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html
# compile foo
gcc -c -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -Wall -Werror -fpic foo.c
# link object into shared library
gcc -shared -o libFoo.so foo.o
# compile bar
gcc -c -Wall -Werror -fpic bar.c
# link object into shared library
gcc -shared -o libBar.so bar.o
LD_LIBRARY_PATH=::$LD_LIBRARY_PATH
export LD_LIBRARY_PATH
gcc -L$SCRIPT_DIR -o test hello-world.c -lFoo -lBar
./test
LD_LIBRARY_PATH=$SCRIPT_DIR:$LD_LIBRARY_PATH
echo "LD_LIBRARY_PATH: $LD_LIBRARY_PATH"
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH
javac HelloWorld.java
javac -h . HelloWorld.java
java -Djava.library.path=. HelloWorld
And for completeness, the source for the c executable that proves that the problem is not in the c code:
#include <stdio.h> // C Standard IO Header
#include "foo.h"
int main(int argc, char *argv[]) {
printf("Hello from executable");
foo();
}
The executable in c runs fine, but the java application gives an error:
Hello from java app
Hello, I am a shared library
java: symbol lookup error: /mnt/data/singer/c/experiment/libFoo.so: undefined symbol: bar
java -version
openjdk version "17.0.5" 2022-10-18
OpenJDK Runtime Environment Temurin-17.0.5+8 (build 17.0.5+8)
OpenJDK 64-Bit Server VM Temurin-17.0.5+8 (build 17.0.5+8, mixed mode, sharing)
gcc --version
gcc (GCC) 12.2.1 20230201
Running on Manjaro 22.1
I think that I have now got it working. I will post my script for posterity:
#!/bin/sh
# Example of building JNI app.
# Designed to run on linux using GCC and a suitable java compiler
#
# Uses an app 'HelloWorld' which invokes a shared library 'foo'.
# Foo depends upon 'bar' which is bound to foo either as
# - an object (bar.o)
# - a static library (bar.a)
# - a dynamic library (libBar.so)
#
# The java app is run once with each of the three forms of the shared library (libFoo.so)
# compile java and create jni header file
javac HelloWorld.java
javac -h . HelloWorld.java
# compile bar
gcc -c -Wall -Werror -fpic bar.c
# link object into shared library
gcc -shared -o libBar.so bar.o
# link object into static library
ar rcs bar.a bar.o
# compile foo
gcc -c -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -Wall -Werror -fpic foo.c
# Set up the LD_LIBRARY_PATH to resolve dynamic libraries at run time
# current working directory as a variable
cwd=$(pwd)
LD_LIBRARY_PATH=$cwd:$LD_LIBRARY_PATH
# echo "LD_LIBRARY_PATH: $LD_LIBRARY_PATH"
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH
echo
echo
echo
echo "************** Using Bar as object *************"
# link object into shared library an object library for Bar
gcc -shared -o libFoo.so foo.o bar.o
java -Djava.library.path=. HelloWorld
echo
echo
echo
echo "************** Using Bar as static library *************"
# link object into shared library a static library for Bar
gcc -shared -o libFoo.so foo.o -L . bar.a
java -Djava.library.path=. HelloWorld
echo
echo
echo
echo "************** Using Bar as dynamic library *************"
# link object into shared library using a dynamic library for Bar
gcc -shared -o libFoo.so foo.o -L . -lBar
java -Djava.library.path=. HelloWorld