I'm trying to compile an application using C++11 and OpenGL ES for Android using NativeActivity. I'm using
APP_STL := gnustl_shared
and everything compiles just fine. But when running my app I get:
dlopen(libjngl-test.so): Cannot load library: soinfo_relocate(linker.cpp:976): cannot locate symbol "_ZSt11_Hash_bytesPKvjj" referenced by "libjngl-test.so"...
jngl-test ist my activity. This is how my loading code looks like:
#include <android/native_activity.h>
#include <android/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdexcept>
void* load_lib(const std::string& l) {
auto handle = dlopen(std::string("/data/data/com.bixense.jngl_test/lib/" + l).c_str(),
RTLD_NOW | RTLD_GLOBAL);
if (!handle) {
throw std::runtime_error(std::string("dlopen(") + l + "): " + dlerror());
}
__android_log_print(ANDROID_LOG_INFO, "bootstrap", "Loaded %s", l.c_str());
return handle;
}
void ANativeActivity_onCreate(ANativeActivity* app, void* ud, size_t udsize) {
try {
load_lib("libogg.so");
load_lib("libvorbis.so");
auto main = reinterpret_cast<void (*)(ANativeActivity*, void*, size_t)>(
dlsym(load_lib("libjngl-test.so"), "ANativeActivity_onCreate")
);
if (!main) {
throw std::runtime_error("undefined symbol ANativeActivity_onCreate");
}
main(app, ud, udsize);
} catch(std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "bootstrap", "%s", e.what());
ANativeActivity_finish(app);
}
}
Does anybody have an idea what I'm doing wrong? Where could _ZSt11_Hash_bytesPKvjj come from?
Related
I don't know if I'm wrong but I found this solution to bypass Android restriction on private shared library like OpenCL. It copies required libs from /system/vendor/lib64 to /data/data/package.name/ and then loads them by calling dlopen.
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <jni.h>
#include <android/log.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <errno.h>
#include <unistd.h>
#define LOG_TAG "JNI"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
void copy_file(const char* in_path, const char* out_path) {
FILE* fin = fopen(in_path, "rb");
check(fin, "fopen()");
FILE* fout = fopen(out_path, "wb");
check(fout, "fopen()");
unsigned char buffer[255];
size_t ret;
while ((ret = fread(buffer, sizeof(*buffer), ARRAY_SIZE(buffer), fin)) == ARRAY_SIZE(buffer)) {
fwrite(buffer, sizeof(*buffer), ret, fout);
}
if (ferror(fin) != 0) {
LOGE("Error on read");
exit(1);
}
fwrite(buffer, sizeof(*buffer), ret, fout);
fclose(fin);
fclose(fout);
}
void* sdlopen(const char* path){
void *handle;
handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
if (!handle) {
LOGE("#1 %s", dlerror());
exit(1);
}
return handle;
}
void load(){
void *handle, *handle1, *handle2, *handle3, *handle4, *handle5, *handle6;
cl_int (*_clGetPlatformIDs)(cl_uint, cl_platform_id*, cl_uint*);
char *error;
copy_file("/system/vendor/lib64/libOpenCL.so", "/data/data/com.app.opencl/libOpenCL.so");
copy_file("/system/lib64/libcutils.so", "/data/data/com.app.opencl/libcutils.so");
copy_file("/system/lib64/libbase.so", "/data/data/com.app.opencl/libbase.so");
copy_file("/system/lib64/libc++.so", "/data/data/com.app.opencl/libc++.so");
copy_file("/system/lib64/libvndksupport.so", "/data/data/com.app.opencl/libvndksupport.so");
copy_file("/system/lib64/libdl_android.so", "/data/data/com.app.opencl/libdl_android.so");
copy_file("/system/lib64/ld-android.so", "/data/data/com.app.opencl/ld-android.so");
handle1 = sdlopen("/data/data/com.app.opencl/libc++.so");
handle2 = sdlopen("/data/data/com.app.opencl/ld-android.so");
handle3 = sdlopen("/data/data/com.app.opencl/libdl_android.so");
handle4 = sdlopen("/data/data/com.app.opencl/libvndksupport.so");
handle5 = sdlopen("/data/data/com.app.opencl/libbase.so");
handle6 = sdlopen("/data/data/com.app.opencl/libcutils.so");
handle = sdlopen("/data/data/com.app.opencl/libOpenCL.so");
_clGetPlatformIDs = dlsym(handle, "clGetPlatformIDs");
if ((error = dlerror()) != NULL) {
LOGE("#2 %s", error);
exit(1);
}
dlclose(handle);
dlclose(handle1);
dlclose(handle2);
dlclose(handle3);
dlclose(handle4);
dlclose(handle5);
dlclose(handle6);
}
With the code above I can dlopen libOpenCL.so but
dlsym(handle, "clGetPlatformIDs");
returns undefined symbol: JNI_OnLoad. I think it shouldn't look for JNI_OnLoad function. What is wrong?
I am following a tutorial on NDK development using C++. The app is a basic fibonacci number printing app. I have the appropriate System.loadLibrary and the JNI_OnLoad call. I can see in the logcat that the library is getting loaded. However, post that, the system still looks for methods based on the package names. Heres the error from logcat .
No implementation found for long com.test.fib.FibLib.fibN(long) (tried Java_com_test_fib_FibLib_fibN and Java_com_test_fib_FibLib_fibN__J)
And here is the class where I have the cpp code and related stuff .
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <android/log.h>
#include <jni.h>
#define LOG_TAG "Fibonacci Stuff"
#define LOG_D(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
namespace com_test_fib {
static long fib(long n) {
if(n==0) return 0;
if(n==1) return 1;
return fib(n-1) + fib(n-2);
}
/* JNI wrapper */
static jlong fibN(JNIEnv* env, jclass clazz, jlong n) {
return fib(n);
}
static JNINativeMethod method_table[] = {
{"fibN", "(J)J", (void *) fibN }
};
}
using namespace com_test_fib;
extern "C" jint JNI_Onload(JavaVM* vm, void* reserved) {
LOG_D ("JNI_OnLoad");
JNIEnv* env;
if(vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK ) {
LOG_D("Initial Get env error");
return JNI_ERR;
}
else {
jclass clazz = env->FindClass("com/test/fib/FibLib");
LOG_D ("Find the class");
if(clazz) {
LOG_D ("Class not found");
jint ret = env->RegisterNatives(clazz, method_table, sizeof(method_table)/sizeof(method_table[0]) );
env->DeleteLocalRef(clazz);
return ret ==0 ? JNI_VERSION_1_6 : JNI_ERR;
} else {
LOG_D("Some error it seems");
return JNI_ERR;
}
}
}
Here is the loadLibrary call
package com.test.fib;
import android.util.Log;
public class FibLib {
public static long fibJ(long n) {
if(n==0) {
return 0;
}
if(n==1) {
return 1;
}
return fibJ(n-1) + fibJ(n-2);
}
/* Wrapper to call the JNI code. This is called by the activity */
public static native long fibN(long n);
static {
Log.d("Sys library loading", "This should call the onLoad function");
System.loadLibrary("Fib");
}
}
I can see the above message in Logcat. After that i directly see the exceptions and no messages from the onLoad method. I am using eclipse, min version is 14 and compile version is 5.1.1. Device is nexus 7. The package names are matching com.test.fib) in the app and the namespace.
Can anyone please let me know whats wrong here. THis is driving me mad..
Thanks
I'm using the following bootstrapping code to load my native activity (jngl-test):
#include <android/native_activity.h>
#include <android/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdexcept>
const std::string LIB_PATH = "/data/data/com.bixense.jngl_test/lib/";
void* load_lib(const std::string& l) {
void* handle = dlopen(l.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!handle) {
throw std::runtime_error(std::string("dlopen(") + l + "): " + strerror(errno));
}
return handle;
}
void ANativeActivity_onCreate(ANativeActivity* app, void* ud, size_t udsize) {
try {
load_lib(LIB_PATH + "libogg.so");
load_lib(LIB_PATH + "libvorbis.so");
auto main = reinterpret_cast<void (*)(ANativeActivity*, void*, size_t)>(
dlsym(load_lib(LIB_PATH + "libjngl-test.so"), "ANativeActivity_onCreate")
);
if (!main) {
throw std::runtime_error("undefined symbol ANativeActivity_onCreate");
}
main(app, ud, udsize);
} catch(std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "bootstrap", e.what());
ANativeActivity_finish(app);
}
}
I get the following error message:
dlopen(/data/data/com.bixense.jngl_test/lib/libjngl-test.so): Invalid argument
This doesn't tell me at all whats going wrong. Is there a way to get more debug output? What could "Invalid argument" mean?
I fixed it:
dlerror()
gives a far better error message.
Here's the bootstrap code if someone is interested:
#include <android/native_activity.h>
#include <android/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdexcept>
void* load_lib(const std::string& l) {
auto handle = dlopen(std::string("/data/data/com.bixense.jngl_test/lib/" + l).c_str(),
RTLD_NOW | RTLD_GLOBAL);
if (!handle) {
throw std::runtime_error(std::string("dlopen(") + l + "): " + dlerror());
}
return handle;
}
void ANativeActivity_onCreate(ANativeActivity* app, void* ud, size_t udsize) {
try {
load_lib("libogg.so");
load_lib("libvorbis.so");
auto main = reinterpret_cast<void (*)(ANativeActivity*, void*, size_t)>(
dlsym(load_lib("libjngl-test.so"), "ANativeActivity_onCreate")
);
if (!main) {
throw std::runtime_error("undefined symbol ANativeActivity_onCreate");
}
main(app, ud, udsize);
} catch(std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "bootstrap", e.what());
ANativeActivity_finish(app);
}
}
You could do this..
put that lib in raw directory and load it
For raw files, you should consider creating a raw folder inside res directory and then call
getResources().openRawResource(resourceName)
from your activity.
then you can use it the way you like.
so I am new to using the JNI for Android so sorry in advance if this is something silly. I have installed the SDK and in Eclipse have added it as a library for the project. After finishing up everything, I have tried to run the ndk-build function but get this error:
Compile++ thumb : face_detect_rec <= jni_part.cpp
In file included from jni/face_detect_rec.h:11:0,
from jni/jni_part.cpp:3:
/Users/Justin/Documents/Android/opencv-2.4.3.2-android-sdk/sdk/native/jni/include/opencv2/core/core.hpp:56:21: fatal error: algorithm: No such file or directory
compilation terminated.
make: *** [obj/local/armeabi/objs/face_detect_rec/jni_part.o] Error 1
That file location is where the core.hpp is so I am not sure why this is a problem. I'll post my code below for reference, thanks guys!
jni_part.cpp:
#include <jni.h>
#include "face_detect_rec.h"
using namespace std;
using namespace cv;
extern "C" {
JNIEXPORT void JNICALL Java_com_example_opencvandroidtest_MainActivity_detectFaces(
JNIEnv* env, jclass mClass, jstring filePath)
{
detectFaces(filePath);
}
}
face_detect_rec.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <time.h>
#include <ctype.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/contrib/contrib.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
using namespace cv;
using namespace std;
static void detectFaces(string filePath);
face_detect_rec.cpp:
#include "face_detect_rec.h"
// Create a string that contains the exact cascade name
string faceCascade_name =
"/Users/Justin/Documents/OpenCV/data/haarcascades/haarcascade_frontalface_alt2.xml";
/* "haarcascade_profileface.xml";*/
string eyeCascade_name =
"/Users/Justin/Documents/OpenCV/data/haarcascades/haarcascade_mcs_lefteye.xml";
//string rightEyeCascade_name =
// "/Users/Justin/Documents/OpenCV/data/haarcascades/haarcascade_mcs_righteye.xml";
// Function to detect and draw any faces that is present in an image
static void detectFaces(string filePath)
{
// Create a new Haar classifier
CascadeClassifier faceCascade;
Mat img = imread(filePath);
//int scale = 1;
// Load the HaarClassifierCascade
faceCascade.load(faceCascade_name);
// Check whether the cascade has loaded successfully. Else report and error and quit
if( faceCascade.empty() )
{
cout << "ERROR: Could not load classifier cascade\n";
return;
}
// There can be more than one face in an image. So create a growable sequence of faces.
// Detect the objects and store them in the sequence
vector<Rect> faces;
faceCascade.detectMultiScale(img, faces, 1.1, 2, CV_HAAR_SCALE_IMAGE, cvSize(70, 70));
// Loop the number of faces found.
for( int i=0; i<faces.size(); i++ )
{
//save image
Mat faceROI = img(faces[i]);
stringstream s;
s << "/mnt/sdcard/Pictures/TagSense" << i << ".jpg";
imwrite(s.str(), faceROI);
}
}
}
Do you have a so-called "Application.mk" file in your "jni" directory? If you don't, try to create it with, for instance, the following code:
Application.mk:
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi-v7a
APP_PLATFORM := android-8
Hope this will help.
Original code that prints to stderr:
extern "C" {
/* error: output error message */
void Error(const int error, char *message, ...)
{
va_list arg;
fflush(stdout);
fflush(stderr);
if (error > 0)
fprintf(stderr, "\nError: ");
else
fprintf(stderr, "\nWarning: ");
va_start(arg, message);
vfprintf(stderr, message, arg);
va_end(arg);
fflush(stderr);
if (error > 0)
exit(error);
}
void main(){
Error(0,"Problem %s in file", "sometext");
}
}//extern "C"
I modified my code to look like that. It should print to logcat.
extern "C" {
#include <android/log.h>
#include <jni.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
/* error: output error message */
void Error(const int error, char *message, ...)
{
va_list arg;
va_start(arg, message);
if (error > 0)
__android_log_print(ANDROID_LOG_ERROR, "HTS_API", message, arg);
else
__android_log_print(ANDROID_LOG_WARN, "HTS_API", message, arg);
va_end(arg);
if (error > 0)
exit(error);
}
void main(){
Error(0,"Problem %s in file", "sometext");
}
}//extern "C"
Problem is that my code outputs: 'Problem |�;A.|�;A. in file'
Calling the logger function directly, I will get the expected output.
__android_log_print(ANDROID_LOG_WARN, "HTS_API","Problem %s in file", "sometext");
The expected output is: 'Problem sometext in file'
What am I doing wrong?
__android_log_print does not accept a va_list as a parameter. It accepts a variable parameter list.
It looks like in the newest NDK they've added
int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap);
which is what you want. Formerly you'd have to fix this problem by either re-implementing Error as a macro with variable argument list, or using vsprintf to format the error message in a buffer and then say
__android_log_print(ANDROID_LOG_WARN, "HTS_API", "%s", buf);