3

I have add a print log statement in JNI_OnLoad, but I found that it is not being called. Here is my JNI_OnLoad method.

extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    __android_log_print(ANDROID_LOG_INFO,  __FUNCTION__, "onLoad");
   // some init code
}

Do I need to declare JNI_OnLoad in a specific file or declare sth in Android.MK tells system where to find the JNI_OnLoad method? Now I just put to one of the many .cpp files.

compiled .so lib is attached. I try to dump the so file, and I am sure the JNI_OnLoad method is exported. https://docs.google.com/file/d/0B089WeEZXTb3ZjZiQllaYThuUUk/edit

Actually, I am trying to port a library from android source (libcorejava.so). To avoid class path conflict, I already change the class path.

And here is the file that declares JNI_OnLoad: https://android.googlesource.com/platform/libcore/+/master/luni/src/main/native/Register.cpp I already change the signature to the above one in order to match the standard signature

EDIT: I found that android source does not load it by System.loadLibrary! It says libcorejava is used to implement System.loadLibrary, so we cannot use System.loadLibrary to load it. But in my case, it should not be a problem as I only need part of the function (ICU related).

https://android.googlesource.com/platform/dalvik/+/master/vm/Init.cpp

 // Most JNI libraries can just use System.loadLibrary, but you can't
    // if you're the library that implements System.loadLibrary!
    loadJniLibrary("javacore");
    loadJniLibrary("nativehelper");

EDIT 2:
It turns out that it is because the name conflict of the library! But it seems that libjavacore requires other library. Does there any tool that can list out what the dependency I am missing?

java.lang.UnsatisfiedLinkError: Cannot load library: reloc_library[1286]: XXX

EDIT 3:
TextClock is a new api for displaying time. It only exists in 4.2+ api up. I am trying to backport it so that older sdk can uses it. It depends on a ICU library which resides in libjavacore. So I modify the Android.mk file to make sure the libjavacore only include the icu related source file and the final compiled so file is being included in my apk.

TextClock:
http://developer.android.com/reference/android/widget/TextClock.html

It now works in the phone which originally support TextClock, but doesn't work in old devices. Here is the exception log. I think it is because libjavacore is the wrapper of ICU library. Apart from the wrapper, I still need to port the ICU library. But I am going to give up as the size of ICU library is quite large and seems doesn't worth for it...

    12-13 14:07:54.859: E/AndroidRuntime(2091): java.lang.UnsatisfiedLinkError: Cannot load library: reloc_library[1306]:    36 cannot locate '_ZN6icu_516Locale14createFromNameEPKc'...
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at java.lang.Runtime.loadLibrary(Runtime.java:370)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at java.lang.System.loadLibrary(System.java:535)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at com.example.time.MainActivity.onCreate(MainActivity.java:20)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.Activity.performCreate(Activity.java:5008)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.ActivityThread.access$600(ActivityThread.java:130)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.os.Handler.dispatchMessage(Handler.java:99)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.os.Looper.loop(Looper.java:137)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.ActivityThread.main(ActivityThread.java:4745)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at java.lang.reflect.Method.invokeNative(Native Method)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at java.lang.reflect.Method.invoke(Method.java:511)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at dalvik.system.NativeStart.main(Native Method)
Bear
  • 5,138
  • 5
  • 50
  • 80
  • Putting it into one of the many cpp files is OK. I don't know what the problem is but i am pretty sure this is not the cause. Only thing that comes to my mind is that your forgot to put extern "C" before the function. – eozgonul Dec 13 '13 at 07:25
  • Ys I havent. Why need it? – Bear Dec 13 '13 at 07:41
  • http://stackoverflow.com/a/1041880/2359247 – eozgonul Dec 13 '13 at 08:05
  • I tried to change to extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {, but still the same... – Bear Dec 13 '13 at 08:06
  • Are you trying to replace a system library? Because that just ain't gonna work... (unless you root the device and copy over the /system/lib/*.so) – Dave Dec 13 '13 at 09:19
  • Yes, replacing system library of coz not working. But how about if I want to port some classes from 4.4 for backward compatibility? I always change the class path to avoid class path conflict. But I think you are somehow correct, they must have made something so that I can't just System.LoadLibrary. But what is this? – Bear Dec 13 '13 at 10:03
  • 1
    You are probably facing a conflict with the factory version which is inherited **already loaded** when the process which becomes your app is forked off of zygote - it may be doing nothing thinking the library is already loaded. Logcat might clarify. Or get a shell as the app uid and look in it's /proc/pid##/maps or dump that from Java code. You may need to name your library something else, but look out for function name conflicts! – Chris Stratton Dec 13 '13 at 17:48
  • @ChrisStratton You are correct. I not get into other error message. It seems that libjavacore depends on other so file. Please see my update. Or actually I should open a new question and you can just answer below, so I can accept you the answer you just mentioned. – Bear Dec 14 '13 at 01:42
  • Please explain exactly what you are doing are you providing this library in an apk or by modifying the system? – Chris Stratton Dec 14 '13 at 02:13
  • Post the **entire** section of logcat involved in the library loading, without trimming the lines short. – Chris Stratton Dec 14 '13 at 02:48
  • I rebuild the so and it is ok now. Thanks for your help. Would you mind post the answer for name conflict, so that I can accept it? – Bear Dec 14 '13 at 04:00
  • Oh, sorry, I can be reproduced in old sdk phone. I have posted the log – Bear Dec 14 '13 at 04:22
  • I have a file on my system which provides that, but with slightly different mangling. Can you post the output of `adb shell grep -ao '5..Locale14createFromName' /system/lib/*.so` ? Maybe you have a version compatibility problem. – Chris Stratton Dec 14 '13 at 05:16
  • It can be found in the latest devices but missing in old devices. I think I need to port libicuuc.so and libicui18n.so as well which I think is not worth to do so. Thanks by the way. Could u post it in answer section, so that I can accept your answer? – Bear Dec 14 '13 at 12:19

2 Answers2

1

You can dump the symbols in your .so file using readelf in your toolchains folder. Check to see that JNI_OnLoad is exported. The -s (symbols) command and the name of the .so file in your libs folder should do it.

1

In older releases of Android, the library was linked directly into the VM (libdvm.so linked against libnativehelper.so which linked against libjavacore.a). In recent releases this changed to always load the library at start time, using the internal native library load mechanism, so JNI_OnLoad will be called if present.

If I run adb shell dalvikvm Foo (where "Foo" doesn't exist), I see this in logcat:

D dalvikvm: Trying to load lib libjavacore.so 0x0
D dalvikvm: Added shared lib libjavacore.so 0x0
D dalvikvm: Trying to load lib libnativehelper.so 0x0
D dalvikvm: Added shared lib libnativehelper.so 0x0
D dalvikvm: No JNI_OnLoad found in libnativehelper.so 0x0, skipping init

So it loaded libjavacore.so and apparently found and ran JNI_OnLoad (no news is good news). It loaded libnativehelper.so and didn't find a JNI_OnLoad, so it logged a message to tell you so in case you were expecting otherwise.

If you replace the libjavacore.so in /system/lib (on a rooted device), and run the dalvikvm command, you should see your message in the log file, mixed in with messages like I've shown above. If you restart the system, you should see your message during zygote startup, and not again unless something runs a Dalvik-based command (like am).

fadden
  • 51,356
  • 5
  • 116
  • 166
  • Yes... if you replace or library path override the original *before* whatever call to an exec() function initialized things from scratch. But I suspect the poster is instead trying to package their own in their .apk and load it explicitly, and that's probably not working since the system version was already loaded into the parent zygote before they try to load theirs into the descendent apk process. – Chris Stratton Dec 13 '13 at 18:58
  • Ah. If the VM thinks it's the same lib there should be an "already loaded" message in logcat; if it doesn't, then I'd expect `JNI_OnLoad` to just work (and the new methods to replace the old because they use explicit registration). Your earlier suggestion of renaming the shared library will probably fix things. – fadden Dec 13 '13 at 19:05
  • 1
    @fadden I am sorry, I am going to port the library instead of replacing. So renaming works. Thanks all of you guy. – Bear Dec 14 '13 at 01:45