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

linux-----------------库制作与原理(下)

1.ELF文件

要理解编译链链接的细节,我们不得不了解⼀下ELF⽂件。其实有以下四种⽂件其实都是ELF⽂件:
可重定位⽂件(Relocatable File ) :即 xxx.o ⽂件。包含适合于与其他⽬标⽂件链接来创
建可执⾏⽂件或者共享⽬标⽂件的代码和数据。
可执⾏⽂件(Executable File ) :即可执⾏程序。
共享⽬标⽂件(Shared Object File ) :即 xxx.so⽂件。
内核转储(core dumps) ,存放当前进程的执⾏上下⽂,⽤于dump信号触发。
⼀个ELF⽂件由以下四部分组成:
ELF头 (ELF header) :描述⽂件的主要特性。其位于⽂件的开始位置,它的主要⽬的是定位⽂
件的其他部分。
程序头表(Program header table) :列举了所有有效的段(segments)和他们的属性。表⾥
记着每个段的开始的位置和位移(offset)、⻓度,毕竟这些段,都是紧密的放在⼆进制⽂件中,
需要段表的描述信息,才能把他们每个段分割开。
节头表(Section header table) :包含对节(sections)的描述。
节(Section ):ELF⽂件中的基本组成单位,包含了特定类型的数据。ELF⽂件的各种信息和
数据都存储在不同的节中,如代码节存储了可执⾏代码,数据节存储了全局变量和静态数据等。
最常⻅的节:
代码节(.text):⽤于保存机器指令,是程序的主要执⾏部分。
数据节(.data):保存已初始化的全局变量和局部静态变量。

2.ELF从形成到加载轮廓

2-1 ELF形成可执⾏

step-1:将多份 C/C++ 源代码,翻译成为⽬标 .o ⽂件
step-2:将多份 .o ⽂件section进⾏合并

2-2 ELF可执⾏⽂件加载

当用户执行一个ELF可执行文件时,操作系统通过加载器(Loader)动态链接器(Dynamic Linker)协作完成内存映射、权限设置、依赖解析等关键操作

⼀个ELF会有多种不同的Section,在加载到内存的时候,也会进⾏Section合并,形成segment
合并原则:相同属性,⽐如:可读,可写,可执⾏,需要加载时申请空间等.
这样,即便是不同的Section,在加载到内存中,可能会以segment的形式,加载到⼀起
很显然,这个合并⼯作也已经在形成ELF的时候,合并⽅式已经确定了,具体合并原则被记录在了
ELF的 程序头表 (Program header table)

注意:为什么要讲section合并成segment

Section合并的主要原因是为了减少⻚⾯碎⽚,提⾼内存使⽤效率。如果不进⾏合并,
假设⻚⾯⼤⼩为4096字节(内存块基本⼤⼩,加载,管理的基本单位),如果.text部分
为4097字节,.init部分为512字节,那么它们将占⽤3个⻚⾯,⽽合并后,它们只需2个
⻚⾯。
此外,操作系统在加载程序时,会将具有相同属性的section合并成⼀个⼤的
segment,这样就可以实现不同的访问权限,从⽽优化内存管理和权限访问控制

3.理解连接与加载

3-1 静态链接

无论是自己的.o文件还是静态库中的.o文件本质都是把.o文件进行链接的过程所以静态链接就是研究静态链接的过程

3-2静态链接的核心流程

  1. 输入文件准备

    • 目标文件:由编译器生成的.o文件,包含代码、数据及未解析的符号引用。

    • 静态库:通过ar工具打包的.a文件,本质是多个.o文件的集合(如libmath.a包含sin.ocos.o等)。

  2. 符号解析(Symbol Resolution)

    • 符号表合并:链接器遍历所有输入文件,构建全局符号表。

    • 处理未定义符号:若同一符号被多文件定义(如两个.o文件均定义global_var),触发重复定义错误

  3. 重定位(Relocation)

    • 地址分配:为所有节(Section)分配运行时内存地址(如.text0x400000开始)。

    • 修正引用:根据最终地址,修改代码中的相对偏移或绝对地址。

      复制

      下载

      // 目标文件中的未重定位指令(假设函数add的地址未确定)  
      call 0x00000000  // 占位符,链接时替换为add的实际地址  
  4. 生成可执行文件

    • 合并所有目标文件的.text.data等节到统一Segment。

    • 生成程序头表(Program Header)供加载器使用。

3-3ELF加载与进程地址空间

3-2-1 虚拟地址/逻辑地址

思考一下一个进程在没有加载到内存的时候它有没有地址呢?

⼀个ELF程序,在没有被加载到内存的时候,本来就有地址,当代计算机⼯作的时候,都采⽤"平坦
模式"进⾏⼯作。所以也要求ELF对⾃⼰的代码和数据进⾏统⼀编址,下⾯是 objdump -S 反汇编
之后的代码

3-4动态链接与动态库加载

进程是如何链接到库的呢
进程间是如何共享库的呢
我们可以思考一下在库加载到内存的时候我们在创建每一个进程的时候是否要给每个进程的重新加载一个库大家可以思考一下
我们是如何跟库具体映射起来的呢看一张图就明白了
总结
静态链接的出现,提⾼了程序的模块化⽔平。对于⼀个⼤的项⽬,不同的⼈可以独⽴地测试和开发
⾃⼰的模块。通过静态链接,⽣成最终的可执⾏⽂件。
我们知道静态链接会将编译产⽣的所有⽬标⽂件,和⽤到的各种库合并成⼀个独⽴的可执⾏⽂件,
其中我们会去修正模块间函数的跳转地址,也被叫做编译重定位(也叫做静态重定位)。
⽽动态链接实际上将链接的整个过程推迟到了程序加载的时候。⽐如我们去运⾏⼀个程序,操作系
统会⾸先将程序的数据代码连同它⽤到的⼀系列动态库先加载到内存,其中每个动态库的加载地址
都是不固定的,但是⽆论加载到什么地⽅,都要映射到进程对应的地址空间,然后通过.GOT⽅式进 ⾏调⽤(运⾏重定位,也叫做动态地址重定位)。
http://www.xdnf.cn/news/7105.html

相关文章:

  • 宝塔9.6.0python项目程序运行卡住bug解决方案
  • mvc-ioc实现
  • 游戏引擎学习第291天:跳跃的怪物与占据的树木
  • Google aab包转成apk,并安装到手机设备中
  • 77.数据大小端赋值的差异与联系
  • 华为云Astro中各种变量与参数的区别与用法
  • C 语言字符串输入输出:scanf, gets, fgets 的选择与陷阱
  • Word文档图片和图表自动添加序号
  • 基于区块链技术的供应链溯源系统:重塑信任与透明度
  • 信奥赛-刷题笔记-栈篇-T2-P3056括号调整问题0518
  • Android日活(DAU)检测的四大实现方案详解
  • 代码随想录算法训练营 Day49 图论Ⅰ 深度优先与广度优先
  • mongodb管理工具的使用
  • 几种基于比较的排序
  • Linux搜索
  • 初始C++:类和对象(中)
  • 第10章 输入与输出流
  • Ansible模块——文件内容修改
  • IntelliJ IDEA设置编码集
  • ngx_http_referer_module 模块概述
  • Protect Your Digital Privacy: Obfuscate, Don’t Hide
  • GLPK(GNU线性规划工具包)中建模语言MathProg的使用
  • 【Linux网络】多路转接poll
  • ESP32C3在ESP-IDF中的nvs操作
  • Django 项目创建全攻略
  • R for Data Science(3)
  • oppo手机安装APK失败报错:安装包异常
  • 常见的数据库问题
  • Binary Prediction with a Rainfall Dataset-(回归+特征工程+xgb)
  • 【C++进阶篇】C++容器完全指南:掌握set和map的使用,提升编码效率