通过示例去看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“ 的相关文章

  • NDK 入门HelloJni 以及 javah 找不到类文件的问题 解决(ubuntu)

    1 首先创建一个android工程 创建一个HelloJni java 文件 并编写如下代码 package com ndk import android app Activity import android os Bundle impo
  • Attention! No symbol directories found - please check your native debug configuration</font>

    我出现问题的版本是Android Studio2 2 3 之前项目是正常的 可以调试JNI代码 但是突然有一次不知道什么原因就无法调试 断点无法断下 调试时有这样的警告 Now Launching Native Debug Session
  • Android JNI(一):JNI基础概念

    本文讲述 NDK和JNI是什么 JNI的原理 JNI开发流程的步骤 认识JNI相关的代码语法 名称概念 什么是NDK NDK 其中NDK的全拼是 Native Develop Kit Android NDK 就是一套工具集合 允许你使用C
  • 最近接触的技术汇集帖

    最近在网上查资料碰到好多没接触过的技术 先汇总在这里备用 以后慢慢吸收 1 JNA JNI的替代品 调用方式比JNI更直接 不再需要JNI那层中间接口 几乎达到Java直接调用动态库 2 SmallSQL 基于JDBC3 0转为Deskto
  • jni入门级别教程

    前提条件 笔者假想着 能看到这篇博客的读者 NDK环境 已经配置好了哈 开发步骤 第一步 新建工程 工程建完之后在真机或模拟器上运行一遍 确保工程建的没问题 第二步 配置NDK 第三步 在Java中添加 native 函数 并 调用 我们这
  • Java调用Native方法

    Java调用Native方法 Java中支持调用其他语言 C C 想要实现调用 需要进行以下几步操作 编写Java类并声明Native方法 package my mynative background public class Studen
  • jni中如何查看函数签名

    操作步骤 第一步 找到 build 文件夹 第二步 找到 javac 文件夹 第三步 找到自己写的 xxx class文件 第四步 右键 xxx class 文件 在 Terminal 中打开 第五步 执行 javap s xxx clas
  • 【OPENGLES】opengles-jni实例1

    首先 说明下为什么要用jni实现opengl es 在进行移动端图像算法开发的过程中 需要完成对大量的数据进行处理 opengl是在进行图形渲染或者大批量图像运算处理时有着很大的优势 如果能够在算法库开发中加入opengl 则对于提升算法运
  • 导致java.lang.UnsatisfiedLinkError错误的一种解决办法

    欢迎转载请注明出处http blog csdn net ning gg article details 53641254 在程序中加入so文件导致java lang UnsatisfiedLinkError错误的一种解决办法 可能这个解决办
  • android通过JNI用C/C++创建本地文件

    通过jni在本地创建文件 1 在android studio创建基本的jni工程 并且在APP界面成功显示 Hello from C 不会的可以看android studio使用jni 2 在native lib cpp文件中创建文件 为了
  • 使用Visual Studio 2019和IntelliJ IDEA 2018实现JAVA调用本地代码

    使用Visual Studio 2019和IntelliJ IDEA 2018实现JAVA调用本地代码 1 我们使用的工具是 IntelliJ IDEA 2018 编写java代码 和VisualStudio 2019 编写Native方法
  • 两个具有相同名称和类型的变量,在两个不同的.c文件中,使用gcc编译

    事情是这样的 我在两个不同的 c 文件中有两个相同的全局变量 它们没有声明为 extern 所以每个 c 文件应该看到自己的变量 对吧 但我得到了一些非常奇怪的行为 就好像一个文件正在读取其他文件变量 将它们链接在一起之后 向两个变量定义添
  • C 和 C++ 中的 static 和 extern 全局变量

    我制作了 2 个项目 第一个项目使用 C 语言 第二个项目使用 C 语言 两者都具有相同的行为 C项目 header h int varGlobal 7 main c include
  • 在头文件中使用 extern 的优点

    这里有一个类似的问题标题 但在阅读答案时 它似乎没有解决该特定问题 C 头文件中的 extern 有什么用 它更像是 为什么使用头文件 在下面的用法中extern extern int a int b structs have no ext
  • 在 Objective-C 代码中使用 extern "C" 时发生链接器错误

    我正在尝试创建一些可以从 iPhone 应用程序中的 Objective C 和 C 代码调用的实用函数 我有无法编译为 ObjectiveC mm 的第三方 C 类 我有一个头文件声明我的函数 然后有一个定义它们的 c 文件 我已经三次检
  • 关于 Objective-C 项目中使用的 extern 的 3 个问题

    当我使用这个词时extern在方法或变量声明之前 我是否将其设置为全局的 从而在整个项目中可读 可写 可用 如果我在关键字之前使用 extern 是否有可能我的项目的一部分仍然无法访问它 例如 仅通过子类 例如当我使用 受保护 时 exte
  • C - 不同文件中全局定义的变量显示不带 extern 修饰符的外部链接

    彻底迷茫了 我很沮丧 因为我认为准确了解范围和链接如何工作很重要 但我一直看到有关的相互矛盾的信息extern我的编译器 链接器与我读到的内容相矛盾 main c int int1 void main int1 6 printf nMain
  • C#:shlwapi.dll 中 StrCmpLogicalW 的实现或替代

    为了在我的应用程序中进行自然排序 我当前在 shlwapi dll 中 P Invoke 一个名为 StrCmpLogicalW 的函数 我正在考虑尝试在 Mono 下运行我的应用程序 但当然我不能拥有这个 P Invoke 东西 据我所知
  • 更改外部变量的值

    我们在 File1 c 中有 int arr 10 在 File2 c 中 extern int arr int main arr 0 10 return 0 这样做可能会出现哪些问题以及为什么 数组不是指针 内存访问将会出错 In Fil
  • extern 在 C# 中如何工作?

    每当我足够深入地观察反射镜时 我都会碰到extern没有来源的方法 我阅读了 msdn 文档http msdn microsoft com en us library e59b22c5 v vs 80 aspx http msdn micr

随机推荐

  • 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 然后生成的函数符号不再跟源代码中定义的函数名一致 这样导致调用方通