What I want to test is the Android NDK JNI called in the following order: C (NDK binary) -> java -> C (shared library). So, I wrote an NDK binary (JNI_src.c) included the shared library (jnitest.so) and ran it on Android. but, at runtime the linker cannot find the shared library jnitest.so used by JNI_src.
08-27 10:29:16.416 1121 1121 F linker : CANNOT LINK EXECUTABLE "/system/bin/JNI_output": library "./obj/local/arm64-v8a/libjnitest.so" not found
The following is the C code and Makefile I wrote (NDK Binary, shared library).
JNI_src.c Code
#include <stdio.h>
#include <android/log.h>
#include <jni.h>
#include <stdlib.h>
extern void shared_func();
int main()
{
printf("hello");
__android_log_print(ANDROID_LOG_INFO, "test", "hello");
// create JNI variable
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args; // env variables to control invocation
JavaVMOption options[4];
jint res;
jstring jstr;
jclass cls;
jmethodID mid;
jobjectArray args;
vm_args.version = JNI_VERSION_1_2;
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=.";
res = JNI_CreateJavaVM(&jvm, (void**) &env, &vm_args);
if(res < 0){
__android_log_print(ANDROID_LOG_INFO, "cnunsrtest", "Can't create Java VM");
fprintf(stderr, "Can't create Java VM\n");
exit(1);
}
cls = (*env)->FindClass(env, "Hello");
if(cls == 0){
__android_log_print(ANDROID_LOG_INFO, "cnunsrtest", "Can't find Hello class");
fprintf(stderr, "Can't find Hello class\n");
exit(1);
}
mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/STring;])V");
if(mid == 0){
__android_log_print(ANDROID_LOG_INFO, "cnunsrtest", "Can't find Hello.main");
fprintf(stderr, "Can't find Hello.main\n");
exit(1);
}
jstr = (*env)->NewStringUTF(env, "Hello, World!");
if(jstr == 0){
__android_log_print(ANDROID_LOG_INFO, "cnunsrtest", "Out of memory1");
fprintf(stderr, "Out of memory1\n");
exit(1);
}
args = (*env)->NewObjectArray(env, 1,
(*env)->FindClass(env, "java/lang/String"), jstr);
if(args == 0){
__android_log_print(ANDROID_LOG_INFO, "cnunsrtest", "Out of memory2");
fprintf(stderr, "Out of memory2\n");
exit(1);
}
(*env)->CallStaticVoidMethod(env, cls, mid, args);
shared_func();
(*jvm)->DestroyJavaVM(jvm);
return 0;
}
jnitest.so (jnitest.c)
#include <jni.h>
#include <stdio.h>
void shared_func()
{
printf("shared func cnunsr");
}
JNIEXPORT void JNICALL java_Hello_jnifunc(JNIEnv *env, jobject obj)
{
printf("Java call cnunsr");
shared_func();
return;
}
Code that built the shared library before make .
aarch64-linux-gnu-gcc -I/home/juno/jdk1.8.0_301/include -I/home/juno/jdk1.8.0_301/include/linux -o libjnitest.so jnitest.c -shared -fPIC
After building the shared library, run the Makefile to get the executable.
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := jvm
LOCAL_SRC_FILES := $(SYSROOT)/home/juno/jdk1.8.0_301/jre/lib/aarch64/server/libjvm.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := jnitest
LOCAL_SRC_FILES := $(SYSROOT)/home/juno/Desktop/nkd/libjnitest.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := JNI_output
LOCAL_SRC_FILES := JNI_src.c
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
LOCAL_SHARED_LIBRARIES := jnitest jvm
include $(BUILD_EXECUTABLE)
I copied the executable file and shared library to Android and ran it. But the linker could not find the shared library. I think the cause is incorrect information about the library in the NDK binary. (./obj/local/arm64-v8a/libjnitest.so)
Therefore, readelf was used. (readelf --all JNI_output)
...
Dynamic section at offset 0xdd0 contains 33 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [./obj/local/arm64-v8a/libjnitest.so]
0x0000000000000001 (NEEDED) Shared library: [libjvm.so]
0x0000000000000001 (NEEDED) Shared library: [liblog.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x0000000000000001 (NEEDED) Shared library: [libm.so]
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so]
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) Flags: NOW PIE
0x0000000000000015 (DEBUG) 0x0
0x0000000000000007 (RELA) 0x650
...
As expected, the dynamic section of libjvm.so is normal, but the dynamic section of libjnitest.so contains an odd path. What part of the Android.mk needs to be modified? I tried setting LD_LIBRARY_PATH to the path where libjnitest.so exists, but the same error occurs.
Related
I encountered an error when I am trying to link the Fbx static library (libfbxsdk.a) to a project on Linux using CMake that keeps reporting for undefined references to fbx-related functions. I checked the configuration in the CMakelist and everything looks alright, but the program cannot be compiled statically or dynamically. I really don't have any idea why the static library is not linked properly and appreciate any helps to fix the linking issue. Here is my cmakelist file for static linking:
cmake_minimum_required(VERSION 3.6)
SET(CMAKE_VERBOSE_MAKEFILE ON)
project(FBX_SDK)
add_definitions(-DFBXSDK_NEW_API)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lm -lrt -luuid -lstdc++ -lpthread -ldl -lX11 -std=c++11")
include_directories(${X11_LIBRARIES}
${FBX_SDK_SOURCE_DIR}/fbxsdk-1.2/include/)
link_directories(${X11_LIBRARIES}
${FBX_SDK_SOURCE_DIR}/fbxsdk-1.2/Lib/Linux/Debug_x64/)
set(SOURCE_FILES main.cpp)
add_executable(FBX_SDK ${SOURCE_FILES})
target_link_libraries(FBX_SDK
${FBX_SDK_SOURCE_DIR}/fbxsdk-1.2/Lib/Linux/Debug_x64/libfbxsdk.a)
and my cpp file is as below:
#include <iostream>
#include <fbxsdk.h>
void InitializeSDKObjects(FbxManager* &pManager, FbxScene* &pScene);
int main()
{
std::cout << "Hello, World!" << std::endl;
FbxManager* lSdkManager = NULL;
FbxScene* lScene = NULL;
bool lResult;
InitializeSDKObjects(lSdkManager, lScene);
return 0;
}
void InitializeSDKObjects(FbxManager* &pManager, FbxScene* &pScene)
{
pManager = FbxManager::Create();
if(!pManager)
{
FBXSDK_printf("Error: Unable to create FBX Manager!\n");
exit(1);
}
else FBXSDK_printf("Autodesk FBX SDK version %s\n", pManager->GetVersion());
FbxIOSettings* ios = FbxIOSettings::Create(pManager, IOSROOT);
pManager->SetIOSettings(ios);
FbxString lPath = FbxGetApplicationDirectory();
pManager->LoadPluginsDirectory(lPath.Buffer());
pScene = FbxScene::Create(pManager, "My Scene");
if(!pScene)
{
FBXSDK_printf("Error: Unable to create FBX scene!\n");
exit(1);
}
}
While the include and lib folders of fbx sdk are like this:
I am making an attempt to write an NDK basic app to understand how NDK works. I have a text view and a button in the MainActivity and a library class HelloWorldLib.java which have the static native function helloWorld. I have created the header file copied it and created the ".c" file in the jni folder.
When i am building via ndk-build i am getting the error "no rule to make target" error. I checked lots of posts and answer but nothing worked.
I included a test.c empty file as indicated in the below link and was able to build the project,However, when i ran my app i got the error that " no native implementation found" which is bizzare because i have the implementation.
https://code.google.com/p/android/issues/detail?id=66937
OnClick from where HelloWorldLib is called.
public void onClick(View v) {
// TODO Auto-generated method stub
String inNDK = HelloWorldLib.helloWorld();
tv.setText(inNDK);
}
HelloWorldLib where native is funciton is defined.
public class HelloWorldLib {
public native static String helloWorld();
static{
System.loadLibrary("com.example.androidndk_HelloWorldLib");
}
}
header file created by Javah -jni
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_androidndk_HelloWorldLib */
#ifndef _Included_com_example_androidndk_HelloWorldLib
#define _Included_com_example_androidndk_HelloWorldLib
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_androidndk_HelloWorldLib
* Method: helloWorld
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_androidndk_HelloWorldLib_helloWorld
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
C file copied from .h and then modified.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <com_example_androidndk_HelloWorldLib.h>
#include <string.h>
extern "C" {
JNIEXPORT jstring JNICALL Java_com_example_androidndk_HelloWorldLib_helloWorld
(JNIEnv *env, jclass clazz){
return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI");
}
Andoid.mk file
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := com.example.androidndk_HelloWorldLib.c
LOCAL_MODULE := com.example.androidndk_HelloWorldLib
include $(BUILD_SHARED_LIBRARY)
The error i am getting in the command prompt is shown below:
D:\Users\gabhatia\Desktop\Android SDK\MyWorkspace\AndroidNDK>ndk-build
make.exe: *** No rule to make target `jni/com.example.androidndk_HelloWorldLib.c
', needed by `obj/local/x86/objs/com.example.androidndk_HelloWorldLib/com.exampl
e.androidndk_HelloWorldLib.o'. Stop.
I am not sure where i am getting wrong but any help will be greatly appreciated.
Thanks.
GB.
Here
LOCAL_SRC_FILES := com.example.androidndk_HelloWorldLib.c
LOCAL_MODULE := com.example.androidndk_HelloWorldLib
change to
FILE_LIST := $(wildcard $(LOCAL_PATH)/*.cpp)
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)
Have a try.
I trying to include a shared library through android build system. It named "libmd5b". I already check this library in NDK and it working well. But when I've built android I didn't found my library not in /system/lib and nor in else place. There is my actions step by step:
1) Put sources into ($AndroidSourceFolder)/external/libmd5b/jni
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS := -fPIC
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib
LOCAL_MODULE := md5b
LOCAL_SRC_FILES := md5b.cpp md5.cpp # source files
LOCAL_MODULE_TAGS := optional
# C++ inclusions:
LOCAL_STATIC_LIBRARIES += libstlport_static
LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_STL:=stlport_static
APP_MODULES := md5b
md5b.cpp
#include <jni.h>
#include <string>
#include <iostream>
#include <fstream>
#include <android/log.h>
#include "md5.h"
using namespace std;
extern "C" {
//Original name changed because we building library system wide visible.
JNIEXPORT jstring JNICALL Md5B
(JNIEnv * env, jobject obj, jstring fpath);
};
JNIEXPORT jstring JNICALL Md5B
(JNIEnv * env, jobject obj, jstring fpath)
{
string strpath = env->GetStringUTFChars(fpath, NULL);
ifstream inFile;
inFile.open(strpath.c_str());
string line;
string strFile;
while (!inFile.eof())
{
getline(inFile, line);
strFile += line;
}
inFile.close();
string md5R = md5(strFile);
char* chmd5R = new char [md5R.length()];
strcpy (chmd5R, md5R.c_str());
return env->NewStringUTF(chmd5R);
}
And there is other library files: md5.cpp and md5.h. This files in pure C++ without any jni preparations. So I think it is no important.
2) Next step is changing ($AndroidSourceFolder)/build/target/product/full.mk to look like this:
PRODUCT_PACKAGES := \
Camera \
libmd5b
$(call inherit-product, $(SRC_TARGET_DIR)/product/full_base_telephony.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/board/generic/device.mk)
# Overrides
PRODUCT_NAME := full
PRODUCT_DEVICE := generic
PRODUCT_BRAND := Android
PRODUCT_MODEL := Full Android on Emulator
3) After all this I launching it to compile:
$source build/envsetup.sh
$lunch full-eng
$make
4) Making libmd5b:
make md5b
<build information>
Install: out/target/product/generic/system/lib/md5b.so
That's it. After end of 'make' I can't found my library. It should be in /system/lib but it is not there. So where is my water library? And why I hasn't got any errors while compile it?
Solved.
There was error in Android.mk file in section named LOCAL_MODULE. Module should be named with lib* preffix:
lib<module name>
I want to create a shared library which can be loaded in two different ways into targets:
LD_PRELOAD
Dynamic loading via dlsym
My shared library looks like this:
#include "stdio.h"
void __attribute__ ((constructor)) my_load(void);
void my_load(void) {
printf("asdf");
}
void someFunc(void) {
printf("someFunc called");
}
I am compiling it like so:
all:
gcc -fPIC -g -c -Wall MyLib.c
gcc -shared -W1,-soname,MyLib.so.1 -o MyLib.so.1.0.1 -lc
I do not wish to install it with ldconfig, etc. The target process looks like this:
#include <stdio.h>
#include <dlfcn.h>
void func1() {
printf("%d\n", 1);
}
void func2() {
printf("%d\n", 2);
}
void func3() {
printf("%d\n", 3);
}
int main() {
void* lib_handle = dlopen("/home/mike/Desktop/TargetProcess/MyLib.so.1.0.1",
RTLD_NOW|RTLD_GLOBAL);
if(lib_handle == NULL) {
printf("Failed loading lib\n");
} else {
printf("Loaded lib successfully\n");
void (*some_func)() = dlsym(lib_handle, "someFunc");
printf("%p\n", some_func);
dlclose(lib_handle);
}
func1();
func2();
func3();
return 0;
}
The target is compiled as so:
all:
gcc TestProg.c -ldl -o TestProg
My questions are:
With the dynamic loading with dlopen as above, why does my_load not appear to be called?
With the same method, why does dlsym always return nil even though dlopen returns non-null? Similarly, nm doesn't list either my_load or someFunc as symbols of the .so.
Is it possible to use LD_PRELOAD to load the library? I tried copying the .so into the same directory as the target then invoking LD_PRELOAD="./MyLib.so.1.0.1" ./TestProg but again my_load seems not to be being called.
Your object files was no linked into your library:
gcc -shared -W1,-soname,MyLib.so.1 -o MyLib.so.1.0.1 -lc
Change it to include your object file MyLib.o:
gcc MyLib.o -shared -W1,-soname,MyLib.so.1 -o MyLib.so.1.0.1 -lc
UPDATE: just tryed your command locally (without any MyLib.c or MyLib.o):
$ gcc -shared -W1,-soname,MyLib.so.1 -o MyLib.so.1.0.1 -lc && echo ok
ok
$ nm MyLib.so.1.0.1
xxxxxxxx a _DYNAMIC
xxxxxxxx a _GLOBAL_OFFSET_TABLE_
w _Jv_RegisterClasses
xxxxxxxx A __bss_start
w __cxa_finalize##xxxxxxxxxxx
xxxxxxxx d __dso_handle
w __gmon_start__
xxxxxxxx t __i686.get_pc_thunk.bx
xxxxxxxx A _edata
xxxxxxxx A _end
xxxxxxxx T _fini
xxxxxxxx T _init
It is an empty dynamic library.
I use the readelf utility to check (-h) an executable file, and i see the e_entry field has the value: 0x8048530 . Then i recompile the checked program and have it to print its own program entry by adding the line: printf("%p\n", (void*)main) and outputs: 0x80485e4. Why do i have this difference? (OS: Linux 32-bit)
The entry point of an executable is usually not main itself but a platform specific function (that we'll call _start) which performs initialization before calling main.
Answering the question "Can i access the _start label from the main body?":
#include <stdio.h>
int main()
{
void* res;
#if defined(__i386__)
asm("movl _start, %%eax" : "=a" (res));
#elif defined(__x86_64__)
asm("movq _start, %%rax" : "=a" (res));
#else
#error Unsupported architecture
#endif
printf("%p\n", res);
return 0;
}