第六章 可执行文件的装载与进程
1. 启动参数将虚拟内存从2GB转到1GB,加入参数/3G
[boot loader]
timeout=2
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /3G
/noexecute=optin /fastdetect
2. PAE(Physical Address Extension)
windows下AWE(Address Windowing Extensions)
linux下mmap()调用
XMS(extended memory specification)
windows下启动打开/PAE, /AWE
3. DSP,覆盖装入(利用时间换空间的方式):覆盖管理器Overlay Manager,树状结构,调用路径,禁止跨树间调用
页映射:FIFO算法,LUR算法
4. 进程建立
创立一个独立的虚拟空间
读取可执行文件(映像文件)头,并且建立虚拟空间与可执行文件的映射关系
将CPU的指令寄存器设置成可执行文件的入口地址,启动运行
5. ELF可执行文件的数据段和BSS段合并了,唯一区别是数据段从文件中初始化内容,而BSS段的内容全部初始化为0。
6. 堆向上扩展,栈向下扩展
随机地址空间分布技术
堆的最大申请数量:受操作系统的版本、程序本身的大小、用到的动态/共享库的数量大小、程序栈的数量大小等
#include <stdio.h>
#include <stdlib.h>
unsigned maximum = 0;
int main(int argc, char *argv[])
{
unsigned blocksize[]={1024*1024, 1024, 1};
int i, count;
for(i=0; i<3; i++)
{
for(count=1; ; count++)
{
void *block=malloc(maximum+blocksize[i]*count);
if(block)
{
maximum=maximum+blocksize[i]*count;
free(block);
}else
{
break;
}
}
}
printf("maximum malloc size=5u bytes\n", maximum);
}
7. minibash
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
char buf[1024] = {0};
pid_t pid;
while (1)
{
printf("minibash$");
scanf("%s", buf);
pid=fork();
if (pid==0)
{
if (execlp(buf, 0)<0)
{
printf("exec error\n");
}else if (pid>0)
{
int status;
waitpid(pid, &status, 0);
}else
printf("fork error %d\n", pid);
}
}
return 0;
}
8. ELF文件的装载问题
检查ELF可执行文件格式的有效性,比如魔数、程序头表中段的数量
寻找动态链接的“.interp”段,设置动态链接器的路径
根据ELF可执行文件的程序头表的描述,对ELF文件进行映射,比如代码、数据、只读数据
初始化ELF进程环境,比如进程启动时EDX寄存器的地址应该是DT_FINI的地址
将系统调用的返回地址修改成ELF可执行文件的入口点,这个入口点取决于程序的链接方式,对于静态链接的ELF可执行文件,这个程序入口点就是ELF文件的文件头中e_entry所指的地址;对于动态链接的ELF可执行文件,程序入口点是动态链接器
9. PE文件的装载过程
先读取文件的第一页,在这个页中,包含了DOS头、PE文件头和段表
检查进程地址空间中,目标地址是否可用,如果不可用,则另外选一个装载地址。这个问题对于可执行文件来说基本不存在,因为它往往是进程第一个装入的模块,所以目标地址不太可能被占用
使用段表中提供的信息,将PE文件中所有段一一映射到地址空间中相应的位置
如果装载地址不是目标地址,则进行rebasing
装载所有PE文件所需要的DLL文件
对PE文件中的所有导入符号进行解析
根据PE头中指定的参数,建立初始化堆和栈
建立主线程并且启动进程