Prevent function exclusion from a static library when connecting to a shared library?

I have a static library Foo, which is used by the shared library Bar. Bar is the native shared library downloaded by my Android app. Foo contains JNI functions that are called only by Java code, not C ++ code in Bar. Because of this, these JNI functions exit the static library (Foo) when the shared library (Bar) is built. I am currently using a slightly hacked method to prevent this from happening.

So, in this case, is there a way to tell the compiler not to separate the JNI (or any) functions when binding?

+7
android jni
source share
2 answers

They are not lost, they are ignored. When the shared library is linked, the linker only pulls out the functions that are actually used in the object files. (This is how static libraries work).

I believe that passing the -whole-archive flag to the linker will cause it to pull all the object files from the static library. You can provide it on the gcc link line with "-Wl, -wall-archive". You must follow it with "-Wl, -no-whole-archive" after specifying your library, or ld will continue the behavior for any other static libraries it encounters, which is most likely not the behavior you want. See also the ld (1) man page on a Linux system.

Another way to do the same is to output a single massive .o file instead of the .a file.

EDIT: A simple command line example using libz on the desktop:

% echo "int main() { return 0; }" > foo.c % gcc -o foo /usr/lib/libz.a foo.c % ls -s foo 12 foo* % gcc -o foo -Wl,-whole-archive /usr/lib/libz.a -Wl,-no-whole-archive foo.c % ls -s foo 104 foo* 

(You should use "/usr/lib/libz.a" instead of "-lz" here, because the latter finds the shared library /usr/lib/libz.so.)

I haven’t used NDK very much, but it seems like adding flags to LOCAL_LDFLAGS can do the trick.

+7
source share

Let's start with the base two-libs samples from the NDK . Here is its original Android.mk file:

  1 # Copyright (C) 2009 The Android Open Source Project 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 # 15 16 # the purpose of this sample is to demonstrate how one can 17 # generate two distinct shared libraries and have them both 18 # uploaded in 19 # 20 21 LOCAL_PATH:= $(call my-dir) 22 23 # first lib, which will be built statically 24 # 25 include $(CLEAR_VARS) 26 27 LOCAL_MODULE := libtwolib-first 28 LOCAL_SRC_FILES := first.c 29 30 include $(BUILD_STATIC_LIBRARY) 31 32 # second lib, which will depend on and include the first one 33 # 34 include $(CLEAR_VARS) 35 36 LOCAL_MODULE := libtwolib-second 37 LOCAL_SRC_FILES := second.c 38 39 LOCAL_STATIC_LIBRARIES := libtwolib-first 40 41 include $(BUILD_SHARED_LIBRARY) 

Note that the JNI function is located in second.c , which is not part of the libtwolib-first static library.

First, let's reproduce your problem. The change is simple:

 ... 27 LOCAL_MODULE := libtwolib-first 28 LOCAL_SRC_FILES := first.c second.c ... 36 LOCAL_MODULE := libtwolib-second 37 LOCAL_SRC_FILES := 

If you run a modified project, you will receive the following error:

 E/AndroidRuntime( 4213): FATAL EXCEPTION: main E/AndroidRuntime( 4213): java.lang.UnsatisfiedLinkError: add E/AndroidRuntime( 4213): at com.example.twolibs.TwoLibs.add(Native Method) E/AndroidRuntime( 4213): at com.example.twolibs.TwoLibs.onCreate(TwoLibs.java:39) E/AndroidRuntime( 4213): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047) E/AndroidRuntime( 4213): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627) E/AndroidRuntime( 4213): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679) E/AndroidRuntime( 4213): at android.app.ActivityThread.access$2300(ActivityThread.java:125) E/AndroidRuntime( 4213): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033) E/AndroidRuntime( 4213): at android.os.Handler.dispatchMessage(Handler.java:99) E/AndroidRuntime( 4213): at android.os.Looper.loop(Looper.java:123) E/AndroidRuntime( 4213): at android.app.ActivityThread.main(ActivityThread.java:4627) E/AndroidRuntime( 4213): at java.lang.reflect.Method.invokeNative(Native Method) E/AndroidRuntime( 4213): at java.lang.reflect.Method.invoke(Method.java:521) E/AndroidRuntime( 4213): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:871) E/AndroidRuntime( 4213): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:629) E/AndroidRuntime( 4213): at dalvik.system.NativeStart.main(Native Method) 

You have precisely explained this problem: the linker "deleted" the "unused" entry Java_com_example_twolibs_TwoLibs_add ().

Now fix this:

  39 LOCAL_STATIC_LIBRARIES: = libtwolib-first
 39 LOCAL_WHOLE_STATIC_LIBRARIES: = libtwolib-first 

And again, the sample works!

+6
source share

All Articles