¿cuándo usar JNIEXPORT y JNICALL en Android NDK?


Estoy tratando de escribir mis propias fuentes jni. Mirando algunas muestras de ndk, encontré que a menudo usan esas macros JNIEXPORT y JNICALL seguidas por el nombre de paquete java como este

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

Lo busqué en Google, pero no puedo entender cuándo y cómo usar estas macros

Author: Stan Kurdziel, 2013-10-17

4 answers

JNIEXPORT y JNICALL están definidos en NDK_ROOT/platforms/android-9/arch-arm/usr/include/jni.h. Dependiendo de su configuración, esta ruta será diferente, pero en su mayoría similar.

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

JNIEXPORT se utiliza para hacer que las funciones nativas aparezcan en la tabla dinámica del binario construido (archivo*.so). Se pueden establecer en "hidden" o "default" (más información aquí). Si estas funciones no están en la tabla dinámica, JNI no será capaz de encontrar las funciones para llamarlas por lo que los RegisterNatives llaman fallará en tiempo de ejecución.

Vale la pena señalar que todas las funciones terminan en la tabla dinámica por defecto, por lo que cualquiera podría descompilar su código nativo con bastante facilidad. Cada llamada de función está integrada en el binario en caso de que JNI necesite encontrarlo. Esto se puede cambiar usando la opción del compilador -fvisibility. Yo recomendaría que todo el mundo establezca esto en -fvisibility=hidden para mantener su código seguro, y luego use JNIEXPORT para marcar las funciones como que tienen visibilidad externa.

Usando el comando strip simplemente elimina símbolos de depuración, la tabla dinámica es independiente. Juega con objdump para ver cuánto puede sacar una persona de tus archivos. so.

Recientemente nos tropezamos con esto, espero que esto ayude a alguien.

EDITAR: Utilizamos un sistema de compilación personalizado, por lo que la opción visibilidad puede configurarse por defecto para otras configuraciones de compilación. Más información está disponible en esta respuesta SO.

 32
Author: Danny Parker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:07

Puede encontrar la definición de esas macros en la parte dependiente de la máquina de su JNI includes (generalmente en $JAVA_HOME/include/<arch>/jni-md.h).

En resumen, JNIEXPORT contiene las directivas del compilador necesarias para asegurar que la función dada se exporta correctamente. En Android (y otros sistemas basados en Linux), eso estará vacío.

JNICALL contiene cualquier directiva del compilador necesaria para asegurar que la función dada es tratada con la convención de llamada apropiada. Probablemente vacío en Android también (es __stdcall on w32).

En general, debe dejarlos, incluso si están vacíos #define s.

 6
Author: technomage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-17 11:33:27

En términos simples:

  • JNIEXPORT si debe usar la familia de funciones registerNatives entonces no debe usar JNIEXPORT. De lo contrario, debes usarlo.
  • JNICALL debe utilizarse SIEMPRE.

JNIEXPORT asegura que la función sea visible en la tabla de símbolos. JNICALL asegura que la función utiliza la convención de llamada correcta. En Android JNICALL tiene un valor diferente basado en la arquitectura. EL BRAZO está vacío, lo que podría engañarte para no incluirlo. Pero debes usar JNICALL.

registerNatives le permite vincular la función de forma programática en JNI_onLoad o en algún momento en el futuro también. registerNatives permite capturar nombres de funciones incorrectos antes y recomiendo esta ruta.

 3
Author: over_optimistic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-05 07:32:52

Simplemente ejecute 'javah' en sus clases nativas y use lo que genere. No necesita saber los entresijos de esto cuando tiene una herramienta que puede producirlo con 100% de confiabilidad.

 1
Author: user207421,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-17 09:31:59