EIF加载---虚拟物理地址加载
目录
原理上理解动态库
可执行程序的格式
重谈区域划分和动态库的加载
我们今天来学习虚拟物理地址加载
原理上理解动态库
动态库也是需要加载到磁盘的,当动态库加载到磁盘时,当一个进程来调度它时,进而进入内存,此时主要的信息main data被保存在内存中,由于进程调度时访问的是库的mm_struct虚拟地址,接着通过页表映射到这个库的地址,当进程需要执行库方法时,每个库方法都会将虚拟地址直接加载到mm_struct上,我们的进程执行库方法时,是在来回跳转的,是在自己的地址空间中跳转的,那再说回动态库,那其加载到内存的共享区,属于共享库,为什么呢,因为一个库可能会有很多个可执行文件来调用,那加载到共享库就所有的可执行程序都可见了,库只需要加载一次,这样节省空间,加载方式也选择动态加载的方式,所以像这种所有进程都共享的库就叫做共享库。
可执行程序加载到内存然后另起一个新的进程来调用,产生新的页表和映射,那可执行程序的地址是这么被找到的,mm_struct在进程调用时是用谁的数据进行初始化的呢?
readelf -h
主要是用来看 ELF 文件的文件头(Header)信息
readelf -l
用来查看 ELF 文件的程序头表(Program Header Table),它和 readelf -h
不同,-h
只给出 ELF 的整体信息,而 -l
具体展示了程序的加载方式,包括段映射信息,对于理解 GOT 映射很重要!
我们可以看到这些load就是需要加载的信息,flag就是加载方式了,有只读,只写等等。
可执行程序的格式
可执行程序遵循ELF格式,这个格式分为ELF头,section,section里面存储多个section,编号从1.到n,里面存储这个可执行程序的数据,所以读取可执行程序到内存的本质就是OS加载这些section,所有的.c文件编译成.o完后链接起来就是将我们一个一个的相同属性的section进行合并,那其实一个可执行程序要加载到操作系统的前提是操作系统要认识这个程序,既然要加载这么多的section,那么对于任何一个文件,文件的内容就是一个巨大的一维数组,多个section叠起来所以像,那标志文件任何一个区域用的就是起始地址+偏移量大小的方式。
mm_struct
主要由 内核 在 进程创建时 进行初始化的,所以当一个可执行程序加载到磁盘和物理内存时,一个进程来调度它,此时内核初始化了虚表,在物理内存里面的程序的内部使用虚拟地址,外部暴露出物理地址,当进行需要调度程序的某个指令时,其内的pc寄存器拿到暴露在外面的物理地址和可执行程序的路口,可执行程序只需要提供路口就可以了,然后页表根据这个物理地址形成映射,pc解析完指令,将指令的虚拟地址交给EIP寄存器,那CR3这个寄存器指向的是页表的映射关系,有帮助形成虚拟地址的功劳,既然要形成虚拟地址,所以CR3内部就肯定是物理地址了。进程在执行程序时使用的是虚拟地址,是直接指向mm_struct的,到头来可执行程序是由地址的,虚拟地址空间是操作系统,cpu,编译器共同协作下的产物。
那可执行程序的地址排布长什么样,可执行程序的地址称为逻辑地址,逻辑地址=起始地址+偏移量,这个偏移量可从(00000...00, FFFFFRF...FFFF),ELF在没有加载到内存的时候就已经按照从最大到最小进行编址了,逻辑地址(磁盘ELF)就是虚拟地址(加载到内存中)。我们程序内部自己在调用的也是虚拟地址!!!
既然讲到了地址是偏移的,所以一个可执行程序加载到内存也只需要加载首地址+偏移量就可以了。
如上,readelf -S a.out
用于查看 ELF 文件的节头表(Section Header Table),它显示 ELF 文件中有哪些段(Section),以及它们在文件中的位置、大小、标志等信息。
可以看到库内部指令地址展示的都是偏移量。
反汇编可以帮助查看指令或者库函数的地址!!!
重谈区域划分和动态库的加载
mm_struct里面的区域划分是由很多个vm_area_struct链接起来的,这些区域都有start和end,那动态库的虚拟地址加载到虚表后,肯定占用的一个空间,位置是不固定的,所以和位置无关,肯定有libstart和libend,进行需要调用库则需要把库映射到进程的地址空间,那库肯定是需要被OS管理的,库加载到内存时,会加载描述库的结构体struct libso,及其基地址+偏移方式和偏移量,当一进程要调用库的方法,那要去虚表的代码区拾取,这时需要借助已经在内存里面的可执行程序,可执行程序里面的got(全局偏移量表)存储不同的指令或者库函数的编号,通过got的下标找到这个函数,然后修改代码区的指向,从而不是修改代码区所调函数的地址,这就是地址重定位,GOT的映射就是数字和所对应库函数地址的映射,所以叫地址重定位,这也解决了代码区只读的特点,所以还可以认为GOT映射+库函数方法偏移量就决定了调用库函数和地址无关了。
操作系统是这么管理动态库的呢,先描述再组织!!!