I am trying to build a Linux library(*.so) to use it in a Java application. This library itself loads an dll-file with native functions.
This is my C++ code:
__delspec(dllexport) void __cdecl GetDllVersion(void){
typedef int(*GetDllVersion)(int*,int*,int*,int*);
void* lib = dlopen("~/lib.dll",RTLD_NOW);
cout << "Loading Symbol..." << endl;
GetDllVersion getVer= (GetDllVersion) dlsym(lib,"GetDllVersion");
dlclose(lib);
}
The code was compiled with wineg++ -shared lib.cpp -o libexports.so -Wl,--no-as-needed -ldl without errors.
The Java application prints out "Loading Symbol..." in a loop and then terinates without any message. I could determine that this has to do something with dlsym().
With nm -D lib.so I could look inside the lib.so. The function GetDllVersion() is indeed a symbol inside this library.
Can somebody tell me why there is a infinite loop and wyh the Java VM is terminating?
Regards Wurmi
This line:
void* lib = dlopen("~/lib.dll",RTLD_NOW);
will always fail, because dlopen does not do tilde-expansion (in general, only shell does). You really should check dlopen return value.
This line:
GetDllVersion getVer= (GetDllVersion) dlsym(lib,"GetDllVersion");
is equivalent to dlsym(RTLD_DEFAULT, ...) (because RTLD_DEFAULT == 0 and lib == NULL) and as such returns you a pointer to the function you are already in, resulting in infinite recursion, and eventual crash due to stack exhaustion.
Related
I like to control 'sooperlooper' from java, a sound looper. This uses the OSC protocol. First try was the Java OSC lib, but this doesn't do anything. Now I'm trying JNA to wrap liblo.so
The program I'm trying to replicate in JAVA is very simple (of course with different commands from "record"):
static lo_address addr;
int main(int argc, char *argv[])
{
addr = lo_address_new(<IP>, "9951");
lo_send(addr, "/sl/-1/down", "s", "record");
}
The C declaration of the failing method is (https://github.com/radarsat1/liblo/blob/master/lo/lo.h.in):
int lo_send(lo_address targ, const char *path, const char *type, ...);
If I understand correctly, the lo_address is some void pointer type declared elsewhere.
My library interface is:
public interface LibLo extends Library {
Pointer lo_address_new(String host, String port);
int lo_send(Pointer address, String command, String a, String... params);
}
And my caller code is this:
System.setProperty("jna.debug_load", "true");
System.setProperty("jna.debug_load.jna", "true");
LibLo libLo = Native.loadLibrary("liblo", LibLo.class);
Pointer pointer = libLo.lo_address_new(<IP>, "9951");
libLo.lo_send(pointer, "/sl/-1/down","s", "record");
It perfectly gets through the lo_address_new call. 'pointer' does have some value. I think my arguments are correct.
And I discovered even with incorrect arguments, it gets past the lo_address_new call.
My stdout is:
...
Found library 'liblo' at /usr/lib/x86_64-linux-gnu/liblo.so.7.3.0
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'lo_send': /usr/lib/x86_64-linux-gnu/liblo.so.7.3.0: undefined symbol: lo_send
at com.sun.jna.Function.<init>(Function.java:245)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:566)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:542)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:528)
at com.sun.jna.Library$Handler.invoke(Library.java:228)
at com.sun.proxy.$Proxy0.lo_send(Unknown Source)
at nl.ronaldteune.coverdownloader.Main.main(Main.java:30)
Other questions about UnsatisfiedLinkError point to more thorough problems. I can't find 'lo_send' when opening the lib with vim, I can find lo_send_message though - but it's more low level. However, I think my C program is using the same lib (compiled with -llo) and it doesn't have problems to run.
So... I'm stuck. Anyone know how I can debug further?
You can't map lo_send() to JNA (and it doesn't appear in your vim output) because it's a macro:
#define lo_send(targ, path, types...) \
lo_send_internal(targ, __FILE__, __LINE__, path, types, \
LO_MARKER_A, LO_MARKER_B)
You could, in theory, map lo_send_internal() but the source code comments for it say:
/* Don't call lo_send_internal directly, use lo_send, a macro wrapping this
* function with appropriate values for file and line */
Which makes sense as it needs to know the source code __FILE__ and __LINE__ number at compile time, and any hack you'd make in JNA for these values would have to assume you have the correct source code used to compile your binary .so. You'd also need the markers but they are at least constants, amusing words spelled out in hex code.
You could probably just toss in dummy values for the file and line to make your code work, but otherwise, your C wrapper function calling lo_send() looks like the best workaround in this case.
OK, I worked around it... With a wrapper lib:
#include <lo/lo.h>
static lo_address addr;
lo_address slGetAddress() {
return lo_address_new("IP", "9951");
}
void slSendFull(lo_address addr, const char *command) {
lo_send(addr, "/sl/-1/down", "s", command);
}
void slSendSimple(const char *command) {
addr = slGetAddress();
lo_send(addr, "/sl/-1/down", "s", command);
}
and this compile script:
#!/bin/bash
# sudo ln -s /usr/lib/arm-linux-gnueabihf/liblo.so.7 /usr/local/lib/liblo.so
# sudo ln -s /usr/lib/x86_64-linux-gnu/liblo.so.7 /usr/local/lib/liblo.so
gcc -c -Wall -O2 -fPIC lowrapper.c -llo
ld -shared lowrapper.o /usr/local/lib/liblo.so -o lowrapper.so
sudo cp lowrapper.so /usr/local/lib/
It's working now. Still interested in a real answer, but not in a hurry for that anymore :)
My app need to run as none-root user but need to switch to other user to execute some commands.
I tried to:
write a JNI,
JNIEXPORT void JNICALL Java_SetUIDJNI_setuid(JNIEnv *env, jobject thisObj,jstring uname) {
const char *name = jstringTostring(env,uname);
int pid;
struct passwd *p;
show_ids();
if ((p = getpwnam(name)) == NULL) {
perror(name);
exit(EXIT_FAILURE);
}
pid = (int) p->pw_uid;
printf("pid=%d\n",pid);
if (seteuid (pid) < 0) {
perror ("setuid");
exit (EXIT_FAILURE);
}
show_ids();
}
build it as root and chmod u+s
-rwsr-xr-x 1 root root 76155 Aug 7 16:56 libsetuid.so*
call it in java
api = new SetUIDJNI(); // invoke the native method
api.setuid("slurm");
But if run it as none-root, it does not work
/opt/jdk1.8/jre/bin/java -Djava.library.path=../jni HelloJNI
The real user ID is: 1000
The effective user ID is :1000 <= which expected is 0
setuid: Operation not permitted
But it works if runner is root
The real user ID is: 0
The effective user ID is :0
pid=1002The real user ID is: 0
The effective user ID is :1002
Anything wrong here?
UPDATE
Modify the JNI part to executable c
void show_ids (void)
{
printf ("The real user ID is: %d\n", getuid());
printf ("The effective user ID is :%d\n", geteuid());
}
int main(void)
{
show_ids();
if (seteuid (1002) < 0) {
perror ("setuid");
exit (EXIT_FAILURE);
}
show_ids();
return (0);
}
Build it as root and run chmod u+s
-rwsr-xr-x 1 root root 8814 Aug 9 11:44 a.out*
Run it as normal user and works
./a.out
The real user ID is: 1000
The effective user ID is :0
The real user ID is: 1000
The effective user ID is :1002
The reason that seteuid was not working for you from JNI was that the effective user id for the JVM process was not 0 (root).
You were apparently attempting to make the effective user id by setting the "setuid" bit on your native library. That won't work. Similarly, making the JAR file (or a class file) as setuid won't work. The setuid bit is only meaningful on a executable file; i.e. a file that the OS itself knows how to execute.
So how do we implement "setuid root behavior" for a Java program?
In theory, you could mark the /usr/bin/java as setuid to root. Don't do that! If you do that, every Java program you run will be run as root. That would be bad.
In theory, you could write a shell script to launch your application; e.g.
#!/bin/sh
java some.pkg.Main "$#"
and mark the script as setuid to root. Don't do that! Setuid shell scripts are a security risk. (On some versions of Linux / UNIX the setuid bit is not respected for shell scripts anyway.)
The solution is to write a custom JVM launcher in native code, compile and link it, and make the launcher executable setuid. Note that the launcher must be written very carefully to project against someone subverting it. For example:
It should ignore CLASSPATH environment variable, and should not allow the classpath to be supplied any other way.
It should not take a user-supplied JAR file as a parameter.
It should take care that the user can't trick it into running the wrong Java code by interfering with the path to the JAR file.
And other things ... that I haven't thought of!
In fact, it is probably safer to run the Java application as a privileged user ... and not rely on "setuid root" at all.
There are lots of examples that Android C/C++ native calls Java APIs.
However, all of these examples I have read are the Android Java APIs call native
first and then the native calls other Java APIs by using the passed JNI-ENV.
Without the passed JNI-ENV, how can the C/C++ get it?
Is it possible that C/C++ native calls Java APIs without JNI-ENV?
Can you give an example or a link for it if it is possible?
Thanks!
You need to include jni.h first. This brings a ton of useful calls; with newer android releases you'll also need JniInvocation.h. To enable this:
LOCAL_C_INCLUDES += ${JNI_H_INCLUDE}
That certainly works with source tree, not sure about NDK, but should be fine, too.
Second, pretty important thing is to have proper signal chain lib selected. Art or Dalvik will load libsigchain.so, which is a stub and abort()s every time any of its methods are being called. On Android it's done with a little hack: local symbols are being exported to global symbol table, so that Art picks exec's symbols instead of loading shared lib. Here's how it's done:
# Enable native helper calls
LOCAL_SHARED_LIBRARIES += libnativehelper
# Include all of the Android's libsigchain symbols
LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain
# Export only libsigchain symbols to global symbol table.
LOCAL_LDFLAGS += \
-Wl,--export-dynamic \
-Wl,--version-script,art/sigchainlib/version-script.txt
Link your executable now. Double check that the required symbols are indeed exported:
% readelf -a <output_binary> | grep InitializeSignalChain
654: 0002cab1 211 FUNC GLOBAL PROTECTED 12 InitializeSignalChain
Done? things get simpler now:
Initialize the JNI (so that your code uses proper VM)
JniInvocation invocation;
if (invocation.Init(nullptr)) return;
Create JavaVM:
JavaVM* vm;
JNIEnv* env;
JavaVMInitArgs* args;
args.version = JNI_VERSION_1_4; // _5, _6
args.options = nullptr;
args.nOptions = 0;
args.ignoreUnrecognized = JNI_FALSE;
if (JNI_CreateJavaVM(&vm, &env, &args) < 0) return;
At this point your vm and env are ready to use. have fun.
Check for exceptions, if any
if (env->ExceptionCheck()) {
// ...
}
When you're done, clean up
vm->DetachCurrentThread();
vm->DestroyJavaVM();
More interesting stuff can be found here and DalvikVM Main is probably the best source of knowledge. Good luck!
This probably sounds like a nightmare, but I'd really like to get this working. I am using this example for the most part: Calling C from Haskell and am trying to get this working on ubuntu.
I am running this in java:
package test;
public class JniTest {
public native int fib(int x);
}
this in c after creating the .h file with javah: (test_JniTest.c)
#include "test_JniTest.h"
#include "Safe_stub.h"
JNIEXPORT jint JNICALL Java_test_JniTest_fib(JNIEnv * e, jobject o, jint f)
{
return fibonacci_hs(f);
}
and then for reference in haskell (before stub): (Safe.hs)
module Safe where
import Foreign.C.Types
fibonacci :: Int -> Int
fibonacci n = fibs !! n
where fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
fibonacci_hs :: CInt -> CInt
fibonacci_hs = fromIntegral . fibonacci . fromIntegral
foreign export ccall fibonacci_hs :: CInt -> CInt
and this is what i'm trying to compile it with:
ghc -c -O Safe.hs
followed by:
ghc -shared -o libTest.jnilib -optc-O test_JniTest.c
-I/usr/lib/jvm/java-6-sun-1.6.0.26/include -I/usr/lib/jvm/java-6-sun-1.6.0.26/include/linux
and I am getting this error:
/usr/bin/ld: test_JniTest.o: relocation R_X86_64_PC32 against
undefined symbol `fibonacci_hs' can not be used when making a shared
object; recompile with -fPIC /usr/bin/ld: final link failed: Bad value
collect2: ld returned 1 exit status
I am not a c expert by any means and have no idea what to do about this. I tried compiling various ways with -fPIC, but I kept on getting the same error. Any idea what I might be doing wrong?
Thanks!
Although I've pretty much answered this question here: Communication between Java and Haskell, since this issue is more about the error itself, I will be adding the details for that here. The issue stems from Haskell not supporting shared libraries very well, while Java requires them.
Buildings plugins as Haskell shared libs gives us this insight and workaround:
In principle you can use -shared without -dynamic in the link step. That would mean to statically link the rts all the base libraries into your new shared library. This would make a very big, but standalone shared library. However that would require all the static libraries to have been built with -fPIC so that the code is suitable to include into a shared library and we don't do that at the moment.
If we use ldd again to look at the libfoo.so that we've made we will notice that it is missing a dependency on the rts library. This is problem that we've yet to sort out, so for the moment we can just add the dependency ourselves:
$ ghc --make -dynamic -shared -fPIC Foo.hs -o libfoo.so \
-lHSrts-ghc6.11 -optl-Wl,-rpath,/opt/ghc/lib/ghc-6.11/
This is a workaround because it requires us to know the version of the rts library at build time.
If your goal is to actually get something done (as opposed to just playing around with JNI) I suggest tackling this as a garden variety RPC problem and utilizing one of the many framework/protocols for it:
Protocol Buffers from Google
Thrift from Facebook
Avro (well this is mostly a wire protocol)
From what you are trying to do, Thrift might be your best bet since it describes a full client/server RPC stack but I'm pretty sure any of them would pretty much work over a simple socket.
I need to run this line from my c++ program:
java -jar test.jar text1 text2
the java app will give a float value and give it to the c++ program.
How can I do this? I never call a java stuff before from my ms visual studio C++ file.
If you want strong coupling use JNI wrapper.
When I run the java command directly on my command prompt, it works. but when I run the command from the c++ file, the error says "The system cannot execute the specified program" .
here's my code, im using ms visual studio 2005 :
#include "stdafx.h"
#include <conio.h>
int _tmain(int argc, _TCHAR* argv[])
{
float value;
FILE *child = _popen("java -jar c:\simmetrics_jar_v1_6_2_d07_02_07.jar text1 ssdyr445", "r");
if (fscanf(child, "%f", &value) == 1)
{
fprintf(stdout,"Got Value from simmetrics: %f\n", value);
}
else
{
fprintf(stdout,"ERROR\n");
}
fclose(child);
return 0;
}
A simple solution is to use popen() and pclose().
The function popen(), forks() and execs() a sub processes. But it attaches the sub-processes standard-in and standard-out the stream object created by popen. Thus writting anything to the stream in the parent sends it to the standard-in of the sub-processes while anything the sub-processes writes to standard-out can be read from the stream by the parent:
double value;
FILE* child = popen("java -jar test.jar text1 text2", "r");
if (fscanf(child, "%f", &value) == 1)
{
fprintf(stdout,"Got Value: %f\n", value);
}
else
{
fprintf(stdout,"ERROR\n");
}
fclose(child);
The easiest if You can modify your java code:
write the result to environment variable (pseudo code below):
solution 1.
(Write directly to env. in java app.)
java:
...
setenv('ret', somefloatvalue);
...
exit..
c++:
system("java -jar test.jar text1 text2")
...
getenv("ret")
(I haven't test it, but important here is the context, does system creates another shell (console),
if yes, you'll not see those envs, therefore some other spawn method is necessary)
CreateProcess() on windows
fork() on linux.
There are also more complex solutions,
send some JASON's through the sockets.... etc.
Write to text file in java, read in
c++.
MPI...
extreme in this case CORBA ;)