2

Using Android NDK r18b (with clang tool chain) and Android Studio 3.2.1.

Relevant part of my mylib.gradle:

task ndkBuild(type: Exec) {
    commandLine "${ndkDir}/ndk-build${ndkExt}"
}

My Application.mk:

APP_PLATFORM := android-17
APP_ABI := armeabi-v7a
# APP_OPTIM := release
APP_CFLAGS += -D_BSD_SOURCE

And relevant part of my Android.mk:

include $(CLEAR_VARS)

LOCAL_PATH := $(BASE_PATH)
LOCAL_MODULE := mylib_jni

LOCAL_STATIC_LIBRARIES := \
  lib1 \
  lib2

LOCAL_WHOLE_STATIC_LIBRARIES := \
  mylib_wrap \
  other_wrap

include $(BUILD_SHARED_LIBRARY)

The static library mylib_jni.so is successfully built. I then run the following command (from the NDK):

arm-linux-androideabi-readelf -a mylib_jni.so


Symbols not stripped

In the output I can see the names of all non-static methods in lib1 and lib2 (not whole libraries as can be seen above). How is this possible? How can I get some outputs from the ndk-build command with information about why the symbols are not stripped? (I cannot find the options.txt for my NDK build step.)

l33t
  • 18,692
  • 16
  • 103
  • 180
  • 1
    There's not really enough information here. Which libraries are you examining? There are two out directories with ndk-build, libs and obj, and the libraries in obj are not stripped. The other unknown is whether those symbols even should be stripped. If they are used (or I think if they are in a section with a symbol that is used) then they should not be stripped. You can run `ndk-build V=1` to see the commands that are run and strip will be one of them. FYI, Clang is not responsible for stripping; strip is. – Dan Albert Nov 07 '18 at 04:19

1 Answers1

8

I am afraid you are confused between strip and visibility=hidden.

The former is a separate, post-linker, step of building a shared library. Its purpose is to reduce the size of the file (which will be packed into APK) by removing some extra information that the linker leaves for debugging purposes. Note that gradle (in Android Studio 3.2+) performs this strip even later, when the native libraries from all modules are merged together.

Strip affects the size of the file, but not the visibility of symbols.

Hiding symbols is another technique to reduce the size of the binaries. It is also highly recommended, to reduce exposure of your libraries to reverse engineering.

This does not happen by default. You must explicitly add this compiler flag:

APP_CFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden

You may combine this with discarding of unused functions:

APP_CFLAGS += -ffunction-sections -fdata-sections
APP_LDFLAGS += -Wl,--gc-sections

You must explicitly mark the external functions with

__attribute__ ((visibility ("default")))

Luckily, thanks to jni.h, this attribute is set for all JNIEXPORT funcitons.

If you use prebuilt static libraries, you may need also

APP_LDFLAGS += -Wl,--exclude-libs,ALL

Consider also providing version script

LOCAL_LDFLAGS += -Wl,-version-script -Wl,mylib_jni.vs
Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • Absolutely brilliant answer! Thanks a lot! `fvisibility=hidden` is the feature I was looking for. – l33t Nov 07 '18 at 12:29
  • The `-Wl,--exclude-libs,ALL` removed pretty much everything, resulting in a corrupt lib. The other settings worked! But still, I can see a number of **libc** entries - e.g. `free@LIBC` - in the symbol table. Are these required or can they also be hidden somehow? – l33t Nov 07 '18 at 22:36
  • It's strange that `exclude-libs` produces a currupt result. You can run **ndk-build -B -V** and step by step find what specifically went wrong. – Alex Cohn Nov 08 '18 at 07:43
  • As for references to **libc** and other system libraries, this is definitely expected. These are for functions that your library *needs*, not what it provides. NDK allows you to build your library with static linkage to **libc**, **libm**, etc. Note that NDK does not provide **liblog.a** for `__android_log_print()` and her kin. – Alex Cohn Nov 08 '18 at 07:50
  • My library got "corrupt" in the way that almost everything was stripped (size reduced by 95% or so). Linking **libc** statically is certainly a wanted option in my case, but haven't succeded with that so far. What is the flag for that? – l33t Nov 08 '18 at 08:40
  • I believe, **ALL** includes `libmylib_wrap.a` and `libother_wrap.a`. But you can try `-Wl,--exclude-libs,lib1 -Wl,--exclude-libs,lib2` – Alex Cohn Nov 08 '18 at 08:51
  • let's continue this discussion in [chat](https://chat.stackoverflow.com/rooms/183284/room-for-alex-cohn-and-l33t) – Alex Cohn Nov 08 '18 at 08:53
  • it seem "-fvisibility=hidden" flags only hide the normal function name, but let the global function variable keep its name.( like void (*func1)(); ) – SDJSK May 13 '19 at 11:01