когда использовать JNIEXPORT и JNICALL в Android NDK?



Я пытаюсь написать свои собственные источники jni. Глядя на некоторые примеры ndk, я обнаружил, что они часто используют эти макросы JNIEXPORT и JNICALL follewed по имени пакета java, как это



JNIEXPORT void JNICALL Java_com_example_plasma_PlasmaView_renderPlasma(JNIEnv * env, jobject obj, jobject bitmap, jlong time_ms)



Я погуглил его, но я не могу понять, когда и как использовать эти макросы

623   4  

4 ответов:

JNIEXPORT и JNICALL определены в NDK_ROOT / platforms / android-9 / arch-arm/usr/include / jni.h.в зависимости от вашей установки этот путь будет отличаться, но в основном похож.

#define JNIIMPORT
#define JNIEXPORT  __attribute__ ((visibility ("default")))
#define JNICALL

JNIEXPORT используется для отображения собственных функций в динамической таблице встроенного двоичного файла ( * . so). Они могут быть установлены в "скрытый " или" по умолчанию " (подробнее здесь). Если эти функции отсутствуют в динамической таблице, JNI не сможет найти функции для их вызова, поэтому вызов RegisterNatives произойдет сбой во время выполнения.

Стоит отметить, что все функции по умолчанию попадают в динамическую таблицу, поэтому любой пользователь может декомпилировать ваш машинный код довольно легко. Каждый вызов функции встроен в двоичный файл на случай, если JNI нужно его найти. Это можно изменить с помощью параметра компилятора -fvisibility. Я бы рекомендовал всем установить это значение в -fvisibility=hidden, чтобы обеспечить безопасность вашего кода, а затем использовать JNIEXPORT, чтобы помечать функции как имеющие внешнюю видимость.

Использование команды strip просто удаляет отладочные символы, динамическая таблица является отдельной. Поиграйте с objdump, чтобы узнать, сколько человек может получить из ваших файлов. so.

Мы недавно столкнулись с этим, надеюсь, это кому-то поможет.

EDIT: мы используем пользовательскую систему сборки, поэтому параметр видимости может быть установлен по умолчанию для других настроек сборки. Более подробная информация доступна в this SO answer .

Вы можете найти определение этих макросов в машинно-зависимой части вашего JNI includes (обычно в $JAVA_HOME/include/<arch>/jni-md.h).

Короче говоря, JNIEXPORT содержит любые директивы компилятора, необходимые для обеспечения правильного экспорта данной функции. На android (и других системах на базе linux) это будет пусто.

JNICALL содержит все директивы компилятора, необходимые для обеспечения того, чтобы данная функция обрабатывалась с надлежащим соглашением о вызове. Вероятно, пусто и на android (это __stdcall на w32).

В общем, вы должны оставить их, даже если они пусты #defines.

Проще говоря:

  • JNIEXPORT если вы должны использовать семейство функций registerNatives, то вы не должны использовать JNIEXPORT. В противном случае вы должны использовать его.
  • JNICALL должно использоваться всегда.

JNIEXPORT гарантирует, что функция видна в таблице символов. JNICALL гарантирует, что функция использует правильное соглашение о вызове. На Android JNICALL имеет другое значение, основанное на архитектуре. Рука пуста, что может обмануть вас, чтобы не включать его. Но вы должны использовать JNICALL.

registerNatives позволяет связать функцию программно на JNI_onLoad или когда-нибудь в будущем тоже. registerNatives позволяет перехватывать неправильные имена функций ранее, и я рекомендую этот маршрут.

Просто запустите "javah" на ваших собственных классах и используйте все, что он генерирует. Вам не нужно знать все тонкости этого, когда у вас есть инструмент, который может производить его со 100% надежностью.

Comments

    Ничего не найдено.