Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which method eventually calls JNI_OnLoad

I was trying to figure out how JNI_OnLoad is called internally. I eventually figured out the below tutorial but it does not throw any light on what code part actually calls JNI_OnLoad as an internal function call. Please help me with finding that link function which explicitly calls JNI_OnLoad. i observed that System.loadLibrary calls Runtime which again calls Classloader. But still could not find the native link.

I was especially interested with the one in OnLoad.cpp (android/platform_frameworks_base/blob/master/services/jni/onload.cpp)

JNI_OnLoad

jint JNI_OnLoad(JavaVM *vm, void *reserved);

The VM calls JNI_OnLoad when the native library is loaded (for example, through 
System.loadLibrary). JNI_OnLoad must return the JNI version needed by the native library.
In order to use any of the new JNI functions, a native library must export a JNI_OnLoad function that returns JNI_VERSION_1_2. If the native library does not export a JNI_OnLoad function, the VM assumes that the library only requires JNI version JNI_VERSION_1_1. If the VM does not recognize the version number returned by JNI_OnLoad, the native library cannot be loaded.

Edit: My file trace based on response of @Code Painters is below:

       System.loadLibrary("android_servers");
            |
            |The call System.loadLibrary(name) is effectively equivalent
            |  to the call
            |
            V
        Runtime.getRuntime().loadLibrary(name)
            |
            |public static Runtime getRuntime() {
            |        return currentRuntime;}
            |
            | // Here, also,Classloader.loadlibrary is called, 
            | but this is over-ridden (?)
            | by the Native function of Runtime.java below
            V
        /dalvik/vm/native/java_lang_Runtime.cpp (The jni native
        implementation of Runtime.java):
        /*
         * static String nativeLoad(String filename, ClassLoader loader)
         *
         * Load the specified full path as a dynamic library filled with
         * JNI-compatible methods. Returns null on success, or a failure
         * message on failure.
         */
        static void Dalvik_java_lang_Runtime_nativeLoad{
        //
        success = dvmLoadNativeCode(fileName, classLoader, &reason);
        }

I now understand Runtime.loadlibrary is overloaded with the Dalvik_java_lang_Runtime_nativeLoad native function and Classloader.loadlibrary is not evoked. Please correct me if I am wrong.

like image 284
Aadishri Avatar asked Nov 22 '12 09:11

Aadishri


People also ask

What is JNI_OnLoad?

The VM calls JNI_OnLoad when the native library is loaded (for example, through System. loadLibrary ). JNI_OnLoad must return the JNI version needed by the native library. In order to use any of the new JNI functions, a native library must export a JNI_OnLoad function that returns JNI_VERSION_1_2 .

What is JNIEnv?

JNIEnv – a structure containing methods that we can use our native code to access Java elements. JavaVM – a structure that lets us manipulate a running JVM (or even start a new one) adding threads to it, destroying it, etc…

What is Jclass in JNI?

The jclass instance is your object on which a method will be invoked; you'll need to look up the getName method ID on the Class class, then invoke it on the jclass instance using CallObjectMethod to obtain a jstring result. So in short yes, you just call the getName function and look at the jstring result.

Why do we use JNI?

JNI provides functions for accessing the contents of array objects. While arrays of objects must be accessed one entry at a time, arrays of primitives can be read and written directly as if they were declared in C.


1 Answers

For Android, you should look into dalvik/vm/Native.c, which defines the JNI interface.

The most relevant function is this one:

bool dvmLoadNativeCode(const char* pathName, Object* classLoader);

This the place, where the library is dlopen()-ed. And the most interesting part of it:

    vonLoad = dlsym(handle, "JNI_OnLoad");
    if (vonLoad == NULL) {
        LOGD("No JNI_OnLoad found in %s %p\n", pathName, classLoader);
    } else {
        /*
         * Call JNI_OnLoad.  We have to override the current class
         * loader, which will always be "null" since the stuff at the
         * top of the stack is around Runtime.loadLibrary().  (See
         * the comments in the JNI FindClass function.)
         */
        OnLoadFunc func = vonLoad;
        Object* prevOverride = self->classLoaderOverride;

        self->classLoaderOverride = classLoader;
        oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
        LOGV("+++ calling JNI_OnLoad(%s)\n", pathName);
        version = (*func)(gDvm.vmList, NULL);
        dvmChangeStatus(self, oldStatus);
        self->classLoaderOverride = prevOverride;

As you can see, JNI_OnLoad is simply resolved using dlsym() and called using the returned pointer. The rest of this code part is checking the value returned by JNI_OnLoad, nothing really exciting.

I believe it should look pretty much the same for other VMs - so just grep for dlopen() and dlsym() - after all it's just plain shared library loading and symbol resolution.

Edit: Speaking of the exact file you mention, Android.mk in the same directory compiles and links this file into libandroid_servers shared library. Grepping around for this library name reveals services/java/com/android/server/SystemServer.java.

What's relevant:

public static void main(String[] args) {
    // ...
    System.loadLibrary("android_servers");
    // ...
}

So, the loading of library (and thus a call to JNI_OnLoad() in onload.cpp) is performed in the context of Android's system service startup. If you want to know more about how/when the system service loads, I recommend this presentation.

like image 144
Code Painters Avatar answered Sep 19 '22 12:09

Code Painters