I have built OpenSSL for Android(using ndk-build) and have linked it to my program. Compilation and linking works, but I get a run time error of UnsatisfiedLinkError.
More specifically, the program breaks on this code:
public class TestActivity extends Activity
{ // load the library - name matches jni/Android.mk
static {
System.loadLibrary("crypto2");
System.loadLibrary("ssl2"); // <=BREAKS HERE!
System.loadLibrary("Test");
}
It specifically breaks when loading ssl2, so it seems it was able to load crypto2.
I think I need to tell ssl2 that it use cryto2, I tried to do that on the Android.mk file, but it didn't help.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libcrypto2
LOCAL_SRC_FILES := OpenSSL/libcrypto2.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libssl2
LOCAL_SRC_FILES := OpenSSL/libssl2.so
LOCAL_SHARED_LIBRARIES := libcrypto2
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS) # Here we give our module name and source file(s)
LOCAL_C_INCLUDES := D:/DevelopTools/OpenSSL-WIN32/include $(LOCAL_PATH)/../../../Framework/Applications/FW2Prototype $(LOCAL_PATH)/../../../Graphics/Libraries/Common $(LOCAL_PATH)/../../../Input/Libraries/Common $(LOCAL_PATH)/../../../Audio/Libraries/Common $(LOCAL_PATH)/../../../Audio/Libraries/NullAudio $(LOCAL_PATH)/../../../Network/Libraries/Common
LOCAL_MODULE := Test
LOCAL_SRC_FILES := Test.cpp Reference.cpp JNINetwork.cpp JNIGraphics2D.cpp JNIInput.cpp JNIAudio.cpp Applications/FatalWars2/FatalWars2.cpp /../../../Audio/Libraries/NullAudio/NullAudio.cpp
LOCAL_SHARED_LIBRARIES := libcrypto2 libssl2
include $(BUILD_SHARED_LIBRARY)
Any idea what is wrong?
Thanks,
I "solved" this.
I simpley built openssl into a single so file. I built both crypto and ssl statically, so ssl's dependency in crypto was hidden inside the static linking.
The issue is apparently that I couldn't load the prebuilt shared library ssl, because it was dependant of another prebuilt shared library(crypto). Maybe it's possible to load such libraries somehow, but my solution was just to static link crypto into ssl and then into a shared library called openssl.
Related
I am writing an android app that wants to make JNI calls into a shared library built in using the NDK. The trick is this shared library calls functions provided by OTHER shared libraries. The other shared libraries are C libraries that have been compiled elsewhere.
Here's what I've tried:
My Environment:
I'm working in Eclipse. I've added native support and have a jni library. In that library I have my code and a \lib directory where I have copied my other .so files.
Attempt #1 Android.mk: Just telling it where the libs are
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib1
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib2
include $(BUILD_SHARED_LIBRARY)
This builds just fine, but when I try to run I get errors indicating that dlopen(libnative_lib) failed because it couldn't load libsupport_lib1.
Coming here I found this:
Can shared library call another shared library?
which said that I needed to call load library on all necessary libraries. Great!
Attempt #2 Opening each library first
static {
System.loadLibrary("support_lib1");
System.loadLibrary("support_lib2");
System.loadLibrary("native_lib");
}
Again, this builds just fine, however when I run I get a new error:
couldn't load libsupport_lib1. findLibrary returned null.
Now we're getting somewhere. It must not be loading the libraries over to the target.
Attempt #3 Copying .so files into project/libs/armeabi
Didn't work. When Eclipse builds it deleted the files I dropped in there.
Attempt #4 Creating a new module for each library
So then I found this:
Android NDK: Link using a pre-compiled static library
It's about static libraries, but maybe I am having a similar problem. The gist is that I need to declare a module for each library. So my new Android.mk looks like this:
LOCAL_PATH := $(call my-dir)
#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib1
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/support_lib1.so
include $(BUILD_SHARED_LIBRARY)
#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib2
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/support_lib2.so
include $(BUILD_SHARED_LIBRARY)
#build native lib
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib1
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib/support_lib2
include $(BUILD_SHARED_LIBRARY)
This builds! Even better, armeabi has the sos now! Even BETTER I get the following messages when I try to run it (telling me that support_lib1 and 2 were opened by LoadLibrary:
Trying to load lib /data/app-lib/com.example.tst/libsupport_lib1.so
added shared lib /data/app-lib/com.example.tst/libsupport_lib1.so
no JNI_OnLoad found in /data/app-lib/com.example.tst/libsupport_lib1.so, skipping init
but then...
dlopen failed: Could not locate symbol func_that_exists_in_libsupport_lib.so referenced by libnative_lib.so
Edit: Attempt 5: Use PREBUILT_SHARED_LIBRARY
So I found this:
How can i Link prebuilt shared Library to Android NDK project?
which seems to be exactly what I'm asking. Their answer seems to be 'don't use 'build_shared_library' but instead 'use PREBUILT_SHARED_LIBRARY
Okay, let's try.
LOCAL_PATH := $(call my-dir)
#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib1
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/support_lib1.so
include $(PREBUILT_SHARED_LIBRARY)
#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib2
LOCAL_SRC_FILES := $(LOCAL_PATH)/lib/support_lib2.so
include $(PREBUILT_SHARED_LIBRARY)
#build native lib
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_SHARED_LIBRARIES := support_lib1 support_lib2
include $(BUILD_SHARED_LIBRARY)
Build... fails! The build complains about missing symbols now.
Edit: Attempt 6: Flatten everything
So I went back to the prebuilts documentation in the NDK. It says:
Each prebuilt library must be declared as a single independent module to the build system. Here is a trivial example where we assume that the file "libfoo.so" is located in the same directory than the Android.mk below:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := foo-prebuilt
LOCAL_SRC_FILES := libfoo.so
include $(PREBUILT_SHARED_LIBRARY)
Notice that, to declare such a module, you really only need the following:
Give the module a name (here 'foo-prebuilt'). This does not need to correspond to the name of the prebuilt library itself.
Assign to LOCAL_SRC_FILES the path to the prebuilt library you are providing. As usual, the path is relative to your LOCAL_PATH.
Include PREBUILT_SHARED_LIBRARY, instead of BUILD_SHARED_LIBRARY, if you are providing a shared, library. For static ones, use PREBUILT_STATIC_LIBRARY.
A prebuilt module does not build anything. However, a copy of your prebuilt shared library will be copied into $PROJECT/obj/local, and another will be copied and stripped into $PROJECT/libs/.
So let's try flattening everything out to match the trivial example. I copied my libraries out of their cozy /lib folder and put them in the jni root. I then did this:
LOCAL_PATH := $(call my-dir)
#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib1
LOCAL_SRC_FILES := support_lib1.so
include $(PREBUILT_SHARED_LIBRARY)
#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib2
LOCAL_SRC_FILES := support_lib2.so
include $(PREBUILT_SHARED_LIBRARY)
#build native lib
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_SHARED_LIBRARIES := support_lib1 support_lib2
include $(BUILD_SHARED_LIBRARY)
and... same error. Moreover I'm most definitely NOT seeing library files getting copied to $PROJECT/obj/local.
sooooo.... now what?
Your problem is with the naming convention. NDK and Android insist on the shared library names to always begin with lib. Otherwise, the libraries will not be linked properly, and not copied to the libs/armeabi folder properly, and not installed on the device (copied to /data/data/package/lib directory properly.
If you rename support_lib1.so to libsupport_1.so and support_lib2.so to libsupport_2.so, and put these two files in jni/lib directory, then your Attempt #5 will work with minor change:
LOCAL_PATH := $(call my-dir)
#get support_lib1
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib1
LOCAL_SRC_FILES := lib/libsupport_1.so
include $(PREBUILT_SHARED_LIBRARY)
#get support_lib2
include $(CLEAR_VARS)
LOCAL_MODULE := support_lib2
LOCAL_SRC_FILES := lib/libsupport_2.so
include $(PREBUILT_SHARED_LIBRARY)
#build native lib
include $(CLEAR_VARS)
LOCAL_MODULE := native_lib
LOCAL_SRC_FILES := native_lib.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog
LOCAL_SHARED_LIBRARIES := support_lib1 support_lib2
include $(BUILD_SHARED_LIBRARY)
BTW, I don't think you need this -L$(SYSROOT)/../usr/lib.
PS Don't forget to update the Java side, too:
static {
System.loadLibrary("support_lib1");
System.loadLibrary("support_lib2");
System.loadLibrary("native_lib");
}
Not sure if this is exactly where you are at, but here's what I know about these sorts of things.
Make each prebuilt libary its own separate Makefile. Multiple targets in Android.mk tends to get wonky. Sad.
Include each make file using $(call import-add-path) and $(call import-module)
Export as much as you can from the prebuilt's make files, using the LOCAL_EXPORT_ family of variables.
Prebuilt Shared Library Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := my_module_name
MY_LIBRARY_NAME := shared_library_name
### export include path
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
### path to library
LOCAL_SRC_FILES := libs/$(TARGET_ARCH_ABI)/lib$(MY_LIBRARY_NAME).so
### export dependency on the library
LOCAL_EXPORT_LDLIBS := -L$(LOCAL_PATH)/libs/$(TARGET_ARCH_ABI)/
LOCAL_EXPORT_LDLIBS += -l$(MY_LIBRARY_NAME)
include $(PREBUILT_SHARED_LIBRARY)
This is assuming that the prebuilt libaries live in a dir structure like this
+ SharedProjectFolderName
+--- Android.mk
+--- include/
+-+- libs/$(TARGET_ARCH_ABI)/
|- libshared_library_name.so
If you are not building for multiple ABI, I guess you can leave that bit out
The Project's Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := my_jni_module
## source files here, etc...
### define dependency on the other library
LOCAL_SHARED_LIBRARIES := my_module_name
include $(BUILD_SHARED_LIBRARY)
$(call import-add-path,$(LOCAL_PATH)/path/to/myLibraries/)
$(call import-module,SharedProjectFolderName)
$(call import-module,AnotherSharedProject)
I recommend you put all shared libraries in one folder. When you say $(call import-module,SharedProjectFolderName) it looks for a folder containing an Android.mk along the search path you told it (import-add-path)
By the way, you probably shouldn't specify LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib. It should be finding the proper libs from NDK by itself. Adding more linker paths will probably confuse it. The proper way is to export the linker paths as flags from the sub-modules.
ALSO, you can use ndk-build V=1 to get a ton of info on why it can't find paths, etc
The -L option gives the linker a directory path in which to look for libraries. The -l option gives the linker a library file name to link in. Library file names must begin with "lib". Your libraries should be named libsupport_lib1.so and libsupport_lib2.so. If you do that, then this is probably what you should do (replacing attempt #1):
LOCAL_LDLIBS := -L$(SYSROOT)/../usr/lib -llog -lsupport_lib1 -lsupport_lib2
LOCAL_LDLIBS += -L$(LOCAL_PATH)/lib
The linker will prefix the library name you specify using -l with "lib" and suffix it with ".so". (Why do you have -L$(SYSROOT)/../usr/lib?)
I believe that attempts #1 and #2 failed because you did not link your libraries into your executable - they are not mentioned in a -l option. By the way, you can verify this yourself. Unzip the .apk file and look in the lib directory and subdirectories. Are your .so files in there?
Looking at the error:
but then... dlopen failed: Could not locate symbol func_that_exists_in_libsupport_lib.so referenced by libnative_lib.so
Can you supply the entire message? dlopen() loads and links libraries into the running process.
I have recently started working with NDK and am stuck with a problem. I basically want to use an external static library (.so) into my application. Here's what I've tried till now.
Create a library
1) Add a method in the Java class with native keyword
public native static void FibNR ();
2) In the terminal navigate to the project folder and run the following command
mkdir jni
javah -jni -classpath bin/classes/ -d jni/ com.example.fibonaccinative.FibLib
3) Refresh project and add a C file corresponding to the generated H file.
4) Create a Android.mk file in the JNI folder and add the following code in it
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
OPENCV_LIB_TYPE :=STATIC
LOCAL_SRC_FILES := com_example_fibonaccinative_FibLib.c
LOCAL_MODULE := com_example_fibonaccinative_FibLib
include $(BUILD_SHARED_LIBRARY)
5) Build the code using the following command
/Developer/android-ndk-r9b/ndk-build all
The above steps execute successfully and I'm able to view the results of the c code also. Now I want to create a new application and use the generated .so file (com_example_fibonaccinative_FibLib.so) into it. For this I do the following
Use the library
follow steps 1, 2 and 3 from above for the new application
4) Create a Android.mk file in the JNI folder and add the following code in it
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
OPENCV_LIB_TYPE :=STATIC
LOCAL_SRC_FILES := com_example_usingstaticlibrary_LibraryTest.c
LOCAL_MODULE := com_example_usingstaticlibrary_LibraryTest
LOCAL_SHARED_LIBRARIES :=libcom_example_fibonaccinative_FibLib.so
LOCAL_LDLIBS := -L$(SYSROOT)/usr -llog
include $(BUILD_SHARED_LIBRARY)
5) Build the code using the following command
/Developer/android-ndk-r9b/ndk-build all
I'm not sure what to do next. What I think is I need to call the function of the library into com_example_usingstaticlibrary_LibraryTest.c. But doing so gives me an error saying
'com_example_fibonaccinative_FibLib' undeclared
EDIT 1:
1) In project 2 (where I want to use the Prebuilt Shared Library (.so)) I copied the library into the 'jni' folder
2) Changed the Android.mk file with the below text.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libcom_example_fibonaccinative_FibLib
LOCAL_SRC_FILES := libcom_example_fibonaccinative_FibLib.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := com_example_usingstaticlibrary_LibraryTest
LOCAL_SHARED_LIBRARIES := libcom_example_fibonaccinative_FibLib
LOCAL_SRC_FILES := com_example_usingstaticlibrary_LibraryTest.c
include $(BUILD_SHARED_LIBRARY)
3) Added the .h file from the old project (com_example_fibonaccinative_FibLib.h) into the 'jni' folder
4) Changed the source of com_example_usingstaticlibrary_LibraryTest.c to
#include "com_example_usingstaticlibrary_LibraryTest.h"
#include "com_example_fibonaccinative_FibLib.h"
JNIEXPORT jlong JNICALL Java_com_example_usingstaticlibrary_LibraryTest_callLibraryFunction
(JNIEnv *env, jclass class) {
Java_com_example_fibonaccinative_FibLib_fibNR(env, class, 500l);
return -500l;
}
Clean/Build results into the following error
undefined reference to 'Java_com_example_fibonaccinative_FibLib_fibNR'
Edit 2:
With reference to this I've edited the Android.mk as follows
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := com_example_fibonaccinative_FibLib
LOCAL_SRC_FILES := libcom_example_fibonaccinative_FibLib.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := com_example_usingstaticlibrary_LibraryTest
LOCAL_SRC_FILES := com_example_usingstaticlibrary_LibraryTest.c
LOCAL_SHARED_LIBRARIES := com_example_fibonaccinative_FibLib
include $(BUILD_SHARED_LIBRARY)
The NDK build command compiles the code without error. When trying to load the library it gives the following error
Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lcom/example/usingstaticlibrary/LibraryTest
When I remove LOCAL_SHARED_LIBRARIES := com_example_fibonaccinative_FibLib it works fine but I cannot use any function of the Prebuilt shared library.
Edit 3:
I tried a few more things including the suggestion by #jcm but nothing worked. I'm now attaching the source code. It has both the projects (Cleaned version to reduce size).
FibonacciNative: Contains the first project.
UsingStaticLibrary: Contains the second project. I intend to use the Prebuilt Shared Library of the first project into the second one.
You say you want to include a precompiled static library .a, but the file you reference is a shared library .so. If you still want to include a static library, here comes the solution and explanation:
LOCAL_SHARED_LIBRARIES :=libcom_example_fibonaccinative_FibLib.so
You're trying to include the static library as a shared one, looks like an accidental slip from using the previous shared library entry as reference :).
Upon that, Android makefiles still requires you to actually make a module for the external library.
include $(CLEAR_VARS)
LOCAL_MODULE := com_example_fibonaccinative_FibLib
LOCAL_SRC_FILES := com_example_fibonaccinative_FibLib.a
include $(PREBUILT_STATIC_LIBRARY)
Then you can simply add it as a dependency:
LOCAL_STATIC_LIBRARIES := com_example_fibonaccinative_FibLib
But if you're actually trying to include an external shared library. You would need to change the above to the following:
include $(CLEAR_VARS)
LOCAL_MODULE := com_example_fibonaccinative_FibLib
LOCAL_SRC_FILES := com_example_fibonaccinative_FibLib.so
include $(PREBUILT_SHARED_LIBRARY)
and include it as a shared library.
LOCAL_SHARED_LIBRARIES := com_example_fibonaccinative_FibLib
Hope I helped!
Disclaimer: I have not actually tried to compile an example, simply referencing my knowledge and quick research.
References:
Android NDK: Static library used is different than precompiled library specified
As you're including libcom_example_fibonaccinative_FibLib.so as LibraryTest's shared library, in your java static method, you should load it before loading LibraryTest:
System.loadlibrary(com_example_fibonaccinative_FibLib);
System.loadLibrary(LibraryTest);
Update:
After checking your code, LibraryTest.java should look like:
package com.kochartech.usingstaticlibrary;
public class LibraryTest {
public native static long callLibraryFunction();
static {
System.loadLibrary("com_kochartech_fibonaccinative_FibLib");
System.loadLibrary("com_kochartech_usingstaticlibrary_LibraryTest");
}
}
I build a C++ library manually using the Android SDK compiler. The result is libMyUtils.a.
I'm using the following in my Java/JNI test application:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := MyUtils
LOCAL_SRC_FILES := ../../../../libs/libMyUtils.a
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_MODULE := AndroidTests
LOCAL_STATIC_LIBRARIES := MyUtils
include $(BUILD_SHARED_LIBRARY)
When I build the console shows the following:
[armeabi] Install : libMyUtils.so => libs/armeabi/libMyUtils.so
Now for some bizarre reason the library ../../../../libs/libMyUtils.a is a couple megabytes, but the library libs/armeabi/libMyUtils.so is only 5KB. Isn't it supposed to be the same library?
When I run my test application I get UnsatisfiedLinkError. Obviously the native function I'm calling isn't located in the library. What am I doing wrong?
A full explanation, as your Android.mk does not make much sense to me: sorry if you already know some of that.
Static libraries should be pure C/C++, and wrapped using the Android NDK in order to be usable from Java.
For example, assuming your static library is built from a simple .c and .h files:
highfive.h:
int giveMeFive();
highfive.c:
#include "highfive.h"
int giveMeFive() {
return 5;
}
This can be compiled as a static library using the Android NDK compiler, which apparently you already know how to do: this will give us a highfive.a library.
In this form, this library is unusable from Java, but it can be wrapped using the Android NDK. See the Android NDK documentation for naming conventions etc...
highfiveWrapper.c:
#include "highfive.h"
jint
Java_your_package_name_HighFive_giveMeFive(JNIEnv *env, jobject o) {
return (jint) giveMeFive();
}
and its corresponding Java file:
package your.package.name;
class HighFive {
static {
System.loadLibrary("highfive");
}
public native int giveMeFive();
}
Now, how do we compile all this to get it to work:
Android.mk:
include $(CLEAR_VARS)
LOCAL_MODULE := libhighfive-prebuilt
LOCAL_SRC_FILES := path/to/highfive.a
LOCAL_EXPORT_C_INCLUDES := path/to/highfive.h/folder
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := highfive
LOCAL_SRC_FILES := path/to/highfiveWrapper.c
LOCAL_STATIC_LIBRARIES := libhighfive-prebuilt
include $(BUILD_SHARED_LIBRARY)
And there you should be able to use your native library as you wished to!
Hope this helps!
Usually, static libraries contain many objects, which contain many functions, of which many are unused. That's why the linker only pulls the referenced objects from static libraries. So, in the example given by #mbrenon, highfiveWrapper.c defines which components of highfive.a will be linked into highfive.so.
But in your setup, you need whole static library to be loaded. OK, there is a special word for it: LOCAL_WHOLE_STATIC_LIBRARIES.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := MyUtils
LOCAL_SRC_FILES := ../../../../libs/libMyUtils.a
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_MODULE := AndroidTests
LOCAL_WHOLE_STATIC_LIBRARIES := MyUtils
include $(BUILD_SHARED_LIBRARY)
In the end the solution was to build the MyUtils library as a prebuilt shared library. This way the linker doesn't strip anything. Then I modified my makefile as follows:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := MyUtils
LOCAL_SRC_FILES := ../../../../libs/libMyUtils.so
include $(PREBUILT_SHARED_LIBRARY)
LOCAL_MODULE := AndroidTests
LOCAL_SHARED_LIBRARIES := MyUtils
include $(BUILD_SHARED_LIBRARY)
Notice the .so extention and the PREBUILT_SHARED_LIBRARY and BUILD_SHARED_LIBRARY scripts.
I have come across a problem with trying to compile cURL with SSL support into an app.
So far, I have successfully compiled the openSSL package into libcrypt.so and libssl.so.
I believe I have successfully compiled a version of libcurl.a with SSL support using the configure script and running it through the cross-chain compiler found in the NDK (under a linux environment).
Now, I am attempting to write a .so library under Eclipse that can be called by the Java code of an Android App.
Here is the file structure so far:
Project Folder ---> jni ---> include ---> curl ---> curl headers
| |
| -> openssl ---> ssl and crypto headers
|
-> libcrypto.so
-> libssl.so
-> libcurl.a
-> jniProcessRequest.c
-> Android.mk
Android.mk reads:
LOCAL_PATH := $(call my-dir)
MY_PATH := $(LOCAL_PATH)
include $(CLEAR_VARS)
LOCAL_PATH := $(MY_PATH)
LOCAL_MODULE := crypto
LOCAL_SRC_FILES := libcrypto.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/openssl/
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := ssl
LOCAL_SRC_FILES := libssl.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/openssl/
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := curl
LOCAL_SRC_FILES := libcurl.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/curl/
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := JNIProcessRequest
LOCAL_SRC_FILES := JNIProcessRequest.c
LOCAL_SHARED_LIBRARIES := crypto ssl
LOCAL_STATIC_LIBRARIES := curl
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
jniProcessRequest.c:
#include <jni.h>
#include <stdlib.h>
#include "include/curl/curl.h"
#include <android/log.h>
JNIEXPORT void JNICALL Java_com_example_jniprocessrequest_MainActivity_jniProcessRequest(JNIEnv * env, jobject obje){
CURL *conn;
conn = curl_easy_init();
}
Every time I attempt to compile the above, I have undefined reference errors in Eclipse:
make: *** [obj/local/armeabi/libJNIProcessRequest.so] Error 1
undefined reference to 'curl_easy_init'
I am thinking this is some sort of linkage error but am struggling to find where the error is occurring. I have spent nearly two days trying all different methods of placing the shared libraries in differing places, switching the static libcurl with a shared libcurl, altering the Android.mk file and following tutorials on how to get cURL working in Android.
Any help would be greatly appreciated!
I believe that the problem was stemming from an incorrectly built libcurl.a static library. I replaced my library with one that was compiled by a user on GitHub and without changing any code, the linkage errors disappeared.
You use curl as a prebuilt module.
As stated by the NDK documentation you should take care to wrap it under include $(CLEAR_VARS) and (most importantly) include $(PREBUILT_STATIC_LIBRARY) directives as follow:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := foo-prebuilt
LOCAL_SRC_FILES := libfoo.a
include $(PREBUILT_STATIC_LIBRARY)
Right after take care to wrap your JNIProcessRequest module with include $(CLEAR_VARS) and include $(BUILD_SHARED_LIBRARY).
For more details please refer to the docs/PREBUILTS.html section that you can find within your Android NDK folder (it looks like this).
I found an example called nativeactivity in FMOD example folder, but unfortunately it use some java code:
package org.fmod.nativeactivity;
public class Example extends android.app.NativeActivity
{
static
{
System.loadLibrary("fmodex");
System.loadLibrary("main");
}
}
Android.mk looks like this:
LOCAL_PATH := $(call my-dir)
#
# FMOD Ex Shared Library
#
include $(CLEAR_VARS)
LOCAL_MODULE := fmodex
LOCAL_SRC_FILES := libfmodex.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/inc
include $(PREBUILT_SHARED_LIBRARY)
#
# Example Library
#
include $(CLEAR_VARS)
LOCAL_MODULE := main
LOCAL_SRC_FILES := main.c
LOCAL_LDLIBS := -llog -landroid
LOCAL_SHARED_LIBRARIES := fmodex
LOCAL_STATIC_LIBRARIES := android_native_app_glue
include $(BUILD_SHARED_LIBRARY)
$(call import-module,android/native_app_glue)
Is it possible to do without the java part? If so what would I need to change?
I don't know why you want to get rid of these few lines of Java. To the best of my knowledge, this has no effect on the rest of your application.
The reason you need Java is that Android system loader cannot find libfmodex.so which is essential to resolve the references in your libghost.so. Therefore, load of libghost.so fails. Java lets you preload the dependency before your library is loaded.
Unfortunately, NativeActivity itself can only load one library. A request has been posted in April 2012 to improve the situation some time in the future.
Currently, you can switch all your code that works with fmod to dynamic linking, or build a third shared library which will load fmod and then load the ghost library. In this situation, the loader will be able to resolve the references in ghost because fmod will already be loaded.