通过示例去看JNI中为什么使用extern “C“

2023-05-16

经验总结

在JNI开发过程中,我们使用C++去写一个动态库,由于C++编译器对于函数的符号的生成需要进行名字修饰处理,然后生成的函数符号不再跟源代码中定义的函数名一致
这样导致调用方通过函数名去调用我们的函数(用函数名充当函数符号去查找函数地址),将会找不到具体的实现,然后崩溃。
JVM调用我们写的native接口/接口,就是这种情况。
所以当我们使用C++去写navtive的接口时,需要用extern “C” 包住native接口,这样就显示告诉C++编译器,对于我们的接口/函数使用C语言的方式(函数符号即函数名称)去编译代码和生成函数符号。
如下是CPP源码中native接口的实现没有使用extern “C” 声明,运行时出现的崩溃栈

2020-06-30 18:45:22.522 10202-10202/? E/art: No implementation found for java.lang.String com.example.hellolibs.MainActivity.stringFromJNI() (tried Java_com_example_hellolibs_MainActivity_stringFromJNI and Java_com_example_hellolibs_MainActivity_stringFromJNI__)
2020-06-30 18:45:22.523 10202-10202/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.hellolibs, PID: 10202
    java.lang.UnsatisfiedLinkError: No implementation found for java.lang.String com.example.hellolibs.MainActivity.stringFromJNI() (tried Java_com_example_hellolibs_MainActivity_stringFromJNI and Java_com_example_hellolibs_MainActivity_stringFromJNI__)
        at com.example.hellolibs.MainActivity.stringFromJNI(Native Method)
        at com.example.hellolibs.MainActivity.onCreate(MainActivity.java:31)
        at android.app.Activity.performCreate(Activity.java:6813)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2805)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2927)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1650)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:159)
        at android.app.ActivityThread.main(ActivityThread.java:6364)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1096)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:883)

如上案例,对应的so中的函数符号是

_Z53Java_com_example_hellolibs_MainActivity_stringFromJNIP7_JNIEnvP8_jobject

示例说明

  1. 本文使用的示例是google官方的JNI demo中的hello-libs跟hello-jni
    在这里插入图片描述
  2. 另外使用llvm-readelf来查看so中的符号信息

示例分析

hello-jni示例

hello-jni的项目结构结下图所示,关键的hello-jni.c的代码如下
在这里插入图片描述

我们进去看下so中的符号,就是看到定义的native接口的名称跟函数符号是一致的

dw_luogongwu@dw-luogongwudeMacBook-Pro hello-jni$ ff *.so
./app/build/intermediates/cmake/arm8Debug/obj/armeabi-v7a/libhello-jni.so
./app/build/intermediates/cmake/arm8Debug/obj/arm64-v8a/libhello-jni.so
./app/build/intermediates/merged_native_libs/arm8Debug/out/lib/armeabi-v7a/libhello-jni.so
./app/build/intermediates/merged_native_libs/arm8Debug/out/lib/arm64-v8a/libhello-jni.so
./app/build/intermediates/stripped_native_libs/arm8Debug/out/lib/armeabi-v7a/libhello-jni.so
./app/build/intermediates/stripped_native_libs/arm8Debug/out/lib/arm64-v8a/libhello-jni.so

dw_luogongwu@dw-luogongwudeMacBook-Pro out$ llvm-readelf --symbols lib/arm64-v8a/libhello-jni.so | grep FUNC
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_finalize@LIBC
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __register_atfork@LIBC
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit@LIBC
    11: 0000000000000608    64 FUNC    GLOBAL DEFAULT   10 Java_com_example_hellojni_HelloJni_stringFromJNI
    40: 00000000000005c0    12 FUNC    LOCAL  DEFAULT   10 __on_dlclose
    41: 00000000000005d0     4 FUNC    LOCAL  DEFAULT   10 __on_dlclose_late
    53: 00000000000005fc    12 FUNC    LOCAL  DEFAULT   10 pthread_atfork
    55: 00000000000005d4    12 FUNC    LOCAL  DEFAULT   10 __atexit_handler_wrapper
    59: 00000000000005e0    28 FUNC    LOCAL  DEFAULT   10 atexit
    60: 00000000000005cc     4 FUNC    LOCAL  DEFAULT   10 __emutls_unregister_key
    63: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_finalize@@LIBC
    64: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __register_atfork@@LIBC
    71: 0000000000000608    64 FUNC    GLOBAL DEFAULT   10 Java_com_example_hellojni_HelloJni_stringFromJNI
    72: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit@@LIBC

总结:使用C来写动态库,不需要使用extern

hello-libs示例

hello-libs的项目结构,跟关键的代码如下图所示
在这里插入图片描述
如下图是hello-lib使用与去掉extern后,native接口对应的函数符号的对比
在这里插入图片描述

总结: 使用C++来写动态库,需要extern “C” 来声明我们实现的natvie的接口/函数

其它说明

通过函数符号调用函数

这个属于dlopen/dlsum的使用范畴,即运行时加载一个动态库,并通过符号找到函数地址,通过函数针指方式调用函数
具体可以看参考文档中的 C语言调用so动态库的两种方式

有关C++的名字修饰

在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
名字修饰(Name Mangling)是一种在编译过程中,将函数、变量的名称重新改编的机制,
简单来说就是编译器为了区分各个函数,将函数通过一定算法,重新修饰为一个全局唯一的名称。

具体可以看篇文章 >> C+±-名字修饰

有关llvm-readelf的配置与使用

我使用的是ndk自带的llvm,我把llvm的bin目录放到了系统环境变量中,方便使用所有用llvm-xxx命令
在.bash_profile配置如下

# for android-ndk env
ANDROID_NDK_LLVM_BIN=${HOME}/Library/Android/sdk/ndk/21.2.6472646/toolchains/llvm/prebuilt/darwin-x86_64/bin
PATH=$PATH:$ANDROID_NDK_LLVM_BIN
export PATH

参考文档

  • jni中使用extern "C"的原因
  • C语言调用so动态库的两种方式
  • C+±-名字修饰
  • 为什么C++支持重载而C语言不支持重载
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通过示例去看JNI中为什么使用extern “C“ 的相关文章

随机推荐

  • ROS TF原理和使用方法

    ROS TF介绍 一 TF是什么 xff1f 1 TF是ROS的一个包 xff08 package xff09 2 TF能让用户随时记录各种坐标系之间的变换关系 3 TF能让用户在一个坐标系中进行坐标运算 xff0c 并将转换关系后的位置关
  • 分布式系统核心—日志

    分布式系统的核心组件 日志 有时也叫write ahead logs commit logs 或者事物 logs 通常指在应用所有的修改之前先写入日志 xff0c 一般会将重放日志 撤销日志都写进去 NoSQL数据库 KV存储 Hadoop
  • Linux 下常见的进程调度算法

    进程调度 xff1a 在操作系统中调度是指一种资源分配 调度算法是指 根据系统的资源分配策略所规定的资源分配算法 操作系统管理了系统的有限资源 xff0c 当有多个进程 或多个进程发出的请求 要使用这些资源时 xff0c 因为资源的有限性
  • Ubuntu18.04更换内核方法(原内核版本 4.15.0-38-generic)

    以下过程全部在root权限下操作 xff08 sudo su xff09 1 安装必备软件编译工具 xff1a apt get install libncurses5 dev build essential kernel package 注
  • Mac下使用Java反编译工具JD-GUI

    下载 下载JD GUI 我们选择 Mac 版的 jd gui osx 1 6 6 tar 下载解压打开即可使用 xff0c 不出意外的话出意外了 竟然提示我没有找到Java 版本 xff0c 我直接zsh 命令行下执行查看 java ver
  • Android 中使用Lambda表达式

    Android Studio默认使用Lambda表达式是会报错的 xff0c 即使你使用的是java 8 xff0c 为了在android studio中使用lambda表达式 xff0c 我们必须借助一个插件retrolambda xff
  • 树莓派安装ros系统

    导语 最近给树莓派安装了ros系统 xff0c 这里记录一下 步骤 xff1a 1 下载ros系统的软件 这里推荐从ubiquityrobotics下载ubiquityrobotics 的系统 这个相当于是给你下载了ubuntu16 04和
  • 嵌入式软件工程师相关的应聘要求

    本文收集从网上找到的嵌入式软件工程师岗位相关的职位要求 xff0c 与自身能力进行对比 xff0c 找出不足 xff0c 查漏补缺 xff0c 为18年的跳槽做好准备 1 嵌入式软件工程师杭州 浙江大华技术股份有限公司 职位描述 xff1a
  • docker无法访问localhost的一种解决方法

    如果你使用的不是toolbox xff0c 可以关掉这个页面了 如果你使用的是toolbox xff0c 请使用192 168 99 100加你的的接口 因为toolbox使用了virtualbox虚拟机 xff0c 相当于包了一层 xff
  • VCC、VDD、VEE、VSS等有关电源标注的区别

    Almost all integrated circuits ICs have at least two pins which connect to the power rails of the circuit they are insta
  • Linux内核学习(三)应用层和内核

    目录 写在前面整体环境学习笔记操作系统和内核简介 96 printf 96 和 96 prinfk 96 应用层对内核的调用从例子看原理 应用层的 96 write 96 如何调用内核中的 96 write 96 调用过程实践实现原理学习笔
  • ROS2安装serial库

    场景及问题描述 xff1a 今天在使用ros2读取IMU数据的时候 xff0c 他需要用到一个serial的包 xff0c 由于我使用的是Ubuntu20 04 43 ROS2humble xff0c 并且没有安装这个包 xff0c 所以出
  • 滚动校验(Rolling Checksum)算法

    滚动校验 Rolling Checksum 算法 Rsync中使用了一种滚动检验 Rolling Checksum 算法 xff0c 用于快速计算数据块的检验值 它是一种弱校验算法 xff0c 采用的是Mark Adler的adler 32
  • ROS2手写接收IMU数据(Imu)代码并发布

    目录 前言接收IMU数据IMU的串口连接问题 python接收串口数据 python解析数据ROS2发布IMU数据可视化IMU数据效果 前言 在前面测试完了单独用激光雷达建图之后 xff0c 一直想把IMU的数据融合进去 xff0c 由于经
  • ROS2+cartographer+激光雷达+IMU里程计数据融合(robot_locazation) 建图

    目录 写在前面总体流程分块解释IMU数据接收和发布车轮编码器数据接收和发布数据融合 robot localization概括使用 cartographer订阅 效果 写在前面 之前写了一篇ROS2 43 cartorgrapher 43 激
  • Ardupilot SITL(Software in the Loop)软件仿真

    参考 xff1a http ardupilot org dev docs sitl native on windows html sitl native on windows 第一步 xff1a 下载MAVProxy 第二步 xff1a 下
  • 多网卡指定网卡进行UDP通信(添加静态路由解决双网卡问题 )全记录

    这片文章的要解决的问题和解决方法在标题就已经解释得很清楚了 这里记录一下我的解决过程 还是各种查资料 这个解决方法适不适用于跨网段就不知道了 xff0c 可以试试 我的工作环境是服务端和客户端都是多网卡 我需要使服务端的网卡10 0 0 1
  • 机器学习_SMOTE:简单原理图示_算法实现及R和Python调包简单实现

    一 SMOTE原理 SMOTE的全称是Synthetic Minority Over Sampling Technique 即 人工少数类过采样法 xff0c 非直接对少数类进行重采样 xff0c 而是设计算法来人工合成一些新的少数样本 S
  • npm ERR! code 128 npm ERR! Command failed: git clone --mirror -q git://github.com/adobe-webplatform/

    拉取VUE项目后 下载以来报错问题 错误描述 npm ERR code 128 npm ERR Command failed git clone mirror q git github com adobe webplatform eve g
  • 通过示例去看JNI中为什么使用extern “C“

    经验总结 在JNI开发过程中 xff0c 我们使用C 43 43 去写一个动态库 xff0c 由于C 43 43 编译器对于函数的符号的生成需要进行名字修饰处理 xff0c 然后生成的函数符号不再跟源代码中定义的函数名一致 这样导致调用方通