quand utiliser JNIEXPORT et JNICALL dans Android NDK?
J'essaie d'écrire mes propres sources jni. En regardant certains échantillons ndk, j'ai trouvé qu'ils utilisent souvent ces macros JNIEXPORT et JNICALL follewed par le nom du paquet java comme ceci
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 peux pas comprendre quand et comment utiliser ces macros
4 réponses
JNIEXPORT et JNICALL sont définis dans NDK_ROOT / platforms / android-9/arch-arm/usr/include / jni.H. Selon votre configuration, ce chemin sera différent, mais surtout similaire.
#define JNIIMPORT
#define JNIEXPORT __attribute__ ((visibility ("default")))
#define JNICALL
JNIEXPORT est utilisé pour faire apparaître des fonctions natives dans la table dynamique du fichier binaire construit ( * . so). Ils peuvent être définis sur "hidden" ou "default" (plus d'Informations ici). Si ces fonctions ne sont pas dans la table dynamique, JNI ne sera pas en mesure de trouver les fonctions pour les appeler afin que les RegisterNatives appellent échoue lors de l'exécution.
Il est à noter que toutes les fonctions se retrouvent dans la table dynamique par défaut, donc n'importe qui peut décompiler votre code natif assez facilement. Chaque appel de fonction est intégré dans le binaire juste au cas où JNI aurait besoin de le trouver. Cela peut être modifié en utilisant l'option du compilateur -fvisibility
. Je recommanderais à tout le monde de définir ceci sur -fvisibility=hidden
pour garder votre code sécurisé, puis d'utiliser JNIEXPORT pour marquer les fonctions comme ayant une visibilité externe.
L'utilisation de la commande strip supprime simplement le symboles de débogage, la table dynamique est séparée. Avoir un jeu avec objdump pour voir combien une personne pourrait sortir de vos fichiers.so.
Nous avons récemment eu trébuché par cela, j'espère que cela aide quelqu'un.
EDIT: nous utilisons un système de construction personnalisé, de sorte que l'option de visibilité peut être définie par défaut pour d'autres configurations de construction. Plus d'informations sont disponibles dans cette réponse SO.
Vous pouvez trouver la définition de ces macros dans la partie dépendante de la machine de votre JNI includes (généralement dans $JAVA_HOME/include/<arch>/jni-md.h
).
En bref, JNIEXPORT
contient toutes les directives du compilateur nécessaires pour s'assurer que la fonction donnée est exportée correctement. Sur android (et d'autres systèmes basés sur linux), ce sera vide.
JNICALL
contient toutes les directives du compilateur nécessaires pour s'assurer que la fonction donnée est traitée avec la convention d'appel appropriée. Probablement vide sur android aussi (c'est __stdcall sur w32).
En général, vous devriez les laisser, même s'ils sont vides #define
s.
, En termes simples:
-
JNIEXPORT
si vous devez utiliser la famille de fonctionsregisterNatives
, 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 la table des symboles. JNICALL s'assure que la fonction utilise la convention d'appel correcte. Sur Android JNICALL a une valeur différente basée sur l'architecture. Le BRAS est vide ce qui pourrait vous tromper pour ne pas l'inclure. Mais vous devez utiliser JNICALL.
registerNatives
vous permet de lier la fonction par programme sur JNI_onLoad
ou dans le futur aussi. registerNatives
permet d'attraper les mauvais noms de fonctions plus tôt et je recommande cette route.
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 les aboutissants de ceci quand vous avez un outil qui peut le produire avec la fiabilité de 100%.