首页
社区
课程
招聘
8
[原创]读书笔记——DOS下可执行文件的加载
发表于: 2008-11-23 08:26 13210

[原创]读书笔记——DOS下可执行文件的加载

2008-11-23 08:26
13210

关于我:http://hi.baidu.com/dp282074009/blog/item/4b6b92828c715aa70df4d248.html

  本文旨在介绍DOS的内存管理与可执行文件的加载的原理。
在学习本篇文章之前,你有必要了解DOS下.EXE文件的结构。我的另一篇文章——《读书笔记——重定位》中有关于DOS下.EXE文件的介绍。

1.PC微机的地址空间分布
  学习内存管理,则不得不对PC微机的地址空间分布有所了解。在不考虑虚拟存储空间的情况下,内存地址空间的分布大致如图:



  在实模式下,8086/8088有1021KB的内存地址空间,80x86有1088KB的内存空间。注意,这里提及的,都是地址空间,而并非物理地址。地址空间指处理器的地址线所能表示的寻址范围。而地址空间可及,并不代表物理地址可及,这得依赖于系统中安装内存的大小。
由于微机的设计与CPU类型的限制,实模式下并不是所有空间对于用户来说都是可用的。地址空间被划分为四个区域,见上图。DOS用户程序只运行于低端640KB连续内存区,而且DOS本身以及各种驻留程序的占用,用户程序的空间将小于640KB。其中最重要的三大区域是:常规内存、上位内存、延伸内存。不同的区域有不同的用途,管理策略也不尽相同。

1.1 常规内存
  低端的640KB RAM称为常规内存(Conventional Memory),也叫基本内存(Base Memory)。中断向量表、系统数据、通信区、DOS本身以及CONFIG.SYS和AUTOEXEC.BAT文件中列出的设备驱动程序、TSR程序都常驻在此区中,剩余的空间才是用户真正可以使用的(600KB左右)。
  DOS存储管理的系统功能用于管理常规内存,主要是管理用户程序空间。采用单一连续可变分区管理方式。
1.2 上位内存
  从640KB,到1M的地址空间被称为上位内存(Upper Memory)。这些空间是为接口卡的数据缓冲区和ROM-BIOS等使用的。BIOS自检时,也只检查A0000h地址以下的区域。
上位内存的的划分情况如下:
   A0000~AFFFFh(610~704KB)    EGA或VGA的ROM和数据区
   B0000~B7FFFh(704~736KB)    MDA数据区
   B8000~C3FFFh(736~812KB)    CGA或EGA使用
               (736~752KB)    CGA数据区
               (736~784KB)    EGA ROM区
   C4000~DFFFFh(812~896KB)    未用
   E0000~EFFFFh(896~960KB)    其他ROM用
   F0000~FFFFFh(960~1024KB)   ROM-BOIS使用
  上位内存中总存在一些闲置的地址空间,被称为上位内存块UMB(Upper Memory Blocks)。它随系统配置的不同而不同,而且这些地址空间不宜用RAM芯片填充,故也被称为空洞(hole)。DOS的相信管理功能不针对此部分内存。在DOS 6.0的EMM386.EXE能检查上位内存的“空洞”,并使用分布技术对“空洞”进行“回填”。
1.3 延伸内存
  高于1MB的内存区域被称为延伸内存(Extended Memory),也称扩展内存。DOS下不能直接使用此区域。对于80x86 CPU,实栻上高位内存是可以直接访问的。
简单地介绍了内存中的各个区域后,下一节将学习DOS的内存管理。
2.内存管理
  MS-DOS的内存管理是通过一组系统功能调用实现的。它们属于单一连续区管理方式,面向单道程序设计。
2.1 MCB
  DOS实现内存管理的重要数据结构是内存控制块MCB(Memory Control Block)。通过MCB把已分配和空闲的内存按位置顺序连成链。DOS的内存块大小以“节”为单位,1节等于16字节。内存块的边界也是节对齐的。每个内存块的前面都有1节长的区域头(area header),即内存控制块MCB,MCB控制内存块的大小与其他相关信息。
  MCB的结构如下:



  各个域的解释如下:
  标记——为“Z”(5Ah)表示最后一个内存块,为“M”(4Dh)表示为非最后块;
  内存块拥有者——0000h,内存块空闲;非0值,拥有此内存块程序的程序段前缀(PSP)段址。
  内存块大小——以节为单位的内存块大小,不包括此MCB的长度。
2.2 MCB链
  若某MCB地址为XXXX:0000h,则其控制的内存块段址为XXXX+1,MCB段址加上其3~4字节的内存块大小则是下一MCB的段址。即:
  MCB段址+内存块大小+1=下一MCB的段址
于是,由一个MCB提供的信息,便可找到其后续的MCB的位置,系统的全部MCB顺序组成一个MCB链。
  下图为DOS 3.30启动后MCB链的最初状况:



  由图可知,COMMAND.COM常驻部分占用三个内存块,除一块供内部使用外,两个内存块一个是环境块,一个是程序(加数据)块。这两个内存块构成一个进程实体。COMMAND.COM常驻部分之后是暂驻程序TPA(Transient Program Area),即用户程序空间。它是最后一个内存块,未分配,大小近似为600KB。块后是以A000:0000h开始的上位内存。注意,COMMAND.COM暂驻部分位于TPA的高端,且无MCB,故未单独构成一个内存块,可被用户程序覆盖。
  内存块的分配策略一般为:在MCB链上找一个大于或等于请求分配长度的空闲块,然后将此内存块的MCB的“拥有者”域由0000h改为“系统当前PSP段址”。
到此为止,我们应该对微机的内存结构及DOS下的内存管理有了一个大体的了解。接下来将切入主题:DOS下可执行文件的加载。
3.用户程序空间
  有关DOS下.exe文件的结构及重定位的知识,请参见我的另一篇文章:【原创】读书笔记——重定位。可执行文件加载时,DOS为装入模块在TPA区域中分配一块连续的存储区,装入之后一次性地,即静态地完成全部的地址重定位工作。也就是说,运行于实地址模式下的DOS及其连接编译工具软件不支持分段存储和动态重定位。DOS的可执行文件可分为.exe和.com两种。其中,.com文件不超过64KB,无重定位信息,装入后不需要重定位。
程序加载时,DOS实际为用户程序分配两个内存块:一个内存块为环境块;另一个才是程序本身。在程序之前还有256字节的程序段前缀PSP.两个内存块都有各自的MCB。
3.1 .EXE文件映象的加载
  经连接器连接之后产生的.EXE文件是一个可执行的浮动代码文件。该文件在磁盘中由头部信息块与浮动装入模块组成。头部信息块用于指导加载器对装入模块的装入。在确定了装入模块的实际装入段基址XXXX后,程序的代码段和堆栈段段值均可确定。
在装入.EXE文件前,系统会检查TPA区中可用内存空间的容量与相当装入模块的大小。装入模块的大小由其头部04~05h域的总扇区数、02~03h域的最后扇区有效字节数和08~09h域的头部信息块长度计算得出。装入内存可分为低端和高端装入:



1.低端装入
  经连接器连接时的未使用/H选项,生成的EXE文件则要求在可用内存的低地址端开始装入,这是一般情况。此时申请人分配块长度,除装入模块本身大小和PSP的10h节长度之外,还要考虑装入模块之后应保留的节数。格式化区字节0A~0Bh域给出应保留节数的最小值,字节0C~0Dh域中给出最大值。前者是强制性因素。若可用内存空间的容量小于装入模块、PSP、最小保留节数三者之各,则不允许装入。4Bh号系统调用出错返回。
  如果装入成功,则如上图(a)和(b)。装入模块紧邻PSP之后,装入起点的段址等于分配块段址加上10h。
  2.高端装入
  经连接器连接时选择/H选项,则生成.EXE文件的格式化区字节0C~0Dh域中值为0,表示要求在可用内存空间的高地址端装入。
  只要可用内存空间容量不小于装入模块本身长度与PSP长度(10h节)之和,即可装入、此时1Bh号系统功能把全部可用内存空间都分配给此.EXE文件。装入模块的最高地址与可用空间的最高地址相重合。但装入模块的最低地址与PSP之间可能有一个示使用的空闲区,如上图(c)所示。装入起点的段址等于分配块段址加分配块长减去装入模块长。
3.2 .COM文件映象的加载
  .COM文件无头部信息块,只有一个无需重定位的、长度不大于64KB的装入模块(实际上不能大于65280字节)。4Bh号系统功能装入.COM文件时也要测试当前可用内存空间容量,并且要求其容量大于PSP长度(100h字节)与文件实际长度(采用实读方式来判断)之和,否则装入出错。
债台高筑装入的情况下,4Bh号系统功能将全部可用内存空间都分配给.COM文件。如果可用内存空间容量大于64KB,则也只是以其低端的64KB作为装入空间。于是高端有一个虽已分配但未使用的区域。当然,对于可用内存空间小于或等于64KB的情况下,则全部可用内存空间都作为装入空间了。两种情况.COM文件的内存映象如下图:



  .COM文件的装入起始位置总在装入空间预留出100h字节给PSP,而且约定它也是程序启动点位置,即装入后令CS:IP指向它。因此装入模块的起始位置应是存放一条指令而不应是数据,此位置的偏移量为100h.4Bh号系统功能还总在装入空间的最高端地址减2的位置上写入全0的一个字,并令SS:SP指向这里。

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    
  .EXE文件和.COM文件装入后,系统对各寄存器的设置情况如下表:



  注意,4Bh号系统功能还可只装入文件但不执行文件,此时将不设置上述这些寄存器。如果是装入并执行的话,4Bh号系统功能还要在启动程序之前,修改分配给程序的两个内存块的MCB中的“拥有者”域为分配块的段址(即PSP段址),使被加载的程序真正成为两个内存块的主人,并使此PSP段址为“系统当前PSP段址”。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 8
支持
分享
赞赏记录
参与人
雪币
留言
时间
Youlor
为你点赞~
2024-1-4 03:06
伟叔叔
为你点赞~
2023-12-2 00:00
QinBeast
为你点赞~
2023-9-10 04:03
PLEBFE
为你点赞~
2023-8-26 02:32
shinratensei
为你点赞~
2023-8-14 03:19
心游尘世外
为你点赞~
2023-8-4 00:15
飘零丶
为你点赞~
2023-7-21 02:21
killsu
为你点赞~
2020-12-16 20:38
最新回复 (11)
雪    币: 163
活跃值: (41)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
2
上传一个PDF版的。
上传的附件:
2008-11-23 08:42
0
雪    币: 211
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
楼主写的不错好文章
2008-11-23 20:59
0
雪    币: 389
活跃值: (92)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
发现自己变懒了,习惯了微软的VC,从来都不去考虑底层的问题,哎!
2008-11-26 20:31
0
雪    币: 233
活跃值: (15)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
5
very good, study
2008-11-27 15:53
0
雪    币: 206
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
谢谢楼主共享
2008-12-8 14:04
0
雪    币: 212
活跃值: (36)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
强人啊 啊啊啊
2008-12-8 19:35
0
雪    币: 560
活跃值: (278)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
收藏了慢慢学
2008-12-8 23:47
0
雪    币: 231
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
qdk
9
附个代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
.586P
 
LOADER_SIZE EQU 1024
PADDING_100h    EQU 100h
 
code_seg segment use16
 
    org PADDING_100h
startup:
    jmp main
 
include int21.inc
 
Error_Str   db  'Error!'
Error_Str_Len   Equ $-Error_Str
 
PrintStr Proc near C USES AX BX CX ES BP,\
    Color:BYTE,Position:WORD,\
    StrAddrOffset:WORD,StrLen:WORD
 
    mov cx,StrLen
    mov bl,Color
    mov dx,Position
 
    push    bp
    push    StrAddrOffset
    pop bp
     
    mov bh,0
    mov al,0
    mov ah,13h
    int 10h
    pop bp
    ret
PrintStr endp
 
 
WaitKey Proc near C USES AX,KeyCode:BYTE
    .repeat
        mov ah,0
        int 16h
    .until  al == KeyCode
    ret
WaitKey endp
 
Relocate_And_Exec   Proc near C
    Local   Reloc_Table_Addr:WORD
    Local   Reloc_Item_Num:WORD
    Local   Exe_Header_Size:WORD
    Local   Exe_Entry_Point:WORD
    Local   Reloc_CS:WORD
    Local   Init_SS:WORD
    Local   Init_SP:WORD
     
        call    Current_Ip
    Current_Ip:
        pop bx
        add bx,Exe_Start-Current_Ip
         
    .if word ptr[bx] !='ZM' ;find the exe file's magic number
        stc
        ret
    .endif
    mov word ptr[Reloc_CS],bx
    shr word ptr[Reloc_CS],4    ;add reloc_cs with loader_size and psp size
    mov ax,cs
    add ax,word ptr[bx+8]  
    add word ptr[Reloc_CS],ax
 
     
    mov ax,word ptr[bx+18h] ;get the reloc_table address
    mov word ptr[Reloc_Table_Addr],ax
     
    mov ax,word ptr[bx+6]   ;get the reloc_item number
    mov word ptr[Reloc_Item_Num],ax
 
    mov ax,word ptr[bx+8]   ;get the exe header_size/16
    shl ax,4            ;X16
    mov word ptr[Exe_Header_Size],ax
 
    mov ax,word ptr[bx+0eh]
    add ax,word ptr[Reloc_CS]
    mov word ptr[Init_SS],ax
 
    mov ax,word ptr[bx+10h]
    mov word ptr[Init_SP],ax
 
    mov ax,word ptr[bx+14h]
    mov word ptr[Exe_Entry_Point],ax
 
    xor cx,cx
    .while  cx < word ptr[Reloc_Item_Num]
        mov si,cx          
        shl si,2            ;reloc_item size is 4 bytes
        add si,Reloc_Table_Addr
        mov di,word ptr[bx+si+2]    ;cs
        shl di,4   
        add di,word ptr[bx+si]  ;ip
         
        add di,bx
        add di,word ptr[Exe_Header_Size]
         
        mov ax,word ptr[Reloc_CS]
        add word ptr[di],ax
        inc cx
    .endw
 
    ;mov    si,0
    ;mov    di,LOADER_SIZE
    ;add    di,word ptr[Exe_Header_Size]
    ;sub    di,100h
    ;mov    cx,100h
    ;cld
    ;rep    movsb [di],[si]
 
    mov ax,word ptr[Reloc_CS]
     
    mov bx,word ptr[Exe_Entry_Point]
    mov cx,word ptr[Init_SS]
    mov dx,word ptr[Init_SP]
     
     
    mov ds,ax
    mov es,ax
 
    mov ss,cx
    mov sp,dx
     
     
    push    ax  ;cs
    push    bx  ;ip
    retf
Relocate_And_Exec endp
main:
    mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov sp,0fffeh
     
    push    ds
 
    push    0
    pop ds
     
    mov word ptr ds:[21h*4],Int21_Handler
    mov word ptr ds:[21h*4+2],cs
    pop ds
     
     
    invoke  Relocate_And_Exec
    .if Carry?
        invoke  PrintStr,57h,0,offset Error_Str,Error_Str_Len
        int     21h
    .endif
    invoke  WaitKey,'n'
ret
;---------------------------
padding_size    equ $-startup
padding db  LOADER_SIZE-padding_size dup(0)
Exe_Start:
code_seg ends
end startup

详细请看
http://hi.baidu.com/quick1/blog/item/9eb0f9ea6aaad8d4d539c916.html
2009-2-22 04:19
0
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
楼主厉害呀!
2009-3-11 10:53
0
雪    币: 496
活跃值: (4969)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
谢谢楼主分享这么好的资料。。。
2009-3-12 21:57
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
很有收获,多谢楼主
2009-3-19 22:18
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册