[英]when to use JNIEXPORT and JNICALL in Android NDK?
我正在尝试编写自己的jni源代码。 看一些ndk示例,我发现他们经常使用那些宏JNIEXPORT和JNICALL,这些宏的名字跟java包一样
JNIEXPORT void JNICALL Java_com_example_plasma_PlasmaView_renderPlasma(JNIEnv * env, jobject obj, jobject bitmap, jlong time_ms)
我用Google搜索,但我无法理解何时以及如何使用这些宏
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文件中获取多少。
我们最近被这个绊倒了,希望这有助于某人。
编辑:我们使用自定义构建系统,因此默认情况下可以为其他构建设置设置可见性选项。 本SO答案中提供了更多信息。
您可以在JNI包含的机器相关部分中找到这些宏的定义(通常在$JAVA_HOME/include/<arch>/jni-md.h
)。
简而言之, JNIEXPORT
包含确保正确导出给定函数所需的任何编译器指令。 在android(以及其他基于linux的系统)上,这将是空的。
JNICALL
包含确保使用正确的调用约定处理给定函数所需的任何编译器指令。 在android上也可能是空的(在w32上是__stdcall)。
一般情况下,你应该把它们留在里面,即使它们是空的#define
。
简单来说:
JNIEXPORT
如果您应该使用registerNatives
系列函数,那么您不应该使用JNIEXPORT。 否则你必须使用它。 JNICALL
必须始终使用。 JNIEXPORT确保函数在符号表中可见。 JNICALL确保函数使用正确的调用约定。 在Android上JNICALL具有基于体系结构的不同值。 ARM是空的,可能会诱使您不包含它。 但是你必须使用JNICALL。
registerNatives
允许您以编程方式在JNI_onLoad
或以后的某个时间链接该函数。 registerNatives
允许更早地捕获错误的函数名称,我推荐这条路线。
只需在您的本机类上运行'javah'并使用它生成的任何内容。 当你拥有一台可以100%可靠地生产它的工具时,你不需要知道这个的细节。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.