nathansizemore
nathansizemore

Reputation: 3196

Linking shared object library without headers with NDK in Android Studio

I've got a shared library file, faceblaster-engine.so, compiled for arm-linux-androideabi, placed in the jniLibs folder for Android Studio. I've also got a simple cpp file in the jni folder.

My library is written in Rust, so I have no header files, and I'd like to call functions inside of it through the cpp file, but can't seem to get the library to link correctly. To test, I've made a simple function:

Rust

#[no_mangle]
pub extern fn rust_test() -> c_int {
    82 as c_int
}

C++

extern "C" {

// Test for calling rust function
int rust_test();

jint
Java_com_fureality_faceblaster_MainActivity_testRustLaunch(JNIEnv* env, jobject thiz)
{
    return rust_test();
}

} // End extern

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := faceblaster-engine
LOCAL_SRC_FILES := ../jniLibs/$(TARGET_ARCH_ABI)/libfaceblaster-engine.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE    := faceblaster
LOCAL_SRC_FILES := gl-tests.cpp
LOCAL_SHARED_LIBRARIES := faceblaster-engine
include $(BUILD_SHARED_LIBRARY)

Error

/home/nathan/Development/projects/faceblaster-android/app/src/main/jni/gl-tests.cpp

Error:(23) undefined reference to `rust_test'
Error:error: ld returned 1 exit status
make: *** [/home/nathan/Development/projects/faceblaster-android/app/build/intermediates/ndk/debug/obj/local/arm64-v8a/libfaceblaster.so] Error 1
Error:Execution failed for task ':app:compileDebugNdk'.
> com.android.ide.common.internal.LoggedErrorException: Failed to run command:
    /home/nathan/Development/bin/android-ndk-r10d/ndk-build NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=/home/nathan/Development/projects/faceblaster-android/app/build/intermediates/ndk/debug/Android.mk APP_PLATFORM=android-21 NDK_OUT=/home/nathan/Development/projects/faceblaster-android/app/build/intermediates/ndk/debug/obj NDK_LIBS_OUT=/home/nathan/Development/projects/faceblaster-android/app/build/intermediates/ndk/debug/lib APP_ABI=all
  Error Code:
    2
  Output:
    /home/nathan/Development/projects/faceblaster-android/app/build/intermediates/ndk/debug/obj/local/arm64-v8a/objs/faceblaster//home/nathan/Development/projects/faceblaster-android/app/src/main/jni/gl-tests.o: In function `Java_com_fureality_faceblaster_MainActivity_testRustLaunch':
    /home/nathan/Development/projects/faceblaster-android/app/src/main/jni/gl-tests.cpp:23: undefined reference to `rust_test'
    collect2: error: ld returned 1 exit status
    make: *** [/home/nathan/Development/projects/faceblaster-android/app/build/intermediates/ndk/debug/obj/local/arm64-v8a/libfaceblaster.so] Error 1

I dunno if my makefile isn't being picked up, or if it is correct? Anyone have an idea on how to properly link against this .so file and register functions I'd like to use?

Thanks in advance for any help!

Edit - Added Java source and rust code compilation method


Java

public class MainActivity extends Activity {

    // External libraries to load
    static {
        System.loadLibrary("faceblaster-engine");
        System.loadLibrary("faceblaster");
    }

    // External functions to register
    public native int testRustLaunch();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Other stuff omitted for brevity
        Log.d(TAG, "Testing call...");
        int test = testRustLaunch();
        Log.d(TAG, "Received: " + test);
    }
}

Rust Compilation

cargo build --target=arm-linux-androideabi

# /project/.cargo/config file
[target.arm-linux-androideabi]
linker = "/opt/ndk_standalone/bin/arm-linux-androideabi-gcc"

# Cargo.toml
[lib]
name = "faceblaster-engine"
crate_type = ["dylib"]

Edit 2


I've edited by build.gradle script, and my I know my Android.mk is being read and used now, but I am still getting the same compilation error :(

Edit 3


Turns out both of the answers below helped in solving the issue. It was mainly in part of Android Studio not picking up my makefile, the rust code not being declared properly as #[no_mangle] pub extern and my makefile being all jacked up.

Upvotes: 3

Views: 1860

Answers (2)

mstorsjo
mstorsjo

Reputation: 13317

Your Android.mk doesn't specify that the JNI wrapper actually should try to link to faceblaster-engine - the LOCAL_SHARED_LIBRARIES line instead says it should link to itself. Change it to LOCAL_SHARED_LIBRARIES := faceblaster-engine and it should hopefully work better.

Then to actually load it at runtime, you need to load the libraries in reverse dependency order, i.e.:

System.loadLibrary("faceblaster-engine");
System.loadLibrary("faceblaster");

Upvotes: 2

Shepmaster
Shepmaster

Reputation: 430574

Give this a shot:

#[no_mangle]
pub extern fn rust_test() -> i32 {
    82 // Note simplified implementation
}

The specific thing to try is #[no_mangle] and pub. pub will mark the function as being callable from outside the compiled library. #[no_mangle] instructs the compiler to not change the function name, so that the exported symbol will be the literal rust_test.

I also took the liberty of making the actual method body more idiomatic.

Another note is that you should match your Rust and C types more closely. If you want to use an int in C, you should use the Rust type c_int. C's int is allowed to change size depending on your platform! You could also use a int32 in Rust, but then you should use something like int32_t in C.

Upvotes: 4

Related Questions