共享库之动态连接

Linux ld命令 将目标文件连接为可执行程序,Linux ldd命令 打印程序或者库文件所依赖的共享库列表,Linux ldconfig命令 动态链接库管理命令。 阅读详情
   静态链接浪费内存和磁盘空间、模块更新困难等问题,因此寻找一种更好的办法来组织程序模块。

   静态链接对程序的更新、部署和发布也会带来很多麻烦。

 

动态链接:

      就是不对那些组成程序的目标文件进行链接,等到程序要运行时才进行链接。

 

动态链接的方式使得开发过程中各个模块更加独立、耦合度更小,便于不同的开发者和开发组织之间进行独立的开发和测试。

动态链接还有一个特点就是程序在运行时可以动态的选择加载各种程序模块,使得插件成为可能。

Linux系统中,ELF动态链接文件被称为动态共享对象(DSO,Dynamic Shared Objects),简称共享对象,它们一般都是以“.so”为扩展名;动态链接文件被称为动态链接库。在windows下为.dll。

/*Program1.c */

#include "Lib.h"

int main()

{

        foobar(1);

        return 0;

}

 

/*Program2.c*/

#include "Lib.h"

int main()

{

        foobar(2);

        return 0;

}

/*Lib.c*/

#include <stdio.h>

void foobar(int i)

{

        printf("Printing from Lib.so %d\n",i);

}

/*Lib.h*/

#ifndef LIB_H

#define LIB_H

void foobar(int i); 

#endif

horst@debian:~/horstdemo/simpleDynamic$ gcc -fPIC -shared -o mylib.so lib.c

将Lib.c 编译成为一个共享对象文件,‘-shared’表示产生共享对象

-fPIC(Position Independent Code)表示使用地址无关代码技术来产生输出文件

horst@debian:~/horstdemo/simpleDynamic$ gcc -o program1 program1.c ./mylib.so

利用 ./mylib.so分别进行编译链接。

运行结果:

horst@debian:~/horstdemo/simpleDynamic$ ./program1 &
[1] 14969
horst@debian:~/horstdemo/simpleDynamic$ Printing from lib.so 1

查看进程的虚拟地址空间:

可以看到有Lib.so映射文件。

 horst@debian:~/horstdemo/simpleDynamic$ cat /proc/14969/maps
08048000-08049000 r-xp 00000000 08:05 395370     /home/horst/horstdemo/simpleDynamic/program1
08049000-0804a000 rw-p 00000000 08:05 395370     /home/horst/horstdemo/simpleDynamic/program1
b7e35000-b7e36000 rw-p b7e35000 00:00 0
b7e36000-b7f8b000 r-xp 00000000 08:01 1971754    /lib/i686/cmov/libc-2.7.so
b7f8b000-b7f8c000 r--p 00155000 08:01 1971754    /lib/i686/cmov/libc-2.7.so
b7f8c000-b7f8e000 rw-p 00156000 08:01 1971754    /lib/i686/cmov/libc-2.7.so
b7f8e000-b7f91000 rw-p b7f8e000 00:00 0
b7f9f000-b7fa0000 rw-p b7f9f000 00:00 0
b7fa0000-b7fa1000 r-xp 00000000 08:05 395365     /home/horst/horstdemo/simpleDynamic/mylib.so
b7fa1000-b7fa2000 rw-p 00000000 08:05 395365     /home/horst/horstdemo/simpleDynamic/mylib.so
b7fa2000-b7fa4000 rw-p b7fa2000 00:00 0
b7fa4000-b7fa5000 r-xp b7fa4000 00:00 0          [vdso]
b7fa5000-b7fbf000 r-xp 00000000 08:01 1962242    /lib/ld-2.7.so
b7fbf000-b7fc1000 rw-p 0001a000 08:01 1962242    /lib/ld-2.7.so
bfdab000-bfdc0000 rw-p bffeb000 00:00 0          [stack]

链接时重定位(静态链接);装载时重定位(动态链接)

地址无关代码:PIC,Position-independent Code,把指令中那些需要修改的部分分离出来,跟数据部分放在一起,这样指令部分就可以保持不变,而数据部分可以再每个进程中拥有个副本。

 

                       指令跳转、调用                          数据访问

模块内部      (1)相对跳转和调用                   (2)相对地址访问

模块外部      (3)间接跳转和调用(GOT)     (4)间接访问(GOT)

 

如果一个共享对象lib.so中定义了一个全局变量G,而进程A和B都使用了lib.so,那么当进程A改变这个全局变量G的值的时,进程B中G会受到影响么?

不会,因为当lib.so被两个进程加载时,它的数据段部分在每个进程中都有独立的副本,从这个角度看,共享对象中的全局变量实际上和定义在程序内部的全局变量没什么区别。如果是线程,则有影响。

 

延迟绑定:

动态链接比i链接慢的主要原因是动态链接下对于全局静态的数据访问要进行复杂的GOT定位,然后间接寻址.

 

由于很多函数在程序执行过程中不一定被用到(错误处理函数,特殊功能模块),ELF采用了一种叫延迟绑定(Lazy Binding)的做法,基本思想就是当函数第一次被用到时才进行绑定。


操作系统如何确定动态连接器?

 

在动态链接的ELF可执行文件中,有一个专门的段叫做“.interp”,如果我们使用objdump来查看:

horst@debian:~/horstdemo/simpleDynamic$ objdump -s ./program1

./program1:     file format elf32-i386

Contents of section .interp:
 8048114 2f6c6962 2f6c642d 6c696e75 782e736f  /lib/ld-linux.so
 8048124 2e3200 

动态链接ELF中最重要的结构应该是“.dynamic”段,这个段里面保存了动态连接器所需要的基本信息,比如依赖哪些共享对象、动态链接符号表的位置、动态链接重定位表的位置、共享对象初始化代码的地址等。

使用readelf工具查看“.dynamic”段的内容:

horst@debian:~/horstdemo/simpleDynamic$ readelf -d ./program1

Dynamic section at offset 0x588 contains 22 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [./mylib.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x804835c
 0x0000000d (FINI)                       0x804854c
 0x00000004 (HASH)                       0x8048148
 0x6ffffef5 (GNU_HASH)                   0x8048188
 0x00000005 (STRTAB)                     0x8048274
 0x00000006 (SYMTAB)                     0x80481c4
 0x0000000a (STRSZ)                      143 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x8049664
 0x00000002 (PLTRELSZ)                   24 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x8048344

mylib.so中的foobar函数称为导出函数

为了表示动态链接这些模块之间的符号导出与导入关系,ELF专门有一个叫做动态符号表的段用来保存这些信息。这个段叫做“.dynsym”。

可以使用如下命令来查看动态符号表:

horst@debian:~/horstdemo/simpleDynamic$ readelf -sD ./mylib.so

Symbol table for image:
  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
    9   0: 00000308     0    FUNC GLOBAL DEFAULT   9 _init
    7   0: 0000160c     0  NOTYPE GLOBAL DEFAULT ABS _edata
    4   0: 00000000   613    FUNC GLOBAL DEFAULT UND sleep
    2   0: 00000000     0  NOTYPE   WEAK DEFAULT UND _Jv_RegisterClasses
    1   0: 00000000     0  NOTYPE   WEAK DEFAULT UND __gmon_start__
   10   0: 0000044c    57    FUNC GLOBAL DEFAULT  11 foobar
    6   1: 00001614     0  NOTYPE GLOBAL DEFAULT ABS _end
   11   1: 000004c8     0    FUNC GLOBAL DEFAULT  12 _fini
    5   2: 00000000   267    FUNC   WEAK DEFAULT UND __cxa_finalize
    8   2: 0000160c     0  NOTYPE GLOBAL DEFAULT ABS __bss_start
    3   2: 00000000    54    FUNC GLOBAL DEFAULT UND printf

Symbol table of `.gnu.hash' for image:
  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
    6   0: 00001614     0  NOTYPE GLOBAL DEFAULT ABS _end
    7   0: 0000160c     0  NOTYPE GLOBAL DEFAULT ABS _edata
    8   1: 0000160c     0  NOTYPE GLOBAL DEFAULT ABS __bss_start
    9   1: 00000308     0    FUNC GLOBAL DEFAULT   9 _init
   10   2: 0000044c    57    FUNC GLOBAL DEFAULT  11 foobar
   11   2: 000004c8     0    FUNC GLOBAL DEFAULT  12 _fini

动态链接的文件中,也有类似静态链接的重定位表,分别叫做“.rel.dyn”和“.rel.plt”,他们分别相当于“.rel.text”和“.rel.data”。

 

查看动态链接文件的重定位表:

horst@debian:~/horstdemo/simpleDynamic$ readelf -r ./mylib.so

Relocation section '.rel.dyn' at offset 0x2c8 contains 4 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00001608  00000008 R_386_RELATIVE   
000015e0  00000106 R_386_GLOB_DAT    00000000   __gmon_start__
000015e4  00000206 R_386_GLOB_DAT    00000000   _Jv_RegisterClasses
000015e8  00000506 R_386_GLOB_DAT    00000000   __cxa_finalize

Relocation section '.rel.plt' at offset 0x2e8 contains 4 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
000015f8  00000107 R_386_JUMP_SLOT   00000000   __gmon_start__
000015fc  00000307 R_386_JUMP_SLOT   00000000   printf
00001600  00000407 R_386_JUMP_SLOT   00000000   sleep
00001604  00000507 R_386_JUMP_SLOT   00000000   __cxa_finalize

动态链接的步骤和实现:

动态链接的步骤基本分为三步:先启动动态连接器本身,然后装载所有需要的共享对象,最后是重定位和初始化。

但是对于动态链接器本身来说,它的重定位工作是由谁来完成的? 

动态连接器本身通过自举(Bootstrap)来完成。

完成基本自举之后,动态连接器将可执行文件和连接器本身的符号表都合并到一个符号表当中,我们可以称它为全局符号表(Global Symbol Table)。

动态连接器按照各个模块之间的依赖关系,当有两个不同的模块定义了同一个符号时怎么办?

当一个符号需要被加入全局符号表时,如果相同的符号已经存在,则后加入的符号被忽略。

显示运行时链接:

动态库的装载则是通过一些列由动态连接器提供的API,具体是4个函数:打开动态库(dlopen)、查找符号(dlsym)、错误处理(dlerror)、以及关闭动态库(dlclose)。

dlopen()函数用来打开动态库,并将其加载到进程的地址空间,完成初始化过程。

void  *dlopen(const char *filename, int flag);

 

dlsym(),我们通过该函数找到所需要的符号。

dlsym(void  *handle,  char  *symbol);



Social-Auto-Upload实战:如何用Python+Playwright实现抖音/B站/小红书一键三连发? 本文详细介绍了如何使用Python和Playwright框架构建Social-Auto-Upload自动化上传工具,实现抖音、B站、小红书等平台的一键多发布。通过模拟真实用户操作和Cookie管理技术,该工具能有效解决内容创作者跨平台发布的重复劳动问题,提升工作效率。 阅读详情

相关推荐

在 STM32CubeIDE 中针对 STM32F407 移植 ThreadX

在 STM32CubeIDE 中针对 STM32F407 移植 ThreadX,不依赖盗版破解的Keil MDK、IAR等软件。

wangyuewu08的博客 3374

全局变量和extern详解

全局变量和extern详解声明与定义extern 声明与定义 首先讲一下声明与定义 声明不等于定义,声明只是指出了变量的名字,并没有为其分配存储空间;定义指出变量名字同时为变量分配存储空间,定义包含了声明 extern int i; //声明变量i,但没分配存储空间,还不能使用, 可以出现很多次,下面的三种情况只能出现一次 int i; //定义了变量i,并分配了空间,可以使用 extern int a =0 //定义一个全局变量a 并给初值 int a =0;//定义一个全局

nbu_dahe的博客 5307

第一弹:llama.cpp编译

1.编译llama.cpp命令行(电脑版本);2.交叉编译安卓命令行版本。

joedan0104的专栏 1万+

extern详解

extern关键字详解

TTdreamloong的博客 4万+

头歌操作系统2.3系统调用命令ls执行的系统调用

设置版本0内核为分析对象。然后一直c直到虚拟机出现。然后一直c就是答案了。

m0_74128952的博客 1万+

百度贴吧爬虫实战:完整实现与使用指南

使用 csv 模块创建写入器,首先写入表头("标题" 和 "链接"),然后遍历帖子字典,将每个标题和链接作为一行写入文件。首先检查内容是否为空,然后通过查找 JSON 结构的起始({)和结束(})位置,提取有效的 JSON 片段。接着遍历帖子列表中的每个条目,提取标题文本和对应的链接,对于相对路径的链接会自动补全为完整的 URL。从 JSON 提取标题和链接此方法专门用于从解析后的 JSON 数据中提取帖子标题和对应的链接。提取到的标题和链接以字典形式返回,键为标题,值为对应的完整链接。

qq_63736560的博客 2016

c语言全局变量(extern)和静态全局变量(static)的使用、拓展作用域(全局、局部等)

c、c++基本知识点:作用域、全局变量(extern)、局部变量等

weixin_55491446的博客 1万+

使用Hyper-V使用下载下来的macOS安装引导安装macos

然后关闭工具返回安装界面选择第二个reinstall,选择119g那个硬盘。我的资源上传了:macOS Monterey on Hyper-V.7z。设置固件的启动顺序,引导安装盘放到第一位,mac安装110g放第二。选择disk utility格式化那个110g。下载解压的macos引导挂载到虚拟硬盘。其他设置安全禁用,检查禁用,集成都选中。新建一个虚拟硬盘装mac,110g。

viviliving的专栏 3023

全局变量 extern

全局变量(外部变量)是在函数的外部定义的,它的作用域为从变量的定义处开始,到本程序文件的末尾。在此作用域内,全局变量可以为本文件中各个函数所引用。编译时将全局变量分配在静态存储区。    有时需要用extern来声明全局变量,以扩展全局变量的作用域。    1. 在一个文件内声明全局变量    如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件终了。如果在定义点之

我不会编程,但不是完全不会,我会一点点 1276

TMC2209的UART模式详解:如何用串口调试助手配置细分和电流(避坑指南)

本文深入解析TMC2209步进电机驱动器的UART模式,详细讲解如何通过串口调试助手配置细分和电流,包括硬件连接、通信协议、关键寄存器设置及高级功能应用。提供实战避坑指南,帮助开发者优化步进电机控制性能,适用于精密仪器、3D打印等场景。

weixin_29166013的博客 541

拉曼光谱入门:10.显微拉曼与共聚焦技术

本文系统阐述了显微拉曼光谱技术与共聚焦原理的融合机制,详细解析了从激光激发、针孔空间滤波到光谱采集的完整光路架构,阐明了共聚焦设计在提升三维空间分辨率、抑制荧光背景及实现微区无损层析方面的核心优势,并探讨了关键光学器件的性能边界与系统技术局限。

qq_53529450的博客 166

深入理解C语言中的extern关键字:跨文件共享的全局变量和函数

本文旨在深入探讨C语言中的extern关键字,为开发者提供关于其在跨文件共享全局变量和函数方面的全面理解。文章首先概述了extern关键字的定义和基本用法,随后详细分析了它在不同上下文中的具体应用,包括全局变量和函数声明。最后,通过技术总结,强调了extern在编程中的重要性。extern关键字是C语言中的一个修饰符,用于声明全局变量和函数的引用。它用于告诉编译器,某个全局变量或函数的定义位于其他文件中。以下是extern全局变量声明:在文件中使用extern。

极客代码 2万+

【教程4>第4章>第20节】基于FPGA的costas环整体实现与仿真测试

Costas 环是一种用于载波同步的闭环反馈控制系统,主要应用于通信系统中的相干解调。其基本原理是通过鉴相器(Phase Detector,PD)比较接收信号与本地载波信号的相位差,将这个相位差信息经过环路滤波器(Loop Filter,LF)滤波后,去控制数控振荡器(Numerically Controlled Oscillator,NCO)的输出频率和相位,使得本地载波能够跟踪接收信号的载波,最终实现载波同步。Costas 环环路滤波器在载波同步过程中起着不可或缺的作用。

FPGA/MATLAB学习教程/源码/项目合作开发 1263

Linux下动态共享库 连接和加载路径

1. 连接时找不到某一个函数,出现“ xxxxx undefined”,应该就是连接时找不到相应的动态库,连接时动态库的路径如下: 1> 默认路径是:/lib,  /usr/lib/,  /usr/local/lib 2> -L: 指定连接时动态库的路径 3> LD_LIB

finding的专栏 7970

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

第7章“链接”:如何动态连接共享库、从应用程序中加载和链接共享库

7.10 动态链接共享库;7.11 从应用程序中加载和链接共享库;7.12 *与位置无关的代码

明朗晨光的专栏 1934

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

linux连接当前目录动态库,linux C 动态共享库编译链接

1.1.1linux编写so文件的方式1首先gcc编译的时候要加-fPIC选项,-fPIC是告诉gcc生成一个与位置无关的代码2gcc链接的时候要加-shared选项,意思是生成一个so共享库。对于linux或者unix,一个so文件,文件扩展名必须是so,文件名的前三个字母必须是lib1.1.2linux使用sogcc链接的时候需要加-L.代表从当前目录下找相关的so文件,-l文件名(但不包括文...

weixin_36147585的博客 527

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

linux下动态共享库的创建,使用与更新(包括ldconfig的使用)g++ -WI -soname

-Wl,-soname  -Wl 告诉编译器将后面的参数传递到连接器。而 -soname 指定了共享库的 soname  这个soname的存在是为了兼容方便。  比如:      有一个程序ap1,以及一个库libtest.so.1      ap1启动的时候需要libtest.so.1      如果链接的时候直接把libtest.so.1传给了ap1,那么

missingu1314的专栏 1289

Linux gcc编译库,如何在Linux上使用GCC编译共享库

原文链接 http://t.cn/RPzyNB3对于任何程序员来说,库都是不可缺少的工具.它们是使用已经存在的代码,编译并可以唯你使用的.它们通常提供一般性的功能,像可以存储任何数据的“链表”和“二叉树”,或者某些特殊的功能,比如数据库服务(如MySQL).绝大多数软件项目包含若干组件,这些组件可能在之后的其它项目中会被用到,或者你构建项目的时候只是想要分出来而已.当你有可重用或者逻辑上不相关的函...

weixin_39934085的博客 163

如何在Linux上使用GCC编译共享库

原文链接 http://t.cn/RPzyNB3 对于任何程序员来说,库都是不可缺少的工具.它们是使用已经存在的代码,编译并可以唯你使用的.它们通常提供一般性的功能,像可以存储任何数据的“链表”和“二叉树”, 或者某些特殊的功能,比如数据库服务(如MySQL).绝大多数软件项目包含若干组件,这...

chenhang8946的博客 185

linux编译的时候带上库,如何在Linux上使用GCC编译共享库

原文链接 http://t.cn/RPzyNB3对于任何程序员来说,库都是不可缺少的工具.它们是使用已经存在的代码,编译并可以唯你使用的.它们通常提供一般性的功能,像可以存储任何数据的“链表”和“二叉树”,或者某些特殊的功能,比如数据库服务(如MySQL).绝大多数软件项目包含若干组件,这些组件可能在之后的其它项目中会被用到,或者你构建项目的时候只是想要分出来而已.当你有可重用或者逻辑上不相关的函...

weixin_39619170的博客 290
上一篇: python学习之路-继承
下一篇: linux memwatch的内存检测-double-free
horst_hu
博客等级 码龄15年 38粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值