Компоновщик Android ndk-build не может найти предварительно созданную библиотечную функцию

Я пытаюсь изменить этот учебник, чтобы включить предварительно созданную библиотеку C в мой проект Android Studio (т. е. без использования экспериментального плагина Gradle) http://kvurd.com/blog/compiling-a-cpp-библиотека-для-android-с-android-studio/

Сама библиотека исходит от клиента, который не раскрывает исходный код, поэтому я не контролирую эту часть процесса сборки, однако они уже следуют одному и тому же руководству.

Проект собирается, загрузка библиотеки работает, и ссылка NDK (/jni/my-wrapper.c) работает нормально, пока я не попытаюсь вызвать фактическую библиотечную функцию, определенную в заголовке моей предварительной сборки. Ошибка, которую я получаю:

$ ndk-build
[arm64-v8a] Compile        : my-wrapper <= my-wrapper.c
[arm64-v8a] SharedLibrary  : libmy-wrapper.so
/Users/me/AndroidStudioProjects/MyProject/app/obj/local/arm64-v8a/objs/my-wrapper/my-wrapper.o: In function `Java_com_my_project_SignInActivity_CallFunction':
/Users/me/AndroidStudioProjects/MyProject/app/jni/my-wrapper.c:44: undefined reference to `MyFunction'
collect2: error: ld returned 1 exit status
make: *** [/Users/me/AndroidStudioProjects/MyProject/app/obj/local/arm64-v8a/libmy-wrapper.so] Error 1

Вот мой Android.mk:

LOCAL_PATH := $(call my-dir)

# static library info
include $(CLEAR_VARS)
LOCAL_MODULE := libMyLib
LOCAL_MODULE_FILENAME := libMyLib
LOCAL_SRC_FILES := ../prebuild/libMyLib.a
LOCAL_EXPORT_C_INCLUDES := ../prebuild/include
include $(PREBUILT_STATIC_LIBRARY)

# wrapper info
include $(CLEAR_VARS)
LOCAL_C_INCLUDES += ../prebuild/include
LOCAL_MODULE    := my-wrapper
LOCAL_SRC_FILES := my-wrapper.c
LOCAL_STATIC_LIBRARIES := libMyLib
include $(BUILD_SHARED_LIBRARY)

И MyLib.h (обратите внимание, что foobar() работает нормально, поскольку он находится в заголовке, но пока я вызываю MyFunction из my-wrapper.c, сборка ndk не выполняется):

#include <math.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int MyFunction(some stuff);
int foobar(){return 1;};

Наконец, my-wrapper.c:

#include <MyLib.h>

jbyte Java_com_my_project_SignInActivity_MyFunction(JNIEnv *env, jobject thiz, some other stuff){

//   return MyFunction(some other stuff which I cast to C types); //linker fails if uncommented

    return foobar(); //works fine
}

person Peter    schedule 14.09.2016    source источник
comment
Вы уверены, что MyFunction действительно существует в библиотеке? Поскольку у вас нет доступа к исходному коду, вероятно, стоит убедиться. Вы можете проверить это с помощью инструмента nm, включенного в Android NDK.   -  person Francesca Nannizzi    schedule 14.09.2016
comment
nm libMyLib.a приводит к 0000458c T _Z12MyFunctionP9my_structPhS1_S1_ - так что похоже, что он там, если нет какой-то тонкости, которую я упускаю   -  person Peter    schedule 14.09.2016


Ответы (1)


Это искаженное имя C++. Вы можете использовать его только из C++, а не из C.

Если вам действительно нужно вызвать его из C, вы можете сделать это так:

extern int _Z12MyFunctionP9my_structPhS1_S1_(/* whatever the function args are */);

jbyte Java_com_my_project_SignInActivity_MyFunction(
        JNIEnv *env, jobject thiz, some other stuff) {
    return _Z12MyFunctionP9my_structPhS1_S1_(args);
}

Это зависит от того, совместим ли код, который вы вызываете, как таковой (в этом случае вы должны попросить клиента создать свои API как extern "C").

Я бы действительно рекомендовал просто перенести ваш код на С++.

person Dan Albert    schedule 14.09.2016
comment
Невероятный! Однако клиент настаивает на том, что это библиотека C - возможно ли, что они по ошибке скомпилировали исходный код C, чтобы изменить имена в C++, слепо следуя шагу 1 из kvurd.com/blog/ ? Будут ли какие-либо побочные эффекты для меня, если я просто переведу код оболочки на C++, и в этом случае я должен вернуться к ним и попросить правильно собрать библиотеку C. Извините за элементарные вопросы, я не разработчик C++! - person Peter; 15.09.2016
comment
Оказывается, у клиента был весь код C в файлах .CPP, что сбивало с толку их компилятор, создавая искаженные имена вместо чистых C-библиотек. - person Peter; 15.09.2016
comment
Да, файлы .cpp по умолчанию будут создавать искаженные имена C++. Если они хотят сохранить файлы .cpp, но при этом сохранить совместимость своего API C, им просто нужно обернуть свои decls функций с помощью extern "C" (внутри #ifdef __cplusplus, как последний фрагмент в stackoverflow.com/a/67985/632035). - person Dan Albert; 15.09.2016