源程序到CPU执行要经历的整个流程

来源:互联网 发布:7.16大连新港火灾知乎 编辑:程序博客网 时间:2024/05/17 00:13

以下内容是个人揣测,不正确的几率很高,这里只是写下来备忘。

这里以X86平台为例进行说明。

 

 

编译:

       编译器在编译代码的时候,是以一个cpp和一个h为基本编译单元的,每个编译单元我们这里称为一个编译模块,每个编译模块都可能定义全局变量和静态变量以及局部变量,同时可能引用其他.编译模块中的全局变量。每个编译单元编译后生成的都是一个.obj文件,这个文件中可以说是由很多段组成,这些段有很多,最基本的有3种,代码段,数据段和BSS段。

代码段中存放的就是我们的函数了,数据段存放的就是我们已经初始化的全局变量和静态变量,由于他们已经都初始化了,所以他们的内存地址在编译的时候就已经分配了(这里的地址是逻辑地址,也就是基于某个段的偏移值。局部变量运行时才能知道逻辑地址)。编译时分配什么意思哪?也就是说这个obj文件的大小会受这些变量类型的影响,等于已经填充了这个OBJ文件。但是对于那些未初始化的全局变量和局部变量,一般都不真正的为他分配内存空间,毕竟还没有初始化吗,所以分配内存空间也是填充0,填充后我们的OBJ文件就要大很多,OBJ文件大了就要浪费磁盘空间,所以BSS段只是一个占位符号一样。这里其实还有很多其他段,都是为了辅助连接程序弄的。不过,编译后,所有涉及和地址有关的代码,基本都是转换成了位置无关代码,什么意思哪,比如一个跳转指令,jmp,他跳转的地址是基于本条跳转指令的偏移,而不是基于段的偏移,所以他就可以被操作系统加载到任意的内存中而不用对代码地址进行调整。但是还有一部分指令是基于段内的偏移地址,比如基于代码段的偏移地址,这种地址相对也好办,但是还有一种地址,是可能跨段的,如下

Jmp CS2::OFFSET,这种地址反映在我们的编译单元中就是我们这个编译单元使用了另外一个编译单元中的数据,这样就会产生这样一个跨段的地址,并且这个地址暂时还是未知的。只能等到链接程序工作的时候才能确定。

 

链接:

       由于每个编译单元编译出的OBJ都含有很多性质类似的代码段,数据段等,显然,太多了没有什么好处,所以这里连接器就把性质相似的段合并在一起,同时,上次编译时那些无法定位地址的指令也会涉及到地址重定位。最终,我们的代码涉及到地址有关的部分,还是大致分成了3类,一类是地址无关的,一类是基于段偏移的,一类是跨段的。但是这里有一点要注意,连接器链接出来的程序,可以指定基础地址(一般叫基址),比如0,或者某个确定的值(EXEDLL都有默认值)。这样,link后的EXE或者DLL中的代码段和数据段又都是基于这个基址偏移的。所以,我们的那个长指令jmp 段:偏移就会被编译成相对于基址的偏移。

 

 

加载:

 这里记住,我们的整个exe或者dll是基于某个基址编译的,所有的涉及段地址的指令都是基于这个基地址的。所以,如果操作系统正好把我们的程序加载到内存的这个物理地址处,那么一切都完美,程序可以正常执行,但是,程序加载到哪里是动态确定的,所以,如果程序加载到了其他地址处,那么我们那些jmp 段址:偏移的指令就会失效。为了避免失效,我们必须修改那些使用段址的指令,把其中的段址部分修正为操作系统实际加载到的物理地址。这样貌似我们的程序就能正确的工作了,但是问题比我们想象的要复杂,因为X86保护模式下段寄存器中存放的不是段址了,而是段的选择子。由选择子才去选择真正的段址。选择子指向一个描述符,其中描述符中记录了这个段的物理基址,以及这个段的长度,甚至段的可读取权限。很多个描述符组织在一起就相当于一个表了,X86中有两种表,一种是GDT,一种是LDTGDT大部分是存放的操作系统本身的代码段和数据段等段的描述符,而LDT表是存放的应用程序自身的各个段的描述符,并且每个应用程序都会有一个LDT表。

所以,为了适应X86CPU的寻址基址,操作系统必须为我们的程序建立一个LDT表格,这个表格中为我们的每个段(代码段,数据段等)建立一个描述符,描述符真正包含了我们的相应段的真实基址。由于操作系统帮我们加载进来的程序,所以他很清楚每个段的真实的地址。所以他完全可以建立这样一个局部描述符表。建立这张表的时候,根据当前程序使用的段,一般有CS,DS,ES,FS,GS,就可以建立各个段的选择子并且填充,同时,GDT表中加入一个我们LDT表的描述符,用于描述我们这个应用程序LDT表在内存中的位置,然后用LDTR指针指向GDT表中的这个描述符。这样,当我们的代码在执行的时候,比如类似

执行:

假设代码段中有这样一条语句

Mov eax  DS::OFFSET

 

很显然,LDTR寄存器可以从GDT中找到我们的LDT表的物理地址,而我们的DS又知道了ES段在LDT表中的选择子,这样我们就可以精确的知道DS这个段的物理地址,然后又知道偏移,那么显然我们就可以寻址了。

 

以上分析是基于找到的资料加个人理解整理出来的,不正确在所难免,只是起一个抛砖引玉的作用,此外,分页模式下可能稍微比这个复杂点。