web-dev-qa-db-fra.com

quand utiliser JNIEXPORT et JNICALL dans Android NDK?

J'essaie d'écrire mes propres sources jni. En regardant quelques exemples ndk, j'ai constaté qu'ils utilisent souvent ces macros JNIEXPORT et JNICALL exécutées par le nom de Java package comme celui-ci

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

Je l'ai googlé mais je ne comprends pas quand et comment utiliser ces macros

26
nawara

JNIEXPORT et JNICALL sont définis dans NDK_ROOT/plates-formes/Android-9/Arch-arm/usr/include/jni.h. En fonction de votre configuration, ce chemin sera différent, mais généralement similaire.

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

JNIEXPORT est utilisé pour faire apparaître les fonctions natives dans la table dynamique du binaire construit (fichier * .so). Ils peuvent être définis sur "caché" ou "par défaut" (plus d'informations ici ). Si ces fonctions ne figurent pas dans la table dynamique, JNI ne pourra pas trouver les fonctions pour les appeler, de sorte que l'appel RegisterNatives échouera au moment de l'exécution.

Il convient de noter que toutes les fonctions se retrouvent dans la table dynamique par défaut, de sorte que n'importe qui pourrait décompiler votre code natif assez facilement. Chaque appel de fonction est intégré au binaire au cas où JNI aurait besoin de le trouver. Cela peut être changé en utilisant l'option du compilateur -fvisibility. Je recommanderais à tout le monde de définir ceci sur -fvisibility=hidden pour sécuriser votre code, puis utilisez JNIEXPORT pour signaler les fonctions comme ayant une visibilité externe.

L'utilisation de la commande strip supprime simplement les symboles de débogage, la table dynamique est séparée. Jouez avec objdump pour voir combien une personne pourrait retirer de vos fichiers .so.

Nous avons récemment été gênés par cela, espérons que cela aide quelqu'un.

EDIT: Nous utilisons un système de construction personnalisé, donc l'option de visibilité peut être définie par défaut pour d'autres configurations de construction. Plus d'informations sont disponibles dans this SO answer .

35
Danny Parker

Vous pouvez trouver la définition de ces macros dans la partie dépendante de la machine de votre JNI comprend (généralement dans $Java_HOME/include/<Arch>/jni-md.h).

En bref, JNIEXPORT contient toutes les directives du compilateur requises pour garantir que la fonction donnée est correctement exportée. Sur Android (et autres systèmes basés sur Linux), ce sera vide.

JNICALL contient toutes les directives du compilateur nécessaires pour garantir que la fonction donnée est traitée avec la convention d'appel appropriée. Probablement vide sur Android également (c'est __stdcall sur w32).

En général, vous devez les laisser, même s'ils sont vides #defines.

6
technomage

En termes simples:

  • JNIEXPORT si vous devez utiliser la famille de fonctions registerNatives, vous ne devez pas utiliser JNIEXPORT. Sinon, vous DEVEZ l'utiliser.
  • JNICALL doit TOUJOURS être utilisé.

JNIEXPORT s'assure que la fonction est visible dans le tableau des symboles. JNICALL s'assure que la fonction utilise la convention d'appel correcte. On Android JNICALL a une valeur différente basée sur l'architecture. ARM est vide, ce qui pourrait vous inciter à ne pas l'inclure. Mais vous devez utiliser JNICALL.

registerNatives vous permet de lier la fonction par programmation sur JNI_onLoad ou dans le futur aussi. registerNatives permet d'attraper les mauvais noms de fonction plus tôt et je recommande cette route.

3
over_optimistic

Exécutez simplement 'javah' sur vos classes natives et utilisez tout ce qu'il génère. Vous n'avez pas besoin de connaître les tenants et aboutissants de cela lorsque vous avez un outil qui peut le produire avec une fiabilité à 100%.

2
user207421