C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解

这篇具有很好参考价值的文章主要介绍了C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1、导入导出声明

2、C++函数名称改编与extern "C"

3、函数调用约定与跨语言调用

3.1、函数调用约定

3.2、跨语言调用dll库接口

3.3、函数调用约定以哪个为准

4、def文件的使用

5、在C++程序中引用ffmpeg库中的头文件链接报错问题

6、最后


VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具从入门到精通案例集锦(专栏文章正在更新中...)https://blog.csdn.net/chenlycly/article/details/131405795C/C++基础与进阶(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_11931267.html       最近有个前同事打微信电话问一个包含ffmpeg开源库头文件后编译链接失败的问题,其实很简单,只需要在包含头文件时加上extern "C"就可以解决了。今天正好有时间,就来详细讲讲C++ dll动态库编程中关于导出接口相关的内容,包括接口的导入导出声明C++函数名称改编extern "C"作用、标准C接口函数调用约定声明跨语言调用dll接口def文件等内容,本文将通过一个具体的dll动态库实例来详细展开,希望能给大家(特别是新人)提供一定的借鉴或参考。

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件


        在这里,给大家重点推荐一下我的两个热门畅销专栏

专栏1(该专栏订阅量接近350个,有很强的实战参考价值,广受好评!)

C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931

本专栏根据近几年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的实战问题分析实例,带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

专栏中的文章都是通过项目实战总结出来的,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2: 

C/C++基础与进阶(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域的多个方面的内容,同时给出C/C++及网络方面的常见笔试面试题,并详细讲述Visual Studio常用调试手段与技巧!


1、导入导出声明

        在Windows平台编写C++ dll动态库时,提供给外部调用的接口需要添加__declspec(dllexport)导出声明,这样外部模块才能调用这些导出接口。一般我们会在dll动态库的api头文件中添加这样的定义:(以下 dll 动态库实例是在 Visual Studio 中创建的,即 IDE 开发环境为 Visual Studio

#ifdef WIN32
#ifdef HCNETSDKDLL_EXPORTS
#define NET_SDK_API __declspec(dllexport)  // 声明为导出
#else
#define NET_SDK_API __declspec(dllimport)  // 声明为导入
#endif  KdvMt_Pc_EXPORTS
#else
#define NET_SDK_API
#endif   WIN32


#ifdef __cplusplus
    extern "C"
    {
#endif
        // 设置业务消息回调接口
        NET_SDK_API void __stdcall SetMsgCallBack(PMsgCallBackFunc pMsgCallBackFunc);
        // 初始化SDK库
        NET_SDK_API DWORD __stdcall InitNetSDK();
        // 登录
        NET_SDK_API DWORD __stdcall LoginServer(const TLoginParam& tLoginParam);

#ifdef __cplusplus
    }
#endif

其中宏HCNETSDKDLL_EXPORTS是dll库中定义的,在dll工程属性配置的C/C++ -> 预处理 -> 预处理定义中可以看到该宏的定义,如下 :

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件

该宏是创建dll工程时自动生成的,宏名称的前半部分就是工程名称。

关于__declspec(dllexport)和__declspec(dllimport)

1)对dll库本身而言,接口是要导出给外部调用的,所以导出接口要声明为__declspec(dllexport);

2)对要调用dll库的外部模块,则是要引入dll库的导出接口,所以要使用__declspec(dllimport)。

2、C++函数名称改编与extern "C"

        C++之所以支持函数重载,是因为C++编译器在编译代码时会对函数名称进行改编。改编后的函数名称包含参数信息,这样就能将重载的函数区分开来了。下面是个简单的函数重载范例:

int AddNum( int a, int b );
double AddNum( double a, double b);

重载的函数名称是相同的,但参数类型是不同的。要将函数重载(overload)和函数重写(override)区分开来,两者有着本质的区别。

       创建了一个简单的dll动态库工程,在工程中提供了几个导出接口,如下所示:

// NET_SDK_API宏用来指定是导入还是导出
#ifdef WIN32
#ifdef HCNETSDKDLL_EXPORTS
#define NET_SDK_API __declspec(dllexport)
#else
#define NET_SDK_API __declspec(dllimport)
#endif  KdvMt_Pc_EXPORTS
#else
#define NET_SDK_API
#endif   WIN32


// 设置业务消息回调接口
NET_SDK_API void SetMsgCallBack(PMsgCallBackFunc pMsgCallBackFunc);
// 初始化SDK库
NET_SDK_API DWORD InitNetSDK();
// 登录
NET_SDK_API DWORD LoginServer(const TLoginParam& tLoginParam);

编译代码,生成dll库文件HCNetSDKDll.dll,然后用Dependency Walker查看该dll库导出接口的名称(进行名称改编后的名称):

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件

从上图中可以看出,改编后的函数名称中包含了函数参数信息

注意,在Win10及以上系统中Dependency Walker打开dll库会很慢,可能是Dependency Walker工具比较老,对新的Win10及以上系统兼容性不太好。有时可能需要数分钟才能打开dll文件,在使用时要耐心等待。

       但有时C++编写的dll模块可能会被C语言程序或者其他语言(比如C#)程序调用,需要导出标准C的接口(函数只有函数名,没有其他额外的信息)才能正常被调用。一般C++项目中,各个模块使用的都是C++开发语言,IDE开发工具基本都是一样的,不用考虑这样的问题。

       C++编译器在默认情况下会对函数名称进行改编,如何让编译器不对函数名称进行改编呢?可以使用extern "C"将所有的导出接口包起来。extern "C"标识告诉编译器在编译时以C语言的方式去处理,不要对声明的函数接口进行名称改编同样以上述dll工程为例,将导出接口用extern "C"包起来,如下所示:

#ifdef WIN32
#ifdef HCNETSDKDLL_EXPORTS
#define NET_SDK_API __declspec(dllexport)
#else
#define NET_SDK_API __declspec(dllimport)
#endif  KdvMt_Pc_EXPORTS
#else
#define NET_SDK_API
#endif   WIN32


#ifdef __cplusplus
    extern "C"  // 使用extern "C"
    {
#endif
        // 设置业务消息回调接口
        NET_SDK_API void __stdcall SetMsgCallBack(PMsgCallBackFunc pMsgCallBackFunc);
        // 初始化SDK库
        NET_SDK_API DWORD __stdcall InitNetSDK();
        // 登录
        NET_SDK_API DWORD __stdcall LoginServer(const TLoginParam& tLoginParam);

#ifdef __cplusplus
    }
#endif

然后重新编译代码,再用Dependency Walker查看dll库的导出接口,如下所示:

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件

确实生成了只有函数名的导出接口。

       此外,C++中可以导出函数,也可以导出整个类。对于导出类,可以直接在外部直接使用类。但extern “C”只对导出函数起作用,对导出类(整个类导出)的成员函数不起作用。

3、函数调用约定与跨语言调用

        让C++实现的dll库导出标准C接口,使用extern "C"就好了,事情到此好像就结束了,但事实上并没有结束。Windows平台上还有个函数调用约定的概念。

3.1、函数调用约定

       调用约定是用来声明函数的,常见的函数调用约定有__cdecl C调用、__fastcall快速调用以及__stdcall标准调用等。调用约定决定了函数调用时参数入栈的先后顺序(参数不一定使用栈传递,可能会直接使用寄存器传递),还决定了谁来释放传递参数占用的栈空间不同的开发语言,默认的调用约定可能是不一样的,比如C++中默认的是C调用、C#中默认的是标准调用。如果存在dll库跨语言调用时,一定要明确声明dll库导出接口的调用约定。

       Windows提供的系统API函数使用的都是标准调用约定,比如获取窗口文字的API函数GetWindowText:(WINAPI是函数调用约定宏,对应__stdcall标准调用)

#if !defined(_USER32_)
#define WINUSERAPI DECLSPEC_IMPORT
#define WINABLEAPI DECLSPEC_IMPORT
#else
#define WINUSERAPI
#define WINABLEAPI
#endif

#ifndef WINAPI
#define WINAPI __stdcall
#endif

WINUSERAPI
int
WINAPI /* 此处设置函数调用约定为__stdcall*/
GetWindowTextW(
    __in HWND hWnd,
    __out_ecount(nMaxCount) LPWSTR lpString,
    __in int nMaxCount);


#ifdef UNICODE
#define GetWindowText  GetWindowTextW
#else
#define GetWindowText  GetWindowTextA
#endif // !UNICODE

参照Windows系统API函数,我们一般也将dll库的导出接口声明为标准约定。这个地方需要注意一下,除了导出接口都要明确声明调用约定,回调函数也要声明调用约定。给dll库设置回调函数,dll库通过调用回调函数,给主调模块回调数据。回调函数是dll中声明的,但在上层调用模块实现的(完整的函数代码实现),回调函数的调用是在dll库内部的。

       关于函数调用约定的详细内容,可以参考我之前写的文章:

C/C++函数的调用约定详解https://blog.csdn.net/chenlycly/article/details/125354572

也可以参看微软官方对函数调用约定的说明:

参数传递和命名约定https://learn.microsoft.com/zh-cn/cpp/cpp/argument-passing-and-naming-conventions在上述页面最下面,给出了Visual C/C++ 编译器支持的调用约定:

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件

点击截图中的调用约定名称,就能跳转到对应调用约定的详细说明页面!

3.2、跨语言调用dll库接口

       为什么在跨语言调用的场景下需要明确声明dll库的导出接口的函数调用呢?是有原因的,假设调用C++实现的dll库的上层模块或程序是C#语言开发的,如果在dll的头文件中不明确声明导出接口的调用约定,则在使用Visual Studio编译C++实现的dll文件时,由于没有函数调用约定,默认使用C调用,而C调用下传递参数占用的栈空间是主调函数去释放的,所以dll库中编译生成的函数代码中就不会有清理传递参数占用的栈空间的二进制代码。

       而上层C#模块,默认是标准调用,在编译到调用dll库导出接口的代码时,由于标准调用下传递参数占用的栈空间是由被调用函数释放的,所以不会生成释放传递参数栈空间的二进制代码。所以在这种场景下,主调函数不会清理传递参数占用的栈空间,被调函数函数也不会清理被调函数占用的栈空间,这样就导致了栈不平衡,就会导致使用ebp去寻址栈内存出现异常,进而引发崩溃。

       我们以前就遇到过这样的问题,我们提供给第三方厂商的软件SDK是用C++实现的,第三方厂商C#开发的程序来调用我们的SDK模块,当时就因为在声明回调函数时没有指定函数调用约定,导致栈不平衡,引发了崩溃

3.3、函数调用约定以哪个为准

       使用Visual Studio创建的dll工程,在工程属性配置(C/C++ -> 高级 -> 调用约定)中,默认为_cdecl C调用,如下所示:

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件

 这是创建工程时的默认配置。

        我们也可以在函数声明处指定调用约定,如下所示:

// 初始化SDK库(将该函数的调用约定指定为__stdcall标准调用)
NET_SDK_API DWORD __stdcall InitNetSDK();

当函数前有指定调用约定,且工程属性配置中也有设置调用约定时,以函数前声明的调用约定为准

4、def文件的使用

       在我们的示例代码中添加调用约定的声明,声明为__stdcall标准调用,如下所示:

#ifdef WIN32
#ifdef HCNETSDKDLL_EXPORTS
#define NET_SDK_API __declspec(dllexport)
#else
#define NET_SDK_API __declspec(dllimport)
#endif  KdvMt_Pc_EXPORTS
#else
#define NET_SDK_API
#endif   WIN32


#ifdef __cplusplus
    extern "C"  // 使用extern "C"
    {
#endif
        // 设置业务消息回调接口
        NET_SDK_API void __stdcall SetMsgCallBack(PMsgCallBackFunc pMsgCallBackFunc);
        // 初始化SDK库
        NET_SDK_API DWORD __stdcall InitNetSDK();
        // 登录
        NET_SDK_API DWORD __stdcall LoginServer(const TLoginParam& tLoginParam);

#ifdef __cplusplus
    }
#endif

重新编译代码,然后使用Dependency Walker工具查看新生成的dll文件,结果看到函数符号变了(本来添加了extern “C”标识后,已经导出了标准C接口,结果添加了__stdcall调用约定后,又不再是标准C函数了),如下:

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件

不再是标准的C接口了,接口前面多了个下划线,接口后面多了个数字,这个数字其实是参数占用的栈空间大小。

       考虑跨语言调用dll库的场景,我们需要导出标准的C接口,结果即使使用了extern "C",还是没有生成标准C接口,这可如何是好呢?是有办法的,下面就轮到def模块定义文件登场了!def文件内容比较简单,主要分两块:

1)第一块是LIBRARY语句部分,指明对应的dll库名称;

2)第二块是EXPORTS语句部分,用来指定要导出的接口。

我们将要导出的接口都罗列到EXPORTS语句部分里面就好了,这样最终生成的dll库文件中导出的就是标准C接口了。本范例中的def文件如下所示:

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件

要手动生成def文件比较简单,先手动创建一个.txt文件,然后手动将之改成.def后缀,然后手动将LIBRARY语句和EXPORTS语句部分的文字拷贝进来修改一下即可。

       这个def文件如何设置到dll工程中呢?其实很简单,在dll工程属性中点击:链接器 -> 输入 -> 模块定义文件,然后将def文件的相对路径设置进去就可以了,如下所示:

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件

       所以,在我们这个dll动态库示例工程中,要实现标准C接口的导出,要使用导入导出声明,要声明函数调用约定,要使用extern "C",也要使用到def文件。

5、在C++程序中引用ffmpeg库中的头文件链接报错问题

       一个前同事在其C++项目中引用了ffmpeg开源库中的头文件,结果编译时报链接的错误,如下所示:

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件

找到我,让我帮忙看一下如何处理这个错误。因为ffmpeg库是用C语言开发的,编译生成的dll库的导出接口肯定是标准C接口。在C++项目中直接包含ffmpeg中的头文件,编译时默认链接的是经过名称改编的函数符号,而ffmpeg.lib中的都是标准C接口,函数符号只有函数名,不是改编后的符号,所以链接时找不到,报错了!

       其实这处理起来也比较简单,在包含头文件时用extern "C"包住就可以了,如下所示:

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件

重新编译代码,就没问题了,不再报错了。extern "C"标识是告诉C++编译器以C语言方式去处理,去链接标准的C接口,所以在引入的ffmpeg.lib中能找到标准的C接口,所以就不再报错了。

       其实很多开源库都是C语言实现的,比如sqlitelibcurl等,在这些开源库提供的api头文件中就添加了extern "C"。比如sqlite开源数据库中的sqlite3.h头文件中:

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件

在curl多协议网络传输开源库中的curl.h头文件中:

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件

6、最后

       本文通过一个具体的dll动态库工程实例,详细讲解了如何一步一步地实现标准C导出接口的过程,这其中包括接口的导入导出声明、extern "C"作用、标准C接口、函数调用约定声明、跨语言调用dll接口以及def文件等内容。

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解,C/C++技术分享,C++动态库,导出接口,导出导入声明,标准C接口,extern C,函数调用约定,def文件

       这里面涉及到的内容,C++新手是需要了解的,甚至有很多C++老手也不太清楚,希望本文能帮到大家,给大家提供一个借鉴或参考!文章来源地址https://www.toymoban.com/news/detail-675614.html

到了这里,关于C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++标准模板(STL)- 类型支持 (数值极限,C 数值极限接口)

    参阅 std::numeric_limits 接口 定义于头文件 cstdint PTRDIFF_MIN (C++11) std::ptrdiff_t 类型对象的最小值 (宏常量) PTRDIFF_MAX (C++11) std::ptrdiff_t 类型对象的最大值 (宏常量) SIZE_MAX (C++11) std::size_t 类型对象的最大值 (宏常量) SIG_ATOMIC_MIN (C++11) std::sig_atomic_t 类型对象的最小值 (宏常量) SIG_ATOMIC_

    2024年02月07日
    浏览(40)
  • C++、STL标准模板库和泛型编程 ——适配器、补充(侯捷)

    侯捷 C++八部曲笔记汇总 - - - 持续更新 ! ! ! 一、C++ 面向对象高级开发 1、C++面向对象高级编程(上) 2、C++面向对象高级编程(下) 二、STL 标准库和泛型编程 1、分配器、序列式容器 2、关联式容器 3、迭代器、 算法、仿函数 4、适配器、补充 三、C++ 设计模式 四、C++ 新标准 五、

    2023年04月27日
    浏览(69)
  • 【Java系列】函数式接口编程

    💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老 导航 檀越剑指大厂系列:全面总

    2024年02月05日
    浏览(33)
  • RAW 编程接口 TCP 函数简介

    LwIP_Periodic_Handle 函数是 一个必须被无限循环调用的 LwIP支持函数,一般在 main函数的无限循环中调用,主要功能是为 LwIP各个模块提供时间并查询链路状态 ,该 函数有一个形参,用于指示当前时间,单位为 ms。 对于 TCP功能,每 250ms执行一次 tcp_tmr函数;对于 ARP,每 5s 执 行一

    2024年04月12日
    浏览(31)
  • 如何查看.dll文件函数接口?(DLL动态链接库)(查看动态链接库、查看接口、查看函数)(Visual Studio的dumpbin工具)(Dependency Walker)

    查看DLL(动态链接库)文件的接口,通常需要使用一些专门的工具。这里有两个比较常见的方法: Dependency Walker 使用Dependency Walker:Dependency Walker是一个免费的实用工具,可以列出DLL文件中的所有导出函数以及它们可能依赖的其他DLL。只需在Dependency Walker中打开想查看的DLL文件

    2024年02月08日
    浏览(52)
  • JUC并发编程学习(十一)四大函数式接口(必备)

    程序员:泛型、反射、注解、枚举 新时代程序员:lambda表达式、链式编程、函数式接口、Stream流式计算 函数式接口:只有一个方法的接口 四大函数式接口:Consumer、Function、Predicate、Supplier Function Function 函数式接口 R apply(T t); 输入一个T类型的参数,返回一个R类型的值 只要是

    2024年02月05日
    浏览(48)
  • 【探索Linux】—— 强大的命令行工具 P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )

    本文将深入探讨使用套接字进行网络通信的基本步骤,包括创建套接字、绑定地址、监听连接(对于服务器端)、连接远程主机(对于客户端)、以及发送和接收数据等操作。套接字编程涉及一系列系统调用和函数,如 socket() 、 bind() 、 listen() 、 connect() 、 send() 、 recv() 等。

    2024年03月10日
    浏览(80)
  • 【c++】string类常见接口函数

    🔥个人主页 : Quitecoder 🔥 专栏 : c++笔记仓 朋友们大家好啊, 本节我们来到STL内容的第一部分: string 类接口函数的介绍 给大家分享一个c++文档库: https://legacy.cplusplus.com/ 字符串类是代表字符序列的对象 标准字符串类为这类对象提供了支持,其接口类似于标准字节容器的

    2024年04月16日
    浏览(32)
  • 外部存储器接口(External Memory Interface, EMIF)

    实际工作中使用的是型号为TMS320C6678的DSP,并通过EMIF接口与FPGA通讯。 由于EMIF接口比较简单,本文以FPGA的角度作为EMIF接口的slave端进行设计。参考手册为KeyStone Architecture External Memory Interface (EMIF16) User Guide - May 2011 FPGA与DSP之间的EMIF接口调试 EMIF接口 DSP之外部设备连接接口之

    2024年01月23日
    浏览(38)
  • windows和Linux下查看动态库dll/so的函数接口

    一.Linux下查看动态库so的函数接口:nm xxxx.so 1.在库文件所在目录右键---在终端打开---输入“nm xxxx.so”或“nm -D xxxx.so”(注意大小写) 2.输出命令说明:  参考:(55条消息) Linux nm命令详解_Imagine Miracle的博客-CSDN博客 nm命令中符号类型详解 - LiuYanYGZ - 博客园 (cnblogs.com) 符号 类型

    2024年02月10日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包