Reputation: 15
I am currently developing a small demo Android application that has C++ integration. I'm using Android Studio 3.1.4 and my project has NDK support (Development environment is Windows 10).
The purpose of the application is to load a shared library (.so) file from storage (sdcard), then dynamically link the library.
Here are the steps I went through to create this application:
1. Using GCC on Cygwin to generate the shared library
These are the files I used to compile the library
"sumLib.h":
#define _MYLIB_H_
#define MAX_INPUT 25
extern double sum(double x, double y);
"sumLib.c":
#include <stdlib.h>
#include "sumLib.h"
double sum(double x, double y) {
return x + y;
}
Then I used these following commands to generate the library file:
gcc -shared -o sumLib.o -c sumLib.c
gcc -shared -o sumLib.so sumLib.o
2. Load the library file on Android client
I transferred the file into the sd card of the Android emulator (using Device File Explored) (/storage/emulated/0/Download/sumLib.so)
Here is the c++ code that reads the library and links it with the symbol "sum":
extern "C" {
JNIEXPORT jdouble JNICALL
Java_com_example_user_demo_MainActivity_sum(JNIEnv *env, jobject obj, jdouble n1, jdouble n2) {
void *handle;
double (*sum)(double, double);
char *error;
char fname[PATH_MAX];
strcpy(fname, internalPath); //internal path is a predefined string that points to the Download folder in the SD card
strcat(fname, "/sumLib.so");
handle = dlopen(fname, RTLD_LAZY);
if (!handle) {
__android_log_write(ANDROID_LOG_ERROR, "Creating handle", "Error creating handle");
exit(EXIT_FAILURE);
}
dlerror();
*(void **) (&sum) = dlsym(handle, "sum");
if ((error = dlerror()) != NULL) {
__android_log_write(ANDROID_LOG_ERROR, "Error linking symbol", error);
exit(EXIT_FAILURE);
}
double result = (*sum)(n1, n2);
dlclose(handle);
return result;
}
}
Here is the Java code that calls the c++ function:
public native double sum(double n1, double n2);
// function was called after the click of a button
public void onClickTestBtn(View v) {
// set up the text view to display the result
TextView tv = findViewById(R.id.sample_text);
double result = sum(134.3, 11.3);
tv.setText(String.format("Result is %f", result));
}
This should theoretically call the "sum" function defined with c++ and use the library to look up symbol "sum" and perform the operation, but dlopen cannot create a handle from the file, it kept throwing errors.
Here is the error message:
"dlopen failed: library \"/storage/emulated/0/Download/sumLib.so\" needed or dlopened by \"/data/app/com.example.user.demo-fzNLN7tBu86LCNun1yLQlg==/lib/x86_64/libnative-lib.so\" is not accessible for the namespace \"classloader-namespace\""
I noticed at the end of the file address in the error message, they added a forward slash instead of back slash, does that have any impact? Also I am not sure what the second half of the error message means.
I've considered that GCC on Cygwin might be compiling on a different architecture than what the Android emulator is running on, but after I checked the file, I found that it was compiled for x64 (according to this, I found PE d+ from reading the file). For the emulator, I'm running the x86_64 image (API 27) and it printed out x86_64 after I called:
String arch = System.getProperty("os.arch");
Log.d("Debug", String.format("system arch is %s", arch));
I have also checked if the file was accessible from the c++ environment, so I used
FILE *f = fopen(fname, "r");
and in the debugger, it did not point to null, it was pointing to something, 0x00007227dd414018 to be exact, is it safe to make the assumption that it found the file and can correctly read it?
I am not sure how to fix this error that dlopen gave me, as for the integrity and correctness of the library file, I have tested it with the following C code:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int agrc, char **agrv) {
void *handle;
double (*sum)(double, double);
char *error;
handle = dlopen("./sumLib.so", RTLD_LAZY);
if (!handle) {
printf("error opening file");
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror();
*(void **) (&sum) = dlsym(handle, "sum");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
printf("Sum is %f\n", (*sum)(13.3, 113.4));
dlclose(handle);
exit(EXIT_SUCCESS);
return 0;
}
It was able to correctly open the library, link the symbol and return the correct result.
What am I doing wrong that is making dlopen throwing that error?
Upvotes: 0
Views: 790
Reputation: 57203
Try to build your app for target SDK 23 or lower, and run it on emulator API 24 or lower. I believe that the failure you see is a manifestation of recent security measures. See Android 7.0 Behavior Changes:
Starting in Android 7.0, the system prevents apps from dynamically linking against non-NDK libraries.
Building the sumLib.so with host gcc compiler is not going to help, either. Even though the resulting library targets same x86_64 ABI, the runtime environment on Android is completely different. Please use NDK toolchain. If you wish, you can use the standalone toolchain instead of ndk-build or CMake scripts.
Upvotes: 0