程序员的自我修养(链接、装载与库)--摘录与汇总(二)

news2024/10/23 2:11:11

程序和进程的区别(P150)

  • 程序是一个静态的概念,是一些预先编译好的指令和数据集合的一个文件
  • 进程是一个动态的概念,它是程序运行时的一个过程

程序和进程有什么区别
程序(或者狭义上讲可执行文件)是一个静态的概念,它就是一些预先编译好的指令
和数据集合的一个文件;进程则是一个动态的概念,它是程序运行时的一个过程,很
多时候把动态库叫做运行时(Runtime)也有一定的含义。有人做过一个很有意思的
比喻,说把程序和进程的概念跟做菜相比较的话,那么程序就是菜谱,计算机的 CPU
就是人,相关的厨具则是计算机的其他硬件,整个炒菜的过程就是一个进程。计算机
按照程序的指示把输入数据加工成输出数据,就好像菜谱指导着人把原料做成美味可
口的菜肴。从这个比喻中我们还可以扩大到更大范围,比如一个程序能在两个 CPU上
执行等。

装载的方式(P152

动态装载的两种方法

  • 覆盖装入(Overlay)
  • 页映射(Paging)

动态装入的基本原理:程序的局部性原理

程序执行时所需要的指令和数据必须在内存中才能够正常运行,最简单的办法就是将程序运行所需要的指令和数据全都装入内存中,这样程序就可以顺利运行,这就是最简单的静态装入的办法。但是很多情况下程序所需要的内存数量大于物理内存的数量,当内存的数量不够时,根本的解决办法就是添加内存。相对于磁盘米说,内存是品贵且稀有的,这种情况自计算机磁盘诞生以来一直如此。所以人们想尽各种办法,希望能够在不添加内存的情况下让更多的程序运行起来,尽可能有效地利用内存。后来研究发现,程序运行时是有局部性原理的。所以我们可以将程序最常用的部分驻留在内存中,而将一些不太常用的数搭存放在磁盘里面,这就是动态装入的基本原理

页映射(P156

概括:

一张图足以
在这里插入图片描述

详情:

将内存和所有磁盘中的数据和指令按照“页”为单位划分为若干个页,所有的装载和操作的单位都是页。
在这里插入图片描述

进程的建立(P157-P159

从操作系统的角度来看,一个进程最关键的特征是它拥有独立的虚拟地址空间,这使得它有别于其他进程。
创建进程的三件事情:

  • 创建一个独立虛拟地址空间
  • 读取可执行文件头,并且建立虚拟空间与可执行文件的映射关系。
  • 将 CPU 的指令寄存器设置成可执行文件的入口地址启动运行
  • 首先是创建虛拟地址空间。回忆第1章的页映射机制,我们知道一个虚拟空间由一组贞
    映射函数将虚拟空间的各个页映射至相应的物理空间,那么创建一个虛拟空间实际上并不是创建空间而是创建映射函数所需要的相应的数据结构,在 i386 的Linux 下,创建虚拟地址空间实际上只是分配一个页目录 (Page Directory)就可以了,甚至不设置页映射关系,这些映射关系等到后面程序发生页错误的时候再进行设置。
  • 读取可执行文件头,并且建立虚拟空间与可执行文件的映射关系。上面那一步的页映射关系两数是虛拟空间到物理内存的映射关系,这一步所做的是虛拟空间与可执行文件的映射关系。我们知道,当程序执行发生页错误时,操作系统将从物理内存中分配
    一个物理页,然后将该“缺页,从磁盛中读取到内存中,再设置缺页的虛拟页和物理页的映射关系,这样程序才得以正常运行
    。但是很明显的一点是,当操作系统捕获到缺页错误时,它应知道程序当前所需要的页在可执行文件中的哪一个位置。这就是虛拟空间与可执行文件之间的映射关系。从某种角度来看,这一步是幣个装载过程中最重要的一步,也是传统意义上 “装载”的过程

由于可执行文件在装载时实际上是被映射的虛拟空间,所以可执行文件很多时候又被
叫做映像文件 (lmage)。

页错误

在这里插入图片描述

segment VS section

  • segment: 从装载的角度看,ELF 文件按照 segment 划分
  • section:从链接的角度看,ELF 文件是按照 section 存储的

当我们站在操作系统装载可执行文件的角度看问题时,可以发现它实际上并不关心可执行文件各个段所包含的实际内容,操作系统只关心一些跟装载相关的问题,最主要的是段的权限(可读、可写、可执行)。
那么我们可以找到一个很简单的方案就是:对于相同权限的段,把它们合井到一起当作一个段进行映射。
从链接的角度看,ELF 文件是按"Section"存储的,事实也的确如此;从装载的角度看,ELF 文件又可以按照"Segment"划分.
“Segment"的概念实际上是从装载的角度重新划分了 ELF 的各个段。在将目标文件链接成可执行文件的时候,链接器会尽量把相同权限属性的段分配在同一空间。
在ELF中把这些属性相似的、又连在一起的段叫做一个"Segment”,而系统正是按照"Segment"而不是"Section"来映射可执行文件的。
总的来说,“Segment"和"Section"是从不同的角度来划分同一个ELF文件。这个在ELF中被称为不同的视图(View),从"Section"的角度来看ELF 文件就是链接视图(Linking View),从"Segment"的角度来看就是执行视图(Execution View)。当我们在谈到ELF 装载时,“段”专门指"Segment”;而在其他的情况下,“段”指的是"Section"。

P164

ELF 的可执行文件和共享库文件有一个专门的数据结构叫做程序头表(Program Header Table)用来保存 Segment 信息。因为 ELF 文件不需要被加载,所以它没有程序头表
注:对应 readelf -l 展示的信息

P166
进程的 maps 解释
在这里插入图片描述

动态链接

地址无关代码(PIC) P190 - P197

PIC(Position-independent Code)
基本思想:把与地址相关的代码放到数据段中

也就是把代码段里需要变化的部分抽出来放到原本就要变化的数据段中,使代码段成为无需变化的

将共享对象模块中的地址引用
按照是否为跨模块分为两类:模块内部引用和模块外部引用
按照不同的引用方式分为:指令引用和数据访问

  • 模块内部

    • 指令引用:都是相对地址调用,不需要重定位
    • 数据访问:相对寻址
      原理:任何一条指令与它需要访问的模块内部数据之间的相对位置是固定的,那么只需要相对于当前指令加上固定的偏移量就可以访问模块内部数据(其实就是找到 .data 段的位置,然后在找到对应变量的位置)
      在这里插入图片描述
  • 模块外部

    • 指令引用:与模块外部数据访问相同,只不过 GOT 中相应的项保存的是目标函数的地址
    • 数据访问:建立全局偏移表,当代码需要引用全局变量时,通过全局偏移表中的条目进行间接引用

    注:模块内部定义的全局变量也被当做模块外部的全局变量来处理,因为编译器编译期间无法确认是模块内的还是模块外的

    很明显,外部模块的全局变量是和装载地址有关的,也就是“变化”的,按照 PIC 的基本思想,需要把这部分变化的挪到数据段中,而数据段中存放这类数据的叫做全局偏移表(GOT:Global Offset Table)

    查找过程:当指令中需要访问变量b时,程序会先找到GOT,然后根据 GOT 中变量所对应的项找
    到变量的目标地址。每个变量都对应一个4个字节的地址,链接器在装载模块的时候会查找
    每个变量所在的地址,然后填充 GOT 中的各个项,以确保每个指针所指向的地址正确
    。由
    于 GOT 本身是放在数据段的,所以它可以在模块装载时被修改,并且每个进程都可以有独
    立的副本,相五不受影响。

    GOT 如何做到地址无关(P194总结):
    关键点:
    1、模块编译时就能确定下来内部变量相对当前指令的偏移,也就能确定 GOT 相对于当前指令的偏移
    2、把代码中对外部变量的引用转换成了对 GOT 中指定条目的引用,而这个指定条目对应的引用会在动态链接时填充成对应变量真实的地址

    感觉就是把代码中使用的外部变量换成了一种特殊的内部变量(放在GOT),只不过这个特殊的内部变量指向外部变量的地址,而这个地址会在动态链接时更新成真实的地址,这个解题的思想也挺好,往已解决的问题上靠拢

在这里插入图片描述

各种地址引用方式汇总:
指令跳转、调用数据访问
模块内部相对跳转和调用相对地址访问
模块外部间接跳转和调用(GOT)间接访问(GOT)

在这里插入图片描述

注意点:编译期间,实际上并不能确定 b 和函数 ext() 是模块外部的还是模块内部的,因为他们有可能被定义在同一个共享对象的其他目标文件中,而由于没法确定,编译器只能把他们当做模块外部的函数和变量来处理

延迟绑定(PLT)P200-P202

基本思想:当函数第一次被用到的时候才进行绑定(符号查找、重定位等),如果没用到就不进行绑定

ELF 将 GOT 拆分成了两个表,叫做 .got 和 .got.plt(与刚才讲的对外部指令和外部数据的引用呼应)

  • .got:保存全局变量引用的地址
  • .got.plt:保存对外部函数引用的地址

    .got.plt 的前三项有特殊含义:

    • 第一项保存 .dynamic 段的地址
    • 第二项保存的是本模块的 ID
    • 第三项保存的是_dl_runtime_resolve()的地址

    第二项和第三项由动态连接器(who)装载共享模块时(when)负责将它们初始化(what)

在这里插入图片描述

#readelf -d lib7z.so
Dynamic section at offset 0x8dc8 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libm.so]
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so]
 0x000000000000000e (SONAME)             Library soname: [lib7z.so]
 0x000000000000001a (FINI_ARRAY)         0x9db0
 0x000000000000001c (FINI_ARRAYSZ)       16 (bytes)
 0x0000000000000004 (HASH)               0x228
 0x000000006ffffef5 (GNU_HASH)           0x288
 0x0000000000000005 (STRTAB)             0x498
 0x0000000000000006 (SYMTAB)             0x2d0
 0x000000000000000a (STRSZ)              198 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x9fa8
 0x0000000000000002 (PLTRELSZ)           168 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x620
 0x0000000000000007 (RELA)               0x5a8
 0x0000000000000008 (RELASZ)             120 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000000000001e (FLAGS)              BIND_NOW
 0x000000006ffffffb (FLAGS_1)            Flags: NOW
 0x000000006ffffffe (VERNEED)            0x588
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x55e
 0x000000006ffffff9 (RELACOUNT)          5
 0x0000000000000000 (NULL)               0x0
d_tag 类型d_un 的含义
DT_SYMTAB动态链接符号表的地址,d_ptr 表示 “.dynsym” 的地址
DT_STRTAB动态链接字符串表地址,d_ptr 表示 “.dynstr” 的地址
DT_STRSZ动态链接字符串表大小,d_val 表示大小
DT_HASH动态链接哈希表地址,d_ptr 表示 “.hash” 的地址
DT_SONAME本共享对象的 “SO-NAME”,我们将在后面介绍 “SO-NAME”
DT_RPATH动态链接共享对象搜索路径
DT_INIT初始化代码地址
DT_FINI结束代码地址
DT_NEED依赖的共享对象文件,d_ptr 表示所依赖的共享对象文件名
DT_REL动态链接重定位表地址
DT_RELA动态链接重定位表地址(基于增量)
DT_RELENT动态重定位项入口数量
DT_RELAENT动态重定位项入口数量(基于增量)
一些段含义及其内容汇总
  • .got:保存全局变量引用的地址
  • .got.plt:保存对外部函数引用的地址

    .got.plt 的前三项有特殊含义:

    • 第一项保存 .dynamic 段的地址
    • 第二项保存的是本模块的 ID
    • 第三项保存的是_dl_runtime_resolve()的地址

    第二项和第三项由动态连接器(who)装载共享模块时(when)负责将它们初始化(what)

  • .interp 段存放 可执行文件需要的动态链接器的路径
  • .dynamic 段(可以通过 readelf -d 指令查看本段内容)
    • 依赖的共享对象
    • 符号表位置
    • 重定位表位置
    • 初始化代码地址
  • .symtab(Symbol Table)静态链接中保存关于该目标文件的符号的定义和引用
  • .dynsym(Dynamic Symbol Table)动态符号表,保存动态链接模块间的符号导入导出关系

    与 .symtab 不同的是, .dynsym 只保存与动态链接相关的符号
    很多动态链接的模块同时拥有 .dynsym 和 .symtab 两个表,.symtab 保存了所有符号,包含 .dynsym 中的符号

  • .strtab(String Table) 符号字符串表,静态链接符号表的辅助表,用于保存符号名的字符串表
  • .dynstr(Dynamic String Table) 动态符号字符串表,动态符号表的辅助表,保存符号名的字符串表
  • .hash 符号哈希表,辅助加快程序运行时符号查找过程的表
  • .rel.text 静态链接中,目标文件的代码段的重定位表
  • .rel.data 静态链接中,目标文件的数据段的重定位表
  • .rel.dyn 动态链接文件中,对数据引用的修正,它所修正的位置位于 .got 以及数据段
  • .rel.plt 动态链接文件中,对函数引用的修正,它所修正的位置位于 .got.plt

共享对象需重定位的主要原因是因为导入符号的存在
目标文件的重定位是在链接时完成的,共享对象的重定位是在装载时完成的

“got.plt〞的前三项是被系统占据的,从第四项开始才是真止存放导入两数地址的地方。而第四项刚好是 Ox000015c8+4*3=0x000015d4,即"gmon start",第五项是“printf",第六项是"sleep",第七项是"exa finalize"。

当动态链接器需要进行重定位时,它先查找“printr” 的地址,“printf"位于 libc-2.6.1.s0。
假设链接器在全局符号表里面找到“printf” 的地址为 Ox08801234,那么链接器就会将这个
地地址填入到“-got.plt〞中的偏移为 Ox000015d8 的位置中去,从而实现了地址的重定位,即
实现了动态链接最关键的一个生骤。

在这里插入图片描述

动态链接器的步骤和实现(P214 - P)

动态链接器自举–> 装载共享对象–>重定位和初始化

动态链接器的特殊性:

  • 本身不能依赖于其他任何共享对象
    本身所需要的全局和静态变量的重定位工作由它自己完成

动态链接器入口地址就是自举代码的入口

完成基本自举以后,动态链接器将可执行文件和链接器本身的符号表都合并到一个符号表中,也就是全局符号表(GOT),然后链接器开始寻找可执行文件所依赖的共享对象,之前提到的 .dynamic 段中,有一种类型的入口是 DT_NEEDED,它所指出的是该可执行文件(或共享对象)所依赖的共享对象。由此,链接器可以列出可执行文件所需要的所有共享对象,并将这些共享对象的名字放入到一个装载集合中,然后链接器开始从集合中取一个所需要的共享对象的名字,找到对应的文件后打开该文件,读取相应的 ELF 文件头和 .dynamic 段,然后将它相应的代码段和数据段映射到进程空间。如果这个 ELF 共享对象还依赖于其他共享对象,那么将所依赖的共享对象的名字放到装载集合中。如此循环直到所有依赖的共享对象都被装载进来为止。
当然链接器可以有不同的装载顺序,如果我们把依赖关系看做一个图的话,那么这个装载过程就是一个图的遍历过程,链接器可能会使用深度优先或者广度优先或者其他的顺序来遍历整个图,这取决于链接器,比较常见的算法一般都是广度优先的。

全局符号介入(Global Symbol Interpose):一个共享对象里面的全局符号被另一个共享对象的同名全局符号覆盖的现象

Linux 动态链接器处理规则:当一个符号需要被加入全局符号表时,如果相同的符号名已经存在,则后加入的符号被忽略

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

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

相关文章

PHP:下拉列表,颜色展示

PHP展示下拉列表,选项设置为数据库存储颜色进制,colorname是颜色名称,color是颜色进制 一、表结构 produce_info_nav1_colorshow produce_info_nav1 二、核心代码 //查询对应默认颜色 $sql_selcolor "SELECT color FROM produce_i…

机器学习篇-day07-朴素贝叶斯和特征降维

一. 朴素贝叶斯算法 朴素贝叶斯算法介绍 利用概率值进行分类的一种机器学习算法 复习概率 相互独立:如果P(AB) P(A)P(B),则称事件A与事件B相互独立 比如:女神喜欢程序员的概率,女神喜欢产品经理的概率,两个事件没有…

詹妮弗洛佩兹的比基尼影集显示,与本阿弗莱克离婚期间她正处于最勇敢的时刻

詹妮弗洛佩兹已然正式终结了其饱含浓情蜜意的时代!此乃我……当下之时代,且于同本阿弗莱克离异之际,步入了迄今最为英勇无畏的时代,此番全新的摄影集便是有力的明证。 10 月 9 日,《采访》杂志展示了一系列洛佩兹用作…

水文监测系统的多功能性与作用深度剖析

在现代水利管理中,水文监测系统作为重要的技术手段,正发挥着日益关键的作用。这一系统,也被称为水文信息自动化采集系统,通过自动或半自动的方式,实现了对江河、湖泊、水库等水体以及地下水的实时监测,涵盖…

功能强大且简单易用的实时算法视频监控,智慧快消开源了。

智慧快消视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。 基于多年的深度…

jmeter 对 dubbo 接口测试是怎么实现的?有哪几个步骤

目录 前言 一.先了解下 dubbo 的原理,最好自己搭建一个案例可参考以下方式搭建 http://09792bb8.wiz03.com/share/s/09uiKU3j2kR120MIpT2AdLm70pfBmE1zFApv2jiDZ01GhE8j 二.编写 dubbo 测试脚本 前言 最近使用工作中使用jmeter调用dubbo接口进行接口测试&#xf…

SLAM中的加权最小二乘法

一、数学描述 机器人携带传感器在环境中运动可由 运动方程 和 观测方程 描述。 其中 表示时刻; 表示 时刻的位姿; 是运动传感器的读数或者输入; 为路标点; 表示观测数据。 为运动噪声,例如对机器人下达了前进 1m 的指…

大模型时代,云原生数据底座的创新和实践

本文整理自百度云智峰会 2024 —— 云原生论坛的同名演讲。 大模型毫无疑问是当前技术发展的热点,成为大家默认的提升生产力工具。 但是,大模型训练主要使用互联网上的公开数据为主,没有企业内部的数据,所以大模型本质上自带的都…

并行 parallel broadcast partition pruning 分区裁剪 optimizer_dynamic_sampling=7

insert into abc 没有PDML所以不是全部并行 只有select 的情况 全部并行,没有 px send broadcast ,所以rows没从103M变成103*8M select *from A,B where A.Pkey B.Pkey and A.Pkey XX A B表都会进行分区裁剪 ----并行为什么更…

定了!OPPO全旗舰新品10月24日发布

今日,OPPO宣布将于2024年10月24日19:00举办OPPO Find X8系列及旗舰生态新品发布会,以全新一代的年度影像旗舰 OPPO Find X8系列为核心,通过新一代的OPPO Enco X3旗舰耳机、OPPO Pad 3 Pro旗舰平板,以及再度升级的安卓全…

解决低版本pytorch和onnx组合时torch.atan2()不被onnx支持的问题

解决这个问题,最简单的当然是升级pytorch和onnx到比较高的版本,例如有人验证过的组合: pytorch2.1.1cu118, onnxruntime1.16.3 但是因为你的模型或cuda环境等约束,不能安装这么高的版本的pytorch和onnx组合时(例如我的环境是pytorch1.12&…

单细胞转录组亚群分析

1 单细胞转录组亚群常见分析内容 重磅综述:三万字长文读懂单细胞RNA测序分析的最佳实践教程 (原理、代码和评述) 如何使用Bioconductor进行单细胞分析? 单细胞转录组亚群分析的内容根据样品数目多少,可以分为单个样…

开源项目 - 轻量级人体姿态 人体关键点检测 机器视觉 深度学习

开源项目 - 轻量级人体姿态 人体关键点检测 机器视觉 深度学习 项目地址:https://gitcode.net/EricLee/light_pose 1、数据集来源:coco2017 数据集 * coco 数据集官方网站:https://cocodataset.org/#home * [数据集下载地址(百度网盘 Pa…

CogVideoX:Text-to-Video Diffusion Models with An Expert Transformer

研究背景 背景介绍: 这篇文章的研究背景是文本到视频模型的快速发展,特别是Transformer架构和扩散模型的应用。早期尝试预训练和扩展Transformer生成视频已经显示出巨大潜力,如CogVideo和Phenaki。扩散模型在多模态生成方面也取得了显著进展&#xff0c…

数据结构 -- 排序算法

一 排序 1.1 排序的概念 所谓排序,就是一种使一串数据记录,按照其中的某个或某些关键字的大小,递增或递减地组织起来的操作。 从排序方式上,排序算法一般被分为比较排序和非比较排序。从比较排序的内容上,它一般被分为…

Excel:vba实现拆分单元格成一字一单元格

我拿到的表格如下: 我想实现的表格效果如下: 要求就是:将A列的千字文拆分成一个单元格一个字,并整理成4列 我这里是将效果呈现到一个新的表里面,没有在原表里面(在原表里…

SpringBoot统一日志框架

在项目开发中,日志十分的重要,不管是记录运行情况还是定位线上问题,都离不开对日志的分析。 1.日志框架的选择 市面上常见的日志框架有很多,它们可以被分为两类:日志门面(日志抽象层)和日志实…

使用 PyTorch 构建 LSTM 股票价格预测模型

目录 引言准备工作1. 训练模型(train.py)2. 模型定义(model.py)3. 测试模型和可视化(test.py)使用说明模型调整结论 引言 在金融领域,股票价格预测是一个重要且具有挑战性的任务。随着深度学习…

【观察】超聚变:跨越智能算力“四座大山”,全方位重构“智算底座”

毫无疑问,今天在人工智能的推动下,企业数智化转型已进入规模化“倍增创新”的阶段,尤其是以大模型为代表的AI技术加速演进,以及应用场景的不断拓展加深,都让各类AI创新应用如雨后春笋般涌现,并加速惠及千行…

C++中cout的一些扩展

需要添加<iomanip>头文件 cout有许多扩展功能&#xff0c;比如一直很麻烦的保留小数数位的问题。 这里用几个问题来引入 cout实现保留小数数位 #include<iostream> #include<iomanip> using namespace std; int main(){double x123.345;double y342.324…