JNI UnsatisfiedLinkError on a native method - java

I'm running a basic Java wrapper for a C++ BSD socket client. I can compile the Java and generate a header file, but when I try to run it, it returns Exception in thread "main" java.lang.UnsatisfiedLinkError: JavaClient.socketComm()V
From what I've been able to find, it seems like this is indicative of a mismatch between method signatures, but I can't find anything wrong with mine.
Java Code
public class JavaClient
{
public native void socketComm();
public static void main(String[] args)
{
System.load("/home/cougar/workspace/ArbiterBSDSocketComms/JNIClient/JavaClient.so");
JavaClient client = new JavaClient();
client.socketComm();
System.out.println("Done");
}
}
C Implementation
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <cstdlib>
#include <stdio.h>
#include <jni.h>
#include "JavaClient.h"
#define MAXHOSTNAME 256
JNIEXPORT void JNICALL Java_JavaClient_socketComm
(JNIEnv *env, jobject obj) {
struct sockaddr_in remoteSocketInfo;
struct hostent *hPtr;
int socketHandle;
char *remoteHost="localhost";
int portNumber = 8080;
memset(&remoteSocketInfo, 0, sizeof(struct sockaddr_in)); //Clear structure memory
if ((hPtr = gethostbyname(remoteHost)) == NULL) //Get sysinfo
{
printf("System DNS resolution misconfigured.");
printf("Error number: ", ECONNREFUSED);
exit(EXIT_FAILURE);
}
if((socketHandle = socket(AF_INET, SOCK_STREAM, 0)) < 0) //Create socket
{
close(socketHandle);
exit(EXIT_FAILURE);
}
memcpy((char *)&remoteSocketInfo.sin_addr,
hPtr->h_addr, hPtr->h_length); //Load sys info into sock data structures
remoteSocketInfo.sin_family = AF_INET;
remoteSocketInfo.sin_port = htons((u_short)portNumber); //Set port number
if(connect(socketHandle, (struct sockaddr *)&remoteSocketInfo, sizeof(struct sockaddr_in)) < 0)
{
close(socketHandle);
exit(EXIT_FAILURE);
}
int rc=0;
char buf[512];
strcpy(buf, "Sup server");
send(socketHandle, buf, strlen(buf)+1, 0);
}
void main(){}
Header File
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JavaClient */
#ifndef _Included_JavaClient
#define _Included_JavaClient
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: JavaClient
* Method: socketComm
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_JavaClient_socketComm
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Excuse some of the bad formatting, I haven't used stackoverflow much and the code formatting is a bit sketchy.
These files all reside in /JNIClient.
I'm running Ubuntu 12.04 x64, and I have both 32- and 64-bit JDKs installed. I tried generating the .so with the 32-bit verison first, which would have been ideal, but I got an ELF mismatch, so I just went with the 64-bit so I wouldn't have to deal with that. (Any insight on that is welcome as well.)
My process is:
$>javac JavaClient.java
$>javah JavaClient
$>cc -m64 -g -I/usr/lib/jvm/java-6-openjdk-amd64/include -I/usr/lib/jvm/java-6-openjdk-amd64/include/linux -shared JavaClient.c -o JavaClient.so
$>java JavaClient
The full error message is
Exception in thread "main" java.lang.UnsatisfiedLinkError: JavaClient.socketComm()V
at JavaClient.socketComm(Native Method)
at JavaClient.main(JavaClient.java:9)
$>nm JavaClient.so returns:
cougar#Wanda:~/workspace/ArbiterBSDSocketComms/JNIClient$ nm JavaClient.so
0000000000200e50 a _DYNAMIC
0000000000200fe8 a _GLOBAL_OFFSET_TABLE_
w _Jv_RegisterClasses
0000000000200e30 d __CTOR_END__
0000000000200e28 d __CTOR_LIST__
0000000000200e40 d __DTOR_END__
0000000000200e38 d __DTOR_LIST__
00000000000005e0 r __FRAME_END__
0000000000200e48 d __JCR_END__
0000000000200e48 d __JCR_LIST__
0000000000201010 A __bss_start
w __cxa_finalize##GLIBC_2.2.5
0000000000000540 t __do_global_ctors_aux
0000000000000490 t __do_global_dtors_aux
0000000000201008 d __dso_handle
w __gmon_start__
0000000000201010 A _edata
0000000000201020 A _end
0000000000000578 T _fini
0000000000000438 T _init
0000000000000470 t call_gmon_start
0000000000201010 b completed.6531
0000000000201018 b dtor_idx.6533
0000000000000510 t frame_dummy
Edit: I have a theory that the .so is being built improperly, as $>nm JavaClient.so doesn't show the method names in it. Any suggestions on what's wrong about that cc command?
Okay, SO: I kept at this because nothing seemed right. The method signatures were all matched, nothing should be wrong, eclipse file properties said it was editing the right file, etc etc. I finally catted the JavaClient.c, and it was blank. Apparently eclipse wasn't ACTUALLY editing the file it said it was. Fixed that up and now everything's fine.

I will give you a far better solution.
public void socketComm() throws IOException
{
Socket socket = new Socket("localhost", 8080);
try
{
socket.getOutputStream().write("Sup server\u0000".getBytes());
}
finally
{
socket.close(); // You forgot this
}
}
No JNI required at all. It also doesn't close an invalid handle if socket() returns < 0, unlike your code.

I kept at this because nothing seemed right. The method signatures were all matched, nothing should be wrong, eclipse file properties said it was editing the right file, etc etc. I finally catted the JavaClient.c, and it was blank. Apparently eclipse wasn't ACTUALLY editing the file it said it was. Fixed that up and now everything's fine.
Not sure why Eclipse claimed to be editing a blank file, I checked and rechecked and it wasn't referencing any links or anything, but if you're having the same problem as me check it out.

Related

While working with ffmpeg: Created function in C file and after compilation with NDK accessing from android activity, getting signal 11 error

After successfully fire command ndk-build I got .so file in my libs folder, then after some changes in gradle file I got native_libs.xml .idea/libraries folder.
Now I am accessing c function from my java code/ android activity. I am getting signal 11 error
My code is
C file
#include <jni.h>
#include <android/log.h>
#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#define LOG_TAG "mylib"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
//JNIEXPORT jint JNICALL Java_com_xxx_xxx_activities_SplashActivity_logFileInfo(JNIEnv * env, jobject this, jstring filename);
jint Java_com_xxx_xxx_activities_TutorialsActivity_logFileInfo(JNIEnv * env, jobject this, jstring filename)
{
av_register_all();
AVFormatContext *pFormatCtx;
const jbyte *str;
str = (*env)->GetStringUTFChars(env, filename, NULL);
if(avformat_open_input(&pFormatCtx, str, NULL, NULL)!=0)
{
LOGE("Can't open file '%s'\n", str);
return 1;
}
else
{
LOGI("File was opened\n");
LOGI("File '%s', Codec %s",
pFormatCtx->filename,
pFormatCtx->iformat->name
);
}
return 0;
}
Loading and try to access method in java code is
private static native int logFileInfo(String filename);
static {
System.loadLibrary("framegrabber");
}
In OnCreate of activity
logFileInfo(file.getAbsolutePath());
Finally error at point logFileInfo(file.getAbsolutePath()); is
A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x106e in tid 8905 (Thread-20972)
Please replay if you have any solution, Thanking in advance.

How to include cspi/spi.h header file in JNI code?

I am trying to include spi.h header file in JNI C code, when the /usr/include/at-spi-1.0/cspi/spi.h file is included in c code I got the following fatal error like " fatal error: cspi/spi.h: No such file or directory ".
for creating '.so' file i used following command like:
sudo gcc -shared -fPIC -o libHelloJNI.so -I/usr/lib/jvm/java-7-openjdk-amd64/include -I/usr/lib/jvm/java-7-openjdk-amd64/include/linux -I/usr/include/at-spi-1.0/cspi HelloJNI.c
using '-I' I included JNI.h header file successful but when I am trying to include the /usr/include/at-spi-1.0/cspi/spi.h, file I got the fatal error that No such file or directory.
so please look at following code and give your feedback please!
#include<jni.h>
#include<cspi/spi.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"HelloJNI.h"
// Implementation of native method sayHello() of HelloJNI class
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env,
jobject thisObj,jint a)
{
printf("Hello World! %d\n",a);
return;
}
JNIEXPORT void JNICALL JAVA_HelloJNI_initSPI(JNIEnv *env, jclass
cls)
{
int init_error;
init_error = SPI_init();
putenv("GTK_MODULES=gail:atk-bridge");
putenv("GNOME_ACCESSIBILITY=1");
if(init_error)
{
printf("First time error %d in Initialising
SPI\n",init_error);
}
else
{
printf("SPI Initialise successfully");
}
SPI_event_main();
return;
}
If you specify cspi/spi.h in the #include directive, you don't need cspi in the -I switch.
Change it to:
-I/usr/include/at-spi-1.0/

Java compiling, "No such file or Directory"

using JNI i'm trying to let a C program works under java. Here's the issue.
First i created the java class testcli.java:
public class testcli {
public native void stdErr();
public native int cliFromC();
static {
System.loadLibrary("ctest");
}
public static void main(String[] args) {
new testcli().stdErr();
new testcli().cliFromC();
}
Then compiled with
javac testcli.java
and created header with
javah testcli
after that created the librari ctest.c and compiled with
gcc -o libctest.so -shared -I/path/to/jni.h ctest.c -lc
and i got the libctest.so created, then added to bashrc the following code:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/Desktop/project2/thesys/src
and when i go to type
java testcli
it says me "No such file or directory".
I'm running java 8 oracle on a 32 bit machine with xubuntu 32 bit. Any ideas?
EDIT: ADDED right here the ctest.c code ( i think there might be some errors here)
#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <signal.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT 20000
#define LENGTH 512
JNIEXPORT void JNICALL Java_testcli_stdErr
(JNIEnv *env, jobject jobj)
{
const char *msg = NULL;
perror(msg);
exit(1);
}
JNIEXPORT jint JNICALL Java_testcli_cliFromC
(JNIEnv *env, jobject jobj)
{
/* Variable Definition */
int sockfd;
int nsockfd;
char revbuf[LENGTH];
struct sockaddr_in remote_addr;
//struct hostent *server; // per la parte scritta da me
/* Get the Socket file descriptor */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
fprintf(stderr, "ERROR: Failed to obtain Socket Descriptor! (errno = %d)\n",errno);
exit(1);
}
/* Fill the socket address struct */
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(PORT);
/*questo lo faccio così perchè localhost è 127.0.0.1, nel caso in cui debba recuperare l'ip dall'hostname uso la parte commentata sopra*/
inet_pton(AF_INET, "127.0.0.1", &remote_addr.sin_addr); //metto in remote_addr.sin_addr l'indirizzo ip 127.0.0.1 nel formato desiderato
bzero(&(remote_addr.sin_zero), 8);
/* Try to connect the remote */
if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
{
fprintf(stderr, "ERROR: Failed to connect to the host! (errno = %d)\n",errno);
exit(1);
}
else
printf("[Client] Connected to server at port %d...ok!\n", PORT);
/* Send File to Server */
//if(!fork())
//{
char* fs_name = "/home/elia/Desktop/project/tesi/PublicKey1";
char sdbuf[LENGTH];
printf("[Client] Sending %s to the Server... ", fs_name);
FILE *fs = fopen(fs_name, "r");
if(fs == NULL)
{
printf("ERROR: File %s not found.\n", fs_name);
exit(1);
}
bzero(sdbuf, LENGTH);
int fs_block_sz;
while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs)) > 0)
{
if(send(sockfd, sdbuf, fs_block_sz, 0) < 0)
{
fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno);
break;
}
bzero(sdbuf, LENGTH);
}
printf("Ok File %s from Client was Sent!\n", fs_name);
//}
close (sockfd);
printf("[Client] Connection lost.\n");
return (0);
}
Actually, everything is fine. The error comes from your stdErr() function, and it actually shows it is working. I get the same error. Why does this happen?
const char *msg = NULL;
perror(msg);
exit(1);
This prints the last error that was caused in the program. Now, you should only use perror() if you actually had an error in your program. Otherwise, it will print the message of whatever was in the errno variable previously.
errno may have been set by something inside Java itself. It may have been looking for some file, but its non-existence is not a problem for Java so it continued to work. But errno is set to that value and keeps that value, and that's what's printed in your program.
To prove this, I have added the following to your testcli.java:
public native void clearErr();
And my main is:
public static void main(String[] args) {
new testcli().clearErr();
new testcli().stdErr();
new testcli().cliFromC();
}
And to ctest.c I added:
JNIEXPORT void JNICALL Java_testcli_clearErr
(JNIEnv *env, jobject jobj)
{
errno = 0;
}
Did the javac, javah and gcc again, ran the program, and now I get:
Success
Note that your third method is not called because of the exit() in stdErr().

Compiling errors in a C program under JNI

first of all i'd like to point the fact i'm not too expert with Java and less with C, after that, i'm using JNI to use a C program which is a client application for sockets.
First i've created the main class testcli:
public class testcli {
public native void stdErr();
public native int cliFromC();
static {
System.loadLibrary("ctest");
}
public static void main(String[] args) {
new testcli().stdErr();
new testcli().cliFromC();
}
}
After that i compiled with
javac testcli.java
then created the header file
javah testcli
At this point i've copied the strings i needed and pasted in the new file ctest:
#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <signal.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT 20000
#define LENGTH 512
JNIEXPORT void JNICALL Java_testcli_stdErr
(JNIEnv *, jobject)
{
perror(msg);
exit(1);
}
JNIEXPORT jint JNICALL Java_testcli_cliFromC
(JNIEnv *, jobject)
{
/* Variable Definition */
int sockfd;
int nsockfd;
char revbuf[LENGTH];
struct sockaddr_in remote_addr;
//struct hostent *server; // per la parte scritta da me
/* Get the Socket file descriptor */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
fprintf(stderr, "ERROR: Failed to obtain Socket Descriptor! (errno =%d)\n",errno);
exit(1);
}
/* Fill the socket address struct */
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(PORT);
/*questo lo faccio così perchè localhost è 127.0.0.1, nel caso in cui debba recuperare l'ip dall'hostname uso la parte commentata sopra*/
inet_pton(AF_INET, "127.0.0.1", &remote_addr.sin_addr); //metto in remote_addr.sin_addr l'indirizzo ip 127.0.0.1 nel formato desiderato
bzero(&(remote_addr.sin_zero), 8);
/* Try to connect the remote */
if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
{
fprintf(stderr, "ERROR: Failed to connect to the host! (errno = %d)\n",errno);
exit(1);
}
else
printf("[Client] Connected to server at port %d...ok!\n", PORT);
/* Send File to Server */
//if(!fork())
//{
char* fs_name = "/home/elia/Desktop/project/tesi/PublicKey1";
char sdbuf[LENGTH];
printf("[Client] Sending %s to the Server... ", fs_name);
FILE *fs = fopen(fs_name, "r");
if(fs == NULL)
{
printf("ERROR: File %s not found.\n", fs_name);
exit(1);
}
bzero(sdbuf, LENGTH);
int fs_block_sz;
while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs)) > 0)
{
if(send(sockfd, sdbuf, fs_block_sz, 0) < 0)
{
fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno);
break;
}
bzero(sdbuf, LENGTH);
}
printf("Ok File %s from Client was Sent!\n", fs_name);
//}
close (sockfd);
printf("[Client] Connection lost.\n");
return (0);
}
At this point i added to my bashrc those lines:
C_INCLUDE_PATH=/usr/lib/jvm/java-8-oracle/include/
C_INCLUDE_PATH=/usr/lib/jvm/java-8-oracle/include/linux/
export C_INCLUDE_PATH
Saved and wrote in terminal
ECHO $C_INCLUDE_PATH
and it gave me only
/usr/lib/jvm/java-8-oracle/include/linux/
which i don't know if it's good or not cause first string is missing. After it i try to compile my ctest.c with this command line:
gcc -o libctest.so -shared -I/usr/lib/jvm/java-8-oracle/include ctest.c -lc
and have this errors, which i completely don't understand.
ctest.c: In function ‘Java_testcli_stdErr’:
ctest.c:19: error: parameter name omitted
ctest.c:19: error: parameter name omitted
ctest.c:22: error: ‘msg’ undeclared (first use in this function)
ctest.c:22: error: (Each undeclared identifier is reported only once
ctest.c:22: error: for each function it appears in.)
ctest.c: In function ‘Java_testcli_cliFromC’:
ctest.c:27: error: parameter name omitted
ctest.c:27: error: parameter name omitted
I'm figurin out that completely copy-pasting my working client.c into the ctest.c not work
well but i sincerely don't know how to fix it. One thing to know is that C code works alone, with obvious
void error(const char *msg)
before the first brace bracket and
int main(int argc, char *argv[])
before second one. If some1 can help it's really really appreciated. Thx very much for the effort!!
Cheers
JNIEXPORT void JNICALL Java_testcli_stdErr
(JNIEnv *, jobject)
{
perror(msg);
exit(1);
}
The first thing wrong here is that your parameters don't have names. While this can be effectively ignored with the proper compiler flags, I wouldn't recommend it. You should give your parameters names, e.g.:
JNIEXPORT void JNICALL Java_testcli_stdErr
(JNIEnv* env, jobject obj)
...
The next issue is perror(msg);, as msg isn't declared nor defined. Did you forget to pass it into your function? Is it supposed to be a global variable? Either way, it doesn't exist, and you can't print something that doesn't exist.

NDK how to get the dvm point?

now I have a question to use "JNI_GetCreatedJavaVMs".
but I couldn't use JNI_OnLoad method because my native code is not provid for java .
void *pHandle = dlopen("/system/lib/libart.so", RTLD_NOW | RTLD_GLOBAL);
JavaVM* m_pJvm = NULL;
void * pFunAddr =dlsym(pHandle, "JNI_GetCreatedJavaVMs");
LOGD("pJNI_GetCreatedJavaVMs = %08X", pFunAddr);
pJNIGetCreatedJavaVMs = (int)pFunAddr - 1;
LOGD("call !!!!!!!");
pJNIGetCreatedJavaVMs(&m_pJvm, 0, &vm_count);
LOGD("pJNIGetCreatedJavaVMs result is %d", result);
when I call the JNI_GetCreatedJavaVMs, process was crashed.
I didn't found what happend in IDA.
who can help me !!!!!!!!!!!!!! THX
ps:JNI_GetCreatedJavaVMs method is found in the android source code.
and another method is use runtime(libart.so) or gdvm(libdvm.so).
Some code from my native app, it is build in the AOSP source tree.
I am not sure if it could work in NDK
#include <jni.h>
#include <android_runtime/AndroidRuntime.h>
...
JNIEnv *env;
jint res;
JavaVM *jvm = AndroidRuntime::getJavaVM();
assert(jvm != NULL);
res = jvm->AttachCurrentThread(&env, NULL);
assert(res >= 0);

Categories

Resources