当前位置: 首页 > news >正文

动态库(二) 创建动态库

文章目录

    • 创建动态库
    • 设计动态库
      • 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::stringstd::list的命令空间进行了更改,相应的符号也变化了。从std::string变为了std::__cx11:string,从std::list变为了std::__cx11::string
所以如果依赖库所使用的编译器是gcc5.1之前的版本,而客户程序使用的是gcc5.1版本,那么在链接时,std::stringstd::list的因为两个版本的编译器生成的符号不一样,而会导致链接失败。有两个解决方法:

  1. 都用同一版本的gcc编译。
  2. 使用_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下符号的可见性:

  1. -fvisibility=hidden

在编译动态库时加入该编译选项,那么所有的动态符号都置为对外不可见,任何尝试链接该动态库的客户程序将无法访问这些符号。

  1. __attribute__((visibility("<default | hidden>")))

通过在函数前面使用编译属性修饰,可以指示链接器运行或禁止对外提供该符号。

  1. 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中创建动态库通过如下过程完成&#xff1a; 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、添加水印 添加水印的原理是调低两张图片的透明度&#xff0c;然后叠加起来。公式如下&#xff1a; dst src1 * opacity src2 * (1 - opacity) gamma; opacity是透明度&a…...

Flume基操

Flume概述 Flume 定义 Flume 是 Cloudera 提供的一个高可用的&#xff0c;高可靠的&#xff0c;分布式的海量日志采集、聚合和传输的系统。Flume 基于流式架构&#xff0c;灵活简单。 Flume最主要的作用就是&#xff0c;实时读取服务器本地磁盘的数据&#xff0c;将数据写入到…...

图文详解红黑树,还有谁不会?

前言在MySQL中&#xff0c;无论是Innodb还是MyIsam&#xff0c;都使用了B树作索引结构(这里不考虑hash等其他索引)。本文将从最普通的二叉查找树开始&#xff0c;逐步说明各种树解决的问题以及面临的新问题&#xff0c;从而说明MySQL为什么选择B树作为索引结构。目录一、二叉查…...

多目标遗传算法NSGA-II原理详解及算法实现

在接触学习多目标优化的问题上&#xff0c;经常会被提及到多目标遗传算法NSGA-II&#xff0c;网上也看到了很多人对该算法的总结&#xff0c;但真正讲解明白的以及配套用算法实现的文章很少&#xff0c;这里也对该算法进行一次详解与总结。会有侧重点的阐述&#xff0c;不会针对…...

Spark 键值对RDD的操作

键值对RDD&#xff08;Pair RDD&#xff09;是指每个RDD元素都是&#xff08;key&#xff0c;value&#xff09;键值对类型&#xff0c;是一种常见的RDD类型&#xff0c;可以应用于很多的应用场景。 一、 键值对RDD的创建 键值对RDD的创建主要有两种方式&#xff1a; &#x…...

【SpringCloud】SpringCloud详解之Feign远程调用

目录前言SpringCloud Feign远程服务调用一.需求二.两个服务的yml配置和访问路径三.使用RestTemplate远程调用(order服务内编写)四.构建Feign(order服务内配置)五.自定义Feign配置(order服务内配置)六.Feign配置日志(oder服务内配置)七.Feign调优(order服务内配置)八.抽离Feign前…...

文档团队怎样使用GIT做版本管理

有不少小型文档团队想转结构化写作和发布&#xff0c;但是因为有限的IT技能和IT资源而受阻。本文为这样的小型文档团队而准备&#xff0c;描述怎样使用Git做内容的版本管理。 - 1 - 为什么需要版本管理 当一个团队进行协同创作内容时&#xff0c;有以下需要&#xff1a; 在对…...

【java】Java中-> 是什么意思?

先看一个例子 EventQueue.invokeLater(() -> {JFrame frame new ImageViewerFrame();frame.setTitle("ImageViewer");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);}); // 上面那一段可以看成如下: EventQueue.invokeLater(ne…...

网络类型部分实验

1.实验思路&#xff1a; 首先用DHCP 给四台PC配置上地址&#xff0c;配置成功后 其次底层IP地址的下发完成的同时&#xff0c;进行检测是否可以ping通 接着进行R1和R5之间使用PPP的PAP认证&#xff0c;R5为主认证方 主认证方ISP 被认证方R1 其次进行R2和R5使用PPP的CHAP认证&am…...

java教程--函数式接口--lambda表达式--方法引用

函数式接口 介绍 jdk8新特性&#xff0c;只有一个抽象方法的接口我们称之为函数接口。 FunctionalInterface ​ JDK的函数式接口都加上了FunctionalInterface 注解进行标识。但是无论是否加上该注解只要接口中只有一个抽象方法&#xff0c;都是函数式接口。 如在Comparato…...

java——代理

什么是代理&#xff1a; 给目标对象一个代理对象&#xff0c;由代理对象控制着对目标对象的引用 为什么使用代理&#xff1a; ①&#xff1a;功能增强&#xff1a;通过代理业务对原有业务进行增强 ②&#xff1a;用户只能同行过代理对象间接访问目标对象&#xff0c;防止用…...

kubernetes中service探讨

文章目录序言kube-proxy代理模型userspace代理模型iptables代理模型ipvs代理模型修改代理模型Service资源类型ClusterIPNodePortLoadBalancerExternalName应用Service资源应用ClusterIP Service资源应用NodePort Service资源应用LoadBalancer Service资源外部IP序言 在Kuberne…...

Python3实现“美颜”功能

导语利用Python实现美颜。。。这是之前在GitHub上下载的一个项目。。。似乎有些日子了。。。所以暂时找不到原项目的链接了。。。今天抽空看了下它源代码的主要思想&#xff0c;似乎挺简单的。。。于是决定用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:集群管理

在今天的文章中&#xff0c;我们应该学习如何管理我们的集群。 备份和分片分配是我们应该能够执行的基本任务。 分片分配过滤 Elasticsearch 将索引配到一个或多个分片中&#xff0c;我们可以将这些分片保存在特定的集群节点中。 例如&#xff0c;假设你有多个数据集群节点&am…...

华为OD机试题 - 非严格递增连续数字序列(JavaScript)| 机考必刷

更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解:非严格递增连续数字序列题目输入输出示例一输入输出说明Code解题…...

lc23. 合并K个升序链表

题目描述给你一个链表数组&#xff0c;每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。示例 1&#xff1a;输入&#xff1a;lists [[1,4,5],[1,3,4],[2,6]]输出&#xff1a;[1,1,2,3,4,4,5,6]解释&#xff1a;链表数组如下&…...

Java笔记029-泛型

泛型泛型的理解和好处看一个需求请编写程序&#xff0c;在ArrayList中&#xff0c;添加3个Dog对象Dog对象含有name和age&#xff0c;并输出name和age(要求使用getXxx)先用传统的方法来解决->引出泛型package com15.generic;import java.util.ArrayList;/*** author 甲柒* ve…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...