I have a library that I want to customize its work (I mean replacing the main of the library by my outside program so I can for example reorder the calls of the library's functions) and for that I had to change some static variable to extern variables so the library and my program can handle them both.
My program works fine when on run on my Linux desktop machine, however when I tried to port that to Android using NDK, I couldn't compile it because of the extern keyword .
So, I was wondering if there's a way concerning the Android.mk or alternative to the use of extern keyword, to make the things work.
You're getting an "undefined reference" because extern is not a variable definition, only a declaration. That means you must have an non-extern declation somewhere else in your code, otherwise the compiler doesn't allocate a symbol for your variable.
As for alternatives, no, an extern variable is the only way to have a global variable accross multiple object files in C.
If you only need a global variable in the same object file, you can use static so the symbol is not exported to other objects.
Related
I'm debugging an android application and am confusing about one android shared library and here i wanna explain the problem.
There is a native function defined in java code (java side) like this:
public static native void nativeInitialize();
Package:
package com.example.ExClass;
I loaded the shared library in IDA Pro.
I thought that i can find that function in exported functions and it should be something like this Java_com_example_ExClass_nativeInitialize
But the function is not in export list and as i debugged the java side codes, i know the function is calling from java but i don't know how it is working ?!
If the name-mangling scheme (Java_...) is not used, then it is possible the library uses the RegisterNatives approach to associate method names with function pointers. Try searching for structs that embed the string "nativeInitialize", they should be the methods argument to RegisterNatives. Look through the JNI_OnLoad function for the call site.
On Aarch64 assembly it should look like:
ldr x4, [x8,#1720] # RegisterNatives is the 215th member, times 8 bytes per pointer
bl x4 # Could also be a br if this was the last call of the function
Im getting the mentioned error while declaring static native method:
From the native side the link is working as expected:
Everything works as expected the problem is just the link(Android Studio 3.1.1) from java to the native reference which is broken for static(works for non-static ones) methods.
There exists a funny workaround:
remove the word 'static' in Java.
click on the red bulb to 'Create function Java_…_jniInitCore()'
restore the word 'static' in Java file.
Try to change the type of the second argument in JNI method from jclass to jobject. Worked for me
I am not expert but I changed the second parameter from jobject to jclass and it worked for me.
i am making an Android Shapefile reader app on Android with the use of NDK. I want to use C++ to parse shape files. I found library "Shapefile C Library". Github: https://github.com/sw897/shapelib.
I am using Android studio and have no idea how to import it to my Android project so i could use functions described in: http://shapelib.maptools.org/shp_api.html
Any tips?
First, start hacking away at the Hello-JNI example from Google:
https://github.com/googlesamples/android-ndk/tree/master/hello-jni
Use that as a test bed.
Then, create a Java Class with public native methods that let you interact with your library. Something of a high level API, probably to pass a file name or buffer to SHPOpenLL and return a pointer to the ShapeFile context (ShapeHandle). Looks like your library is written in C, so you should be able to write a similar interface to query the shapefile passing a (jint) c-style cast of your ShapeHandle pointer.
Next, you need to play around with javah to generate the header for your shapefile interface. Once the header is generated, you can use it to implement your interface in a .cc file. From there you will basically make Java calls to your C interface and it will return jobjects. (jint, jbool, jstring, etc...)
I'm looking at the ShapeLib that you want to use and while it's easy enough, there will be some gotchas.
You will need to implement SAHooks for file I/O. I suggest looking at NVFile for an example how to access APK files (or downloaded files). You will need to init it using activity.context.assetmanager. Then use nv_file to wrap FRead/FSeek/etc...
Passing info back to java is going to be tough. Personally, I would build a Java class to hold the shape information and fill it out on the C side. However, you might be tempted to query these parameters from the SHPObject one at a time. Will be slow, but less error prone.
E.g.
// Java
MyJavaShapeObject obj = new MyJavaShapeObject();
_c_retrieveShapeObj((jint)pShapeFile, obj);
// C
java_blah_blah_c_retrieveShapeObj(JNIEnv* env, jclass activity, jint theShapeFile, jobject theObj){
ShapeHandle pShapeFileHandle = (ShapeHandle)theShapeFile; // c style conversion
// http://stackoverflow.com/questions/11647646/how-to-use-the-jni-to-change-the-fields-of-a-java-class
// http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp16613
// http://stackoverflow.com/a/36759159/7949696
}
I made a c++ main aplication that loads a so library also made by me.
Both sources shares a common header (TestFlags.h).
Inside TestFlags.h I have an class and a pointer declaration of it which is intended to be global to the whole application, that is define a instance in the main app and use it inside a library function.
class TestFlags {
public:
TestFlags() : behaviour(1)
{}
int behaviour;
};
extern __attribute__ ((visibility("default"))) TestFlags * gpTestFlags;
then a sequence of execution steps followed to reach the named goal are:
main application creates a new instance of TestFlags ---> gpTestFlags = new TestFlags();
main application load the library ---> dlopen(library.so, RTLD_LAZY|RTLD_GLOBAL)
invoke a function that resides inside the library which uses previous instance declared ---> gpTestFlags->behaviour = 2;
Received a SIGSEGV: Segmentation fault because gpTestFlags is NULL
It seems that inside the library gpTestFlags instance is not seen for some reason.
Same thing also happens with other static class I have, values which are configured on the main application not seen inside the library.
As far I can research it seems that the library manages a totally different memory space for those declarations like if it was duplicated.
This is the expected way dlopen() would work.
The two modules have independent global symbols called gpTestFlags. If you try to link them together, the linker would scream about duplicates.
You can declare the pointer in library as weak, or you can use dlsym() to resolve the linkage programmatically.
I'm just messing around with a Ndk tutorial I found. The native code uses one "package", while the activity is in another. When this mismatch occurs, I can't call the native function without getting an unsatisfied link exception. I know the "why's" I just don't know the resolution.
Here is the sample .c code that I've placed in my jni folder:
#include <string.h>
#include <jni.h>
jstring Java_com_mindtherobot_samples_ndkfoo_NdkFooActivity_invokeNativeFunction(JNIEnv* env, jobject javaThis) {
return (*env)->NewStringUTF(env, "Hello from native code!");
}
Notice that this .c code's package translates to com.mindtherobot.samples.ndkfoo.NdkFooActivity.
If I create a new activity that matches that Package/Class, I can call the invokeNativeFunction just fine. However, what if I can't match it? What if instead I need to run it from com.mydomain.activity?
I figured I could maybe change things around, such that my native declaration looked like this:
package com.mydomain;
public class Activity {
private native String com_mindtherobot_samples_ndkfoo_NdkFooActivity_invokeNativeFunction();
}
But that's a no-go. Just to be clear, I know how to make this work if I change my package to match what is compiled in the .c code; however, I need to be able to call a method from a different package... is this possible?
You need to make a basic class with the sole functionality of talking to C, not an activity. Then activities can instantiate this class, or possibly even statically call it, whenever they need to talk to C.
Your question is pretty scrambled, but the package declaration in the Java source code has to agree with what is encoded into the native method name, i.e. it must agree with what is generated by javah. If you change the package in the Java code, you must regenerate the .h file, and adjust the .c file to suit. There is no other way to fudge around this.