静态链接库与动态链接库

news2024/10/7 8:31:33

静态链接库与动态链接库的区别

静态链接库:在项目中引用了库函数,编译时链接器会将引用的函数代码或变量,链接到可执行文件里,和可执行程序组装在一起

动态链接库:在编译阶段不参与链接,不会和可执行文件组装在一起,在程序运行时才被加载到内存参与链接,加载到内存的动态链接库可以被多个运行的程序共享

静态链接库的制作

使用ar命令制作静态库时,一些常用的参数:

  •  -c:禁止在创建库时产生的正常消息。
  • -r:如果指定的文件已经在库中存在,则替换它。
  • -s:无论库是否更新都强制重新生成新的符号表。
  • -d:从库中删除指定的文件。
  • -o:对压缩文档成员进行排序。
  • -q:向库中追加指定文件。 
  • -t:打印库中的目标文件。
  • -x:解压库中的目标文件。编译器是以源文件为单位编译程序的,链接器在链接过程中逐个对目标文件进行分解组装

r若想把test.c文件打包成一个库,然后在main.c中调用该库中的函数

gcc -c test.c  //生成可重定位文件test.o
ar rcslibtest.a test.o    //打包test.o  为静态库
gcc main.c -L . -l test    //-L  静态链接
./a.out

编译器是以源文件为单位编译程序的,链接器在链接过程中逐个对目标文件进行分解组装,为了减少内存占用,尽量单独使用一个源文件实现一个函数

在一个多任务环境中,当多个进程并发运行时,内存中有大量重复的指令代码,这时动态链接就开始低调登场了。

动态链接库的制作

动态链接对静态链接做了一些优化:对一些公用的代码,如库,在链接期间暂不链接,而是推迟到程序运行时再进行链接。

动态链接的好处是节省了内存资源:加载到内存的动态链接库可以被多个运行的程序共享,使用动态链接可以运行更大的程序、更多的程序,升级也更加简单方便。

windows中为.dll    Linux中为.so

# gcc -fPIC -shared test.c libtest.so 
//PIC是Position-Independent Code的简写,即与地址无关的代码。加上-fPIC参数生成的指令,实现了代码与地址无关
//-shared 生成可共享文件
# gcc main.c libtest.so
# ./a.out

可执行文件a.out是采用动态链接生成的,所以在运行a.out之前,libtest.so这个动态链接库要放到/lib、/usr/lib等系统默认的库路径下,否则a.out就会动态链接失败,无法正常运行。

在Linux环境下,当我们运行一个程序时,操作系统首先会给程序fork一个子进程,接着动态链接器被加载到内存,操作系统将控制权交给动态链接器,让动态链接器完成动态库的加载和重定位操作,最后跳转到要运行的程序。

动态链接器本身也是一个动态库,即/lib/ld-linux.so文件。动态链接器被加载到内存后,会首先给自己重定位,然后才能运行。动态链接器解析可执行文件中未确定的符号及需要链接的动态库信息,将对应的动态库加载到内存,并进行重定位操作。这个过程其实和静态链接的重定位过程一样.定位结束后,程序中要引用的所有符号都有了地址和定义,动态链接器将控制权交给要执行的程序,跳转到该程序运行。

动态链接库加载到内存中的地址则是随机的,因为每一个可执行文件的大小不同,加载到内存后剩余的地址空间也不尽相同,动态链接库的地址要根据进程地址空间的实际空闲情况随机分配。

PIC 与地址无关的代码

如果想让我们的动态库放到内存的任何位置都可以运行,都可以被多个进程共享,一种比较好的方法是将我们的动态库设计成与地址无关的代码。

将指令中需要修改的部分(如对绝对地址符号的引用)分离出来,剩余的部分就和地址无关了,放到哪里都可以执行,而且可以被多个进程共享。需要被修改的指令(符号)和数据在每个进程中都有一个副本,互不影响各自的运行。

实现PIC(PIC是Position-Independent Code的简写,即与地址无关的代码)需要底层相关的技术支撑,不同的平台有不同的实现方式。实现代码与地址无关,在模块内部,对函数和全局变量的引用要避免使用绝对地址,一般可以使用相对跳转代替。以ARM平台为例,可以采用相对寻址来实现。

在动态库的设计中,对于模块内的符号相互引用,我们通过相对寻址很容易实现代码与地址无关。但是当动态库作为第三方模块被不同的应用程序引用时,库中的一些绝对地址符号(如函数名)将不可避免地被多次调用,需要重定位。动态库中的这些绝对地址符号,如何能做到同时被不同的应用程序引用呢?解决这个问题的核心思想其实也很简单:每个应用程序将引用的动态库(绝对地址)符号收集起来,保存到一个表中,这个表用来记录各个引用符号的地址。当程序在运行过程中需要引用这些符号时,可以通过这个表查询各个符号的地址。这个表被称为全局偏移表(Global Offset Table,GOT)。在一个可执行文件中,其引用的动态库中的绝对地址符号(如函数名)会被分离出来,单独保存到GOT表中,GOT表以section的形式保存在可执行文件中,这个表的地址在编译阶段就已经确定了。当程序运行需要引用动态库中的函数时,会将动态库加载到内存,根据动态库被加载到内存中的具体地址,更新GOT表中的各个符号(函数)的地址。等下次该符号被引用时,程序可以直接跳到GOT表查询该符号的地址,如果找到要调用的函数在内存中的实际地址,就可以直接跳过去执行了。因为GOT表在可执行文件中的位置是固定不变的,所以程序中访问GOT表的指令也是固定不变的,唯一需要变化的是:动态库加载到内存后,库中的各个函数的位置确定,在GOT表中实时更新各个符号在内存中的真实地址就可以了。这样做的好处是:在内存中只需要加载一份动态库,当不同的程序运行时,只要修改各自的GOT表,它们引用的符号都可以指向同一份动态库,就可以达到不同程序共享同一个动态库的目标了.

延时绑定

ARM相对寻址的本质其实就是寄存器间接寻址,只不过基址换成了PC而已,访问效率还是比较低的,包括程序运行之前的动态链接和重定位操作,也会对程序的及时响应和性能造成一定的影响。我们假设一个软件中有几百个地方使用了动态链接,如果把所有的动态库一次性全部加载到内存并一一对它们进行重定位,会耗费不少的时间。程序中存在大量的if-else分支,并不是所有的指令都能执行到,我们加载到内存的动态库可能根本就没有被调用到,这又会白白浪费内存空间。基于这个原因,可执行文件一般都采用延迟绑定:程序在运行时,并不急着把所有的动态库都加载到内存中并进行重定位。当动态库中的函数第一次被调用到时,才会把用到的动态库加载到内存中并进行重定位。这样做既节省了内存,又可以提高程序的运行速度,因此得到广泛应用。

 分析以下反汇编代码

指令代码中每一个使用动态链接的符号<x@plt>,都被保存在过程链接表(Procedure Linkage Table,PLT,以.plt为后缀)中。过程链接表其实就是一个跳转指令,它无法单独工作,要和GOT表相关联,协同工作。当程序中引用某个符号时,就会从过程链接表跳转到GOT表,跳到GOT表中对应的项。如当程序中第一次引用<printf@plt>符号时,会跳到GOT表的0x21010处。在0x21010处,存放的是动态链接库的地址0x10490;动态链接库加载printf()函数到内存,然后会将printf()函数在内存中的实际地址保存在0x21010处,再将控制权交给printf()函数执行。等程序第二次调用printf()函数时,再次通过PLT表跳到GOT表的0x21010处,因为此时该地址上保存的是printf()函数在内存中的实际地址,所以就可以直接跳转过去执行了。

找到main()函数中调用add的代码部分(第10624行),我们可以看到:调用add的指令跳到了0x104a4<add@plt>处执行。在0x104a4地址处,我们看到这里并不是add()函数实现的地方,而是一个跳转命令,跳到了GOT表中地址为0x2100c的地方。一般情况下,GOT表中的每一项存放的都是符号的真实地址,但此时因为add第一次被调用,相应的动态库还没有加载到内存中,需要调用动态链接器去加载add的动态库,所以此时大家可以看到GOT表中每一项都是相同的值:0x10490。在0x10490地址处是一个跳转指令,跳转到动态链接器去执行,动态链接器的入口地址保存在GOT表的0x21008~0x2100b处。动态链接器的主要工作就是加载动态库到内存中并进行重定位操作:把add动态库加载到内存中,然后将add的实际地址更新到GOT表中保存add地址的那一项0x2100c地址处。此时在GOT表的0x2100c处保存的不再是默认的动态链接器地址0x10490,而是add()函数加载到内存中的实际地址。等第二次再调用add()函数时,就可以根据GOT表中的实际地址直接跳过去执行了。

插件的工作原理

很多软件为了扩展方便,具备通用性,普遍都支持插件机制:主程序的逻辑功能框架不变,各个具体的功能和业务以动态链接库的形式加载进来。这样做的好处是软件发布以后不用重新编译,可以直接通过插件的形式来更新功能,实现软件升值。插件的本质其实就是共享动态库,只不过组装的形式比较复杂。

Linux提供了专门的系统调用接口,支持显式加载和引用动态链接库,常用的系统调用API如下。

void *dlopen (const char *filename, int flag);
void *Handle = dlopen("./libtest.so",RTLD_LAZY);
void *dlsym(void *handle, char *symbol);
void (* funcp) (int , int);
funcp = (void(*)(int,int)) dlsym(Handle,"myfunc");
int dlclose(void *Handle); //关闭动态链接库,将加载到内存的共享库的引用计数减一,当引用计数为0时,该动态共享库便会从系统中被卸载。

dlopen() 函数返回的是一个void*类型的操作句柄,我们通过这个句柄就可以操作显式加载到内存中的动态库。函数的第一个参数是要打开的动态链接库,第二个参数是打开标志位,经常使用的标记位有如下几种。

  • RTLD_LAZY:解析动态库遇到未定义符号不退出,仍继续使用。
  • RTLD_NOW:遇到未定义符号,立即退出。
  • RTLD_GLOBAL:允许导出符号,在后面其他动态库中可以引用。

dlsym() 函数根据动态链接库句柄和要引用的符号,返回符号对应的地址。一般我们要先定义一个指向这种符号类型的指针,用来保存该符号对应的地址。通过这个指针,我们就可以引用动态库里的这个函数或全局变量了。

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

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

相关文章

【Unity】流式播放远端音频:WAV格式音频篇(一)

先了解一下wav的格式&#xff1a; 参考1&#xff1a;【音频】WAV 格式详解_tyustli的博客-CSDN博客_wav文件格式详解wav 文件支持多种不同的比特率、采样率、多声道音频。WAV 文件格式是 Microsoft 的 RIFF 规范的一个子集&#xff0c;用于存储多媒体文件。RIFF&#xff08;res…

git-secret:在 Git 存储库中加密和存储密钥(上)

目前市面上已经存在许多较为成熟的密钥管理产品&#xff0c;比如 HashiCorp Vault&#xff0c;AWS Secrets Manager 以及 GCP Secret Manager。由于这些产品需要集成和维护等服务&#xff0c;因此在项目中引入会增加一定成本和开销。阅读本文&#xff0c;将带你了解如何在 Dock…

numpy数值差分

文章目录diffediff1ddiff diff是numpy中用于求差分的函数&#xff0c;函数定义为 diff(a, n1, axis-1, prepend<no value>, append<no value>)其中a为数组&#xff0c;n为差分的阶数&#xff0c;axis为求导对应的坐标轴&#xff0c;默认-1表示最后一个轴。 例如…

提分必练,中创教育PMP全真模拟题分享

湖南中创教育每日五题分享来啦&#xff0c;“日日行&#xff0c;不怕千万里&#xff1b;常常做&#xff0c;不怕千万事。”&#xff0c;每日五题我们练起来&#xff01; 1、一个建筑项目所在的地区即将进入台风季节&#xff0c;恶劣的天气会严重影响项目的进度。高层管理者要求…

Java poi之word文本替换

目录结构前言文档准备引入Maven依赖代码块替换结果验证孤勇者替换结果对比青鸟替换结果对比前言 应公司需求&#xff0c;需实现以下功能 word文本内容的替换&#xff1b;word文本内容的提取&#xff1b;word文档中图片的提取存放 此文章将使用Apache POI实现Word文档中文本内…

【C++】揭开“引用”的庐山真面目

目录 一、引用的概念 二、引用的应用 1.特性 2.使用场景 2.1 引用作为函数参数 2.2 引用作为函数返回值 三、引用的权限问题 四、引用和指针的区别 一、引用的概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟…

【数据结构之二叉树简介·顺序存储·应用:堆·堆排序·TOPK问题】

​ &#x1f57a;作者&#xff1a; 迷茫的启明星 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f383;相关文章 【数据结构从0到1之树的初识】 &#x1f3c7;家人们&#xff0c;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64…

Kotlin SharedFlowStateFlow 热流到底有多热?

前言 协程系列文章&#xff1a; 一个小故事讲明白进程、线程、Kotlin 协程到底啥关系&#xff1f;少年&#xff0c;你可知 Kotlin 协程最初的样子&#xff1f;讲真&#xff0c;Kotlin 协程的挂起/恢复没那么神秘(故事篇)讲真&#xff0c;Kotlin 协程的挂起/恢复没那么神秘(原理…

50条必背JAVA知识点(二)

16.强制类型转换&#xff1a;将容量大的数据类型转换为容量小的数据类型&#xff0c;但可能造成精度降低或溢出。 17.字符串不能直接转换为基本类型&#xff0c;但通过基本类型对应的包装类则可以实现把字符串转换成基本类型。 18.计算机底层都以二进制补码的方式来存储数据。…

将现实问题转换为编程问题

将现实问题转换为编程问题需要转换思维&#xff0c;不过孰能生巧&#xff0c;见多了就自然懂如何做了&#xff0c;所以动起手来是决没错的。1.猜名次问题改进一&#xff1a;改进二&#xff1a;改进三&#xff1a;2.猜凶手问题总结&#xff1a;1.猜名次问题 每个选手都说了两句话…

深入浅出学习透析Nginx服务器的架构分析及原理分析「底层技术原理+运作架构机制」

Nginx再次回顾 也许你已经忘记了Nginx是做什么的&#xff1f;我来再次给你夯实一下概念。 多协议反向代理 Nginx是个高性能的Web和反向代理服务器及HTTP服务器&#xff0c;它能反向代理HTTP&#xff0c;HTTPS和邮件相关(SMTP&#xff0c;POP3&#xff0c;IMAP)的协议链接&am…

四十、Kubernetes1.25中安全认证详解

1、访问控制概述Kubernetes作为一个分布式集群的管理工具&#xff0c;保证集群的安全性是其一个重要的任务。所谓的安全性其实就是保证对Kubernetes的各种客户端进行认证和鉴权操作。客户端在Kubernetes集群中&#xff0c;客户端通常有两类&#xff1a;User Account&#xff1a…

视频剪辑必备的6个免费素材库~

视频剪辑必备素材&#xff0c;那自然是视频、配乐、音效啦&#xff0c;但最重要的还是内容&#xff0c;这些素材只是点缀。 那要如何获取素材&#xff1f;很多朋友应该都知道&#xff0c;网上很多素材版权不明确&#xff0c;使用不当就会造成侵权&#xff0c;找素材成为了一大…

电脑重装系统装不了如何解决

重装系统装不了如何解决&#xff1f;当电脑出现故障时&#xff0c;大部分人都会选择重装系统来解决这个问题&#xff0c;但是有人出现系统重装不了&#xff0c;下面小编就来为大家解决系统重装不了的问题。 工具/原料&#xff1a; 系统版本&#xff1a;win7 品牌型号&#xff…

为什么 B 站的弹幕可以不挡人物?

那天在 B 站看视频的时候&#xff0c;偶然发现当字幕遇到人物的时候就被裁切了&#xff0c;不会挡住人物&#xff0c;觉得很神奇&#xff0c;于是决定一探究竟。 高端的效果&#xff0c;往往只需要采用最朴素的实现方式&#xff0c;忙碌了两个小时&#xff0c;陈师傅打开了 F1…

Spring Boot(二):第一种导入依赖方式的实战案例

文章目录 第一种导入依赖方式的实战案例 一、导入依赖 二、依赖传递结构图 三、开发案例代码 第一种导入依赖方式的实战案例 一、导入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0…

Android开发——HOOK技术【解析】

1. 什么是 Hook Hook 英文翻译过来就是「钩子」的意思&#xff0c;那我们在什么时候使用这个「钩子」呢&#xff1f;在 Android 操作系统中系统维护着自己的一套事件分发机制。应用程序&#xff0c;包括应用触发事件和后台逻辑处理&#xff0c;也是根据事件流程一步步地向下执…

前端算法之二分查找

在数组中查找指定元素,如果存在就返回它的位置,如果不存在,就返回-1。 这是一道非常经典的算法题&#xff0c;考的就是二分查找算法&#xff0c;首先分析二分查找的思路&#xff1a; 假设一个数组为 [3,5,19,22,25,33,45,47,57,66,71,78]&#xff08;已经从小到大排好序&…

dapr本地托管的服务调用体验与Java SDK的Spring Boot整合

1 简介 之前在文章《dapr入门与本地托管模式尝试》中介绍了dapr和本地托管&#xff0c;本文我们来介绍如果在代码中使用dapr的服务调用功能&#xff0c;并把它整合到Spring Boot中。 Dapr服务调用的逻辑如下&#xff1a; 本次实验会创建两个服务&#xff1a; pkslow-data&am…

2023华数杯B题社会稳定预警首版思路

文章目录2023华数杯B题社会稳定预警首版思路B题题目如下&#xff1a;2023华数杯B题社会稳定预警首版思路 这个思路对下面这五问有了非常详细的思路&#xff0c;并且提供了支持材料。对本次的比赛进度有很大的帮助。 思路下载&#xff1a; https://math.jobpig.top/?p237 B题题…