假设程序都是静态链接的,先从整体上把握程序的装载过程,下一章将把程序拆成模块来观察。
Linux下的分段故障Segmentation fault
与Windows的“进程因非法操作需要关闭,很多时候是因为进程访问了未经允许的地址。
1 | /**Linux操作系统 |
PAE
(Physical Address Extension),是针对32位CPU内存不足的一种修补。类似的,针对16位CPU,借助段偏移寻址能够达到1MB,每次读块只能读64KB,使用XMS(一种中断处理技术)读取大于1MB内存的地方。
OS采用36位地址线\内存地址时,程序使用的最大虚拟地址空间仍旧不会超过4GB,但是它程序的虚拟地址空间可以映射到的物理内存的范围扩大到64GB。
通过这种页或块映射在Win下访存的操作方式叫地址窗口映射扩展AWE(Address Windowing Extension);在Linu下通过mmap()系统调用来实现。
装载的方式
- overlay覆盖装入,适合内存受限场景比如木马或者嵌入式设备;
保证调用路径上的块都在内存;禁止跨树间调用。
编写程序时将程序分块,写一小段辅助代码,管理模块在内存的驻留和更替。
1 | /** |
如上图所示,A和B模块互不依赖,都被Main模块调用时,则可以采用Overlay的方式,使得需要的内存空间节省256Bytes。
- Paging页映射 和页替换算法 MMU的地址映射
从操作系统可执行文件的装载
进程的建立
- 1.创建虚拟地址空间(只创建映射函数需要的数据结构,并不实际创建空间)
- Linux i386中是分配一个页目录,不设置页映射关系,在后面程序发生页错误的时候才设置)
- 2.读取可执行文件头,建立虚拟空间与可执行文件的映射关系。(装载)
(Linux中)
3.令EIP=EOP of Executable File
内核堆栈和用户堆栈的切换、CPU运行权限切换
1 | /** |
Linux将进程虚拟空间中的一个段叫VMA(Virtual Mempry Area),包括
1 | start address;size;Attributes(RWE);State;Type; |
对于相同权限状态的节Section,把它们合并到一起作为一个段Segment映射。这样在进程虚拟空间中只有一个VMA而不是多个,可以减少页面内部碎片,节省内存空间。
页错误
程序执行时进程虚拟空间中发生Page Fault,控制权从进程转移到OS;
根据数据结构找到VMA,计算页面在磁盘文件中的偏移,
然后物理内存分配物理页,读取该磁盘块到物理页中,
将发生页错误的虚拟页与物理页之间建立映射关系。
控制权归还给进程,从发生页错误的位置继续执行。
ELF文件
从链接角度(Linking View)看,elf文件按照节section存储,描述它的结构叫做节表Section Headers;
从装载角度或者执行视图(Execution View)看,elf文件可以按照段segment划分,描述它的结构叫做程序头Program Header;
elf可执行文件和共享库文件处于装载的需要,比目标文件多一个程序头表(Program Header Table)。
1 |
|
对于LOAD类型的Segment,p_memsz一定>=p_filesz(bss被合并在数据类型的段里,存放那些未被初始化的数据)。
PE文件的装载
步骤
PE文件可以装载到任何内存位置。PE文件中选用RVA,因为RVA可以始终保持一致。
1.读取文件第一个页,获取到DOS头、PE头、段表。
2.选择装载地址。检查进程空间地址里,目标地址是否可用。若不可用,则另外选择装载地址。
3.段映射。使用段表将PE文件中的段,映射到进程内存空间地址。—>若装载地址≠目标地址,Rebasing。
4.装载所需要的dll文件—>解析PE文件中的导入符号——>
5.建立初始化栈和堆—>建立主线程并启动进程
数据结构
1 | coffHeader{ |