库的制作与原理2.0---动静态链接,main全解析,CPU在执行文件时的作用,GOT表。

@bit::Shadow
✧(≖ ◡ ≖✿

目录

静态库链接Section段

ELF文件加载与地址、进程

[objdump -S 【-g选项下的可执行文件/.o文件】](#objdump -S 【-g选项下的可执行文件/.o文件】)

☆☆☆可执行文件在操作系统的处理

可执行文件的起始入口地址:

CPU处理可执行程序流程:

CPU图:

CPU处理功能区:

完整流程:

[int main()前言](#int main()前言)

动态链接

动静态执行程序的区别

连接器

内核中程序是怎样和动态库映射起来的?

程序是怎么进行库函数的调用的?

GOT表的影响:

总结


上篇

静态库链接Section段

静态链接就是将编译后的所有.o目标文件与用到的一些静态库拼接成为一个独立的可执行文件。其中包括地址修正:++当所有模块组合在一起后,链接器根据我们的.o文件及静态库中的重定位表找到那些需要被重定位的函数全局变量,修正它们的地址。++这就是静态链接的过程。

此过程可以很好的用下图表示出来:

Section下拥有许多节,.text .data .bss .symtab等等,据此.o目标文件也被称为"可重定位目标文件",重定位体现在数据段、代码段的重新定位组合。

ELF文件加载与地址、进程

前言:当文件编译好后,地址就已经(并未加载到内存)是虚拟地址。*虚拟地址自0从上向下增加。

objdump -S 【-g选项下的可执行文件/.o文件】

将文件的代码与源代码混合显示。

bash 复制代码
objdump -S a.out > txt.s  #此处.s类型文件是为了高亮显示,-S生成的并非真正的汇编类型

最左侧就是映射的虚拟地址,严格来说是"逻辑地址"(起始地址(0)+ 偏移量)。

基于此图探讨:CPU处理可执行程序

☆☆☆可执行文件在操作系统的处理

Entry point address:作为可执行文件在磁盘中读取的入口地址

可执行文件的起始入口地址:

_start是程序真正的入口点

CPU处理可执行程序流程:

磁盘加载到内存,内存映射到页表(在执行可执行程序前已经确定)

CPU图:

CPU处理功能区:
  1. EIP(指令指针寄存器):CPU从EIP中读取下一条指令的虚拟地址(线性分布)。
  2. CR3:存放当前进程页目录的表的物理基址。在进程切换时,新进程的物理地址写入CR3。
  3. MMU(内存管理单元):使用CR3把EIP的虚拟地址转成物理地址,去内存取指令。

完整流程:

EIP提供虚拟地址->MMU翻译为物理地址->读取物理内存中的指令字节->送回CPU译码执行->EIP自增,进入下一轮。

int main()前言

_start是语言程序真正的入口点,它会调用__libc_start_main来初始化C运行时环境,然后调用你的main函数。

现在这个阶段你应该能看懂以下图解:

动态链接

体现在动态链接中推迟:

动静态执行程序的区别

静态加载是文件在编译时就已经确定了call的地址,因此在执行时,是包含于代码区内部的跳转。

动态加载是执行时从代码区跳转到共享区执行。

对比项 静态库 (.a) 动态库 (.so)
链接时机 编译链接时 程序加载/运行时
代码归属 复制进可执行文件 独立文件,程序依赖它
文件大小 大(包含库代码) 小(不含库代码)
内存占用 高(每个程序一份) 低(多程序共享一份)
独立性 独立运行,不需库文件 需要库文件存在
更新库 需重新编译程序 替换库文件即可
启动速度 稍慢(需加载/链接)
链接命令 gcc main.c libfoo.a gcc main.c -lfoo
库命名 libxxx.a libxxx.so

连接器

链接器是将多个目标文件(.o)和库文件 合并成可执行文件或共享库的工具。

连接器的库体现:

绿色框内就是一个动态链接器。

内核中程序是怎样和动态库映射起来的?

  • 动态库也是一个文件,要访问也是要被先加载,要加载也是要被打开的。
  • 进程找到动态库的本质,也是文件操作,不过我们访问库函数是通过虚拟地址跳转访问的,所以需要把动态库映射到进程的地址空间中。

程序是怎么进行库函数的调用的?

通过GOT(全局偏移量表):来实现相对位置与绝对位置的映射。

这种方式实现的动态链接校正PIC点地址无关代码,换句话说我们的动态库(位于虚拟内存)无需做任何修改下当各个进程运行到此处链接时,使用GOT表来实现动态库的定位。因此.o文件也叫做可重定向目标文件。

GOT表的影响:

进程间独立:每个进程都有自己的虚拟地址表对应独立的GOT表。

-fPIC的产生的原因:GOT下这种方式实现的动态链接叫做PIC地址无关码。换句话说,++我们的动态库无需做任何修改,被加载到任意内存地址都可以运行,并且被所有进程所共享++,这就是为什么给编译器指定-fPIC的原因,PIC=相对编址+GOT。

总结

  • 静态链接的出现提高了程序的模块化水平。对于一个大的项目,不同的人可以独立地测试和开发自己的模块通过静态链接,生成最终的可执行文件。
  • 我们知道静态链接会将编译产生的所有目标文件,和用到的各种库合并成一个独立的可执行文件,其中我们会修正模块间函数的调整地址,也被叫做编译重定位。
  • 而动态链接实际上是将整个过程推迟到了程序加载的时候。比如我们去运行一个程序,操作系统会首先将程序的数据代码连同它用到的一系列动态库先加载到内存,其中每个动态库的加载地址都是不固定的(每次程序启动加载),但是无论加载到什么地方,都要映射到进程对应的地址空间,然后通过GOT表的方式进行调用(运行重定位,也叫做动态地址重定位)。

倾囊相授,持续深耕Linux

欢迎关注

相关推荐
zincsweet1 小时前
System V 共享内存:原理剖析、代码架构分析与双端通信实战
linux·c++
EMTime10 小时前
Docker运行OpenWRT
运维·docker·容器
lolo大魔王10 小时前
Linux 文件系统超全面详解(原理、结构、挂载、分区、inode、日志、管理命令)
linux·运维·服务器
磊 子11 小时前
详细讲解一下epoll
linux·io·epoll·io多路复用
printfLILEI12 小时前
php中的类与对象以及反序列化
linux·开发语言·php
zyl8372112 小时前
Docker 使用手册
运维·docker·容器
古月方枘Fry13 小时前
MGRE实验
运维·服务器
博客-小覃13 小时前
Zabbix之华为交换机的日志记录信息操作详细教程
服务器·网络·华为·zabbix
叠叠乐13 小时前
redmi k90 pro max 强解BL,刷海外rom, 并刷入sukisu ultra
linux