当前位置: 首页 > 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…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...