|
运行重定位exe文件,重定位因子调度覆盖模块,链接器3制静态覆盖块,析栈初始startup
(1) X86汇编语言的组,段,类
伪指令group/segment,定义组/段,这使:
(1.1) 同组的各段,处于同一64k空间
(1.2) 段可声明'对齐,组合,类'可选属性:
(1.2.1) 段首,能对齐到BYTE,WORD,PARA(16字节),PAGE(256字节),缺省对齐到PARA. (1.2.2) 不同源文的同名同类段,想靠链接,形成空间邻接或重叠的同名组合逻辑 段时,需用PUBLIC,COMMON,指明组合形式:
(1.2.2.1) PUBLIC,指明此段按对齐属性,邻在已包含在此组合逻辑段内的最末字面段之后 (1.2.2.2) COMMON,指明此段与同属此组合逻辑段的其他字面段,从组合逻辑段首铺展
同属组合逻辑段的各参与段的最大对齐属性(例如,WORD比BYTE大),决定组合逻辑段的对齐属性.
不与其它段组合的段,称为单逻辑段.
以下,用"逻辑段",统称组合逻辑段及单逻辑段.
(1.3) 类用'cls'指明,未指明类的各段,属'匿名'类.同类各段,邻接排列.
(2) 解释各逻辑段空间关系的3个源文
a1.asm,声明1个组,4个段,涉及'匿名','dec_'类
(2.1) grp组,含seg1段 (2.2) seg1段 (2.3) ovlap段,用COMMON参与组合,'dec_'类 (2.4) touch段,用PUBLIC参与组合 (2.5) stk1段,用STACK指明栈段及PARA对齐
grp GROUP seg1
seg1 SEGMENT PAGE str1 DB "S1$" seg1 ENDS
ovlap SEGMENT PARA COMMON 'dec_' str3 DB '11$' ovlap ENDS
touch SEGMENT PUBLIC ASSUME cs:touch,ds:NOTHING
ORG 11H
@: mov ah,9
mov bx,grp ;取grp段值 mov ds,bx
mov dx,OFFSET str1 ;取str1相对seg1偏置 int 21h
EXTRN str2:far
mov dx,OFFSET grp:str2 ;取str2相对grp偏置 int 21h
ASSUME ds:ovlap
mov bx,SEG str3 ;取str3段值 mov ds,bx lea dx,str3 ;取str3相对ds偏置 int 21h
touch ENDS
stk1 SEGMENT PAGE STACK DW 16H DUP (4AH) ;初值4AH的16H个字 stk1 ENDS
END @
a2.asm,声明1个组,3个段,涉及'匿名','dec_','_stk'类:
(2.6) ovlap段,用COMMON参与组合,'dec_'类 (2.7) stk2段,用STACK指明栈段及PARA对齐,'_stk'类 (2.8) grp组,含seg3段 (2.9) seg3段,'dec_'类
ovlap SEGMENT COMMON 'dec_' DB '2' ovlap ENDS
stk2 SEGMENT WORD STACK '_stk' DW 13H DUP (2BH) ;初值2BH的13H个字 stk2 ENDS
grp GROUP seg3
seg3 SEGMENT 'dec_' PUBLIC str3 str3 DB '33$' seg3 ENDS
END
a3.asm,声明1个组,4个段,涉及'匿名','_stk'类:
(2.10) grp组,含seg2段 (2.11) touch段,用PUBLIC参与组合 (2.12) seg2段 (2.13) stk2段,用STACK指明栈段及PARA对齐,'_stk'类 (2.14) seg1段
grp GROUP seg2
touch SEGMENT BYTE PUBLIC
EXTRN str3:far ASSUME cs:touch,ds:NOTHING
@1: mov cx,SEG grp ;取grp段值 mov ds,cx
mov dx,OFFSET grp:str3 ;取str3相对grp偏置 int 21h
mov cx,seg1 ;取seg1段值 mov ds,cx
mov dx,OFFSET seg1:str4 ;取str4相对seg1偏置 int 21h
mov ah,4ch ;程序终止 int 21h
touch ENDS
seg2 SEGMENT BYTE PUBLIC str2 str2 DB "S2$" seg2 ENDS
stk2 SEGMENT WORD STACK '_stk' DW 31H DUP (0B2H) ;初值B2H的31H个字 stk2 ENDS
seg1 SEGMENT BYTE str4 DB "%$" seg1 ENDS
END @1
用masm5.exe(版5.10B,囿95 DDK),生成同名obj及lst文件.例如,汇编a1,命令行是: masm5 a1,,a1;
用含覆盖管理器的link4.exe(版5.01.17,同囿DDK),依序链接obj,生成a.exe程序文件,a.map映像文件,命令行是:link4 a1+a2+a3,a,a,,,
a.exe,显'S1S22133%'
a.map含:
Start Stop Length Name Class 00000H 00002H 00003H SEG1 00010H 00055H 00046H TOUCH 00060H 0008BH 0002CH STK1 0008CH 0008EH 00003H SEG2 0008FH 00090H 00002H SEG1 000A0H 000A2H 00003H OVLAP DEC_ 000B0H 000B2H 00003H SEG3 DEC_ 000C0H 00147H 00088H STK2 _STK
Origin Group 0000:0 GRP
entry point at 0001:0011
(3) 逻辑段排列准则:按链接次序,按字面段的源文次序,按类名大写ASCII次序
因此,a.exe含8个逻辑段(段名被大写):
(3.1) '匿名'类的a1的单逻辑段SEG1 (3.2) '匿名'类的a1与a3的组合逻辑段TOUCH,参与的a1及a3中touch字面段,代码相接. (3.3) '匿名'类的a1的单逻辑段STK1 (3.4) '匿名'类的a3的单逻辑段SEG2 (3.5) '匿名'类的a3的单逻辑段SEG1 (3.6) 'DEC_'类的a1与a2的组合逻辑段OVLAP,参与的a2及a3中ovlap字面段,使str3变为'21$' (3.7) 'DEC_'类的a2的单逻辑段SEG3 (3.8) '_STK'类的a2与a3的组合逻辑段STK2,参与的a2及a3中stk2字面段,按PUBLIC,两空间邻接
(4) 链接时,程序cs:ip及ss:sp设定
用'END expression'形式指明的各伪指令,最先者,expression所在段,及expression相对所在段的偏置,是程序的cs:ip.无此伪指令,cs,ip是0.
例如,a1的'END @'先于a3的'END @1',因此,程序a.exe入口,cs=1,是@所在段相对exe文件装入模块首的节数,ip=11,是@相对所在段的偏置.
用组合属性STACK指明的各逻辑段,map文件最末者,是栈段;无此逻辑段,map文件最先逻辑段,是栈段.
例如,指明STACK的组合逻辑段STK2,后于单逻辑段STK1,因此,程序a.exe栈区,ss=STK2相对exe文件装入模块首的节数0ch,sp=88h,是STK2尺寸.
(5) 汇编时的机器码暂定
能/不能被链接解决的作为操作数的段值/段偏置,其指令后面,带后缀R被暂定;能被链接解决的外部定义,其指令后面,带后缀E被暂定.因此,
a1.lst含:
0013 BB ---- R mov bx,grp 0018 BA 0000 R mov dx,OFFSET str1 001D BA 0000 E mov dx,OFFSET grp:str2 0022 BB ---- R mov bx,SEG str3 0027 8D 16 0000 R lea dx,str3
a3.lst含:
0000 B9 ---- R @1: mov cx,SEG grp 0005 BA 0000 E mov dx,OFFSET grp:str3 000A B9 ---- R mov cx,seg1 000F BA 0000 R mov dx,OFFSET seg1:str4
(6) 链接时的机器码暂定,及运行重定位exe文件
作为操作数的段值,相对装入模块首,被暂定.外壳command.com启动命令行上的程序时,先开辟被节号x,节长1的存储控制块(MCB)标识的空闲内存,此MCB前3个域值为:字节值5A(内存块链尾),字0(无效进程PSP),字y(x+1+y=A000段,即640K内存尾),然后在x+1段首,先建10节程序段前缀(PSP),后铺展程序的装入模块.
这时,exe装入头的cs/ss值,与x+11h(叫"装入段值")相加,形成运行cs/ss值,此为"运行重定位exe".装入模块中,链接时的各机器码暂定,与外壳指定的"重定位因子"相加,形成运行逻辑段值.
一般地,重定位因子=装入段值.也可用功能4b03的装入段值/重定位因子两参数,分别指定,如(8)所述.
因此,改a1.exe为a,做debug a,用U 321,反出代码区:
0321 B409 MOV AH,09 0323 BB0000 MOV BX,0000 ;取grp段值 0326 8EDB MOV DS,BX 0328 BA0000 MOV DX,0000 ;取str1相对seg1偏置 032B CD21 INT 21 032D BA8C00 MOV DX,008C ;取str2相对grp偏置 0330 CD21 INT 21 0332 BB0A00 MOV BX,000A ;取str3段值 0335 8EDB MOV DS,BX 0337 8D160000 LEA DX,[0000] ;ASSUME ds:ovlap时,取str3相对ds偏置 033B CD21 INT 21 033D B90000 MOV CX,0000 ;取grp段值 0340 8ED9 MOV DS,CX 0342 BAB000 MOV DX,00B0 ;取str3相对grp偏置 0345 CD21 INT 21 0347 B90800 MOV CX,0008 ;取seg1段值 034A 8ED9 MOV DS,CX 034C BA0F00 MOV DX,000F ;取str4相对seg1偏置 034F CD21 INT 21 0351 B44C MOV AH,4C 0353 CD21 INT 21
(7) MS-DOS的exe文件两部构成:
前部,含装入头(1ch字节长)及重定位表;后部,是装入模块.
链接时的各机器码暂定,相对装入模块首的偏置:节,用2个字w_o,w_p,存于重定位表.
(7.1) 装入头的字节偏置:
0,1 : 4d(M),5a(Z)标识 2,3 : exe文件净长度,除以512后的余数 4,5 : exe文件净长度,被512量的值 6,7 : 重定位表的项数 8,9 : 前部占用节数 a,b : 装入模块之后,所需最小节数 c,d : 装入模块之后,所需最大节数 e,f : ss相对装入模块首的节数 10,11 : sp 12,13 : exe文件的字检查和 14,15 : ip 16,17 : cs相对装入模块首的节数 18,19 : 重定位表相对exe文件的字节偏置 1a,1b : 覆盖号
a,长840字节(348h),做debug a,用D 100 L2e,显出exe文件头的前2Eh个字节:
0100 4D 5A 48 01 02 00 04 00-20 00 00 00 FF FF 0C 00 0110 88 00 D3 A2 11 00 01 00-1E 00 00 00 01 00 14 00 0120 01 00 23 00 01 00 2E 00-01 00 38 00 01 00
(7.2) 解释装入头:
(7.2.1) a的长度,除以512后,余148H,放2,3字节 (7.2.2) a的长度,被512量,占2,放4,5字节 (7.2.3) 重定位表,含4项 (7.2.4) exe文件前部,占20H节 (7.2.5) 重定位表相对a,字节偏置是1eh
(7.3) 相对a,字节偏置是1eh的重定位表解释:
第1项,w_o=14h,w_p=1,指出此机器码暂定,相对装入模块首,是1节14h偏置,即1*10h+14h=24h(字节).
debug从100h装a,向高20h节(200h字节),是装入模块首,(100h+200h)加上24h的324h处,恰为MOV BX,0000中的段值位置.
第2项,w_o=23,w_p=1,相对装入模块首,是1节23h偏置,即1*10h+23h=33h(字节).300h加上33h的333h处,恰为MOV BX,000A中的段值位置.
同理,第3,4项,是MOV CX,0000及MOV CX,0008中的段值位置.
(8) 重定位因子调度覆盖模块
功能4b03,铺展exe装入模块.前部略去PSP的装入模块,称覆盖模块.
调用覆盖模块cs:ip入口的常驻者(如ovlayer.exe),要做:
(8.1) 将覆盖模块所在exe装入头中的程序cs:ip,读到entry (8.2) 调用功能4a,释放其尾后内存 (8.3) 填写4b03的装入段值/重定位因子两参数 (8.4) 调用功能4b03,它在空闲内存MCB之高1节,铺展覆盖模块 (8.5) 用call entry语句,调用覆盖模块cs:ip入口
覆盖模块(如ovlayee.exe)工作完时,用retf,返回控制到常驻者.
覆盖模块被功能403铺展时,与(6)同样处理,作为mov ax,work操作数的段值work,与重 定位因子相加,这使ovlayee.exe,被ovlayer.exe调度,显示某区/某串值,例如,先显 o1区串s1值'os1',再显o2区串s2值'os2'.
(8.6) ovlayee文 要点: 只含1个逻辑段work,故work的机器码暂定,相对装入模块首,必为0,与重 定位因子的相加结果,恰=重定位因子.
要点: 不需写"END @"伪指令,让cs/ip为0,而ss/sp,借用ovlayer.exe栈区.
work segment assume cs:work
push ds mov ax,work mov ds,ax
mov ah,9 ;显串 int 21h ;dx被ovlayer指定 pop ds
retf ;远返回
work ends END
(8.7) ovlayer文 要点: 引入PARA对齐的tail空段,使长度可变的ovlayer.exe,从PSP首到tail,作为新占用内存,tail之上,是空闲内存MCB. 要点: mov bx,tail语句之后,勿写标号,否则masm5报错.
o1 segment s1 db 'os1$' o1 ends
o2 segment s2 db 'os2$' o2 ends
arg segment loadseg dw 2 ;装入段值 factor dw 2 ;重定位因子 entry dd 4 filenam db 'ovlayee.exe',0 header db 1ch dup(1) arg ends
stk segment STACK dw 16 dup(9) stk ends
root segment assume cs:root,ds:NOTHING
@: mov ax,arg mov ds,ax
mov ax,3d00h ;读打开 mov dx,offset arg:filenam int 21h jnc @F ;@F,@B,向前/后找最近的@@标号
mov ah,4ch int 21h
@@: mov bx,ax ;句柄送bx
mov ah,3fh ;读装入头到header mov cx,1ch mov dx,offset arg:header int 21h
mov ah,3eh ;关闭 int 21h
mov bx,tail ;tail指向映像尾节 mov di,es sub bx,di ;bx是此程序新占用内存的节数
mov ah,4ah ;新占用内存,始于es所指PSP节,长bx节 int 21h
add bx,di inc bx
mov ds:loadseg,bx ;装入段值=MCB之高1节
mov ax,seg s1 ;重定位因子=s1所在段 mov ds:factor,ax
mov ax,word ptr ds:[header+14h] ;取覆盖模块ip mov word ptr ds:[entry],ax
add bx,word ptr ds:[header+16h] ;覆盖模块的cs,加上MCB之高1节 mov word ptr ds:[entry+2],bx
mov ax,ds mov es,ax
mov ax,4b03h mov bx,offset arg:loadseg ;es:bx指参数块 mov dx,offset arg:filenam ;ds:dx指程序名 int 21h
mov dx,offset o1:s1 ;dx是s1相对o1的偏置 call ds:entry
mov ax,seg s2 mov ds:factor,ax ;重定位因子=s2所在段
mov ax,4b03h mov bx,offset arg:loadseg ;es:bx指参数块 mov dx,offset arg:filenam ;ds:dx指程序名 int 21h
mov dx,offset o2:s2 ;dx是s2相对o2的偏置 call ds:entry
mov ah,4ch int 21h
root ends
tail segment ;空段 tail ends
END @
(9) 链接器3制静态覆盖块
link4的obj参数,囿于括号时,生成OVERLAY_DATA,OVERLAY_AREA,OVERLAY_END块,使DOS的exe长达16M.
用4个源文阐述:
(9.1) root文 要点: 入口@需处于装入模块首 要点: extrn的near/far,声明近/远标号
c_root segment assume cs:c_root
extrn ov_near:near,ov_far:far,ov_far1:far
@: call ov_near ;近调用 call ov_far ;远调用 jmp ov_far1 ;远转移 c_root ends
stk segment STACK dw 16 dup(1) stk ends
END @
(9.2) ov_near文 要点: ret是近返回
c_near segment assume cs:c_near
public ov_near
ov_near:mov ah,2 mov dl,'N' int 21h
ret ;近返回
c_near ends END
(9.3) ov_far文 要点: retf是远返回
c_far segment assume cs:c_far
public ov_far
ov_far: mov ah,2 mov dl,'F' int 21h retf ;远返回
c_far ends END
(9.4) ov_far1文: c_far1 segment assume cs:c_far1
public ov_far1
ov_far1:mov ah,2 mov dl,'f' int 21h
mov ah,4ch int 21h
c_far1 ends END
用masm5,生成扩展名为obj的root,ov_near,ov_far,ov_far1
用link4 root (ov_near ov_far) (ov_far1),root3,3,,, 生成root3.exe,3.map
exe文件,未囿于括号的obj的各逻辑段,及不属于'CODE'类的代码,是常驻部分,计为0号覆盖块.括号组,生成1号,2号覆盖块.
root3.exe,显'NFf'
改root3.exe为root3,做debug root3,用U 300,反出代码区:
0300 E82D00 CALL 0330 0303 9A40000000 CALL 0000:0040 0308 EA50000000 JMP 0000:0050
3.map含:
Start Stop Length Name Class Resident 00000H 0000CH 0000DH C_ROOT 00010H 0002FH 00020H STK 00030H 00036H 00007H C_NEAR 00040H 00046H 00007H C_FAR 00050H 00059H 0000AH C_FAR1 00060H 00082H 00023H OVERLAY_DATA DATA 00090H 00090H 00000H OVERLAY_AREA CODE 00090H 00090H 00000H OVERLAY_END CODE Overlay 1H Overlay 2H
Origin Group 0006:0 DGROUP
改root3.exe为root3,做debug root3,用D 100 L2A,显出exe文件头的前2Ah个字节:
0100 4D 5A 83 00 02 00 03 00-20 00 01 00 FF FF 01 00 0110 20 00 79 52 00 00 00 00-1E 00 00 00 01 00 04 00 0120 06 00 06 00 00 00 0B 00-00 00
root3的重定位表,含3项:
第1项,w_o=4,w_p=6,指出OVERLAY_DATA块的字节偏置4,5 第2项,w_o=6,w_p=0,指出CALL 0000:0040中的段值位置. 第3项,w_o=b,w_p=0,指出JMP 0000:0050中的段值位置.
用D 360 L23,显出OVERLAY_DATA块:
0360 01 00 03 00 00 00 00 00-00 00 00 00 00 00 00 00 0370 00 00 00 52 4F 4F 54 33-2E 45 58 45 00 00 00 00 0380 00 00 3F
笔者探出,对OVERLAY_DATA块,字节偏置2,3,是覆盖块数3,偏置4,5,是cs相对装入模块首的节数.
(10) 析栈初始
ML611,汇编map含
00000H , 00000H _TEXT CODE 00000H , 00019H _DATA DATA 00020H , 00020H STACK STACK 00040H , 00033H TAIL
Origin Group 0000:0 DGROUP
entry point at 0004:0000
的t.asm
.model small ;仅数据段,代码段
.data ;随psp
assume cs:@data
cry dw '$!'
i72: push ax push dx push ds
mov ah,9
mov dx,cs mov ds,dx
lea dx,cry int 21h
pop ds pop dx pop ax iret
i27 dw 0,@data
.stack 32
tail segment
.startup
mov ax,2572h lea dx,i72 int 21h
mov dx,256+i27 ;计psp的驻容
mov byte ptr es:[1],27h ;es矢psp
sub i27[2],16 ;矢psp call dword ptr i27 ;转psp:0
tail ends end
lst含
0015 0000 ---- R i27 dw 0,@data
0000 tail segment
.startup
0017 mov ax,2572h 002F call dword ptr i27
exe文件头
0100 4D 5A 73 00 02 00 02 00-20 00 00 00 FF FF 02 00 0110 20 00 00 00 00 00 04 00-1E 00 00 00 01 00 17 00 0120 00 00 01 00 04 00
重定位表,第1项,矢i27的@data,第2项,矢17字节长的.startup第1字节
00 BA0000 MOV DX,0000 ;运行时,字节1,2,加mcb所占节号及11h,制DS 03 8EDA MOV DS,DX 05 8CD3 MOV BX,SS ;外壳,已加好mcb所占节号及13h到SS 07 2BDA SUB BX,DX ;13h-11h,差值2节,合20h字节 09 D1E3 SHL BX,1 0B D1E3 SHL BX,1 0D D1E3 SHL BX,1 0F D1E3 SHL BX,1 11 FA CLI ;防来中断 12 8ED2 MOV SS,DX ; 14 03E3 ADD SP,BX ;本模式,移栈段环境到数据段高端 16 FB STI
(10.1) DEBUG t.exe实例
栈初始前,DS矢psp,SS已加好,SP=exe头偏移10,11字节值20h:
AX=0000 BX=0000 CX=0073 DX=0000 SP=0020 BP=0000 SI=0000 DI=0000 DS=188B ES=188B SS=189D CS=189F IP=0000
初始后,DS,SS矢@data,SP多加栈容20h字节:
AX=0000 BX=0020 CX=0073 DX=189B SP=0040 BP=0000 SI=0000 DI=0000 DS=189B ES=188B SS=189B CS=189F IP=0017
|