一文搞懂系列——你真的了解如何生成动态库了吗?

news2024/11/26 1:49:27

引言

动态库的编译,这有什么难度,这不是手到擒来的事情吗?无非不就是:

gcc -FPIC -shared -o libxxx.so  *.o  *.c  

我若是提出这些需求场景,阁下又如何应对呢?

  • 动态库A依赖其他部分提供的能力。但是却不想将内部的能力暴露出去
  • 动态库A依赖外部函数func_xxx,但是该符号即可能存在我们自己的库B中,也可能存在客户动态库C中。如何保证调用指定接口
  • 对外提供的库,如何让客户只能访问指定接口,实现其它接口的隐藏等。

至此你还能面不改色,自信的说:动态库编译简单吗?嘴角颤抖,不屈的低语:就是简单

也有朋友可能就会说:“现实工作中怎么会有这么奇葩的要求?就是你难为人,没事找事”。但是我想说的是,这些场景真的很常见,我在工作中就遇到过,不妨听我细说。

-Wl,–exclude-libs,ALL

工作场景:当今IT行业,一个产品的输出,基本都是有多个部门相互合作,紧密配合才能实现的。而各个部门之间的常见的配合方式就是提供SDK。比如:我之前在海康是做门禁产品的。其中有一个重要功能就是人脸识别。该功能流程可以分解

视频流获取 --> 提取图片帧 --> 人脸识别算法获取唯一ID --> 比对数据库中的ID --> 放行

其中视频流获取 --> 提取图片帧是bsp团队开发,他们提供动态库libB.a,我们调用其中对应接口。人脸识别算法计算唯一ID则是研究院团队提供的算法库libC.a。比对数据库中的ID --> 放行则是我们团队的开发内容。我们对外提供libA.so。

很明显我们,我们仅仅是做门禁业务开发,总不能把BSP团队或研究院的核心能力也提供给甲方吧?否则一定要加钱的。

但是我们该怎么做呢?因为我们知道,正常编译静态库libC.so的方式,肯定会将bsp和研究院提供的能力对外开放。客户集成时,是可以直接引用到libA.a和libB.a的对外接口。代码示例如下:

//a.c
extern int printf(char* ftm,...);
int a()
{
    printf("i'am liba.so");
    return 0;
}

//b.c
extern int printf(char* ftm,...);
int b()
{
    printf("i'am libb.so");
}

//c.c
extern int a(void);
extern int b(void);

int c()
{
    a();
    b();
    return 0;
}

编译:

yihua@ubuntu:~/test/1207$ gcc -c a.c
yihua@ubuntu:~/test/1207$ ar rcs -o libA.a a.o
yihua@ubuntu:~/test/1207$ gcc -c b.c
yihua@ubuntu:~/test/1207$ ar rcs -o libB.a b.o
yihua@ubuntu:~/test/1207$ gcc -FPIC -shared -o libC.so c.c -lA -lB -L.

符号关系:

如图所示,外部是可以通过libC.so去直接调用libA.a和libB.a的接口

如何解决这个问题呢?

链接器为我们提供了 -Wl,–exclude-libs 参数选项:隐藏静态库文件的符号。

我们加上编译选项再试试。

yihua@ubuntu:~/test/1207$
yihua@ubuntu:~/test/1207$ gcc -FPIC -shared -o libC.so c.c -L.  -Wl,--exclude-libs,ALL -lA -lB
yihua@ubuntu:~/test/1207$

符号关系:

由图可知,libA.a 和 libB.a的内部符号已经被隐藏了。完结,撒花~~~

-Wl,-Bsymbolic

工作场景:在工作中,我们无法避免的会依赖其它动态库。比如:我司开发的SDK libA.so底层用到了mqtt通信,因此会依赖开源库libpaho-mqtt3c.so,但是我司对内部源码做了一些定制化修改;同时,我们也依赖第三方供应商的SDK libB.so,并且他们内部也采用了mqtt协议通信,同样集成了mqtt开源库,也许他们也在内部做了定制化处理。

关系如下:

分析:

情况一:当admfotaApp运行时,链接器会根据它的动态库依赖关系,加载相应的动态库。而libpaho-mqtt3c.so仅会加载一次。之后再进行符号链接时,就会出现异常。—— 加载库了非预期的库

情况二:会导致mqtt开源库代码段被加载两次,也就是说进程中会有两套相同的符号和对应的代码段。libB.so和libA.so在进行符号链接时,可能就会出现异常。 —— 符号链接时出现问题

示例代码如下:

//a.c
extern int printf(const char* ftm,...);
int a()
{
    printf("i‘am OEM a\n");
}

int c()
{
    printf("i'am OEM c\n");
}

//b.c
extern int printf(const char* ftm,...);
extern int c(void);
extern int d(void);
int b()
{
    printf("i‘am abup a\n");
    c();
    d();
    return 0;
}

//c.c
extern int printf(const char* ftm,...);
int c()
{
    printf("i‘am abup c\n");
}

//d.c
extern int printf(const char* ftm,...);
int d()
{
    printf("i‘am abup d\n");
}

//main.c
extern int printf(const char* ftm,...);
extern int a(void);
extern int b(void);
int main()
{
        a();
        b();
        return 0;
}

编译如下:

gcc -c c.c
gcc -c d.c
ar -crs -o libC.a c.o d.o   // 生成静态库
gcc -FPIC -shared -o libB.so b.c -lC -L.    //从这可以看出,b.c期望时引用c.c中的c()
gcc -FPIC -shared -o libA.so a.c
gcc main.c -o main -lA -lB -lC -L.

运行:

由上可知:输出内容是非预期的。我们更新一下编译命令gcc main.c -o main -lB -lA -L.,运行结果如下:

链接库的顺序不一样,居然会有不一样的结果?这是为什么呢?有兴趣的朋友可以搜索:全局符号介入相关知识点。或参考我的一篇博客:全局符号介入引起的问题

针对该场景如何处理呢?

链接器中的-Wl,-Bsymbolic参数:告诉链接器强制使用本地的符号,也就是说,编译libB.so时,就确定符号地址。不需要等待运行时再链接。
比如我们在编译main.c时,增加该参数:

gcc -FPIC -shared -o libB.so b.c -lC -L. -Wl,-Bsymbolic
gcc main.c -o main -lA -lB -L.

输出:

完结撒花~~~。

有兴趣的同学可以通过反汇编查看增加-Wl,-Bsymbolic参数前后,libB.so的汇编内容。

总结

本文从两个实际存在的场景,向大家介绍了动态库生成过程中的一些特定需求。简单介绍了 -Wl,-Bsymbolic-Wl,–exclude-libs,ALL两个链接属性及使用方式。

当然很多其它常用的参数比如:-fvisibility=hidden-Wl,--whole-archive。有兴趣的朋友可以了解一下。

若我的内容对您有所帮助,还请关注我的公众号。不定期分享干活,剖析案例,也可以一起讨论分享。

我的宗旨:

踩完您工作中的所有坑并分享给您,让你的工作无bug,人生尽是坦途

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1292137.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IT外包对中小企业的独特优势

在竞争激烈的商业环境中,企业的发展稍有缓慢,就很有可能被竞争对手快速赶超、趁机抢占市场。一些中小企业为了更好地应对市场变化和提高自身竞争力,越来越多地转向了IT外包服务。相较于大型企业,中小企业在选择IT外包时能够获得一…

SSL证书代理

众所周知,SSL证书已经成为当下网络安全中不可或缺的一个环节,对于很多开发公司来说,给自己的客户提供SSL证书安全服务也是最为基础的。 但是目前市面上像阿里云之类的证书服务商对于开发公司需要的证书并没有太大的一个优惠政策,给…

一:C语言常见概念

一:C语言常见概念 1.认识C语言: ​ C语言是人和计算机交流的语言 ​ C语言是一门面向过程的语言,而C,Java,Python等是一门面向对象的语言 ​ 软件开发(项目):面向过程面向对象 …

python爬虫来抓取闲鱼二手机信息,小赚一笔

虽然海鲜市场现在已经不如以前了,但是还是可以捡漏的,省钱也是赚钱,最近正好有换机的准备,每天刷来刷去的浪费了好多时间,也会进入选择困难症。 参考了一些大神的思路写法,写了个简单抓取指定需求的爬虫代码…

微信服务号转订阅号的流程

服务号和订阅号有什么区别?服务号转为订阅号有哪些作用?很多小伙伴想把服务号改为订阅号,但是不知道改了之后具体有什么作用,今天跟大家具体讲解一下。首先我们知道服务号一个月只能发四次文章,但是订阅号每天都可以发…

JSP以监听生命周期为例 讲解监听器

好 最后 我们说说监听器 内容还是非常多的 这里 从老师哪里拿到的一个文案 大家可以查看具体内容 我们这里以监听声明周期为例 这边 我们在项目java模块下创建一个包 叫 listener 名字随便取 我们就这样 看着明显一点 然后 我们在下面创建一个java类 叫 test 因为是用来测试的…

一张图理解接口测试框架

测试框架先向测试数据库中插入测试数据(如:name”Tom“) 调用被测系统提供的接口(传参:name”Tom“) 从测试数据库中查到符合参数的数据 将查询到的数据组成Json格式,并返回给测试框架 提供…

坐标机械手配件有哪些?

直线模组是一种常见的机械传动装置,广泛应用于机械手等自动化设备中,在机械手中的主要作用是实现机械手的运动控制和定位。 直线模组具有高精度、高可靠性的特点,可以满足坐标机械手对运动精度和稳定性的要求,在坐标机械手的关节处…

详解线段树

前段时间写过一篇关于树状数组的博客树状数组,今天我们要介绍的是线段树,线段树比树状数组中的应用场景更加的广泛。这些问题也是在leetcode 11月的每日一题频繁遇到的问题,实际上线段树就和红黑树 、堆一样是一类模板,但是标准库…

Linux篇之在Centos环境下搭建Nvidia显卡驱动

一、前提条件 1、首先确认内核版本和发行版本,再确认显卡型号 uname -a // Linux localhost.localdomain 4.18.0-408.el8.x86_64 #1 SMP Mon Jul 18 17:42:52 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux1.2 cat /etc/redhat-release // CentOS Stream release 81.3…

logback日志框架使用

依赖引入 <dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.1.7</version> </dependency> 使用logback日志框架只需要引入以上即可&#xff0c;(我们平时使用较多的Slf4j…

获取Github Copilot的Token

可以在线提取出Github Copilot插件的Token&#xff0c;这样的话就可以把Token拿来做别的用处了&#xff0c;比如共享给其他人 Github Copilot是一款由GitHub和OpenAI合作开发的人工智能编程助手。它利用机器学习和自然语言处理技术&#xff0c;能够根据用户的输入自动生成代码…

使用DockerUI结合内网穿透工具轻松实现公网访问和管理docker容器

文章目录 前言1. 安装部署DockerUI2. 安装cpolar内网穿透3. 配置DockerUI公网访问地址4. 公网远程访问DockerUI5. 固定DockerUI公网地址 前言 DockerUI是一个docker容器镜像的可视化图形化管理工具。DockerUI可以用来轻松构建、管理和维护docker环境。它是完全开源且免费的。基…

Python小案例:while练习题

目录 while练习题&#xff1a;1、存款多少年能翻倍2.小球坠落长度计算3、猴子吃桃4、计算&#xff1a;1-23-4...99-100的和 while练习题&#xff1a; 1、存款多少年能翻倍 1万本金&#xff0c;年利息&#xff1a;0.0325&#xff0c;求连本带息多少年能翻倍 解析&#xff1a;…

IP地址定位技术的原理与应用

在当今的数字化时代&#xff0c;我们的在线活动每时每刻都在生成大量数据。其中&#xff0c;IP地址作为网络设备在互联网上的唯一标识&#xff0c;提供了一种独特的方式来追踪和定位这些活动。本文将深入探讨IP定位技术的原理及其在现实生活中的应用。 IP定位技术的原理 IP地址…

【Hive】——概述

1 什么是Hive 2 Hive 优点 3 Hive和Hadoop 的关系 4 映射信息记录 5 SQL语法解析、编译 Hive能将一个文件映射成为一张表&#xff0c;文件和表之间的关系称为映射 Hive的功能职责是将SQL语法解析编译成为MapReduce 6 Hive 架构 6.1 分析 6.2 架构图 6.3 用户接口 6.4 元数据存…

通信线缆是什么

通信线缆 电子元器件百科 文章目录 通信线缆前言一、通信线缆是什么二、通信线缆的类别三、通信线缆应用实例四、通信线缆的作用原理总结前言 每种线缆都有其特定的特性和用途。通信线缆起到连接和传输信号的作用,是实现通信和数据传输的重要组成部分。 一、通信线缆是什么 …

评论送书:一本书讲透Java线程:原理与实践

摘要&#xff1a;互联网的每一个角落&#xff0c;无论是大型电商平台的秒杀活动&#xff0c;社交平台的实时消息推送&#xff0c;还是在线视频平台的流量洪峰&#xff0c;背后都离不开多线程技术的支持。在数字化转型的过程中&#xff0c;高并发、高性能是衡量系统性能的核心指…

春风十里不如你——掌握Spring Boot的常用关键注解

引言 在Java的世界里&#xff0c;Spring Boot以其简化的配置和开箱即用的特性&#xff0c;成为了构建现代微服务和企业级应用的首选框架。Spring Boot的注解是这一切的核心。在本文中&#xff0c;我们将深入探讨最常用的Spring Boot注解&#xff0c;帮助你轻松驾驭Spring Boot…

AMEYA360分析兆易创新GD32A490系列车规级MCU

兆易创新GigaDevice今日宣布&#xff0c;正式推出全新GD32A490系列高性能车规级MCU&#xff0c;以高主频、大容量、高集成和高可靠等优势特性紧贴汽车电子开发需求&#xff0c;适用于车窗、雨刷、智能车锁、电动座椅等BCM车身控制系统&#xff0c;以及仪表盘、娱乐影音、中控导…