JNI / JNA Unsatisfied Link, but symbol is present - java

i am trying to integrate a legacy system via a provided c-library "libext.so".
To test JNA/JNI i want to call the "setProperty" function.
objdump libext.so -t | grep setProperty
0000000000104d50 g F .text 000000000000000e Java_ExtClass_setProperty
0000000000104be0 g F .text 000000000000016a Java_com_company_ExtClass_setProperty
this is my code, using java 8, jna 4.5.1 and/or native jni, both ways fail with an UnsatisfiedLinkError when calling the function - loading the library works without an Exception.
public class TestClass
{
static {
try {
System.load("/path/to/libext.so");
} catch (Throwable e) {
e.printStackTrace();
}
}
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)
Native.loadLibrary(("/path/to/libext.so"), CLibrary.class);
void setProperty(String key, String value);
}
public static native synchronized void setProperty(String key, String value);
public static void main(String[] args) {
// setProperty("a", "b");
CLibrary.INSTANCE.setProperty("a", "b");
}
}
what am i missing?
#Update:
I now moved both, the JNI class and the JNA INterface to the correct package and renamed the jna interface
package com.company;
public interface LibExtLibrary extends Library {
LibExtLibrary INSTANCE = (LibExtLibrary)
Native.loadLibrary(("/path/to/libext.so"),
LibExtLibrary.class);
void Java_com_company_ExtClass_setProperty(String key, String value);
void Java_ExtClass_setProperty(String key, String value);
void ExtClass_setProperty(String key, String value);
void setProperty(String key, String value);
}
calling the first two methods on INSTANCE gives an InvalidMemoryAccess, the second two an UnsatisfiedLinkError.
The JNI Method worked, as soon as the fully qualified classname was equal to the one defined in the native library

If the question is "why can't I call JNI methods with JNA?":
To be able to call external libraries from Java you have to use JNI. You declare some methods as native and javah will generate special functions for you that you can fill in with your code. Every function will have two additional arguments: an env pointer that is bound to the calling Java thread and the object or class that is the method receiver. If you pass a Java object as one of the other arguments you will get a jobject in your function.
If the implementation of the JNI functions would only consist of forwarding the calls to another external library you can also use JNA. JNA uses JNI to wrap libffi so you can call an existing external library directly. This way you don't have to create a wrapper library yourself. JNA will not create an env pointer or pass the method receiver as argument. And it will try to marshal all the other arguments into something the external library understands. That means no jobjects here.
Summary: JNI calls functions explicitly created for it and the arguments are Java specific. JNA uses JNI internally to call functions in non-Java external libraries.

Related

JNI how to bridge some of the complex object and callbacks

My android project requires some C++/C libraries,and I've got a library (.so) and some headers(.h). and I have reference to some simple demo, using JNI bridge some simple functions, but This lib is complicated for me. How bridging these complex objects and callback.
Created a android project
Copy xxx.so into lib/armeabi, copy all .h files to jni folder.
I want use JNI bridging these c ++ function, I konw I need creat a MStack.h and MStack.cpp to implement these functions, But these headers class makes me very confused,I'm not familiar with C++
some .h like this :
class SIPSTACK_API ISipStack : public Component::IUnknown
{
public:
class IFactory : public Component::IFactoryUnknown
{
public:
virtual ISipStack* create() = 0;
static const char* iid() {return "SipStack";}
};
public:
virtual ~ISipStack(void){}
///#brief
///\param [in] config
///\note
virtual bool setConfig(SipConfigInfo& config)
COMPONENT_UNIMPLEMENTED_OPERATION
///\param [in] handler
///\note
virtual bool start(void)
COMPONENT_UNIMPLEMENTED_OPERATION
///#brief
enum
{
inviteHandler = 0,
actRegHandler,
pasRegHandler,
outCallMsgHandler,
subscribeHandler,
proxyHandler
};
/// #brief
///\param[in] type inviteHandler、registerHandler
///\param[in] handler
///\note
virtual bool attachHandler(int type, Component::IUnknown* handler)
COMPONENT_UNIMPLEMENTED_OPERATION
.....
How can I put these into the corresponding .h and implemented in the cpp. Thanks!

Loading a static void C function via AndroidfromJNI another Package

I am trying some things out with JNI and by that I found the following problem:
If I want to use a native function in Java I load the needed lib, in which the needed function is stored, via
static{
System.loadLibrary("lib");
}
and use
native private static int calculate(byte[] numberArray);
to declare the native method in the java file. During the program itself I can use this function to calculate something with:
int result = calculate(array);
This works only if I compiled the shared object with the header-file created by javah so that each function is named on c side as:
static void Java_com_packagename_File_calculate(const void* array, void* result){
code[...]
}
If I delete the reference in the java code ("native [...] calculate[...]")to this c function; is there any possibility to access / execute the still existing c-code via java (of course without editing the exisiting file ;-)) for example via reflections or inheritance? Or is there something possible like:
public class NewClass{
public int nativeCheater(){
System.loadLibrary("lib");
native private static int Java_com_packagename_File_calculate;
}
}
It is important that I want to use a whole new class without any relations to the prior used package com.packagename.(File).
Thanks in advance :-)
No, but you can create a new class with same package and class name and access the same native method. The new class can declare this method public.
An alternative is to use dynamic binding via Jni_OnLoad() and RegisterNatives(). This way, your native implementations may bind to any Java class, or even more than one.
But if you have access neither to the Java class nor to the native source, you can always create your own native method, in your own class, and inside your C explicitly call the original:
static void Java_com_mypackagename_File_calculate(const void* array, void* result) {
Java_com_packagename_File_calculate(array, result);
}

JNI RegisterNatives does not work on class loaded by ClassLoader.loadClass()

I am embedding a JVM in an existing C++ application and need to register implementation of native Java functions with a class.
Consider this simple class with native functions:
class Native {
static {
System.out.println("Class 'Native' static initializer called.");
}
public native int f(int i);
}
Inside the JVM I am running OSGi which is the reason I need to get the classes using Java code (using the correct class loader) instead of loading them from JNI. However, to keep this example simple, I have left out OSGi.
I have these four different Java methods for getting a jclass value in C++:
class Bridge {
public Class<?> getNativeClass1() throws ClassNotFoundException {
return getClass().getClassLoader().loadClass("org.example.Native");
}
public Class<?> getNativeClass2() throws ClassNotFoundException {
return Class.forName("org.example.Native", false, getClass().getClassLoader());
}
public Class<?> getNativeClass3() throws ClassNotFoundException {
final Class<?> clazz = getClass().getClassLoader()
.loadClass("org.example.Native");
clazz.getMethods();
return clazz;
}
public Class<?> getNativeClass4() throws ClassNotFoundException {
return Class.forName("org.example.Native", true, getClass().getClassLoader());
}
}
To register the native function implementation in C++ for Native.f() I call:
JNIEnv* env = ...
jclass clazz = ...; // Calling one of the four methods above.
JNINativeMethod nativeMethod = {
(char*) "f", // Method name 'f'.
(char*) "(I)I;", // Signature 'int --> int'.
(void*) f // Pointer to C++ implementation of function.
};
env->RegisterNatives(clazz, &nativeMethod, 1);
Depending on which method I use for getting the Class<?> instance, I get different results:
getNativeClass1(): The static initializer is not executed in class Native when loading the class (but of course when creating an instance of the class) and the native implementation is not bound correctly. (Calling the native function in Java gives incorrect results or crashes the JVM.)
getNativeClass2(): Same as above.
getNativeClass3(): The static initializer is still not called in class Native when the class is loaded but the native implementation is bound correctly and I can call f() successfully.
getNativeClass3(): The static initializer is called in class Native when the class is loaded and the native implementation is bound correctly.
So it seems that ClassLoader.loadClass() loads the class in a way so it is not properly initialized and JNIEnv::RegisterNatives() will not work properly. However, calling Class.getMethods() will somehow initalize the class (without invoking the static initializer) so that binding the native methods works.
On the other hand, Class.forName(clazz, false, classLoader) seems to work exactly like Class.loadClass() returning an uninitialized Class instance.
Can anybody explain the difference between
an uninitialized class like the one returned by getNativeClass1() and getNativeClass2()
a partially initialized class like the one returned by getNativeClass3()
a fully initialized class like the one returned by getNativeClass4()
and what is the most portable way to load a class before calling JNIEnv::RegisterNatives()?
So it seems that ClassLoader.loadClass() loads the class in a way so it is not properly initialized
According to the documentation:
loadClass(String): "Invoking this method is equivalent to invoking loadClass(name, false)."
loadClass(String,boolean) (emphasis added): "If the class was found using the above steps, and the resolve flag is true, this method will then invoke the resolveClass(Class) method on the resulting Class object"
Those two methods are intended for internal use by classloaders that need to do something between loading and linking. I'm not sure why loadClass(String) is marked public, but it arguably shouldn't be.
and what is the most portable way to load a class before calling
Class.forName(), which uses the context classloader and ensures that the class is ready for use.

Android NDK - Include a c++ header in a different header?

I have an Ability.h file that is dependent on an Effect.h file.
I need to use javah to generate my header, but I'm unable to define an Effect dependency in my Ability.java class from which I'd like the c++ header to be generated.
Example:
public class Ability {
static {
System.loadLibrary("com_test_Effect");
System.loadLibrary("com_test_Ability");
}
public native Effect foo(Effect x);
}
This code generates an *.h file without the foo() function, as if it couldn't recognize it. It does generate a proper file if I swap the return type to int and don't include the com_test_Effect.
I do have both of the modules defined in the Android.mk file (com_test_Effect and com_test_Ability).
How to include an another c++ file directly in the Xyz.java class from which the *.h is generated by javah ?
Edit: The question can also be asked like this: Is there a way to pass C++-type arguments or return a C++-type value from a function that is an interface between C++ and Java ? (The interfacing medium being JNI.) For example, you can do so with basic types like int which then gets converted to jint and so on.
What about returning an Object:
private native Object fooNative(Object x);
Then convert it so that it has the same signature:
public Effect foo(Effect x) {
return (Effect)fooNative(x);
}

JNA direct call not working with argument Structure[]

I have a C++ function:
struct Result {
//...
};
bool doSomething(Result[]);
If I use the following JNA binding, the function call works fine:
public class Result extends Structure {
//...
}
public interface CPPLibrary extends Library {
public static final CPPLibrary INSTANCE = (CPPLibrary)Native.loadLibrary("dllname");
boolean doSomething(Result[]);
}
But with direct call, I hit an IllegalArgumentException saying class [Lcom.usta.Result; is not a supported argument type (in method calcPV01 in class com.usta.CPPLibrary). My JNA code for the direct call-mapping:
public class CPPLibrary implements Library {
Native.register("dllname");
public static native boolean doSomething(Result[]);
}
I can see in com.sun.jna.Function#convertArgument() explicitly handles Structure[] but com.sun.jna.Native#getConversion(), which is used by direct call-mapping, does not handle Structure[].
The conversion is trivial, just call Structure.getPointer() on the first element of your structure array (assuming that you got the array from Structure.toArray in the first place).
You're actually better off with that when using direct mapping; when passing non-primitive, non-pointer types the JNI layer has to call back into the VM to derive the appropriate native data.
Feel free to file an issue for support of Structure[] arguments in direct mappings. That should be supported (JNA documentation notes that arrays of Pointer/String/WString/NativeMapped are not supported).
If I use a different method signature:
boolean doSomething(Pointer results);
it does work. But then I have to convert from Result[] to a Pointer my self.

Categories

Resources