когда использовать 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)
Я погуглил его, но я не могу понять, когда и как использовать эти макросы
4 ответов:
JNIEXPORT и JNICALL определены в NDK_ROOT / platforms / android-9 / arch-arm/usr/include / jni.h.в зависимости от вашей установки этот путь будет отличаться, но в основном похож.
#define JNIIMPORT #define JNIEXPORT __attribute__ ((visibility ("default"))) #define JNICALLJNIEXPORT используется для отображения собственных функций в динамической таблице встроенного двоичного файла ( * . 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