4

I found instructions for how to link and use c/c++ code in Android by utilizing the NDK. What I'm wondering is how different this process is when you have a .so from a third party?

Calling System.loadLibrary() seems to load the library properly, but whenever I try to call the method I need, I get an UnsatisfiedLinkError.

The function prototype is declared according to the details provided by the third party who compiled the .so file. Is there any way that I can decompile the file effectively to at least check that the prototype is correct?

Is the link process for a precompiled .so library somehow different? Maybe I'm missing some steps which cause the link error.

EDIT:

I wish I could post code but I can only write from my phone. My Android.mk file is set as you would expect, with a reference to the .so file and the $(PREBUILT_SHARED_LIBRARY) specification.

All my code does is load the library and declare the function prototype with the native keyword. When I try calling the function I get a link error.

Mike
  • 825
  • 3
  • 12
  • 30
  • check this http://stackoverflow.com/questions/17818058/android-ndk-unsatisfiedlinkerror-a-surprising-reason?rq=1 – Maveňツ Sep 18 '14 at 13:04
  • 2
    Is the third party library intended to be used with JNI? You can't call **arbitrary** C++ functions via JNI - only specially named ones, like `Java_packagename_classname_methodname()`. If it is, are you matching the class and package name from the Java side? The library probably wasn't built for **your** package and class. – Seva Alekseyev Sep 18 '14 at 19:23
  • I didn't consider the class and package naming. I was told the class is intended for use with JNI, but all I was given is the function prototype and the .so file. – Mike Sep 19 '14 at 01:17

2 Answers2

1

What I'm wondering is how different this process is when you have a .so from a third party?

Here's what one of my projects look like. The project produces libprng.so. It uses Crypto++ for the underlying PRNG (it also samples the sensors to reseed the PRNG before returning bytes in GetBytes).

The Crypto++ library is located in /usr/local/cryptopp/android-ARCH, where ARCH is armeabi, armeabi-v7a, x86 or mips.

My shared object and the Crypto++ shared object each depend on STLport. Because multiple modules depend upon STLport, we must use the shared object version of STLport (i.e., libstlport_shared.so).

Here's what the Java class file looks like:

public class PRNG {

    static {
        System.loadLibrary("stlport_shared");
        System.loadLibrary("cryptopp");
        System.loadLibrary("prng");
    }
    ...
}

Application.mk

APP_ABI   := armeabi x86 mips armeabi-v7a

Android.mk

LOCAL_PATH := $(call my-dir)

# NDK_DEBUG_IMPORTS := 1

#########################################################
# STLport library
include $(CLEAR_VARS)

STLPORT_INCL     := /opt/android-ndk-r9/sources/cxx-stl/stlport/stlport
STLPORT_LIB      := /opt/android-ndk-r9/sources/cxx-stl/stlport/libs/$(TARGET_ARCH_ABI)

LOCAL_MODULE := stlport_shared
LOCAL_SRC_FILES := $(STLPORT_LIB)/libstlport_shared.so

LOCAL_EXPORT_CPPFLAGS :=
LOCAL_EXPORT_C_INCLUDES := $(STLPORT_INCL)

include $(PREBUILT_SHARED_LIBRARY)

LOCAL_SHARED_LIBRARIES  := stlport_shared

#########################################################
# Crypto++ library
include $(CLEAR_VARS)

CRYPTOPP_INCL   := /usr/local/cryptopp/android-$(TARGET_ARCH_ABI)/include
CRYPTOPP_LIB    := /usr/local/cryptopp/android-$(TARGET_ARCH_ABI)/lib

LOCAL_MODULE       := cryptopp
LOCAL_SRC_FILES    := $(CRYPTOPP_LIB)/libcryptopp.so

LOCAL_EXPORT_CPPFLAGS := -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function
LOCAL_EXPORT_C_INCLUDES := $(CRYPTOPP_INCL) $(CRYPTOPP_INCL)/cryptopp

include $(PREBUILT_SHARED_LIBRARY)

LOCAL_SHARED_LIBRARIES  := cryptopp

#########################################################
# PRNG library
include $(CLEAR_VARS)

APP_STL         := stlport_shared
APP_MODULES     := prng stlport_shared cryptopp

# My ass... LOCAL_EXPORT_C_INCLUDES is useless
LOCAL_C_INCLUDES   := $(STLPORT_INCL) $(CRYPTOPP_INCL)

LOCAL_CPP_FEATURES := rtti exceptions

LOCAL_CPP_FLAGS    := -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function
LOCAL_CPP_FLAGS    += -Wl,--exclude-libs,ALL

LOCAL_LDLIBS            := -llog -landroid
LOCAL_SHARED_LIBRARIES  := cryptopp stlport_shared

LOCAL_MODULE    := prng
LOCAL_SRC_FILES := libprng.cpp

include $(BUILD_SHARED_LIBRARY)
jww
  • 97,681
  • 90
  • 411
  • 885
1

First you need your methods prototypes to match the ones the lib was designed for, but usually also the packagename and class name have to match as well.

You can get what symbols are declared inside your .so file using readelf from binutils:

readelf -s libYourLib.so

The symbols to look for are starting with Java_ followed by the package name, then the class name, finally the method name, with . replaced by _.

Instead of using readelf, you can also use this app I've developed: https://play.google.com/store/apps/details?id=com.xh.nativelibsmonitor.app

Sometimes there is only a JNI_OnLoad method declared instead of Java_* methods. In that case you can't get the information you need straight away from the declared symbols.

ph0b
  • 14,353
  • 4
  • 43
  • 41