动态库(二) 创建动态库
文章目录
- 创建动态库
- 设计动态库
- ABI兼容
- 动态符号的可见性
- 示例
- 控制符号可见性
- 通过`-fvisibility`
- 通过`strip`工具删除指定符号
创建动态库
在Linux中创建动态库通过如下过程完成:
gcc -fPIC -c first.c second.c
gcc -shared first.o second.o -o libdynamiclib.so
按照Linux的惯例,动态以lib作为前缀,并以.so作为文件扩展名。
需要两个必要的选项
fPIC,告诉编译器使用位置无关代码技术。
fPIC本质上是用于辅助加载器加载程序,可以使多个进程可以无缝映射到已加载动态库的内存映射中。
有一点还需要注意的是:如果静态库是链接到动态库中,那么就在编译静态库时就必须使用**-fPIC**选项编译。否则链接器会报错。
shared,告诉编译器生成动态库。
设计动态库
动态库本质上是一种代码复用的技术,动态库提供功能接口给客户程序,而使客户程序不必关心内部实现细节。
对动态库的,需要特别注意ABI兼容和接口的可见效性。
ABI兼容
ABI的全称是Application Binary Interfance,应用程序二进制接口。
ABI与API相同的概念,但是两者的侧重不一样。API是指代码层面的接口,在代码层面客户程序关注所依赖库,提供的接口定义,返回值等特征。而ABI是指客户程序和所依赖的库被编译成二进制文件后,在链接器,加载器层面对应的符号是否匹配。
其实站在开发者的角度,认为API与ABI是应该等价的,代码里的接口匹配,难道编译后,二进制文件中的接口对应的符号还不匹配吗?是的,对C++来说是要需要关注这些问题。
ABI兼容也是C++饱受诟病的问题,它是C++的复杂性造成,并且因为各个编译器厂商没有统一ABI标准而加剧。同一套代码,使用不同的编译器而导致无法链接到客户程序。
最被广泛认识的造成C++ ABI兼容的问题是:符号修饰。
C++引入的命名空间,类,私有,公有等一些面向对象的语法。在编码层面这些机制很好的保证了模块的划分及命名冲突。但是在编译层面也引入了符号命名的复杂性,因为在变成二进制符号时,可没这些概念,但是编译器也需要保证符号唯一,所以会有一套符号命令规则。
这套规则并没有标准化,各个厂家间,甚至同一厂家不同的版本间也可能不兼容。
gcc中有个比较典型的ABI兼容情况,也是我们比较容易遇到的,从GCC 5.1开始为了兼容C++ 11标准,对std::string和 std::list的命令空间进行了更改,相应的符号也变化了。从std::string变为了std::__cx11:string,从std::list变为了std::__cx11::string。
所以如果依赖库所使用的编译器是gcc5.1之前的版本,而客户程序使用的是gcc5.1版本,那么在链接时,std::string和std::list的因为两个版本的编译器生成的符号不一样,而会导致链接失败。有两个解决方法:
- 都用同一版本的gcc编译。
- 使用
_GLIBCXX_USE_CXX11_ABI宏
加入编译选项 -D_GLIBCXX_USE_CXX11_ABI=1/0
所以容易遇到ABI兼容问题的场景:
- 客户程序与所依赖的库,使用的是不同的编译器编译
比如在Linux下客户程序用的gcc,而所依赖的库使用clang编译。
- 客户程序与所依赖的库,使用同一编译器的不同版本
比如我们上面所举的GCC的例子。
当然我们也并不需要掌握ABI兼容问题的细节,只要我们做到以下几点就可以避免绝大多数ABI兼容问题:
- 客户程序与所依赖的库,尽量用同一个编译器同一版本编译
- 以C语言风格设计接口
在C语言中并没有符号修饰,对调用约定,内存布局,编译器也做到了统一。
通过extern "C"让编译器生成不带修辞的符号名。
#ifdef __cplusplus
extern "C"
{int Function(int x,int y);
}
#endif //__cplusplus
接口中的数据类型也使用C语言的类型。比如将std::string替换为char[]。如果是自定义类则改为结构体。在C++中是兼容C语言的类型内存布局的。在接口实现时,可以使用C++实现。
动态符号的可见性
动态库应该只向客户程序暴露所需求的接口。所以需要控制接口对外的可见性。
**在Linux中所有符号默认都是外部可见的,**可以通过以下几种方法来控制Linux下符号的可见性:
-fvisibility=hidden
在编译动态库时加入该编译选项,那么所有的动态符号都置为对外不可见,任何尝试链接该动态库的客户程序将无法访问这些符号。
__attribute__((visibility("<default | hidden>")))
通过在函数前面使用编译属性修饰,可以指示链接器运行或禁止对外提供该符号。
strip工具
直接通过strip工具抹除动态库中的某个符号。
示例
如下是两个简单的示例代码文件:
void function1(void);
void function2(void);
void function3(void);
#include <stdio.h>
#include "sharedLib.h"
void function1(void) {printf("function1\n");
}void function2(void) {printf("function2\n");
}void function3(void) {printf("function3\n");
}
#include "sharedLib.h"
int main() {function1();function2();function3();
}
gcc -fPIC -shared sharedLib.c -o libsharedLib.so
生成动态libsharedLib.so
通过nm libsharedLib.so查看它的符号,如下:
00000000000006f5 T function1
0000000000000707 T function2
0000000000000719 T function3
符号的类型都是T类型,表示位于代码区的符号,对外可见。
链接动态库,生成test可执行程序
gcc main.c -L. -lsharedLib -o test
在运行test程序时,需要设置LD_LIBRARY_PATH环境变量,用于指定libsharedLib.so的路径。使加载器能找到它。
控制符号可见性
通过-fvisibility
- 在编译时添加
-fvisibility编译选项,让所有符号默认不可见
-fvisibility=hidden -fvisibility-inlines-hidden
再通过该编译选项将libsharedLib.so的接口都改为不可见。
gcc -fPIC -shared sharedLib.c -fvisibility=hidden -fvisibility-inlines-hidden -o libsharedLib.so
通过nm libsharedLib.so查看符号,如下
0000000000000675 t function1
0000000000000687 t function2
0000000000000699 t function3
符号类型变为t,表示为内部符号,对外不可见。
那么链接libsharedLib.so库时就会报错,如下:
gcc main.c -L. -lsharedLib -o testmain.c:(.text+0x5):对‘function1’未定义的引用
main.c:(.text+0xa):对‘function2’未定义的引用
main.c:(.text+0xf):对‘function3’未定义的引用
- 通过
__attribute__((visibility("default")))修饰需要导出的符号
将符号改为不可见,同时通过改属性,设置某个方法为可见。设置function1为可见。
#include <stdio.h>
#include "sharedLib.h"
#define FUNC_EXPORT __attribute__((visibility("default")))
void FUNC_EXPORT function1(void) {printf("function1\n");
}void function2(void) {printf("function2\n");
}void function3(void) {printf("function3\n");
}
将function1设置为了可见,重新编译。
gcc sharedLib.c -fPIC -shared -fvisibility=hidden -fvisibility-inlines-hidden -o libsharedLib.so
通过nm libsharedLib.so查看其符号,如下:
0000000000000675 T function1
0000000000000687 t function2
0000000000000699 t function3
function1变为了可见,其它两个都是不可见。
通过strip工具删除指定符号
strip --strip-symbol 符号名,直接通过strip命令简单粗暴的删除动态库中的符号。当然它也可以删除静态库中的符号。
strip --strip-symbol function1 libsharedLib.so,删除libsharedLib.so中的符号function1。
相关文章:
动态库(二) 创建动态库
文章目录创建动态库设计动态库ABI兼容动态符号的可见性示例控制符号可见性通过-fvisibility通过strip工具删除指定符号创建动态库 在Linux中创建动态库通过如下过程完成: gcc -fPIC -c first.c second.c gcc -shared first.o second.o -o libdynamiclib.so 按照Li…...
opencv加水印
本文介绍opencv给图片加水印的方法。 目录1、添加水印1.1、铺满1.2、在指定区域添加1.3、一比一铺满1、添加水印 添加水印的原理是调低两张图片的透明度,然后叠加起来。公式如下: dst src1 * opacity src2 * (1 - opacity) gamma; opacity是透明度&a…...
Flume基操
Flume概述 Flume 定义 Flume 是 Cloudera 提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统。Flume 基于流式架构,灵活简单。 Flume最主要的作用就是,实时读取服务器本地磁盘的数据,将数据写入到…...
图文详解红黑树,还有谁不会?
前言在MySQL中,无论是Innodb还是MyIsam,都使用了B树作索引结构(这里不考虑hash等其他索引)。本文将从最普通的二叉查找树开始,逐步说明各种树解决的问题以及面临的新问题,从而说明MySQL为什么选择B树作为索引结构。目录一、二叉查…...
多目标遗传算法NSGA-II原理详解及算法实现
在接触学习多目标优化的问题上,经常会被提及到多目标遗传算法NSGA-II,网上也看到了很多人对该算法的总结,但真正讲解明白的以及配套用算法实现的文章很少,这里也对该算法进行一次详解与总结。会有侧重点的阐述,不会针对…...
Spark 键值对RDD的操作
键值对RDD(Pair RDD)是指每个RDD元素都是(key,value)键值对类型,是一种常见的RDD类型,可以应用于很多的应用场景。 一、 键值对RDD的创建 键值对RDD的创建主要有两种方式: &#x…...
【SpringCloud】SpringCloud详解之Feign远程调用
目录前言SpringCloud Feign远程服务调用一.需求二.两个服务的yml配置和访问路径三.使用RestTemplate远程调用(order服务内编写)四.构建Feign(order服务内配置)五.自定义Feign配置(order服务内配置)六.Feign配置日志(oder服务内配置)七.Feign调优(order服务内配置)八.抽离Feign前…...
文档团队怎样使用GIT做版本管理
有不少小型文档团队想转结构化写作和发布,但是因为有限的IT技能和IT资源而受阻。本文为这样的小型文档团队而准备,描述怎样使用Git做内容的版本管理。 - 1 - 为什么需要版本管理 当一个团队进行协同创作内容时,有以下需要: 在对…...
【java】Java中-> 是什么意思?
先看一个例子 EventQueue.invokeLater(() -> {JFrame frame new ImageViewerFrame();frame.setTitle("ImageViewer");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);}); // 上面那一段可以看成如下: EventQueue.invokeLater(ne…...
网络类型部分实验
1.实验思路: 首先用DHCP 给四台PC配置上地址,配置成功后 其次底层IP地址的下发完成的同时,进行检测是否可以ping通 接着进行R1和R5之间使用PPP的PAP认证,R5为主认证方 主认证方ISP 被认证方R1 其次进行R2和R5使用PPP的CHAP认证&am…...
java教程--函数式接口--lambda表达式--方法引用
函数式接口 介绍 jdk8新特性,只有一个抽象方法的接口我们称之为函数接口。 FunctionalInterface JDK的函数式接口都加上了FunctionalInterface 注解进行标识。但是无论是否加上该注解只要接口中只有一个抽象方法,都是函数式接口。 如在Comparato…...
java——代理
什么是代理: 给目标对象一个代理对象,由代理对象控制着对目标对象的引用 为什么使用代理: ①:功能增强:通过代理业务对原有业务进行增强 ②:用户只能同行过代理对象间接访问目标对象,防止用…...
kubernetes中service探讨
文章目录序言kube-proxy代理模型userspace代理模型iptables代理模型ipvs代理模型修改代理模型Service资源类型ClusterIPNodePortLoadBalancerExternalName应用Service资源应用ClusterIP Service资源应用NodePort Service资源应用LoadBalancer Service资源外部IP序言 在Kuberne…...
Python3实现“美颜”功能
导语利用Python实现美颜。。。这是之前在GitHub上下载的一个项目。。。似乎有些日子了。。。所以暂时找不到原项目的链接了。。。今天抽空看了下它源代码的主要思想,似乎挺简单的。。。于是决定用Python3自己复现一下。。。T_T感觉还是挺有趣的。。。Just have a tr…...
【创建“待选项”按钮02计算坐标 Objective-C语言】
一、之前,我们已经把“待选项”按钮,创建好了,但是唯一的问题是,坐标都是一样的,所以都显示在一起了 1.下面,我们来设置一下,这些“待选项”按钮的坐标, 现在,“待选项”按钮的坐标,是不是都在同一个位置啊, 回忆一下,这个待选项按钮,是怎么生成的, 首先,是在…...
自组织( Self-organization),自组织临界性(Self-organized criticality)
文章目录1. 自组织概述原则历史按领域物理化学生物学2. 自组织临界性概述3. 自组织临界性的特征4. 自组织临界模型5. 自然界中的自组织临界6. 自组织临界性和优化7. 自组织临界性的控制7.1 方案7.2 应用1. 自组织 wiki: Self-organization 图 200 C 水热处理过程中微米级 Nb3O…...
Elasticsearch:集群管理
在今天的文章中,我们应该学习如何管理我们的集群。 备份和分片分配是我们应该能够执行的基本任务。 分片分配过滤 Elasticsearch 将索引配到一个或多个分片中,我们可以将这些分片保存在特定的集群节点中。 例如,假设你有多个数据集群节点&am…...
华为OD机试题 - 非严格递增连续数字序列(JavaScript)| 机考必刷
更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解:非严格递增连续数字序列题目输入输出示例一输入输出说明Code解题…...
lc23. 合并K个升序链表
题目描述给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。示例 1:输入:lists [[1,4,5],[1,3,4],[2,6]]输出:[1,1,2,3,4,4,5,6]解释:链表数组如下&…...
Java笔记029-泛型
泛型泛型的理解和好处看一个需求请编写程序,在ArrayList中,添加3个Dog对象Dog对象含有name和age,并输出name和age(要求使用getXxx)先用传统的方法来解决->引出泛型package com15.generic;import java.util.ArrayList;/*** author 甲柒* ve…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
flow_controllers
关键点: 流控制器类型: 同步(Sync):发布操作会阻塞,直到数据被确认发送。异步(Async):发布操作非阻塞,数据发送由后台线程处理。纯同步(PureSync…...
数据库——redis
一、Redis 介绍 1. 概述 Redis(Remote Dictionary Server)是一个开源的、高性能的内存键值数据库系统,具有以下核心特点: 内存存储架构:数据主要存储在内存中,提供微秒级的读写响应 多数据结构支持&…...
负载均衡器》》LVS、Nginx、HAproxy 区别
虚拟主机 先4,后7...
