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

引言

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

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.rhkb.cn/news/212042.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LinkedList详解

LinkedList详解 LinkedList是List接口的一个主要的实现类之一&#xff0c;基于链表的实现。以java8为例来了解一下LinkedList的源码实现 继承关系 public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>,…

第十五届蓝桥杯模拟赛B组(第二期)C++

前言&#xff1a; 第一次做蓝桥模拟赛的博客记录&#xff0c;可能有很多不足的地方&#xff0c;现在将第十五届蓝桥杯模拟赛B组&#xff08;第二期&#xff09;的题目与代码与大家进行分享&#xff0c;我是用C做的&#xff0c;有好几道算法题当时自己做的也是一脸懵&#xff0c…

DELL EMC unity 存储系统日志收集方法

对于一些非简单的硬件故障&#xff0c;解决故障最有效、最快速的方法就是收集日志&#xff0c;而不是瞎搞。常见的乱搞方法就是 1. reimage系统‘ 2. 更换控制器&#xff1b;3&#xff0c; 重启。 本文详细介绍了图形界面GUI和命令行CLI下如何收集DELL EMC Unity日志的方法和常…

WPS导出的PDF比较糊,和原始的不太一样,将带有SVG的文档输出为PDF

一、在WPS的PPT中 你直接输出PDF可能会导致一些问题&#xff08;比如照片比原来糊&#xff09;/ 或者你复制PPT中的图片到AI中类似的操作&#xff0c;得到的照片比原来糊&#xff0c;所以应该选择打印-->高级打印 然后再另存为PDF 最后再使用AI打开PDF文件再复制到你想用…

挑选数据可视化工具:图表类型、交互功能与数据安全

作为一名数据分析师&#xff0c;我经常需要使用各种数据可视化工具来将数据以直观、清晰的方式呈现出来&#xff0c;以便更好地理解和分析。在市面上的众多可视化工具中&#xff0c;我根据实际需求和项目特点进行选择。本文将从以下几个角度对市面上的数据可视化工具进行对比&a…

flutter开发实战-轮播Swiper更改Custom_layout样式中Widget层级

flutter开发实战-轮播Swiper更改Custom_layout样式中Widget层级 在之前的开发过程中&#xff0c;需要实现卡片轮播效果&#xff0c;但是卡片轮播需要中间大、两边小一些的效果&#xff0c;这里就使用到了Swiper。具体效果如视频所示 添加链接描述 这里需要的效果是中间大、两边…

安装postgresql驱动及python使用pyodbc指定postgresql驱动调用postgresql

注&#xff1a;Python解释器版本(32位/64位)和postgresql驱动版本(32位/64位)需一致。 一、安装postgresql驱动 https://www.postgresql.org/ftp/odbc/versions/msi/ &#xff08;1&#xff09;32位&#xff1a; &#xff08;2&#xff09;64位&#xff1a; 双击安装。全程默…

高精度加法,减法,乘法,除法(上)(C语言)

前言 加&#xff0c;减&#xff0c;乘&#xff0c;除这些运算我们自然信手捏来&#xff0c;就拿加法来说&#xff0c;我们要用c语言编程算ab的和&#xff0c;只需让sum ab即可&#xff0c;可是这是局限的&#xff0c;我们都知道int的表示的最大值为2147483647&#xff08;32位…

2023.12.3 分布式SQL查询引擎-Presto

目录 目录 1.Prosto简介 Apache Hadoop-MapReduce Apache Hive 2.Presto的优缺点 3.个人自用启动服务 个人自用启动服务 3.Presto的架构 4.presto和hive的区别 5.presto优化 6.Presto-内存调优 1.Prosto简介 Apache Hadoop-MapReduce 优点&#xff1a;统一、通用、简…

C - 语言->内存函数

目录 系列文章目录 前言 1. memcpy使⽤和模拟实现 1.2 memcpy函数的模拟实现: 2. memmove 使⽤和模拟实现 2.1memmove的模拟实现&#xff1a; 3. memset 函数的使⽤ 4. memcmp 函数的使⽤ 系列文章目录 ✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff…

论文精读 MOG 埃里克·格里姆森

Adaptive background mixture models for real-time tracking 用于实时跟踪的自适应背景混合模型 1999年的MOG&#xff0c;作者是麻省理工学院前校长——埃里克格里姆森&#xff08;W. Eric L. Grimson&#xff09;。 目录 摘要 结论 1 介绍 1.1 以往的工作和现在的缺点 …

学生成绩管理系统(Java)

开发环境: Windows 11 IDEA 2021.3.3 需求: package com.it.neu;import java.util.ArrayList; import java.util.Scanner;import static java.time.Clock.system;class Student { //创建学生类private String Stu_name;private String Stu_id;public Student(String id, S…

履带吊,笔记

0.前言 履带吊使用了与传统的门桥式起重机不同的技术路线。因为它是移动式设备&#xff0c;所以它的动力是燃油发动机。为了精确调控升降。它的整套动力系统似乎采用了某种液压传动系统。履带吊国内也有生产商。但是下文中&#xff0c;还是从国外的一款产品说起。这款产品的pd…

Android蓝牙协议栈fluoride(二) - 软件框架

概述 fluoride 协议栈在整个软件框架中作为一个中间件的角色&#xff0c;向上对接APP&#xff0c;向下对接蓝牙芯片。fluoride采用C语言实现&#xff0c;与APP(Jave)通信采用JNI机制&#xff1b;与蓝牙芯片通信使用HCI硬件接口&#xff08;HCI软件协议参考蓝牙核心规范&#x…

Linux环境搭建(Ubuntu22.04)+ 配置共享文件夹(Samba)

Linux开发环境准备 搭建Linux开发环境所需要的软件如下&#xff1a; VMware虚拟机&#xff1a;用于运行Linux操作系统的虚拟机软件之一&#xff0c;VMware下载安装在文章中不做说明&#xff0c;可自行百度谢谢Ubuntu光盘镜像&#xff1a;用于源代码编译&#xff0c;有闲置计算…

Docker部署开源分布式任务调度系统DolphinScheduler与远程访问办公

文章目录 前言1. 安装部署DolphinScheduler1.1 启动服务 2. 登录DolphinScheduler界面3. 安装内网穿透工具4. 配置Dolphin Scheduler公网地址5. 固定DolphinScheduler公网地址 前言 本篇教程和大家分享一下DolphinScheduler的安装部署及如何实现公网远程访问&#xff0c;结合内…

Qt OpenCV 学习(一):环境搭建

对应版本 Qt 5.15.2OpenCV 3.4.9MinGW 8.1.0 32-bit 1. OpenCV 下载 确保安装 Qt 时勾选了 MinGW 编译器 本文使用 MinGW 编译好的 OpenCV 库&#xff0c;无需自行编译 确保下载的 MinGW 和上述安装 Qt 时勾选的 MinGW 编译器位数一致&#xff0c;此处均为 x86/32-bit下载地址…

Python语言基础知识(一)

文章目录 1、Python内置对象介绍2、标识符与变量3、数据类型—数字4、数据类型—字符串与字节串5、数据类型—列表、元组、字典、集合6、运算符和表达式7、运算符和表达式—算术运算符8、运算符和表达式—关系运算符9.1、运算符和表达式— 成员测试运算符in9.2、运算符和表达式…

C++中单引号‘‘和双引号““的区别

操作系统&#xff1a;Windows 10 IDE&#xff1a;CLion 单引号&#xff1a;表示一个字符&#xff0c;例如 a 双引号""&#xff1a;表示一个字符串&#xff0c;例如 "a" 在C中&#xff0c;使用双引号可以方便地创建字符串&#xff0c;而使用单引号可以方便…